PAYATHON 2026

巴西订阅服务如何选择 recurrent billing 支付网关?

支付阿杰

结论

不要用小型刷卡机配合自行保存信用卡号的方式处理订阅扣款。刷卡机适合客户在场的一次性交易,不适合无人值守的每月续费。自行保存卡片数据还会大幅扩大 PCI DSS 合规范围。一旦数据泄露,造成的损失往往远高于接入支付服务商的成本。

更合适的做法是选择具备以下能力的巴西本地或跨境支付服务商(PSP):

  • 支持 recurring payment 或 card-on-file;
  • 支持卡片 tokenization,由服务商保存完整卡号;
  • 可以在后台手动扣款,或通过 API 发起 merchant-initiated transaction;
  • 支持 Pix、boleto 等本地支付方式;
  • 提供 webhook、失败重试、退款和拒付管理;
  • 符合 PCI DSS,并能明确说明数据存储地点和责任边界。

你的系统只需保存支付服务商返回的 token、客户标识和卡片摘要,例如卡片品牌、末四位和有效期。不要保存完整卡号或 CVV。

可行的支付处理方式

1. 托管式支付页面加 tokenization

小型订阅业务通常可以优先考虑这种方式。

客户首次付款时进入服务商托管的结账页,或者在服务商提供的安全组件中填写卡片信息。卡号会直接提交给服务商,不经过你的服务器。服务商随后返回一个不能直接用于消费的 token。

后续操作分为两类:

  • 每月自动续费:由服务商按照订阅计划自动扣款,或者由你的系统定期调用 API。
  • 临时手动收费:员工在后台选择客户和金额,后端再使用 token 发起扣款。

这种方式既支持自动续费,也保留了人工收费能力,同时不需要员工查看或保存完整卡号。

选择供应商时,不能只问对方是否支持订阅,还应确认:

  • 是否支持巴西发行的信用卡;
  • 是否支持 BRL 结算;
  • 是否支持巴西常见的分期付款;
  • 保存支付凭证后,是否允许发起金额不固定的扣款;
  • 是否区分 customer-initiated transaction 和 merchant-initiated transaction;
  • 是否支持失败重试、换卡和过期卡更新;
  • 是否可以通过控制台完成手动收费;
  • 资金结算周期,以及退款费、拒付费和跨境费用;
  • 是否能提供 PCI DSS 合规证明;
  • 是否支持你的公司类型和开户注册材料。

有些服务商虽然提供 token,但只允许固定周期、固定金额的续费,未必支持任意金额的临时扣款。应要求对方针对你的具体业务场景作出书面确认。

2. 由你的系统管理订阅,PSP 只负责扣款

如果收费周期或金额比较复杂,可以在自己的数据库中维护订阅状态,并在账单到期时调用 PSP API。

典型流程如下:

客户填写卡片
    ↓
PSP 托管页面或安全组件
    ↓
PSP 返回 payment_method_token
    ↓
你的系统保存 token 和客户授权记录
    ↓
定时任务创建每月扣款
    ↓
webhook 更新支付结果

下面是示意代码。API 路径和字段只是通用结构,实际名称应以所选服务商的文档为准:

const payment = await paymentProvider.createPayment({
  amount: 9900,
  currency: "BRL",
  customer_id: customer.providerCustomerId,
  payment_method_token: customer.paymentMethodToken,
  off_session: true,
  idempotency_key: `invoice-${invoice.id}`,
  metadata: {
    invoice_id: invoice.id
  }
});

金额通常使用最小货币单位,例如 9900 表示 R$ 99,00,但仍要确认具体 API 对金额格式的要求。

这种方案更灵活,但以下工作需要由你负责:

  • 生成账单;
  • 做好幂等控制;
  • 制定重试策略;
  • 验证 webhook 签名;
  • 管理欠费状态;
  • 通知客户;
  • 处理取消订阅和退款。

3. 使用 PSP 自带的订阅管理

如果套餐数量不多,价格也固定,可以把计划、账单周期和自动重试交给 PSP 管理,你的系统只负责同步订阅状态。

这种方式开发成本较低,但需要留意供应商锁定。迁移服务商时,卡片 token 通常不能直接转移,除非双方支持合规的支付凭证迁移。

4. 组合使用 Pix、boleto 和银行卡

巴西用户可能更习惯 Pix 或 boleto,但这两种方式未必能直接取代信用卡自动续费。

可以这样安排:

  • 信用卡默认用于自动续费;
  • Pix 用于首次付款、补缴失败账单或客户主动续费;
  • boleto 作为不方便使用信用卡时的备用方式;
  • 如果 PSP 支持自动周期扣款类 Pix 产品,再评估它是否适合订阅业务。

Pix 自动扣款能力、银行覆盖范围和 PSP 接口可能随时间变化。接入前要确认当前支持范围、客户授权流程、撤销机制和失败处理方式。

5. 人工发送付款链接

