SMARTPAY

菲律宾周期性自动扣款怎么实现:授权令牌、失败重试与取消流程

这两年接的扣款类需求里,有一类问题的问法几乎一样:"客户已经同意过一次了,后面每个月怎么让他不用再点一次确认就能扣上钱?"这跟"怎么收一笔钱"是两个完全不同的技术问题。一次性收款解决的是"这一次怎么让客户付成功",靠的是渠道跳转、扫码或一次性验证码;周期性自动扣款解决的是"第一次授权之后,系统怎么在客户不在场的情况下,凭合法凭证把钱扣出来"。

如果你还在纠结该用月付、年付还是按量计费,或者产品本身是会员、软件订阅类,业务选型角度的讨论可以看订阅计费、多币种与自动扣款方案。这篇文章只聚焦技术实现:授权令牌怎么生成、怎么用令牌发起周期扣款、扣款失败了怎么重试、客户要取消商户怎么接住。

一次性支付和周期性扣款,底层根本不是一套流程

一次性支付的每一笔都带着完整的"支付上下文":客户要么跳转钱包App确认,要么扫一次码,要么输一次验证码。周期性扣款省掉了这些环节,靠的是第一次授权时留下的一个"令牌"(有的渠道叫mandate,有的叫token),后续每个周期商户系统直接拿着令牌去问渠道"按这份授权再扣一笔",客户全程不需要再出现。

这里要先说清楚一个容易被误解的点:自动扣款不是"偷偷扣钱"的技术,它的前提永远是客户主动完成过一次明确的授权动作,而且这份授权必须是客户随时可以撤回的。把这套机制做扎实,核心就是三件事:令牌怎么生成、令牌怎么被安全地复用、客户撤回之后系统怎么立刻跟上。

第一次授权:把客户的"同意"变成一个可复用的令牌

首次授权流程通常是一次正常的支付或绑定动作——客户跳转到钱包App里确认一次,或者在页面上完成一次绑卡,这次交互完成后,渠道侧会生成一个代表"这次同意"的令牌返回给商户系统。这个令牌通常会携带几类信息:对应的客户标识、绑定的具体渠道账户、生效起止时间、是否带金额或频次上限、以及当前状态(待激活/生效中/已失效)。商户系统要做的事情很简单,把这个令牌和客户、产品、周期规则一一对应存起来,后续所有周期扣款都靠这个令牌发起,不再需要客户二次输入任何信息。

一个常见的首次授权请求大致是这样:

curl -X POST https://apix.example.com/api/v1/mandate/create \
  -H "Content-Type: application/json" \
  -d '{
    "merid": "100234567",
    "customer_ref": "CUST-88291",
    "channel": "gcash",
    "auth_type": "first_payment",
    "cycle_rule": "monthly",
    "max_amount_per_cycle": "cap_tier_2",
    "return_url": "https://yourapp.com/callback/mandate",
    "notify_url": "https://yourapp.com/webhook/mandate",
    "sign": "..."
  }'

渠道返回的结果里会带一个mandate_token和一个状态字段,商户系统只需要在状态变成"生效中"之后才可以进入下一步——用这个令牌去跑周期扣款。

令牌怎么存、怎么用才安全

一个容易踩的坑是把令牌当成"万能钥匙"来用。实际上令牌的设计初衷正好相反:商户系统永远不应该拿到、也不应该存客户的原始卡号或钱包凭证,令牌本身就是替代这些敏感信息的一个受限凭证。它通常会被限定在某个金额上限、某个频次规则之内,而且客户本人随时可以在自己的钱包App或银行App里把它撤销——这跟直接存一份密码或卡号的风险等级完全不是一个量级。

实现上建议把令牌按"客户+产品+周期规则"三个维度一一对应存储,每条记录能回答清楚:这个令牌是谁的、对应哪个产品、下一次扣款时间、当前状态。这样客户发起取消,或者连续失败需要人工介入时,能第一时间定位到是哪条授权出了问题。

做代扣类业务,持有 BSP 认定的 OPS 或 EMI 牌照的机构,在令牌(授权凭证)的留痕、客户可撤销入口、备付金隔离这几项上都有明确的监管要求,这也是选支付服务商时值得优先确认的一点。

