PAYATHON 2026

Authorize.Net recurring billing 订阅更新与续费问题

支付老李

结论

  1. Authorize.Net 的 ARB(Automated Recurring Billing)支持通过 ARBUpdateSubscription 修改后续扣款金额。不过,账单周期 interval、下一扣款日期等计划字段通常不能通过这个接口随意调整。
  2. ARB 没有单独的 autoRenew 开关。只要订阅有效,系统就会按照设定的 totalOccurrences 自动扣款。要停止续费,需要取消订阅,或让订阅在完成指定扣款次数后自然结束。
  3. 订阅取消后不能直接恢复。用户再次开启自动续费时,通常要创建一个新订阅。
  4. 可以提供手动付款入口,但 ARB 本身不会生成“下期账单链接”。一次性付款可以使用 Accept Hosted,也可以通过自己的结账页面调用 createTransaction。
  5. 修改订阅金额不会自动补收本期差额。以 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 后,停止尚未处理的后续自动扣款。
  • 用户再次开启自动续费时,通常要创建新订阅,不能恢复已经取消的订阅。

系统中的“关闭自动续费”可以按以下流程实现:

  1. 在应用数据库中记录用户关闭自动续费的选择。
  2. 根据业务规则,决定立即取消 ARB,还是等当前付费周期结束前再取消。
  3. 调用 ARBCancelSubscription。
  4. 在本地记录的有效期结束前,保留用户当前周期内的使用权限。
  5. 用户重新开启自动续费时,创建新的 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:

  1. 在数据库中创建一张待支付账单。

  2. 用户打开你自己的付款地址,例如:

    https://example.com/invoices/INV-2021-0001/pay
    
  3. 后端验证账单状态和应付金额。

  4. 后端调用 getHostedPaymentPageRequest 获取 Accept Hosted token。

  5. 将用户重定向到 Authorize.Net 托管的支付页面,或者使用 iframe/redirect 方式集成。

  6. 根据 webhook 和交易查询结果确认付款。

  7. 付款成功后延长服务期限。

  8. 如果用户同时开启自动续费,再创建新的 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 生成的类型为准。

备注:内容仅供参考。