Authorize.Net recurring billing 订阅更新与续费问题
结论
- Authorize.Net 的 ARB(Automated Recurring Billing)支持通过
ARBUpdateSubscription修改后续扣款金额。不过,账单周期interval、下一扣款日期等计划字段通常不能通过这个接口随意调整。 - ARB 没有单独的
autoRenew开关。只要订阅有效,系统就会按照设定的totalOccurrences自动扣款。要停止续费,需要取消订阅,或让订阅在完成指定扣款次数后自然结束。 - 订阅取消后不能直接恢复。用户再次开启自动续费时,通常要创建一个新订阅。
- 可以提供手动付款入口,但 ARB 本身不会生成“下期账单链接”。一次性付款可以使用 Accept Hosted,也可以通过自己的结账页面调用
createTransaction。 - 修改订阅金额不会自动补收本期差额。以 1 月 15 日新增 license 为例,需要自行决定立即收取
$10,还是从下一账期开始按$60扣款。
Query 1:更新订阅金额
创建订阅后,可以通过 ARBUpdateSubscriptionRequest 更新 amount。新金额通常适用于尚未处理的后续扣款,不会影响已经完成的交易,也不会自动计算当前账期的 prorated amount。
例如:
- 2021-01-01:创建一个每 30 天扣款
$50的订阅。 - 2021-01-15:用户新增一个价值
$10的 license。 - 将订阅金额更新为
$60。 - 下一次 ARB 扣款通常为
$60。 - 如果需要收取 1 月 15 日至下个账期之间的费用,还要另外创建一笔一次性交易。
C# 更新订阅金额示例
下面使用 Authorize.Net .NET SDK 的典型请求结构。SDK 版本不同,具体命名空间和控制器的构造方式可能也会不同,请以项目实际引用的版本为准。
using AuthorizeNet.Api.Contracts.V1;
using AuthorizeNet.Api.Controllers;
using AuthorizeNet.Api.Controllers.Bases;
public static ANetApiResponse UpdateSubscriptionAmount(
string apiLoginId,
string transactionKey,
long subscriptionId,
decimal newAmount)
{
ApiOperationBase<ANetApiRequest, ANetApiResponse>.RunEnvironment =
AuthorizeNet.Environment.SANDBOX;
ApiOperationBase<ANetApiRequest, ANetApiResponse>.MerchantAuthentication =
new merchantAuthenticationType
{
name = apiLoginId,
ItemElementName = ItemChoiceType.transactionKey,
Item = transactionKey
};
var subscription = new ARBSubscriptionType
{
amount = newAmount,
amountSpecified = true
};
var request = new ARBUpdateSubscriptionRequest
{
subscriptionId = subscriptionId,
subscription = subscription
};
var controller = new ARBUpdateSubscriptionController(request);
controller.Execute();
return controller.GetApiResponse();
}
生产代码应检查响应状态,不能只判断返回对象是否为 null:
var response = UpdateSubscriptionAmount(
apiLoginId,
transactionKey,
subscriptionId,
60.00m);
if (response?.messages?.resultCode == messageTypeEnum.Ok)
{
// 更新成功:同步保存新的订阅价格
}
else
{
string error = response?.messages?.message?
.FirstOrDefault()?.text ?? "Unknown Authorize.Net error";
// 记录失败原因,并保持本地订阅金额不变
}
本期新增费用的处理方式
如果新增 license 需要立即生效,可以选择以下计费方式:
- 不按比例计费:立即增加 license,下一账期再按
$60扣款。 - 立即补收固定金额:将 ARB 金额更新为
$60,同时创建一笔$10的一次性交易。 - 按剩余天数计费:由业务系统计算本期差额,再发起一次性交易。
例如,一个 30 天的账期还剩 15 天,可以按天计算:
Prorated charge = 10.00 × 15 / 30 = 5.00
Authorize.Net 不会判断 license 从何时开始生效,也不会自动完成这类比例计算。这些逻辑需要由业务系统处理。
Query 2:控制 Auto Renewal
ARB 本身就是自动扣款服务,但它没有可以反复切换的布尔字段,例如:
autoRenew = true | false
后续是否继续扣款,主要取决于订阅状态和 totalOccurrences:
- 订阅有效且尚未达到
totalOccurrences时,继续自动扣款。 - 达到设定的扣款次数后,订阅自然结束。
- 调用
ARBCancelSubscription后,停止尚未处理的后续自动扣款。 - 用户再次开启自动续费时,通常要创建新订阅,不能恢复已经取消的订阅。
系统中的“关闭自动续费”可以按以下流程实现:
- 在应用数据库中记录用户关闭自动续费的选择。
- 根据业务规则,决定立即取消 ARB,还是等当前付费周期结束前再取消。
- 调用
ARBCancelSubscription。 - 在本地记录的有效期结束前,保留用户当前周期内的使用权限。
- 用户重新开启自动续费时,创建新的 ARB 订阅。
C# 取消订阅示例
using AuthorizeNet.Api.Contracts.V1;
using AuthorizeNet.Api.Controllers;
using AuthorizeNet.Api.Controllers.Bases;
public static ARBCancelSubscriptionResponse CancelSubscription(
string apiLoginId,
string transactionKey,
long subscriptionId)
{
ApiOperationBase<ANetApiRequest, ANetApiResponse>.RunEnvironment =
AuthorizeNet.Environment.SANDBOX;
ApiOperationBase<ANetApiRequest, ANetApiResponse>.MerchantAuthentication =
new merchantAuthenticationType
{
name = apiLoginId,
ItemElementName = ItemChoiceType.transactionKey,
Item = transactionKey
};
var request = new ARBCancelSubscriptionRequest
{
subscriptionId = subscriptionId
};
var controller = new ARBCancelSubscriptionController(request);
controller.Execute();
return controller.GetApiResponse();
}
取消 ARB 只会停止未来的自动交易。用户在应用中的 license、服务有效期和访问权限,仍要由业务系统根据已经支付的周期管理。
Query 3:关闭自动续费后支付下一期账单
Authorize.Net ARB 不会为已经取消的订阅自动生成一个可以长期使用的“下期账单付款链接”。这种场景更适合使用 Accept Hosted:
-
在数据库中创建一张待支付账单。
-
用户打开你自己的付款地址,例如:
https://example.com/invoices/INV-2021-0001/pay -
后端验证账单状态和应付金额。
-
后端调用
getHostedPaymentPageRequest获取 Accept Hosted token。 -
将用户重定向到 Authorize.Net 托管的支付页面,或者使用 iframe/redirect 方式集成。
-
根据 webhook 和交易查询结果确认付款。
-
付款成功后延长服务期限。
-
如果用户同时开启自动续费,再创建新的 ARB 订阅。
Hosted payment token 的有效期较短,不适合作为永久付款链接保存,也不应通过邮件长期发送。更稳妥的方式是发送业务系统自己的账单 URL,等用户访问时再实时生成新的 Accept Hosted token。
如果业务已经保存了 Authorize.Net Customer Profile,也可以在用户明确授权后,使用对应的 payment profile 创建一次性 authCaptureTransaction。但用户已经关闭自动续费时,不能在没有明确授权的情况下自动扣取下一期费用。
实施时的注意事项
- 处理金额时应使用
decimal,不要用double。 - 更新 ARB 金额前,先更新或验证本地订单、license 数量和计费规则。
- API 请求同步返回成功,并不代表实际扣款一定成功。ARB 的每笔交易仍可能因余额不足、卡片过期等原因失败。
- 使用 webhook 接收订阅和交易事件,同时保留主动查询交易状态的补偿机制。
- 所有 webhook 都要验证真实性,并保证重复通知不会造成 license 重复开通。
- 取消订阅前,应确认业务语义是“立即停止服务”,还是“当前周期结束后停止续费”。
- 已取消的订阅应按不可恢复处理。重新订阅时保存新的
subscriptionId,不要覆盖历史记录。 - 测试环境使用
AuthorizeNet.Environment.SANDBOX。上线时切换到生产环境,并从安全配置中读取凭据。 - 不要在日志、数据库明文字段或客户端代码中保存
transactionKey、完整卡号或 CVV。 - SDK 中的可选值可能需要设置对应的
Specified属性,例如amountSpecified = true。具体要求应以实际 SDK 生成的类型为准。
备注:内容仅供参考。
