官方文档引导 https://stripe.com/docs/billing/subscriptions/overview
1. 几个核心对象
- product
产品的描述。
- plan/price
产品的计划,价格。即计费周期扣费相关定义。
- payment_method
支付方式,包含卡号,cvc,过期年月、账单邮箱电话地址信息。
- customer
订阅产品计划和支付产品的顾客。创建时候可以设置payment_method,将支付方式 attach 到顾客里面,并且设置顾客的默认支付方式。
创建时候,会校验支付方式的格式、过期等校验
- subscription
订阅,包含 plan、customer、payment_method。当创建成功时,会订阅会创建 invoice 及 payment_intent 对象。
- invoice
发票,订阅每次需要扣款时,都会开一张发票。但发票可能因为支付成功是否,有不同的状态
支付成功和失败会发送对应的webhook事件
- payment_intent
支付意向,即发票的尝试付款信息。
什么是尝试付款信息?
包含支付的成功是否状态,失败时需要进行哪些步骤才能付款成功。
如有些地区及银行卡,需要3d验证才能支付成功,如信用卡无效需要收集新的支付方式才能支付成功,此类信息会在支付意向中体现
2. 对接流程
2.1 两个参数
# 业务客户端使用,如前端js,可公开暴露,如进行支付时使用
STRIPE_PUBLISHABLE_KEY=pk_test_abc
# 业务服务端使用,如java,不可公开使用
STRIPE_SECRET_KEY=sk_test_edf
2.2 后端sdk的使用
- 2.2.1 maven依赖,官方sdk
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>20.90.0</version>
</dependency>
- 2.2.1 Java 后端调用样例
Stripe.apiKey = NacosConfig.STRIPE_PUBLISHABLE_KEY;
// 创建产品
ProductCreateParams productCreateParams = ProductCreateParams
.builder()
.setActive(true)
.setName("Product- MyyShoper Basic")
.build();
Product product = Product.create(productCreateParams);
2.3 前端sdk的使用
- 2.3.1 依赖
<html>
<head>
<script src="https://js.stripe.com/v3/"></script>
</head>
<body>
demo
</body>
</html>
- 2.3.2 前端调用样例
// STRIPE_PUBLISHABLE_KEY 是后端返给前端的配置参数
var stripe = Stripe('${STRIPE_PUBLISHABLE_KEY}');
const confirmToPay = () => {
// PAYMENT_INTEND_CLIENT_SECRET 是后端返给前端订阅时支付意向客户端密钥
stripe.confirmCardPayment('${PAYMENT_INTEND_CLIENT_SECRET}')
.then((result) => {
if(result.error) {
// 支付失败,失败原因详细信息在此:${result.error.message}
} else {
// 支付成功跳转页面
}
});
}
2.4 创建订阅
- 2.4.1 创建支付方式
请求报文:
{
"payment_method":{
"type":"card",
"card":{
"number":"4242xxxx4242",
"expMonth":"12",
"expYear":"2023",
"cvc":"123"
},
"billingDetails":{
"email":"930092177@qq.com",
"name":"liuzhicong",
"phone":"+8618826108955",
"address":{
"country":"CN",
"state":"Guangdong",
"city":"Shenzhen",
"postCode":"510006",
"line1":"",
"line2":""
}
}
}
}
响应报文,返回支付方式实体,主要是ID,如:pm_xxx
- 2.4.2 创建客户
请求报文:
{
"customer":{
"email":"930092177@qq.com",
"description":"",
// warn:attach to customer
"paymentMethod":"pm_xxx",
"invoiceSettings":{
// warn:default payment method
"defaultPaymentMethod":"pm_xxx"
}
}
}
创建时候,会校验支付方式的格式、过期等校验
响应报文,返回顾客实体,主要是ID,如:cus_xxx
- 2.4.3 创建商品及计划
请求报文:
// product
{
"product":{
"name":"Myshipping - MyyShoper Basic"
}
}
// plan
{
"plan":{
"product":"prod_xxx",
"amount":900,
"currency":"USD",
"interval":"MONTH"
}
}
响应报文,返回商品及价格的实体,主要是ID,如:商品ID=prod_xxx
及计划ID=plan_xxx
- 2.4.4 创建订阅
请求报文:
{
"subscription":{
"customer":"cus_xxx", // 哪个顾客
"item":{
"price":"plan_xxx" // 哪个商品价格
},
"defaultPaymentMethod":"pm_xxx" // 支付方式
}
}
2.5 创建订阅后响应处理
- 2.5.1 订阅成功及成功扣费
{
// @subscription
"object": "subscription",
"id": "sub_1KBCdfAQe605kX9nQXAmTXzg",
"status": "active", // ********* subscription status = active *********
"latest_invoice": {
"id": "in_1KBCdfAQe605kX9nSKDY3MEB",
"expandedObject":{
// @invoice
"object": "invoice",
"id": "in_1KBCdfAQe605kX9nSKDY3MEB",
"status": "paid", // ********* latest_invoice status = paid *********
"paid": true,
"hosted_invoice_url": "https://invoice.stripe.com/i/acct_1K945EAQe605kX9n/test_YWNjdF8xSzk0NUVBUWU2MDVrWDluLF9LcXVTUjZyb3dBY0xvUGRteVRBaVdyRWRmOHlyQVdG01003h9J8Klc",
"invoice_pdf": "https://pay.stripe.com/invoice/acct_1K945EAQe605kX9n/test_YWNjdF8xSzk0NUVBUWU2MDVrWDluLF9LcXVTUjZyb3dBY0xvUGRteVRBaVdyRWRmOHlyQVdG01003h9J8Klc/pdf",
"payment_intent": {
"id": "pi_3KBCdgAQe605kX9n1QEtEle3",
"expandedObject": {
// @payment_intent
"object": "payment_intent",
"id": "pi_3KBCdgAQe605kX9n1QEtEle3",
"status": "succeeded", // ********* payment_intent status = succeeded *********
// ********* next_action not existed *********
"client_secret": "pi_3KBCdgAQe605kX9n1QEtEle3_secret_EHrdDAbfcSg7b4g4GeyaCwkyD", // ********* client_secret existed *********
"payment_method": {
"id": "pm_1KBCdSAQe605kX9n33fS7Xmk"
},
"invoice": {
"id": "in_1KBCdfAQe605kX9nSKDY3MEB"
},
}
}
}
}
}
参数小结:
subscription.status = active
latest_invoice.status = paid
latest_invoice.paid = true
latest_invoice.payment_intent status = succeeded
latest_invoice.payment_intent.next_action not existed
latest_invoice.payment_intent.client_secret = 'pi_xxxxx'
后端响应前端订阅成功,前端跳转到订阅成功页面。此后异步等待扣费成功事件,激活会员套餐
- 2.5.2 订阅失败,需要3d验证
{
// @subscription
"object": "subscription",
"id": "in_1KBC5mAQe605kX9nHGSPFOU3",
"status": "incomplete", // ********* subscription status = incomplete *********
"latest_invoice": {
"id": "in_1KBC5mAQe605kX9nHGSPFOU3",
"expandedObject":{
// @invoice
"object": "invoice",
"id": "in_1KBC5mAQe605kX9nHGSPFOU3",
"status": "open", // ********* latest_invoice status = open *********
"paid": false,
"hosted_invoice_url": "https://invoice.stripe.com/i/acct_1K945EAQe605kX9n/test_YWNjdF8xSzk0NUVBUWU2MDVrWDluLF9LcXR0TG1HR3E0b3c2cWZ2NTJFVjZ3YzdRYnlPZDZZ0100CALfGIiN",
"invoice_pdf": "https://pay.stripe.com/invoice/acct_1K945EAQe605kX9n/test_YWNjdF8xSzk0NUVBUWU2MDVrWDluLF9LcXR0TG1HR3E0b3c2cWZ2NTJFVjZ3YzdRYnlPZDZZ0100CALfGIiN/pdf",
"payment_intent": {
"id": "pi_3KBC5mAQe605kX9n0y0Zkhvo",
"expandedObject": {
// @payment_intent
"object": "payment_intent",
"id": "pi_3KBC5mAQe605kX9n0y0Zkhvo",
"status": "requires_action", // ********* payment_intent status = requires_action *********
"next_action": { // ********* next_action existed *********
// redirect_to_url
// ********* use_stripe_sdk *********
// alipay_handle_redirect
// oxxo_display_details
// verify_with_microdeposits
"type": "use_stripe_sdk",
"use_stripe_sdk": {
"type": "three_d_secure_redirect",
"stripe_js": "https://hooks.stripe.com/redirect/authenticate/src_1KBC5mAQe605kX9nhsX97k6W?client_secret=src_client_secret_mMxXglz7tm3C204yVDSUWEwy&source_redirect_slug=test_YWNjdF8xSzk0NUVBUWU2MDVrWDluLF9LcXR0QTIyMDd6NjZFeE82TmRNdGhhanZLeWExQUhR0100NYxq9Xrr",
"source": "src_1KBC5mAQe605kX9nhsX97k6W"
}
},
"client_secret": "pi_3KBC5mAQe605kX9n0y0Zkhvo_secret_za8UgrcOaqHKLfAzDqRiPdAF6", // ********* client_secret existed *********
"payment_method": {
"id": "pm_1KBC5gAQe605kX9nrXLIFnE6"
},
"invoice": {
"id": "in_1KBC5mAQe605kX9nHGSPFOU3"
},
}
}
},
}
}
参数小结:
subscription.status = incomplete
latest_invoice.status = open
latest_invoice.paid = false
latest_invoice.payment_intent status = requires_action
latest_invoice.payment_intent.next_action.type = use_stripe_sdk
latest_invoice.payment_intent.next_action.use_stripe_sdk={"type": "three_d_secure_redirect"}
latest_invoice.payment_intent.client_secret = 'pi_xxxxx'
后端响应前端订阅失败,包含3d验证所需要的参数。3d验证后,扣费是否成功等是stripe的sdk提示。如果验证成功,前端跳转到订阅成功页面。此后异步等待扣费成功事件,激活会员套餐
后端返回前端核心参数:
{
"client_secret" : "pi_xxxxx",
"stripe_publishable_key" : "pk_test_abc"
}
前端使用这两个参数,配合前端sdk进行3d验证支付,例子在前面章节
(前端sdk文档 https://stripe.com/docs/js 前端官方example https://stripe.dev/elements-examples/)
- 2.5.3 其他订阅失败
后端抛出异常,内容为stripe提示的信息或订阅失败请联系管理员
2.6 几个重要事件回调
- invoice.payment_failed
- invoice.payment_succeeded
- customer.subscription.deleted
2.6.1 invoice.payment_failed
{
"api_version": "2020-08-27",
"created": 1641279084,
"data": {
"object": {
"id": "in_1KE5ukAQe605kX9nqoJjSMcp",
"object": "invoice",
// 首次失败 subscription_create
// 循环扣费失败 subscription_cycle
// 其他 upcoming subscription_threshold manual
"billing_reason": "subscription_cycle",
"created": 1641275430,
"customer": "cus_KqttrF9wjePEcp",
"paid": false,
"payment_intent": "pi_3KE6reAQe605kX9n3H9Iqcv8",
"status": "open",
"subscription": "sub_1KBC5lAQe605kX9nYaJ1uQk9"
}
},
"id": "evt_1KE6rgAQe605kX9ndcD1QB1Y",
"object": "event",
"type": "invoice.payment_failed"
}
后端处理:billing_reason=subscription_cycle,暂停订阅,其他情况不管
2.6.2 invoice.payment_succeeded
{
"api_version": "2020-08-27",
"created": 1641281392,
"data": {
"object": {
"id": "in_1KE6ViAQe605kX9nlQATpkHX",
"object": "invoice",
// 首次成功 subscription_create
// 循环扣费成功 subscription_cycle
// 其他 upcoming subscription_threshold manual
"billing_reason": "subscription_cycle",
"created": 1641277722,
"customer": "cus_KquSV42aaf47ct",
"paid": true,
"status": "paid",
"subscription": "sub_1KBCdfAQe605kX9nQXAmTXzg",
}
},
"id": "evt_1KE7SuAQe605kX9nDWz3uXhd",
"object": "event",
"type": "invoice.payment_succeeded"
}
后端处理:billing_reason=subscription_create,进行激活及增加权益套餐时长。其他情况,如 subscription_cycle 等,全部增加权益套餐时长
2.6.3 customer.subscription.deleted
{
"api_version": "2020-08-27",
"created": 1641367955,
"data": {
"object": {
"id": "sub_1KByy7AQe605kX9nMfEnfecD",
"object": "subscription",
"canceled_at": 1641367955,
"created": 1640772075,
"customer": "cus_KriO4LMmXY3VZV",
"default_payment_method": "pm_1KByy5AQe605kX9ndTyC3n3h",
"latest_invoice": "in_1KE9sWAQe605kX9n0aSNY3FJ",
"plan": {
"id": "plan_KriO4rMF7AzYiy",
"object": "plan",
"active": true,
"product": "prod_KriOmXENCXm9qL"
},
"status": "canceled"
}
},
"id": "evt_1KETz6AQe605kX9nL7suqsNK",
"object": "event",
"type": "customer.subscription.deleted"
}
后端处理:暂停套餐
3. 重要实体在状态变化情况
3.1 首次订阅支付
PAYMENT OUTCOME | PAYMENTINTENT STATUS | INVOICE STATUS | SUBSCRIPTION STATUS |
---|---|---|---|
Success | succeeded | paid | active |
Fails because of a card error | requires_payment_method | open | incomplete |
Fails because of authentication | requires_action | open | incomplete |
23 小时后没有收集新的支付方式或通过3d验证进行成功支付
这个订阅的状态被更新为: subscription.status = incomplete_expired
这个订阅的发票状态更新为 invoice.status = void
这个订阅的发票的支付意向状态更新为 payment_intent.status = canceled
3.2 循环扣费失败
webhook回调 | PAYMENTINTENT STATUS | INVOICE STATUS | SUBSCRIPTION STATUS |
---|---|---|---|
invoice.payment_failed | requires_payment_method | open | past_due |
invoice.payment_action_required | requires_action | open | past_due |
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
如有版权疑问交流,请给我留言:oneisall8955@gmail.com
本文永久链接:https://liuzhicong.cn/index.php/study/45.html