0


Stripe Web 购买集成

图片被吞了可以来这里看:https://juejin.cn/post/7346388511338381364

1. 准备事项

  • Stripe 账号
  • 域名以及配套的网站
  • Stripe 账号付款信息
  • 公钥和私钥

2. 配置产品以及价格

可以通过 API 或者 Stripe 管理后台来进行配置

产品:就是商品,只需要配置一个名称和一个类型(用于计算税额)

image.png

价格:价格有定期和一次性两种收费方式,定期其实就是订阅。价格实体非常灵活,适合多种场景,一般就使用固定费率的一次性付款和定期付款。

image.png

3. 设计一下流程

image.png

4. 代码集成

4.1 依赖导入

stripe/stripe-java: Java library for the Stripe API. (github.com)

<dependency><groupId>com.stripe</groupId><artifactId>stripe-java</artifactId><version>23.3.0</version></dependency>

4.2 配置

# 公钥
stripe.key=pk_test_51Nxxxx
# 私钥
stripe.secret=sk_test_51xxxx
# webhook 密钥签名
stripe.endpoint_secret=whsec_Tcxxxx
@Data@Configuration@ConfigurationProperties(prefix ="stripe")publicclassStripeConfig{privateString key;privateString secret;privateString endpointSecret;@BeanpublicStripeClientstripeClient(){returnnewStripeClient(secret);}}

4.3 创建收银

Stripe 中有两种方式能进行收款,

Stripe-hosted page

Embedded form

Stripe-hosted page

:指的是收费的时候跳转到 Stripe 提供的一个收银台页面进行付款。

Embedded form

:则是需要高度自定义页面的产品使用,或者是客户端。

文档:Stripe Checkout | Stripe 文档

Demo: docs.stripe.com/checkout/quickstart

Web 端一般使用

Stripe-hosted page

来简化开发,像

ChatGPT

也是使用这种方式。

image.png

后端创建收银台