周期扣款怎么跑:定时任务用令牌发起扣款

到了扣款日,商户系统的账单引擎(通常是一个定时任务)把这一期该扣的客户、金额、周期序号整理出来,逐条拿对应的令牌去调扣款接口。有个细节必须处理好:每次调用都要带一个唯一的幂等键,防止网络重试或任务重复触发导致同一期被扣两次。

// 周期扣款任务示例:按到期日取出待扣清单,逐条用令牌发起扣款
const axios = require('axios');

async function chargeCycle(subscription) {
  const idempotencyKey = `cyc-${subscription.cycleNo}-${subscription.customerRef}`;
  try {
    const res = await axios.post('https://apix.example.com/api/v1/charge/recurring', {
      merid: subscription.merid,
      tno: `ORDER-${subscription.cycleNo}-${subscription.customerRef}`,
      mandate_token: subscription.mandateToken,
      cycle_no: subscription.cycleNo,
      idempotency_key: idempotencyKey,
      sign: subscription.sign,
    });
    return res.data.status; // success / pending / failed
  } catch (err) {
    // 网络层异常和渠道返回的业务失败要分开处理,见下一节的重试策略
    return 'error';
  }
}

注意这段代码里没有出现任何需要客户当场确认的环节——这正是周期扣款和一次性支付最直观的区别。扣款结果可能是成功、处理中,也可能是失败,失败之后要怎么处理,是整套机制里最容易被低估的一环,下面单独展开。

扣款失败了怎么办:失败分类与重试策略设计

周期扣款的失败原因大致分四类:客户余额不足、卡或钱包账户状态异常(过期、冻结、限额触顶)、渠道侧临时性故障(超时、限流、系统维护),以及客户已主动撤销授权导致令牌失效。这四类"该不该重试"完全不一样——余额不足过几天可能就有钱了,值得延后重试;令牌已失效再重试多少次都没用,还可能触发渠道风控;临时故障通常短时间内重试就能成功。批量场景下失败重试实操里的分批、限额思路同样适用于周期扣款。

把常见的几种重试策略放在一起对比会更清楚该怎么选:

重试策略适用失败类型间隔设计优点风险/局限
固定间隔重试渠道临时性故障每次间隔相同,到上限次数停止实现简单,逻辑直观对余额不足类失败效果差,容易无意义重试
指数退避重试网络超时、渠道限流间隔逐次翻倍,设置最大间隔上限减少对渠道接口的集中冲击总耗时变长,不适合需要较快确认结果的场景
按失败原因分流重试混合类型失败,尤其是余额不足/状态异常不同失败码对应不同间隔和次数,甚至不同的重试时间点(如发薪日后)贴合客户真实情况,减少无效调用需要维护一张失败码到策略的映射表,实现成本更高
不重试,转人工/转通知令牌已失效、客户主动拒绝不设重试,直接标记并触发客户通知或客服跟进避免在明确无望的场景里反复骚扰风控系统依赖人工或客户自助跟进,处理周期不可控

实际项目里很少用单一策略打天下,比较稳妥的做法是先按失败码分流,余额不足类走延后重试,临时性故障走指数退避,令牌失效类直接停止重试并触发通知。想进一步系统性地看整体支付成功率怎么提升,可以参考降低掉单率的实用方法,里面有些思路同样能搬到周期扣款的失败处理上。

客户怎么取消,商户怎么接住这个请求

取消授权应该双向都能发起:客户可以直接在钱包App或银行App里关掉授权,商户也可以在后台主动发起取消。容易出问题的是客户在渠道那边取消了,但商户系统还不知道——账单引擎没及时拿到通知,下一个周期照样会尝试扣款,结果自然是失败。

正确的做法是渠道侧令牌状态一旦发生变化,就主动推送通知给商户,而不是让商户自己盲猜或者靠定期轮询去发现。这跟商户回调验签与对账闭环里讲的通知机制是同一套逻辑——收到状态变更通知后要做验签确认来源可信,再更新本地令牌状态,并立刻停止后续的扣款任务。如果产品本身是预付费或者按年收费,取消时还要处理好多退少补的问题,这部分建议走单独的退款流程,不要和扣款接口混用。

