Authorize.net 延迟付款与 recurring 扣款如何设置
结论
不要把 $20 membership 费用并入月度订阅的常规扣款。建议在用户注册时分别设置三笔交易:
- 立即执行一笔
$5的普通交易。 - 创建一份两周后开始、每月扣款
$30的 ARB 订阅。 - 创建另一份两周后执行的 ARB 订阅,金额为
$20,并将totalOccurrences设为1。
按此设置,两周后的首次扣款合计为 $50,此后每月只扣 $30。
不建议使用 ARB 的 trialAmount 表示首次收取的 $50,因为部分 Authorize.net ARB 接口要求 trialAmount 不得高于正常订阅金额。即使某个接口版本允许这样设置,也容易让人把这笔首期附加费误认为试用优惠价。
支付安排
最终会有以下三项交易:
| 时间 | 金额 | 实现方式 |
|---|---|---|
| 注册时 | $5 | createTransactionRequest 普通支付 |
| 两周后 | $20 | 单次 ARB 订阅,totalOccurrences = 1 |
| 两周后及以后每月 | $30 | 月度 ARB 订阅 |
两份 ARB 订阅可以共用同一个 customerProfileId 和 customerPaymentProfileId,不必再次收集信用卡信息。
创建月度订阅
下面是 ARBCreateSubscriptionRequest 的核心结构。示例省略了完整命名空间,只保留认证信息和计费计划相关内容:
<ARBCreateSubscriptionRequest>
<merchantAuthentication>
<name>API_LOGIN_ID</name>
<transactionKey>TRANSACTION_KEY</transactionKey>
</merchantAuthentication>
<subscription>
<name>Monthly product subscription</name>
<paymentSchedule>
<interval>
<length>1</length>
<unit>months</unit>
</interval>
<startDate>2026-09-12</startDate>
<totalOccurrences>9999</totalOccurrences>
<trialOccurrences>0</trialOccurrences>
</paymentSchedule>
<amount>30.00</amount>
<profile>
<customerProfileId>CUSTOMER_PROFILE_ID</customerProfileId>
<customerPaymentProfileId>PAYMENT_PROFILE_ID</customerPaymentProfileId>
</profile>
</subscription>
</ARBCreateSubscriptionRequest>
startDate 应按注册日期加 14 天计算。9999 通常表示持续订阅,实际使用时仍需确认当前账户和 API 文档允许的取值。
创建一次性的 membership 扣款
另建一份金额为 $20、仅执行一次的订阅:
<ARBCreateSubscriptionRequest>
<merchantAuthentication>
<name>API_LOGIN_ID</name>
<transactionKey>TRANSACTION_KEY</transactionKey>
</merchantAuthentication>
<subscription>
<name>One-time membership fee</name>
<paymentSchedule>
<interval>
<length>1</length>
<unit>months</unit>
</interval>
<startDate>2026-09-12</startDate>
<totalOccurrences>1</totalOccurrences>
<trialOccurrences>0</trialOccurrences>
</paymentSchedule>
<amount>20.00</amount>
<profile>
<customerProfileId>CUSTOMER_PROFILE_ID</customerProfileId>
<customerPaymentProfileId>PAYMENT_PROFILE_ID</customerPaymentProfileId>
</profile>
</subscription>
</ARBCreateSubscriptionRequest>
这里仍需填写月度间隔,但 totalOccurrences 为 1,所以该计划只会执行一次。
如果当前 Authorize.net 账户或使用的 SDK 不允许创建单次 ARB 计划,就无法只靠 ARB 完成这笔延迟的一次性扣款。此时需要保存支付档案 ID,再由服务端定时任务在两周后调用 createTransactionRequest。Authorize.net 的 Customer Profiles API 可以保存支付方式,但不会在指定日期自动替应用程序发起普通交易。
注册时的处理顺序
可以按以下顺序处理:
1. 创建或取得 Customer Profile
2. 创建 Customer Payment Profile
3. 使用 Payment Profile 立即收取 $5
4. 创建两周后开始的 $30/月 ARB 订阅
5. 创建两周后执行一次的 $20 ARB 订阅
6. 保存两个 subscriptionId
两份订阅会分别生成一个 subscriptionId。如果用户在两周内取消,应同时取消:
$30/月的订阅;- 尚未执行的
$20 membership订阅。
PHP 伪代码
下面的示例用于说明业务结构。具体类名和方法可能随 Authorize.net PHP SDK 版本变化:
$startDate = (new DateTimeImmutable('today'))
->modify('+14 days')
->format('Y-m-d');
// 注册时立即扣款
chargeCustomerProfile(
customerProfileId: $customerProfileId,
paymentProfileId: $paymentProfileId,
amount: '5.00'
);
// 长期月度订阅
$monthlySubscriptionId = createArbSubscription(
name: 'Monthly product subscription',
customerProfileId: $customerProfileId,
paymentProfileId: $paymentProfileId,
startDate: $startDate,
amount: '30.00',
intervalLength: 1,
intervalUnit: 'months',
totalOccurrences: 9999
);
// 只执行一次的 membership 费用
$membershipSubscriptionId = createArbSubscription(
name: 'One-time membership fee',
customerProfileId: $customerProfileId,
paymentProfileId: $paymentProfileId,
startDate: $startDate,
amount: '20.00',
intervalLength: 1,
intervalUnit: 'months',
totalOccurrences: 1
);
注意事项
ARB 不能精确到分钟调度任务
ARB 的 startDate 只能指定日期,不能指定具体时间。Authorize.net 会通过批处理机制执行订阅扣款,因此无法保证交易恰好发生在注册时间的 14 天后。
订阅创建成功不等于后续扣款一定成功
创建 ARB 订阅时支付资料验证成功,并不能保证两周后的扣款也会成功。信用卡届时可能已经过期、被冻结,或因余额不足而被拒绝。系统至少应接收 Authorize.net Webhooks,并处理付款成功、付款失败及订阅状态变化。
完全不保存数据会影响取消操作
即使注册时已经创建所有扣款计划,也建议至少保存以下信息:
customerProfileId
customerPaymentProfileId
monthlySubscriptionId
membershipSubscriptionId
内部用户或订单编号
缺少这些信息时,用户取消订阅后,很难可靠地找到并取消两份计划。如果确实不能使用数据库,可以将这些 ID 保存到现有用户系统、订单元数据或其他持久化存储中。只保存在浏览器会话中并不可靠。
两笔同时扣款可能显示为两条账单记录
两周后,用户通常会看到 $20 和 $30 两笔交易,而不是一笔 $50。如果业务要求信用卡账单上只能出现一笔 $50,应用程序就需要在两周后主动收取 $50,之后再创建或启用 $30/月 的订阅。该方案需要服务端定时任务和持久化状态,仅靠 ARB 后台很难安全实现。
在“不使用数据库、所有计划必须在购买时配置”的限制下,创建两份同日起始的 ARB 计划是最直接的做法。
备注:内容仅供参考。