publicCheckoutCreateResultcreate(CheckoutCreateRequest request){// 查询或者创建客户String customerId =queryOrCreateCustomer();// 查询价格idString priceId =queryPrice();// 构建成功URL和取消URLUriComponents successUrl =UriComponentsBuilder.fromHttpUrl(request.getSuccessUrl()).queryParam("checkout_id", checkoutId).queryParam("receipt","{CHECKOUT_SESSION_ID}")// 模板变量 https://stripe.com/docs/payments/checkout/custom-success-page#modify-success-url.build();UriComponents cancelUrl =UriComponentsBuilder.fromHttpUrl(request.getCancelUrl()).queryParam("checkout_id", checkoutId).build();// 创建checkout 收银台SessionCreateParams.Builder builder =SessionCreateParams.builder().setSuccessUrl(successUrl.toUriString()).setCancelUrl(cancelUrl.toUriString())// 指定付款用户.setCustomer(customerId)// 自动扣税.setAutomaticTax(SessionCreateParams.AutomaticTax.builder().setEnabled(false).build())// 购买项目:和订单明细类似.addLineItem(SessionCreateParams.LineItem.builder()// 数量.setQuantity(request.getCount().longValue())// 价格.setPrice(priceId).build())// 元数据:额外附加的数据。 webhook 通知的时候可以取出来.putAllMetadata(ImmutableMap.of(MetaDataKey.CHECKOUT_ID, checkoutId,MetaDataKey.APP_ID, request.getAppId()))// 是否允许优惠码.setAllowPromotionCodes(Boolean.TRUE);if(productPrice.getPriceType()==PriceTypeEnum.RECURRING){// 定期价格,最后会创建订阅对象。可以为付款成功后生成的订阅对象设置一些数据
        builder.setMode(SessionCreateParams.Mode.SUBSCRIPTION).setSubscriptionData(// 试用期SessionCreateParams.SubscriptionData.builder().putMetadata(MetaDataKey.APP_ID, request.getAppId()).build());}else{// 一次性价格,最后会创建付款对象。可以为付款成功后生成的付款对象设置一些数据
        builder.setMode(SessionCreateParams.Mode.PAYMENT).setPaymentIntentData(SessionCreateParams.PaymentIntentData.builder().putMetadata(MetaDataKey.APP_ID, request.getAppId()).build());}SessionCreateParams params = builder.build();/*.addDiscount( // 优惠券
                    SessionCreateParams.Discount.builder()
                            .setCoupon("bBfCjIMt")
                            .build())*/Session session =null;try{
        session = stripeClient.checkout().sessions().create(params);}catch(StripeException e){
        log.error("failed to create checkout session. {}, {}, {}", request, customerId, priceId, e);thrownewRuntimeException("failed to create checkout session: "+ e.getMessage());}returnnewCheckoutCreateThirdResult()// checkout session 的 id.setId(session.getId())// 可供用户进行付款的页面链接,前端直接打开即可跳转到Stripe.setTokenThird(session.getUrl());}

4.4 完成收银

4.4.1 前端提交

用户付款完成后,Stripe 会将页面重定向到创建

Checkout Session

时设置的

success_url


页面可以从URL中获取到订单id和sessionId来进一步调用后端接口完成收银。

4.4.2 接收 Webhook

用户付款完成后,Stripe 的后台还会将对应的事件通过

WebHook

的方式 POST 我们预先提供的接口。

第一步,先提供一个 Webhook 回调接口

本地测试的方式不是很友好,可以使用内网穿透工具将请求转到本地来进行调试

@RestController@Slf4jpublicclassWebhookController{@ResourceprivateStripeConfig stripeConfig;@ResourceprivateList<WebhookHandler> webhookHandlers;@PostMapping("/webhook")publicObjecthandle(@RequestHeader("Stripe-Signature")String sigHeader,@RequestBodyString payload){
        log.info("stripe webhook payload: {}", payload);returnwebhook(payload, sigHeader, stripeConfig.endpointSecret());}privateObjectwebhook(String payload,String sigHeader,String endpointSecret){Event event;try{
            event =Webhook.constructEvent(payload, sigHeader, endpointSecret);StripeEventType stripeEventType =StripeEventType.convert(event.getType());
            webhookHandlers.stream().filter(webhookHandler -> webhookHandler.supports(stripeEventType)).findFirst().get().handle(event);}catch(Exception e){
            log.error("failed to handle webhook event. {}, {}", sigHeader, payload, e);returnResponseEntity.status(500).body(e.getMessage());}returnResponseEntity.ok().body("OK");}}

Stripe 事件枚举

publicenumStripeEventTypeimplementsEnumBase{// 收银完成  CHECKOUT_SESSION_COMPLETED("checkout.session.completed"),// 退款  CHARGE_REFUNDED("charge.refunded"),IGNORED("");privatefinalString message;StripeEventType(String message){this.message = message;}publicstaticStripeEventTypeconvert(String message){for(StripeEventType value :StripeEventType.values()){if(StringUtils.equals(value.message(), message)){return value;}}returnIGNORED;}@OverridepublicStringmessage(){returnthis.message;}@OverridepublicNumbervalue(){returnnull;}}

Webhook 处理器

publicinterfaceWebhookHandler{booleansupports(StripeEventType stripeEventType);voidhandle(Event event)throwsEventDataObjectDeserializationException;}publicabstractclassWebhookHandlerBase<T>implementsWebhookHandler{@SuppressWarnings("unchecked")@Overridepublicvoidhandle(Event event)throwsEventDataObjectDeserializationException{EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer();StripeObject stripeObject = dataObjectDeserializer.deserializeUnsafe();handle((T) stripeObject);}publicabstractvoidhandle(T stripeObject);}@Component@Slf4jpublicclassWebhookHandlerDefaultImplimplementsWebhookHandler{@Overridepublicbooleansupports(StripeEventType stripeEventType){return stripeEventType.equals(StripeEventType.IGNORED);}@Overridepublicvoidhandle(Event event){  
        log.info("ignored event: {} {}", event.getType(), event.toJson());}}@Component@Slf4jpublicclassWebhookHandlerCheckoutSessionCompletedImplextendsWebhookHandlerBase<Session>{@Overridepublicbooleansupports(StripeEventType stripeEventType){return stripeEventType.equals(StripeEventType.CHECKOUT_SESSION_COMPLETED);}@Overridepublicvoidhandle(Session session){// 完成收银}}@Component@Slf4jpublicclassWebhookHandlerChargeRefundImplextendsWebhookHandlerBase<Charge>{@Overridepublicbooleansupports(StripeEventType stripeEventType){return stripeEventType.equals(StripeEventType.CHARGE_REFUNDED);}@Overridepublicvoidhandle(Charge charge){// 订单退款}}

配置 Stripe Webhook

管理平台 – FeloTranslator – Stripe [Test]

image.png

4.4.3 完成收银

步骤流程:

  1. 判断对应的订单是否存在
  2. 订单所有者
  3. 对应的Stripe checkout session 状态是否正常
  4. 订单完成
  5. 发送订单完成事件
  6. 事件订阅者处理后续流程
protectedSessioncheckCheckoutSession(String sessionId){// 查询是否完成Session session =null;try{
        session = stripeClient.checkout().sessions().retrieve(sessionId);}catch(StripeException e){
        log.error("failed to query checkout session. {}", sessionId, e);thrownewRuntimeException("failed to query checkout session:"+ sessionId);}// https://stripe.com/docs/api/checkout/sessions/object#checkout_session_object-payment_statusString status = session.getPaymentStatus();if(StringUtils.notEquals(status,"paid")){thrownewRuntimeException("Checkout has no completed: "+ status);}return session;}

Ref

Documentation | Stripe 文档

Stripe-hosted page | Stripe 文档

stripe/stripe-java: Java library for the Stripe API. (github.com)

Stripe API Reference

标签: 支付 stripe 收费

本文转载自: https://blog.csdn.net/mkii_hong/article/details/136744621
版权归原作者 karlhong_weihao 所有, 如有侵权,请联系我们删除。

“Stripe Web 购买集成”的评论:

还没有评论