通知与对账:扣款结果怎么同步回商户系统

周期扣款的状态同步建议走"推送通知+主动查询"两条腿:渠道推送是第一时间获知结果的方式,但网络抖动、队列延迟都可能导致通知丢失,商户系统还需要一条定时查询的兜底逻辑,把"处理中"超过一定时间的记录主动查一次最终结果。对账时,每一笔都要能对应回"哪个客户、哪一期、哪个令牌",幂等键在这里不仅防重复扣款,也是核对请求和结果是否一一对应的关键字段。

合规边界:自动扣款不只是技术问题

代扣类业务的合规要求比单纯技术实现更严格:每一次授权都必须留痕可查,商户必须给客户一个随时可查询甚至自助取消的入口,扣款金额和频次不能超出客户当初同意的范围。

菲律宾本地对代收代付类业务的监管主体,主要落在 BSP 认定的 OPS(运营支付系统)或 EMI(电子货币机构)牌照范围内,具体哪类业务主体适用哪种牌照,可以参考BSP OPS 支付业务牌照是什么里的判定思路。接自动扣款业务之前,先确认你接的通道方是不是在这个合规框架内运作,比事后补救要省心得多。

自动扣款和"免密支付"是一回事吗?

两者经常被混用,但不完全等同。免密支付强调的是客户单次支付时省掉验证环节,而自动扣款强调的是客户不需要在场、系统凭令牌周期性地发起扣款。免密只是让单次体验更顺畅,自动扣款解决的是"后续周期要不要客户重新参与"的问题,两者可以结合,也可以独立存在。

客户绑定的卡或钱包过期了,令牌还有效吗?

不会自动有效。令牌背后绑定的是具体的支付工具,如果这个工具本身状态异常(过期、更换、冻结),渠道侧通常会把令牌标记为失效或者要求重新授权,商户系统这时候应该主动触发客户重新完成一次授权流程,而不是继续按旧令牌盲目重试。

扣款失败应该重试几次才合适?

没有一个固定次数适用所有场景,关键是先按失败原因分流。余额不足类可以给几次延后重试的机会,临时性故障用指数退避在短时间内多试几次,而令牌失效或客户主动拒绝类,重试次数应该是零,直接转通知或人工处理。

客户在钱包App里把授权关了,系统还不知道怎么办?

这正是前面提到的通知机制要解决的问题——不能只靠商户单方面判断,必须依赖渠道的状态变更推送,再加上定期主动查询令牌状态作为兜底,两者缺一个都容易出现"客户已取消、系统还在扣"的尴尬局面。

周期扣款前要不要提前通知客户?

建议提前通知,尤其是金额或周期有变化时。即便渠道侧没有强制要求逐笔提前通知,主动把下一期扣款的时间和产品信息告知客户,也能降低事后投诉和退款申请。

令牌会不会被渠道单方面收回?

会。渠道出于风控考虑(比如长期未使用、触发异常交易规则),有可能单方面让令牌失效,商户系统需要能识别这类状态变化,并及时引导客户重新完成授权,而不是把它当成系统故障去反复重试。

换渠道或者换产品线,旧令牌能不能继续用?

不能跨渠道复用。令牌是绑定在具体渠道和具体授权范围内的凭证,换渠道意味着要重新走一次首次授权流程,生成新的令牌;换产品线如果涉及金额规则或周期规则的变化,通常也需要客户重新确认一次,不建议直接套用旧令牌硬改参数。

自动扣款这套机制说到底是在"客户体验"和"系统责任"之间找平衡——省掉客户每次手动确认的麻烦,换来的是商户系统要在令牌管理、失败重试、取消响应这几个环节都做到位。这些细节单独看都不复杂,但凡有一个没接上,出问题时往往是客户投诉最多、排查最费时间的那种。接入过程中遇到具体的渠道差异或重试策略问题,可以联系 @LouisSMARTPAY 或发邮件到 [email protected] 一起讨论。

相关推荐