SDK 订阅支付
API 请求参数
重要提示
通过 SDK 集成创建订阅交易的关键区别:
设置
subProductType: 'SUBSCRIBE'
(而非DIRECT
或TOKEN
)包含
subscription
参数和订阅配置详情
订阅类型
Onerway 支持两种订阅管理模式:
托管订阅 (selfExecute: "1"
) 由 Onerway 全面管理:
- 基于频率设置的自动周期性付款
- 在关键订阅事件时通知客户
- 为最终用户提供订阅管理门户
- 支持试用期
订阅参数详情
subscription
参数是包含以下字段的 JSON 字符串:
托管订阅
Name | Type | Length | Required | Description |
---|---|---|---|---|
requestType | String | 1 | Yes | Subscription request type, please refer to RequestTypeEnum |
merchantCustId | String | 40 | Yes | Unique identifier for the user in the merchant's system, used to associate the user with subscription information. This |
productName | String | 256 | Conditional | Subscription product name, used to distinguish different subscription services or plans. If not provided, each user can only create one subscription; if provided, the same user can subscribe to multiple different products. |
frequencyType | String | 1 | Conditional | Subscription cycle unit. See FrequencyTypeEnum |
frequencyPoint | String | 2 | Conditional | Subscription cycle value for managed subscriptizons. |
cycleCount | int | 256 | Conditional | Number of cycles, supports |
expireDate | String | 10 | Conditional | Subscription termination date, format is |
mode | String | 1 | Conditional | Subscription authorization mode. See SubscriptionModeEnum |
selfExecute | String | 1 | Yes | Subscription billing mode. See SubscriptionExecuteModeEnum |
bindCard | String | 300 | No | Subscription credit card binding option. See SubscriptionBindCardEnum |
trialFromPlan | String | 256 | Conditional | Trial period billing mode. See SubscriptionTrialModeEnum |
trialDays | int | 256 | Conditional | Trial period days, supports range from |
trialEnd | String | 256 | Conditional | Trial period end date, format is |
notificationEmail | String | 256 | No | Buyer's email address for subscription notifications (including successful subscription billing, subscription cancellation, and other notifications). |
metaData | String | 2048 | No | Merchant custom information, used to store business data. Supports JSON format, returned as-is when querying subscriptions. |
代码示例
// 试用首购 - 每天扣款一次,共30天,前3天为试用期且包含在订阅计划内,试用结束后自动扣款
"subscription": "{\"requestType\":\"0\",\"merchantCustId\":\"custId_1640247522000\",\"productName\":\"自动扣30天-试用3天(含)-1天1次\",\"frequencyType\":\"D\",\"frequencyPoint\":\"1\",\"cycleCount\":30,\"selfExecute\":\"1\",\"trialFromPlan\":\"1\",\"trialDays\":3,\"notificationEmail\":\"abel.wang@onerway.com\"}"
2
3
// 试用首购 - 每天扣款一次,共33天,前3天为试用期且不包含在订阅计划内,试用结束后自动扣款
"subscription": "{\"requestType\":\"0\",\"merchantCustId\":\"custId_1640247522000\",\"productName\":\"自动扣30天-试用1天(不含)-1天1次\",\"frequencyType\":\"D\",\"frequencyPoint\":\"1\",\"cycleCount\":30,\"selfExecute\":\"1\",\"trialFromPlan\":\"0\",\"trialEnd\":\"2024-11-07\",\"notificationEmail\":\"abel.wang@onerway.com\"}"
2
3
// 非试用首购 - 每天扣款一次直到指定过期日期(2024-12-04),无试用期,立即开始自动扣款
"subscription": "{\"requestType\":\"0\",\"merchantCustId\":\"custId_1640247522099\",\"productName\":\"自动扣30天-无试用-1天1次-到期时间6\",\"frequencyType\":\"D\",\"frequencyPoint\":\"1\",\"expireDate\":\"2024-12-04\",\"selfExecute\":\"1\",\"notificationEmail\":\"abel.wang@onerway.com\"}"
2
3
// 仅签约模式 - 每月扣款一次,签约但不立即扣款
"subscription": "{\"requestType\":\"0\",\"merchantCustId\":\"custId_1640247522100\",\"productName\":\"仅签约-1月1次\",\"frequencyType\":\"M\",\"frequencyPoint\":\"1\",\"expireDate\":\"2025-12-04\",\"selfExecute\":\"1\",\"notificationEmail\":\"abel.wang@onerway.com\",\"mode\":\"1\"}"
2
3
// 升降级 - 每天扣款一次,共30天,立即升级,补差价30
"subscription": "{\"requestType\":\"2\",\"tokenId\":\"tokenId_xxx\",\"contractId\":\"contractId_xxx\",\"productName\":\"订阅升级-立即生效并补差价\",\"proration\":\"30\",\"merchantCustId\":\"custId_xxx\"}"
2
3
自主管理订阅
Name | Type | Length | Required | Description |
---|---|---|---|---|
requestType | String | 1 | Yes | Subscription request type: Fixed value |
merchantCustId | String | 40 | Yes | Unique identifier for the user in the merchant's system, used to associate the user with subscription information. This ID will be used for subscription creation, query, and management. Please ensure its uniqueness in your system. |
productName | String | 256 | Conditional | Subscription product name, used to distinguish different subscription services or plans. If not provided, each user can only create one subscription; if provided, the same user can subscribe to multiple different products. |
frequencyType | String | 1 | Yes | Subscription cycle unit: |
frequencyPoint | String | 2 | Yes | Suggested subscription cycle value, indicating how many days between billings. |
expireDate | String | 10 | Yes | Subscription termination date, format is |
mode | String | 1 | Yes | Subscription authorization mode. See SubscriptionModeEnum |
selfExecute | String | 1 | Yes | Subscription billing mode. See SubscriptionExecuteModeEnum |
代码示例
// 自主管理订阅 - 仅签约模式,不立即扣款
// 适用于微信、DANA、GCash 或 TOUCH_GO_EWALLET 等本地支付方式
"subscription": "{\"requestType\":\"0\",\"merchantCustId\":\"custId_xxx\",\"productName\":\"自主管理-仅签约\",\"frequencyType\":\"D\",\"frequencyPoint\":\"1\",\"expireDate\":\"2025-01-01\",\"selfExecute\":\"2\",\"mode\":\"1\"}"
2
3
4
// 自主管理订阅 - 立即扣款模式,立即扣款
// 通常用于信用卡支付或某些本地支付方式
"subscription": "{\"requestType\":\"0\",\"merchantCustId\":\"custId_xxx\",\"productName\":\"自主管理-立即扣款\",\"frequencyType\":\"D\",\"frequencyPoint\":\"1\",\"expireDate\":\"2025-01-01\",\"selfExecute\":\"2\",\"mode\":\"2\"}"
2
3
4
// 自主管理订阅 - 续期扣款,使用现有订阅合同发起新一期扣款
// 使用从初始订阅 webhook 收到的 contractId 和 tokenId
"subscription": "{\"requestType\":\"1\",\"contractId\":\"contractId_xxx\",\"tokenId\":\"tokenId_xxx\",\"merchantCustId\":\"custId_xxx\"}"
2
3
4
// DANA、微信、GCash 或 TOUCH_GO_EWALLET 订阅示例
// 零金额初始交易来创建订阅合同
"lpmsInfo": "{\"lpmsType\":\"DANA\",\"iBan\":\"DE89370400440532013000\"}",
"productType": "LPMS",
"subProductType": "SUBSCRIBE",
"subscription": "{\"requestType\":\"0\",\"merchantCustId\":\"custId_xxx\",\"frequencyType\":\"D\",\"frequencyPoint\":\"1\",\"expireDate\":\"2025-01-01\",\"selfExecute\":\"2\",\"mode\":\"1\"}"
2
3
4
5
6
7
SDK 集成流程
SDK 订阅的集成流程遵循与标准 支付意图创建 相同的三个阶段:
- 订阅意图创建:在您的服务器上创建订阅意图,使用
subProductType: 'SUBSCRIBE'
和subscription
参数 - SDK 支付流程:使用收到的
transactionId
和redirectUrl
初始化 SDK - 订阅结果通知:处理订阅结果和相关通知
重要提示
当使用 SUBSCRIBE
与 bindCard: true
时,系统会创建 两个交易:
- 生成
tokenId
的卡绑定交易 - 使用相同
transactionId
(用于初始化 SDK)的实际订阅交易
因此,您将收到 两个独立的 webhook 通知:
- 一个用于卡绑定结果,
txnType: "BIND_CARD"
- 另一个用于订阅支付结果,
txnType: "SALE"
API 使用示例
{
"billingInformation": "{\"address\":\"8016 Breitenberg Forks\",\"city\":\"West Emelie\",\"country\":\"US\",\"email\":\"Kendra61@hotmail.com\",\"firstName\":\"Jillian\",\"identityNumber\":\"97386542147\",\"lastName\":\"Moore\",\"phone\":\"16656382461\",\"postalCode\":\"42189\",\"province\":\"CO\"}",
"merchantCustId": "CustId-T4DJ-E3JJ",
"merchantNo": "800209",
"merchantTxnId": "89ae7689-184b-40d3-a3a5-d6b3eb921ddb",
"merchantTxnTime": "2025-05-05 15:42:05",
"orderAmount": "5",
"orderCurrency": "USD",
"productType": "CARD",
"shippingInformation": "{\"address\":\"540 King's Road\",\"city\":\"East Maziestad\",\"country\":\"US\",\"email\":\"Caitlyn_Graham63@hotmail.com\",\"firstName\":\"Julianne\",\"identityNumber\":\"35516907344\",\"lastName\":\"Hackett\",\"phone\":\"13290664062\",\"postalCode\":\"22307-4261\",\"province\":\"CO\"}",
"sign": "f00aecf28e9a58c6d3c1b106a3b5ea4d33690468f851657c08089f4453c0d790",
"subProductType": "SUBSCRIBE", // 设置为 SUBSCRIBE 用于订阅
"subscription": "{\"requestType\":\"0\",\"selfExecute\":\"1\",\"productName\":\"Fantastic Cotton Soap\",\"frequencyType\":\"D\",\"bindCard\":true,\"merchantCustId\":\"CustId-J340-UE29\",\"frequencyPoint\":\"31\",\"notificationEmail\":\"Adam62@hotmail.com\",\"metaData\":null,\"trialFromPlan\":\"0\",\"cycleCount\":54,\"trialDays\":213}", // 订阅配置
"txnOrderMsg": "{\"accept\":\"*/*\",\"appId\":\"1727880846378401792\",\"colorDepth\":\"32\",\"contentLength\":\"16384\",\"javaEnabled\":true,\"language\":\"en-US\",\"products\":\"[{\\\"currency\\\":\\\"USD\\\",\\\"name\\\":\\\"kiwi fruit\\\",\\\"num\\\":\\\"4\\\",\\\"price\\\":\\\"377.09\\\",\\\"type\\\":\\\"labore incididunt nulla in adipisicing\\\"},{\\\"currency\\\":\\\"USD\\\",\\\"name\\\":\\\"pear\\\",\\\"num\\\":\\\"1\\\",\\\"price\\\":\\\"952.38\\\",\\\"type\\\":\\\"cupidatat\\\"}]\",\"returnUrl\":\"https://docs.onerway.com/\",\"notifyUrl\":\"https://docs.onerway.com/apis\",\"screenHeight\":\"932\",\"screenWidth\":\"320\",\"timeZoneOffset\":\"-720\",\"transactionIp\":\"71.26.248.147\",\"userAgent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\"}",
"txnType": "SALE"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"respCode": "20000",
"respMsg": "Success",
"data": {
"transactionId": "1919296539923447808", // 用于 SDK 初始化
"responseTime": "2025-05-05 15:42:08",
"txnTime": null,
"txnTimeZone": "+08:00",
"orderAmount": "5.00",
"orderCurrency": "USD",
"txnAmount": null,
"txnCurrency": null,
"status": "U",
"redirectUrl": "https://sandbox-checkout-sdk.onerway.com", // 用于 SDK 初始化
"contractId": null,
"tokenId": null,
"eci": null,
"periodValue": null,
"codeForm": null,
"presentContext": null,
"actionType": null,
"subscriptionManageUrl": "https://sandbox-myscribe.onerway.com/pages/subscription-list/details?contractId=eyJjb250cmFjdElkIjoxOTE5Mjk2NTQxMDgxMDc1NzEyLCJtZXJjaGFudE5vIjo4MDAyMDksIm1lcmNoYW50Q3VzdElkIjoiQ3VzdElkLUozNDAtVUUyOSIsIm5vdGlmaWNhdGlvbkVtYWlsIjpudWxsLCJhcHBJZCI6IjE3Mjc4ODA4NDYzNzg0MDE3OTIifQ==", // 订阅管理页面链接
"sign": "5e77e177056c26cbd53aa75f094d76f014366b25ef652e5e57c9f321c4ce793b"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
SDK 初始化
订阅专用配置选项
虽然大多数配置选项与 标准支付意图创建 相同,但这些选项与订阅特别相关:
config: {
subProductType: 'SUBSCRIBE', // 订阅必需
}
2
3
通过 API 创建订阅意图后,使用 transactionId
和 redirectUrl
初始化 SDK:
// 导入 Onerway SDK 脚本
const script = document.createElement('script')
script.src = 'https://checkout-sdk.onerway.com/v3/'
document.body.appendChild(script)
// 创建 SDK 容器
const container = document.createElement('div')
container.id = 'onerway_checkout'
document.body.appendChild(container)
// 加载完成后初始化 SDK
script.onload = function () {
const pacypay = new Pacypay(transactionId, {
container: 'onerway_checkout',
locale: 'zh',
environment: 'sandbox',
mode: 'CARD',
redirectUrl: 'REDIRECT_URL_FROM_API',
config: {
subProductType: 'SUBSCRIBE'
// 其他标准配置选项
},
onPaymentCompleted: function (result) {
// 处理订阅完成
const subscriptionResult = result.data
if (result.respCode === '20000') {
// 处理成功响应
if (subscriptionResult.status === 'S') {
// 订阅成功
console.log('订阅成功')
} else if (subscriptionResult.status === 'R') {
// 需要 3DS 验证
console.log('需要 3DS 验证')
window.location.href = subscriptionResult.redirectUrl
}
// 对于托管订阅,存储或向客户展示订阅管理URL
} else {
console.error('订阅失败:', result.respMsg)
}
},
onError: function (error) {
console.error('SDK 错误:', error)
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
SDK 回调响应
客户完成订阅表单后,SDK 将返回订阅结果的回调:
{
"respCode": "20000",
"respMsg": "Success",
"data": {
"transactionId": "1919297495302012928",
"responseTime": "2025-05-05 15:47:10",
"txnTime": "2025-05-05 15:45:56",
"txnTimeZone": "+08:00",
"orderAmount": "300.00",
"orderCurrency": "USD",
"txnAmount": null,
"txnCurrency": null,
"status": "R",
"redirectUrl": "https://sandbox-gw-dmz.onerway.com/3dsSecure/direct/RDT_3DS_DDC_8002091919297799061635073",
"contractId": null,
"tokenId": "",
"eci": null,
"periodValue": null,
"codeForm": null,
"presentContext": null,
"actionType": "RedirectURL",
"subscriptionManageUrl": null,
"sign": null
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Webhook 通知
Onerway 向原始订阅请求中指定的 notifyUrl
发送所有订阅事件的 webhook 通知。这些通知提供订阅状态变更的实时更新。
Webhook 场景
webhook 通知中的 scenarios
字段表示触发通知的特定订阅事件。
订阅场景枚举:
Value | Description |
---|---|
SUBSCRIPTION_INITIAL | Initial subscription purchase - Customer signs up and makes first payment to activate new subscription |
SUBSCRIPTION_RENEWAL | Recurring subscription payment - Billing for ongoing subscription service according to billing cycle |
SUBSCRIPTION_CARD_REPLACEMENT | Payment method update - Customer updates expired or invalid card information for existing subscription |
SUBSCRIPTION_CHANGED | Subscription modification - Customer upgrades, downgrades, or changes subscription plan terms |
SUBSCRIPTION_CANCELED | Subscription termination - Customer or merchant initiates cancellation, stopping future billing |
SUBSCRIPTION_ENDED | Subscription expiration - Natural conclusion when subscription reaches its predetermined end date |
这些场景值帮助您准确识别订阅的不同生命周期阶段,便于实施相应的业务逻辑处理。
对于使用 bindCard: true
的订阅,您将收到两个独立的 webhook 通知:
{
"notifyType": "TXN",
"transactionId": "1919297867005427712", // 卡绑定交易 ID
"txnType": "BIND_CARD", // 交易类型为 BIND_CARD
"merchantNo": "800209",
"responseTime": "2025-05-05 15:47:26",
"txnTime": "2025-05-05 15:47:24",
"txnTimeZone": "+08:00",
"orderAmount": "0.00",
"orderCurrency": "USD",
"status": "S",
"tokenId": "d057f8e7c1f887a78de8849b30224a95af11ce8c5e7b4b24d4abb471af77d845", // 用于未来收费的令牌
"tokenExpireTime": "2099-12-31 23:59:59",
"cardBinCountry": "US",
"reason": "{\"respCode\":\"20000\",\"respMsg\":\"Success\"}",
"sign": "3aac4034f2244330f64d07dd9a9a13fdf66d04883a94191144df4ce439be67b9",
"paymentMethod": "VISA",
"channelRequestId": "8002091919297871069446145"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"notifyType": "TXN",
"transactionId": "1919297867005427712",
"txnType": "SALE", // 交易类型为 SALE
"merchantNo": "800209",
"responseTime": "2025-05-05 15:47:26",
"txnTime": "2025-05-05 15:47:24",
"txnTimeZone": "+08:00",
"orderAmount": "5.00", // 实际订阅金额
"orderCurrency": "USD",
"status": "S",
"contractId": "1919297867047370752", // 订阅合同 ID
"tokenId": "d057f8e7c1f887a78de8849b30224a95af11ce8c5e7b4b24d4abb471af77d845", // 用于未来收费的令牌
"cardBinCountry": "US",
"reason": "{\"respCode\":\"20000\",\"respMsg\":\"Success\"}",
"sign": "3aac4034f2244330f64d07dd9a9a13fdf66d04883a94191144df4ce439be67b9",
"paymentMethod": "VISA",
"subscriptionManageUrl": "https://sandbox-myscribe.onerway.com/pages/subscription-list/details?contractId=eyJjb250cmFjdElkIjoxOTE5Mjk3ODY3MDQ3MzcwNzUyLCJtZXJjaGFudE5vIjo4MDAyMDksIm1lcmNoYW50Q3VzdElkIjoiQ3VzdElkLUozNDAtVUUyOSIsIm5vdGlmaWNhdGlvbkVtYWlsIjpudWxsLCJhcHBJZCI6IjE3Mjc4ODA4NDYzNzg0MDE3OTIifQ==", // 管理页面链接
"subscriptionStatus": "active", // 订阅状态
"scenarios": "SUBSCRIPTION_INITIAL", // 订阅场景
"channelRequestId": "8002091919297871069446145"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
有关订阅交易期间收到的所有 webhook 通知字段的完整描述,包括订阅特定字段如 contractId
、tokenId
、subscriptionStatus
和 scenarios
,请参阅 Webhook 通知文档。
订阅生命周期管理
对于托管订阅:
- Onerway 根据定义的计划自动处理所有周期性账单
- 使用 订阅扣款 API 进行订阅更新/降级
- 客户可以通过
subscriptionManageUrl
管理其订阅 - 订阅生命周期事件(试用、续期、到期)自动处理
- 每个账单事件和状态变更都会发送 webhook 通知
实施最佳实践
订阅配置:
- 对于订阅操作始终使用
subProductType: 'SUBSCRIBE'
- 选择
selfExecute: 1
用于托管,或selfExecute: 2
用于自主管理订阅 - 对于周期性卡支付,考虑启用
bindCard: true
进行单独的卡令牌化
- 对于订阅操作始终使用
数据存储:
- 安全存储成功订阅响应中的
contractId
和tokenId
- 对于自主管理订阅,维护自己的账单计划系统
- 保存
subscriptionManageUrl
以为客户提供订阅管理访问
- 安全存储成功订阅响应中的
Webhook 处理:
- 处理所有订阅 webhook 通知以跟踪订阅状态变更
- 当使用
bindCard: true
时,处理绑定和订阅 webhook - 实施幂等 webhook 处理以处理潜在的重复通知
- 验证 webhook 签名以确保通知的真实性
3DS 身份验证处理:
- 正确实施初始订阅交易的 3DS 重定向流程
- 在将客户重定向到 3DS 验证 URL 之前存储
transactionId
- 在请求中配置有效的
returnUrl
以处理 3DS 验证后的客户返回 - 设计返回页面以处理成功和失败的 3DS 验证
- 等待 webhook 通知确认最终交易状态
用户体验考虑:
- 对于自主管理订阅:
- 开发自定义订阅管理界面
- 为即将到来的收费实施自定义邮件通知系统
- 提供关于订阅条款和账单周期的清晰信息
- 设计直观的界面供客户更新付款方式或取消订阅
- 对于自主管理订阅:
试用期:
- 使用
trialFromPlan
或trialEnd
参数配置试用 - 通过 webhook 通知监控试用到期
- 对于托管订阅,从试用到付费的转换是自动的
- 使用
订阅管理:
- 对于托管订阅,使用 订阅扣款 API 进行计划更新
- 对于自主管理订阅,跟踪账单周期并使用订阅扣款 API 进行续期
- 为失败的续期尝试实施适当的错误处理和重试逻辑