如何通过 Authorize.net API 收取逾期费用
结论
可以,但 Authorize.net 没有专门用于处理“逾期费用”或“未结余额”的 API。
一般按以下方式实现:
- 学校的业务系统负责计算逾期费用和未结余额。
- 获取 Customer Information Manager(CIM)中保存的
customerProfileId和paymentProfileId。 - 调用
createTransactionRequest,通过authCaptureTransaction创建一笔独立交易。 - 在本地账务系统中记录这笔交易对应的学生、账单、逾期费用,以及 Authorize.net 返回的
transId。
无论客户是否加入 recurring billing subscription,只要有可用的 Customer Profile 和 Payment Profile,并且学校取得了相应的扣款授权,就可以尝试发起单次扣款。
为什么不能直接通过订阅 API 收取
Recurring Billing Subscription 用于按既定计划自动扣款,不负责管理学校的应收账款,也不会自动判断某笔费用是否逾期。
不同类型的费用应分别处理:
- 正常的周期性学费由 recurring billing subscription 收取。
- 一次性逾期费用通过
createTransactionRequest单独收取。 - 历史未结余额在计算出实际金额后,通过
createTransactionRequest单独收取。 - 如果需要永久调整以后每期的金额,可以在业务规则允许的情况下更新订阅,不要把一次性逾期费用混入后续周期。
Create a Subscription from Customer Profile 适合创建未来的周期性收费计划,不适合立即收取临时产生的逾期费用。
推荐实现流程
1. 在学校系统中计算余额
Authorize.net 不会替学校维护“应付金额”“逾期天数”或“滞纳金规则”。这些数据应由学校自己的账务系统管理,例如:
amount_to_charge = overdue_principal + late_fee
计算时应保留费用明细,不能只保存合计金额:
{
"invoiceId": "INV-2026-00831",
"overduePrincipal": "120.00",
"lateFee": "10.00",
"amountToCharge": "130.00"
}
处理财务金额时,应使用十进制定点数或最小货币单位,避免直接使用二进制浮点数计算。
2. 确定 Customer Profile 和 Payment Profile
需要取得以下两个 ID:
customerProfileIdpaymentProfileId
可以通过 getCustomerProfileRequest 或 getCustomerPaymentProfileRequest 检查支付资料是否仍然存在。返回的银行卡信息通常经过掩码处理。发起扣款时不需要,也不应重新取得完整卡号。
如果一个客户保存了多个 Payment Profile,学校必须在自己的系统中明确哪种支付方式已获授权,可用于收取逾期费用,不能随意选择。
3. 创建独立交易
下面是使用 Customer Profile 发起 authCaptureTransaction 的 JSON 示例:
{
"createTransactionRequest": {
"merchantAuthentication": {
"name": "API_LOGIN_ID",
"transactionKey": "TRANSACTION_KEY"
},
"refId": "latefee-INV-2026-00831",
"transactionRequest": {
"transactionType": "authCaptureTransaction",
"amount": "130.00",
"profile": {
"customerProfileId": "123456789",
"paymentProfile": {
"paymentProfileId": "987654321"
}
},
"order": {
"invoiceNumber": "INV-2026-00831",
"description": "Overdue balance and late fee"
},
"transactionSettings": {
"setting": {
"settingName": "duplicateWindow",
"settingValue": "120"
}
}
}
}
}
各字段的用途如下:
amount是本次实际收取的总额。refId用于关联学校内部的收费请求,但不能代替本地幂等控制。invoiceNumber建议填写学校账单号。description可以说明交易用途,但不要包含儿童姓名、健康信息等不必要的敏感数据。duplicateWindow只能协助发现短时间内的疑似重复交易,不能作为完整的防重复扣款机制。
如果业务需要先授权,再确认扣款,可以使用 authOnlyTransaction,之后对已授权的交易执行 capture。对于一般的即时逾期费用,使用 authCaptureTransaction 通常更直接。
响应处理
不能只检查 HTTP 状态码,还需要检查 API 消息和交易级响应,例如:
function handleAuthorizeNetResponse(response) {
const apiResult = response?.messages?.resultCode;
const transaction = response?.transactionResponse;
if (
apiResult === "Ok" &&
transaction?.responseCode === "1" &&
transaction?.transId
) {
return {
success: true,
transactionId: transaction.transId,
authorizationCode: transaction.authCode
};
}
return {
success: false,
responseCode: transaction?.responseCode,
errors: transaction?.errors || response?.messages?.message || []
};
}
交易成功后,至少应保存以下信息:
- 内部账单号
customerProfileIdpaymentProfileId- 收取金额和费用明细
transId- 授权码
- 交易时间
- 当前结算状态
只有确认交易成功后,才能把对应账单标记为已支付。如果交易被拒绝,应保留未结余额,并按照学校的重试和通知规则处理。
已加入订阅的客户
客户已经加入 recurring billing subscription,不代表所有额外费用都会自动计入订阅。
对于一次性逾期费用,可以继续使用同一个 Customer Profile 下经过授权的 Payment Profile,另行创建一笔交易。不要为了收取一次逾期费用而新建订阅,也不要先修改订阅金额再立即改回,否则会增加账务对账和失败恢复的复杂度。
还需要防止以下情况:
- 订阅当天已经完成扣款,学校系统又针对同一余额发起扣款。
- 订阅扣款失败后,自动重试程序和人工补扣同时发起交易。
- 修改订阅金额后,又单独收取了相同的逾期费用。
可以在本地为每次收费生成唯一的业务键,例如:
customerId + invoiceId + feeType + billingPeriod
发起 API 请求前,可以对该业务键设置唯一约束或事务锁,防止并发请求造成重复收费。
没有订阅的客户
没有 recurring billing subscription 不影响单次扣款。只要客户已有有效的 Customer Profile 和 Payment Profile,就可以通过同样的方式创建交易。
如果客户还没有 Payment Profile,需要先通过符合 PCI 要求的方式采集支付信息并创建 Payment Profile。除非现有系统和合规范围明确允许,否则不要让银行卡数据经过学校自己的服务器。通常应优先使用 Authorize.net 提供的托管式支付信息采集方案。
部分付款和剩余余额
如果只收取部分余额,本次扣款金额应由学校系统确定。交易成功后,再更新剩余余额:
remaining_balance = previous_balance - captured_amount
每笔付款都应保存为独立的账务记录。Authorize.net 返回的是支付交易结果,不是学校账单的最终余额,因此不能只依靠 Authorize.net 的交易列表重建完整的应收账款。
退款也应关联原交易,并通过相应的退款交易流程处理,不能只在本地调低余额。
注意事项
扣款授权
技术上可以使用已保存的 Payment Profile,不代表学校一定有权收取逾期费用。学校应确认:
- 家长已事先同意保存支付方式。
- 授权条款明确涵盖额外费用或逾期费用。
- 逾期费用的金额或计算方式已经充分披露。
- 通知、宽限期和扣款时间符合合同及适用法律。
- 已确认银行卡网络对 stored credential 或 merchant-initiated transaction 的要求。
具体的法律和卡组织要求会因地区、商户类别、收单机构及账户配置而异,应由学校的法律顾问、支付服务商或收单机构确认。
交易失败
支付资料存在并不代表扣款一定会成功。常见的失败原因包括卡片过期、资金不足、发卡行拒绝,以及 Payment Profile 已失效。
不要无限自动重试。学校应设置明确的重试次数、重试间隔和家长通知流程,并记录每次尝试。
对账和通知
可以使用 Webhook 或定期查询交易,处理后续状态变化,并把结算、退款、撤销和拒付同步到学校账务系统。API 返回“已授权或已提交”,不一定表示资金已经完成最终结算。
测试
应先在 Authorize.net Sandbox 中验证以下场景:
- 正常收取逾期费用
- Payment Profile 无效
- 发卡行拒绝
- 重复提交
- 部分付款
- 订阅扣款和额外扣款发生在同一天
- 退款和撤销
- API 请求超时,但交易实际已经创建
最后一种场景尤其需要注意。请求超时后不要直接重发,应先根据本地业务键、refId 和 Authorize.net 交易记录确认第一次请求是否成功,以免重复扣款。
备注:内容仅供参考。