临时收费并不意味着员工必须手工录入卡号。更安全的做法是从后台生成一次性付款链接,让客户自行填写支付信息。

这种方式适合:

  • 收取额外服务费;
  • 补缴欠费;
  • 购买订阅之外的一次性项目;
  • 原卡失效后重新绑定支付方式。

如果业务量不大,自动订阅配合付款链接通常已经能满足大多数需求。

为什么不建议使用小型刷卡机

刷卡机并非完全不能使用,但不适合充当订阅系统的核心。

交易场景不匹配

实体刷卡通常属于 card-present 交易,需要客户本人和实体卡在场。每月续费则属于客户不在场的 card-not-present 交易或凭证在档交易。

如果员工每月把保存的卡号手工输入终端,可能遇到以下问题:

  • 终端或收单合同不允许这样使用;
  • 申报的交易类型与实际场景不符;
  • 风控系统更容易拒绝交易;
  • 发生拒付时,很难证明客户已经授权;
  • 无法可靠处理换卡、卡片过期和失败重试;
  • 人工录入可能输错金额或造成重复扣款;
  • 员工可以接触完整卡号,内部泄露风险很高。

把客户卡号抄在纸上,再拿到刷卡机输入,并不能绕过 PCI DSS。只要企业接收、保存或处理这些数据,就已经承担了相应责任。

成本未必更低

刷卡机看似没有开发成本,但还会产生不少隐性成本:

  • 每月人工操作;
  • 对账和补扣;
  • 处理错误收费;
  • 处理拒付与退款;
  • 保护敏感数据;
  • 管理员工权限;
  • 响应安全事故;
  • 接受合规审计。

如果暂时找不到合适的自动扣款服务商,宁可每月向客户发送付款链接,也不要长期保存卡号并由员工手工录入。

正确的“手动收费”设计

手动收费应当是“由员工决定是否发起交易”,而不是“由员工查看并输入卡号”。

后台只需展示以下信息:

Customer: Maria Silva
Card: Visa •••• 4242
Expires: 08/28
Outstanding balance: R$ 149,00

员工点击收费按钮后,浏览器将客户 ID、账单 ID 和金额发送给后端:

POST /admin/invoices/inv_2026_0081/charge
Content-Type: application/json

{
  "amount": 14900
}

后端先检查权限和账单状态,然后使用已保存的 token 调用 PSP:

app.post("/admin/invoices/:invoiceId/charge", requireFinanceRole, async (req, res) => {
  const invoice = await invoices.findById(req.params.invoiceId);

  if (!invoice || invoice.status !== "open") {
    return res.status(409).json({ error: "Invoice is not chargeable" });
  }

  const payment = await paymentProvider.createPayment({
    amount: req.body.amount,
    currency: "BRL",
    customer_id: invoice.providerCustomerId,
    payment_method_token: invoice.paymentMethodToken,
    off_session: true,
    idempotency_key: `manual-charge-${invoice.id}-${req.body.amount}`
  });

  await auditLog.record({
    action: "manual_charge_requested",
    invoice_id: invoice.id,
    operator_id: req.user.id,
    provider_payment_id: payment.id
  });

  res.status(202).json({
    payment_id: payment.id,
    status: payment.status
  });
});

这个流程至少要有三层保护:

  • 只有获得授权的员工才能发起收费;
  • 每次操作都要写入不能随意修改的审计日志;
  • 使用 idempotency_key,防止重复提交导致重复扣款。

支付是否成功,应以验签后的 webhook 或服务端查询结果为准,不能只看浏览器的跳转结果。

客户授权不可省略

保存 token 不等于可以任意扣款。客户首次绑定卡片时,应取得清晰授权,并说明:

  • 收费金额或金额的计算方式;
  • 收费周期;
  • 首次扣款和后续扣款的时间;
  • 哪些额外项目可能单独收费;
  • 如何取消;
  • 退款规则;
  • 失败后是否重试,以及重试频率;
  • 如何更新支付方式。

对于金额不固定的临时费用,最好在收费前单独取得客户确认,或者在合同中写明可收费项目、计价方式和上限。保存授权时间、授权文本版本、IP、客户操作记录和相关账单,有助于降低争议与拒付风险。

如果确实必须接触卡片信息

原则很简单:能不保存就不保存,能少接触就少接触。

至少要遵守当前适用的 PCI DSS 要求,并请合格的支付安全顾问或收单机构确认合规范围。PCI DSS 的具体版本、商户等级和验证方式会发生变化,不能只凭一份通用清单判断自己已经合规。

永远不要保存 CVV

交易授权完成后,无论是否加密,都不应保存以下信息:

  • CVV、CVC、CID 等卡片验证码;
  • 完整的磁条数据;
  • 芯片中的敏感认证数据;
  • PIN 或 PIN block。

这些数据并不是加密后就可以保存。

限制保存内容

业务数据库通常只需要保存:

{
  "provider_customer_id": "cus_xxxxx",
  "payment_method_token": "pm_xxxxx",
  "card_brand": "visa",
  "last4": "4242",
  "expiry_month": 8,
  "expiry_year": 2028
}

