订阅续费
无需PCI合规要求
与初始订阅设置不同,订阅续费操作不需要PCI合规,因为:
- 续费操作期间不传输敏感卡数据
- 所有操作都使用安全存储的
tokenId
和contractId
- 卡详细信息在初始订阅过程中已经令牌化
介绍
订阅续费API用于处理现有订阅合同的后续扣款。此API有两个主要用途:
自主管理订阅续费:对于使用自主管理订阅模式的商户,必须调用此API来使用存储的合同和令牌信息处理定期付款。
订阅计划更新:对于托管订阅,此API可用于更新订阅计划(升级/降级)。
本文档涵盖所有集成方法的订阅续费,包括订阅API、收银台和SDK订阅实现。
API请求参数
注意
- 所有
JSON
字段必须在提交前进行字符串化处理 - 嵌套对象必须序列化为
JSON
字符串格式 JSON
字段不能包含未转义的特殊字符JSON
中的数组应该正确格式化JSON
字符串字段示例:
{
"object": "{\"obj-key1\":\"v1\",\"obj-key2\":\"v2\"}",
"complex": "{\"k1\":\"v1\",\"array\":\"[{\\\"obj-key3\\\":\\\"v3\\\",\\\"obj-key4\\\":\\\"v4\\\"}]\"}"
}
2
3
4
Parameter | Type | Length | Required | Signed | Description |
---|---|---|---|---|---|
billingInformation | String | / | Yes | Yes | Transaction billing information in JSON string format. See TransactionAddressfor complete structure. |
merchantCustId | String | 50 | Conditional | Yes | Unique customer identifier in merchant system. |
merchantNo | String | 20 | Yes | Yes | Merchant number assigned by |
merchantTxnId | String | 64 | Yes | Yes | Unique transaction identifier for each customer payment, generated by the merchant system. |
merchantTxnOriginalId | String | 128 | No | Yes | Master transaction ID generated by merchant for grouping related transactions. |
merchantTxnTime | String | / | No | Yes | Transaction timestamp when the merchant initiated the transaction. |
merchantTxnTimeZone | String | 64 | No | Yes | Transaction timezone offset for the merchant transaction time. |
orderAmount | String | 19 | Yes | Yes | Transaction amount in the specified currency, formatted as a decimal string. |
orderCurrency | String | 8 | Yes | Yes | |
productType | String | 16 | Yes | Yes | Payment method category that determines which payment options are available to customers. See ProductTypeEnumfor all available options. |
sign | String | / | Yes | No | Digital signature string for request verification and security. Please refer to Signature for signature generation method. |
subProductType | String | 16 | Yes | Yes | Specific implementation method within the selected product type, defining how the payment is processed. See SubProductTypeEnumfor all available options. |
subscription | String | / | Conditional | Yes | Subscription information required for subscription payments, JSON string format. See Subscription Parameters below. |
txnOrderMsg | String | / | Yes | Yes | Transaction business information in JSON string format. See TxnOrderMsgfor complete structure. |
txnType | String | 16 | Yes | Yes | Transaction type that defines the payment operation to be performed. See TxnTypeEnumfor all available options and detailed usage scenarios. |
订阅参数
subscription
参数是包含以下字段的JSON字符串:
订阅续费流程
对于后续订阅扣款(非初始付款):
- 托管订阅:Onerway根据您的配置自动处理定期付款
- 自主管理订阅:您需要调用订阅续费API来启动续费付款
续费流程使用在初始付款期间建立的订阅contractId
和tokenId
,其中包含所有必要的计费详情和付款方式。
Name | Type | Length | Required | Description |
---|---|---|---|---|
requestType | String | 1 | Yes | Subscription request type. See RequestTypeEnum |
contractId | String | 20 | Yes | Subscription contract ID obtained from initial subscription response. |
tokenId | String | 300 | Yes | Subscription token ID obtained from initial subscription response. |
merchantCustId | String | 40 | No | Unique identifier for the user in the merchant's system, must be consistent with the ID provided during initial subscription. |
TxnOrderMsg
对于订阅续费和更新操作,txnOrderMsg
参数应包括:
Name | Type | Length | Required | Signature | Description |
---|---|---|---|---|---|
returnUrl | String | 256 | Yes | No | Synchronous return address. After payment completion, customer is redirected to this URL |
products | String | 1024 | Yes | No | List of product information purchased by the customer See Productsfor complete structure. |
transactionIp | String | 64 | Conditional | No | Cardholder transaction |
appId | String | 20 | Yes | No | Store |
javaEnabled | Boolean | / | Conditional | No | Whether the cardholder's browser has |
colorDepth | String | 64 | Conditional | No | Cardholder screen color depth, retrieved via: |
screenHeight | String | 64 | Conditional | No | Cardholder's screen resolution, retrieved via: |
screenWidth | String | 64 | Conditional | No | Cardholder's screen resolution, retrieved via: |
timeZoneOffset | String | 64 | Conditional | No | Cardholder browser's time zone, retrieved via: |
accept | String | 2048 | Conditional | No | Cardholder browser's |
userAgent | String | 2048 | Conditional | No | Cardholder's browser type, retrieved via: |
contentLength | String | 64 | Conditional | No | Content length of the cardholder's browser content beyond the headers |
language | String | 64 | Conditional | No | Cardholder's browser language, retrieved via: |
periodValue | String | / | Conditional | No | Installment payment period. |
notifyUrl | String | 256 | Yes | No |
响应参数
Name | Type | Description |
---|---|---|
respCode | String | Response code from |
respMsg | String | Response message from |
data | Object | Response data. Refer to object data |
data
Name | Type | Description |
---|---|---|
transactionId | String | Transaction order number created by |
responseTime | String | Interface response time Format: |
txnTime | String | Transaction completion time Format: |
txnTimeZone | String | Transaction time zone Format: |
orderAmount | String | Order amount |
orderCurrency | String | |
txnAmount | String | Order amount after conversion to settlement currency |
txnCurrency | String | |
status | String | Transaction processing result Refer to TxnStatusEnum |
redirectUrl | String | Redirection URL for 3D Secure verification |
contractId | String | Subscription contract number |
tokenId | String | Payment token |
eci | String | Electronic Commerce Indicator |
periodValue | String | Installment payment number of periods |
codeForm | String | Code form for specific payment methods See CodeFormEnum |
presentContext | String | Context information for presentation layer |
actionType | String | Action type for the transaction See ActionTypeEnum |
subscriptionManageUrl | String | Subscription management URL |
sign | String |
代码片段
// 自主管理订阅 - 仅签约模式,不立即计费
// 适用于本地支付方式如微信、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
订阅续费流程
订阅续费流程根据订阅类型遵循不同的流程:
自主管理订阅续费
在此流程中:
- 商户系统必须跟踪订阅计费计划
- 在每个计费周期,商户发送带有存储的
contractId
和tokenId
的续费请求 - Onerway使用令牌化的卡详细信息处理付款
- 响应包括交易结果和更新的订阅详细信息
- 带有
scenarios=SUBSCRIPTION_RENEWAL
的webhook通知将发送到商户的通知URL
托管订阅更新
在此流程中:
- 商户使用存储的
contractId
和tokenId
启动订阅更新 - 如果
changeMode=1
(立即生效):- Onerway根据当前计费周期的剩余天数计算按比例金额
- 对于升级,处理立即收费;对于降级,开发人员需要调用退款API来处理退款
- 如果
changeMode=2
(下一计费周期):- Onerway更新订阅而不立即收费
- 更改在下一个计费日期生效
- 响应包括更新的订阅详细信息
- 带有
scenarios=SUBSCRIPTION_CHANGED
的webhook通知将发送到商户的通知URL - Onerway继续使用更新的订阅详细信息进行自动计费
API使用示例
Onerway 会将所有订阅事件的 webhook 通知发送到原始订阅请求中指定的 notifyUrl
。这些通知提供订阅状态变更的实时更新。
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 |
这些场景值帮助您准确识别订阅的不同生命周期阶段,便于实施相应的业务逻辑处理。
订阅状态参考
响应和 Webhook 中的 subscriptionStatus
字段表示订阅的当前状态。
订阅状态枚举:
Code | Description |
---|---|
trialing | In trial period |
paymentdue | Payment pending |
active | Active subscription |
pastdue | Payment overdue |
paused | Temporarily suspended |
canceled | Canceled by user or merchant |
ended | Completed subscription term |
这些状态值帮助您理解订阅生命周期,并为每个状态实施适当的业务逻辑。
自主管理订阅续费
{
"billingInformation": "{}",
"merchantCustId": "CustId-JK6B-8850",
"merchantNo": "800209",
"merchantTxnId": "6a28ac11-d98c-4323-84a7-426b9322c4f7",
"merchantTxnTime": "2025-05-07 18:02:35",
"orderAmount": "2", // 续费金额
"orderCurrency": "USD",
"productType": "CARD",
"sign": "e3564ff939c35860ab0f41659588d3e9cfa556f35d3b2121d6f99faf82991c77",
"subProductType": "SUBSCRIBE",
"subscription": "{\"merchantCustId\":\"CustId-JK6B-8850\",\"requestType\":\"1\",\"tokenId\":\"b05d9de9836fe3e0dce5ba42078885cb90c729fe0604219a9cf24c092e71eb60\",\"contractId\":\"1919781071080529920\"}", // 使用存储的contractId和tokenId
"txnOrderMsg": "{\"accept\":\"*/*\",\"appId\":\"1727880846378401792\",\"colorDepth\":\"32\",\"contentLength\":\"4096\",\"javaEnabled\":false,\"language\":\"en-US\",\"products\":\"[{\\\"currency\\\":\\\"USD\\\",\\\"name\\\":\\\"incaberry\\\",\\\"num\\\":\\\"10\\\",\\\"price\\\":\\\"824.99\\\",\\\"type\\\":\\\"laborum irure veniam velit\\\"}]\",\"returnUrl\":\"https://docs.onerway.com/\",\"notifyUrl\":\"https://docs.onerway.com/apis\",\"screenHeight\":\"915\",\"screenWidth\":\"390\",\"timeZoneOffset\":\"300\",\"transactionIp\":\"109.214.114.89\",\"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
{
"respCode": "20000",
"respMsg": "Success",
"data": {
"transactionId": "1920061365985615872", // Onerway生成的交易ID
"responseTime": "2025-05-07 18:21:21",
"txnTime": "2025-05-07 18:21:17",
"txnTimeZone": "+08:00",
"orderAmount": "2.00", // 续费金额
"orderCurrency": "USD",
"txnAmount": null,
"txnCurrency": null,
"status": "S",
"redirectUrl": null,
"contractId": "1919781071080529920", // 订阅合同ID
"tokenId": "b05d9de9836fe3e0dce5ba42078885cb90c729fe0604219a9cf24c092e71eb60", // 订阅令牌ID
"eci": null,
"periodValue": null,
"codeForm": null,
"presentContext": null,
"actionType": null,
"subscriptionManageUrl": null,
"sign": "67046b9c8a966c39d24294a8b336caae6517182b77c66be54c63212d98f29563"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"notifyType": "TXN",
"transactionId": "1920061365985615872", // 交易ID
"txnType": "SALE",
"merchantNo": "800209",
"merchantTxnId": "6a28ac11-d98c-4323-84a7-426b9322c4f7",
"responseTime": "2025-05-07 18:21:21",
"txnTime": "2025-05-07 18:21:17",
"txnTimeZone": "+08:00",
"orderAmount": "2.00", // 续费金额
"orderCurrency": "USD",
"status": "S",
"contractId": "1919781071080529920", // 订阅合同ID
"tokenId": "b05d9de9836fe3e0dce5ba42078885cb90c729fe0604219a9cf24c092e71eb60", // 订阅令牌ID
"cardBinCountry": "US",
"reason": "{\"respCode\":\"20000\",\"respMsg\":\"Success\"}",
"sign": "345f09b2240dcd2084605c28024b4d6fe7da4f84e59a7268b92bb66a36c433b3",
"paymentMethod": "VISA",
"subscriptionStatus": "active", // 订阅状态
"dataStatus": "1", // 数据状态
"products": "[{\"currency\":\"USD\",\"name\":\"feijoa\",\"num\":\"88\",\"price\":\"968.79\",\"type\":\"consectetur adipisicing reprehenderit sint\"},{\"currency\":\"USD\",\"name\":\"avocado\",\"num\":\"52\",\"price\":\"414.89\",\"type\":\"consectetur labore\"}]",
"metaData": "vary",
"channelRequestId": "8002091920061371823816709",
"scenarios": "SUBSCRIPTION_RENEWAL" // 订阅续费场景
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
托管订阅更新(计划升级)
{
"billingInformation": "{}",
"merchantCustId": "CustId-0WQA-3549",
"merchantNo": "800209",
"merchantTxnId": "d9de61b7-3480-48ed-90e6-4b0b9e2e4640",
"merchantTxnTime": "2025-05-07 23:00:28",
"orderAmount": "42",
"orderCurrency": "USD",
"productType": "CARD",
"sign": "6a430ec13dd3249114bd000435f759d3078ed67e50c9fa2a623a0bb9b79e2fa9",
"subProductType": "SUBSCRIBE",
"subscription": "{\"requestType\":\"2\",\"tokenId\":\"195f7e75785863a66d3801ae02c12e24ea16abe164303cf3e3fd10e04d5e4188\",\"contractId\":\"1919772912228114432\",\"changeMode\":\"1\",\"productName\":\"Premium Plan - Monthly\",\"proration\":\"20\",\"prorationMode\":\"0\"}", // 订阅更新参数
"txnOrderMsg": "{\"accept\":\"*/*\",\"appId\":\"1727880846378401792\",\"colorDepth\":\"24\",\"contentLength\":\"1024\",\"javaEnabled\":true,\"language\":\"en-US\",\"products\":\"[{\\\"currency\\\":\\\"USD\\\",\\\"name\\\":\\\"passionfruit\\\",\\\"num\\\":\\\"90\\\",\\\"price\\\":\\\"696.29\\\",\\\"type\\\":\\\"eu anim\\\"},{\\\"currency\\\":\\\"USD\\\",\\\"name\\\":\\\"kiwiberry\\\",\\\"num\\\":\\\"36\\\",\\\"price\\\":\\\"948.79\\\",\\\"type\\\":\\\"pariatur amet ea sint aute\\\"}]\",\"returnUrl\":\"https://docs.onerway.com/\",\"notifyUrl\":\"https://docs.onerway.com/apis\",\"screenHeight\":\"736\",\"screenWidth\":\"2560\",\"timeZoneOffset\":\"720\",\"transactionIp\":\"201.10.41.195\",\"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
{
"respCode": "20000",
"respMsg": "Success",
"data": {
"transactionId": "1920133309141491712", // Onerway生成的交易ID
"responseTime": "2025-05-07 23:07:18",
"txnTime": "2025-05-07 23:07:09",
"txnTimeZone": "+08:00",
"orderAmount": "20.00", // 按比例收费金额
"orderCurrency": "USD",
"txnAmount": null,
"txnCurrency": null,
"status": "S",
"redirectUrl": null,
"contractId": "1919772912228114432", // 订阅合同ID
"tokenId": "195f7e75785863a66d3801ae02c12e24ea16abe164303cf3e3fd10e04d5e4188", // 订阅令牌ID
"eci": null,
"periodValue": null,
"codeForm": null,
"presentContext": null,
"actionType": null,
"subscriptionManageUrl": "https://sandbox-myscribe.onerway.com/pages/subscription-list/details?contractId=eyJjb250cmFjdElkIjoxOTE5NzcyOTEyMjI4MTE0NDMyLCJtZXJjaGFudE5vIjo4MDAyMDksIm1lcmNoYW50Q3VzdElkIjoiQ3VzdElkLUtYUlAtMzcxMSIsIm5vdGlmaWNhdGlvbkVtYWlsIjpudWxsLCJhcHBJZCI6IjE3Mjc4ODA4NDYzNzg0MDE3OTIifQ==",
"sign": "2cc6ee5c9d063b48267a6b8255c5a11f1cfebcabc2561d5616c6579b683a7e6e"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"notifyType": "TXN",
"transactionId": "1920133309141491712", // 交易ID
"txnType": "SALE",
"merchantNo": "800209",
"merchantTxnId": "d9de61b7-3480-48ed-90e6-4b0b9e2e4640",
"responseTime": "2025-05-07 23:07:18",
"txnTime": "2025-05-07 23:07:09",
"txnTimeZone": "+08:00",
"orderAmount": "20.00",
"orderCurrency": "USD",
"status": "S",
"contractId": "1919772912228114432", // 订阅合同ID
"tokenId": "195f7e75785863a66d3801ae02c12e24ea16abe164303cf3e3fd10e04d5e4188", // 订阅令牌ID
"cardBinCountry": "US",
"reason": "{\"respCode\":\"20000\",\"respMsg\":\"Success\"}",
"sign": "cd220e77f9566e6cf20869c712c01dff34af9d0b52ffe2194510d88288827265",
"paymentMethod": "VISA",
"subscriptionManageUrl": "https://sandbox-myscribe.onerway.com/pages/subscription-list/details?contractId=eyJjb250cmFjdElkIjoxOTE5NzcyOTEyMjI4MTE0NDMyLCJtZXJjaGFudE5vIjo4MDAyMDksIm1lcmNoYW50Q3VzdElkIjoiQ3VzdElkLUtYUlAtMzcxMSIsIm5vdGlmaWNhdGlvbkVtYWlsIjpudWxsLCJhcHBJZCI6IjE3Mjc4ODA4NDYzNzg0MDE3OTIifQ==",
"subscriptionStatus": "active", // 订阅状态
"dataStatus": "1", // 数据状态
"products": "[{\"name\":\"passionfruit\",\"desc\":null,\"price\":\"696.29\",\"num\":\"90\",\"currency\":\"USD\"},{\"name\":\"kiwiberry\",\"desc\":null,\"price\":\"948.79\",\"num\":\"36\",\"currency\":\"USD\"}]",
"metaData": "eventually",
"channelRequestId": "8002091920133320860106752", // 渠道请求ID
"scenarios": "SUBSCRIPTION_CHANGED" // 订阅更新场景
}
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
集成流程
自主管理订阅续费集成
实施自主管理订阅续费的步骤:
- 跟踪订阅计划:维护订阅合同和计费计划的记录
- 存储合同和令牌ID:安全存储从初始订阅中收到的
contractId
和tokenId
- 启动续费付款:在每个计费周期,使用以下参数调用订阅续费API:
requestType=1
(续费付款)- 存储的
contractId
和tokenId
- 当前计费金额
- 处理结果:处理API响应和后续的webhook通知
- 更新订阅记录:使用最新的付款状态更新本地订阅记录
托管订阅更新集成
实施托管订阅更新的步骤:
- 提供更新界面:为更改订阅计划创建用户或管理界面
- 处理更新请求:当请求计划更改时,使用以下参数调用订阅续费API:
requestType=2
(订阅更新)- 存储的
contractId
和tokenId
- 更新的订阅参数(例如,
productName
、orderAmount
) changeMode=1
用于立即计费调整或changeMode=2
用于下一周期更改- 对于立即更新(
changeMode=1
),包括proration
金额和prorationMode
- 处理按比例计费:如果使用
changeMode=1
,准备处理按比例金额的立即收费或退款 - 处理结果:处理API响应和webhook通知
- 更新客户信息:告知客户成功的计划更改
实施最佳实践
- 安全存储:始终使用加密和访问控制安全存储
contractId
和tokenId
- Webhook验证:验证webhook签名以确保通知是真实的
- 错误处理:为失败的续费尝试实施强大的错误处理
- 订阅跟踪:维护所有订阅详细信息和付款历史的准确记录
- 恢复机制:为失败的续费付款实施重试逻辑
- 数据同步:定期将您的订阅记录与Onerway的系统对账
- 用户通知:通知客户即将到来的续费和成功/失败的付款
- 计划更改指南:
- 对于重大价格上涨,考虑使用
changeMode=2
给客户提前通知 - 对于轻微升级或客户请求立即更改时,使用
changeMode=1
- 当使用
changeMode=1
时,根据剩余天数正确计算按比例金额
- 对于重大价格上涨,考虑使用
- 测试:在上线前在沙盒环境中彻底测试所有订阅场景
常见错误代码
安全注意事项
- API认证:所有API请求必须包含有效签名
- 安全传输:始终使用HTTPS进行API调用和webhook处理
- Webhook验证:验证所有传入webhook通知的签名
- 幂等性:幂等地处理webhook通知以处理潜在的重复项
商户集成检查清单
- 实现自主管理订阅续费逻辑(如适用)
- 配置订阅续费失败的重试机制
- 实现托管订阅计划更新功能
- 设置webhook接收和处理逻辑
- 建立订阅状态监控和报警
- 验证金额计算逻辑的准确性
- 测试各种续费和更新场景
- 确认客户通知机制正常工作
- 配置适当的日志记录和监控
- 在生产环境中验证端到端流程
- 实施安全的存储和传输机制
- 验证webhook签名验证逻辑