不要把完整卡号写入以下位置:

  • 应用数据库;
  • 日志;
  • 客服工单;
  • 分析平台;
  • 错误追踪系统;
  • 邮件或聊天工具;
  • 数据库备份;
  • 屏幕截图。

日志过滤也很重要。任何疑似卡号的字段,都应在进入日志系统前删除或遮蔽:

function redactPaymentData(payload) {
  const sanitized = structuredClone(payload);

  delete sanitized.card_number;
  delete sanitized.cvv;
  delete sanitized.cvc;

  return sanitized;
}

字段过滤只能作为辅助措施,不能代替“不让卡号进入应用服务器”的架构设计。

如果业务迫使你保存 PAN

PAN 是完整的 Primary Account Number。一旦保存 PAN,系统就会进入更严格的 PCI DSS 合规范围,通常至少需要:

  • 使用强加密,并妥善隔离密钥;
  • 定期轮换密钥,并使用独立的密钥管理系统;
  • 实行最小权限和基于角色的访问控制;
  • 启用多因素认证;
  • 进行网络分段;
  • 保留完整的访问审计记录;
  • 持续进行漏洞管理和安全更新;
  • 定期扫描、渗透测试和合规验证;
  • 规定数据保留期限,并建立安全删除机制;
  • 制定事件响应方案;
  • 对员工、供应商和备份系统采取同等控制。

不要自行设计卡片数据加密协议,也不要把加密密钥和密文放在同一个数据库中。对小型公司来说,建立并长期维护这些控制通常并不划算。

还要考虑 LGPD

卡片 token、账单、姓名、CPF、邮箱和交易记录通常都与个人有关,因此还要按照巴西《通用数据保护法》LGPD 管理这些数据。

至少应明确:

  • 收集每项数据的目的和法律依据;
  • PSP、收单机构和你的公司分别承担什么数据处理角色;
  • 数据保存多久;
  • 哪些供应商可以访问数据;
  • 是否涉及跨境传输;
  • 如何响应访问、更正和删除等请求;
  • 如何处理数据泄露事件。

PCI DSS 主要处理支付卡数据安全问题,并不意味着已经符合 LGPD。满足 LGPD 也不等于符合 PCI DSS。

推荐的落地步骤

第一步:明确需求范围

先把支付场景整理成清单:

  • 金额是固定还是浮动;
  • 扣款日是每月固定日期还是注册周年日;
  • 是否需要试用期;
  • 是否需要分期;
  • 是否需要收取额外的临时费用;
  • 是否接受 Pix 和 boleto;
  • 每月交易量和平均客单价;
  • 可以接受的结算周期和失败率。

第二步:向候选 PSP 提交同一份问题表

重点确认 recurring billing、tokenization、后台收费、webhook、退款、拒付、BRL 结算和巴西卡支持情况。不要只看营销页面,还要核对合同和技术文档。

第三步:优先使用托管页面

尽量让客户的浏览器直接与 PSP 交互。服务器不接收 PAN 和 CVV,可以大幅缩小 PCI DSS 合规范围。不过,企业通常仍需完成相应的商户合规验证,并非完全免除责任。

第四步:实现账单状态机

账单至少应区分以下状态:

draft
open
processing
paid
failed
past_due
cancelled
refunded

不要把一次 API 超时直接判定为付款失败。遇到网络超时,应使用相同的幂等键查询或重试,以免重复扣款。

第五步:处理扣款失败

建议设置有限次数的重试,不要无限扣款。例如:

首次扣款失败
    ↓
通知客户并提示更新支付方式
    ↓
按预先公布的策略重试
    ↓
仍失败则暂停服务或转为待补缴

具体的重试间隔应结合 PSP 建议、发卡行反馈和客户协议确定。对于明确拒绝或疑似欺诈的交易,不应盲目重试。

第六步:准备替代付款路径

自动扣款失败后,可以向客户发送新的托管付款链接,或者提供 Pix、boleto 等补缴方式。不要要求客户通过邮件、聊天或电话发送完整卡号和 CVV。

最终建议

对小型订阅公司来说,比较稳妥的组合通常是:

  1. 使用 PSP 托管结账页或安全卡片组件;
  2. 由 PSP 保存卡片并返回 token;
  3. 正常月费通过自动订阅收取;
  4. 临时费用由后台通过 token 发起,并经过适当授权;
  5. 使用付款链接、Pix 或 boleto 处理补缴;
  6. 自己只保存 token、账单状态、授权证据和卡片摘要。

如果现有供应商不能同时满足自动续费和临时收费,可以采用“订阅 PSP + 托管付款链接”的组合,不要退回到自行保存卡号并由员工录入刷卡机的做法。只有当业务规模足以承担专业安全团队、审计和持续的 PCI DSS 合规成本时,才值得考虑自行保存完整卡号。

备注:内容仅供参考。