PAYATHON 2026

Braintree Drop-in UI 如何为 recurring billing 选择付款方式

支付老李

结论

Drop-in UI 可以用于 recurring billing。创建 subscription 时,客户无需先修改 Vault 中的 default payment method。

处理流程如下:

  1. 客户通过 Drop-in UI 选择已有付款方式,或输入新的付款方式。
  2. 客户端取得 paymentMethodNonce。
  3. 服务端使用该 nonce 创建或保存 payment method,并取得对应的 paymentMethodToken。
  4. 使用该 token 创建 subscription。
  5. 业务确有需要时,再将该 payment method 设为默认方式。

Drop-in UI 用于收集和选择付款方式,通常不提供“设为默认付款方式”按钮。是否设为默认方式,应由应用在服务端决定。

服务端生成 Client Token

要在 Drop-in UI 中显示客户已经保存在 Vault 里的付款方式,生成 Client Token 时需要传入 customerId:

$clientToken = Braintree_ClientToken::generate([
    'customerId' => $_customer->id,
]);

如果不传 customerId,Drop-in UI 通常无法显示该客户已经 Vault 化的付款方式。

客户端获取 paymentMethodNonce

客户选择已有付款方式或输入新付款方式后,Drop-in UI 会返回 nonce:

instance.requestPaymentMethod(function (err, payload) {
    if (err) {
        console.error(err);
        return;
    }

    const paymentMethodNonce = payload.nonce;

    // 将 paymentMethodNonce 提交到自己的服务端
});

paymentMethodNonce 是供服务端执行一次性操作的凭据。不要尝试在客户端解析它,也无法根据这个字符串推导出 paymentMethodToken。

将 nonce 保存为 Payment Method

服务端可以使用 nonce,为指定客户创建 Vault payment method:

$result = Braintree_PaymentMethod::create([
    'customerId' => $_customer->id,
    'paymentMethodNonce' => $_POST['payment_method_nonce'],
    'options' => [
        'makeDefault' => true,
    ],
]);

if (!$result->success) {
    // 记录并向客户端返回验证错误
    foreach ($result->errors->deepAll() as $error) {
        error_log($error->code . ': ' . $error->message);
    }

    throw new RuntimeException('Unable to save payment method.');
}

$paymentMethodToken = $result->paymentMethod->token;

需要取出的值是:

$result->paymentMethod->token

它是本次 nonce 操作所生成的 Vault token,可以长期用于 subscription。

如果不想改变客户的默认付款方式,删除 makeDefault 即可:

$result = Braintree_PaymentMethod::create([
    'customerId' => $_customer->id,
    'paymentMethodNonce' => $_POST['payment_method_nonce'],
]);

使用所选付款方式创建 Subscription

取得 token 后,可以直接用它创建 subscription:

$subscriptionResult = Braintree_Subscription::create([
    'planId' => 'some_package',
    'paymentMethodToken' => $paymentMethodToken,
]);

if (!$subscriptionResult->success) {
    foreach ($subscriptionResult->errors->deepAll() as $error) {
        error_log($error->code . ': ' . $error->message);
    }

    throw new RuntimeException('Unable to create subscription.');
}

$subscription = $subscriptionResult->subscription;

这样创建的 subscription 会使用客户刚刚选择的付款方式,不依赖:

$_customer->defaultPaymentMethod()->token

因此,“本次订阅使用的付款方式”和“客户的默认付款方式”是两个可以独立管理的概念。

不要把 Customer ID 当成 Subscription ID

示例中的这一项需要注意:

'id' => $_customer->id,

在 Braintree_Subscription::create() 中,id 是自定义的 subscription ID,不是 customer ID。一个客户可能有多个 subscription,因此不应直接使用 customer ID。只有在业务明确保证两者一一对应,而且 ID 不会冲突时,才适合这样处理。

通常可以省略 id,由 Braintree 生成 subscription ID:

$subscriptionResult = Braintree_Subscription::create([
    'planId' => 'some_package',
    'paymentMethodToken' => $paymentMethodToken,
]);

是否可以直接用 nonce 创建 Subscription

部分 Braintree SDK/API 版本允许在创建 subscription 时提交 paymentMethodNonce,是否可用取决于项目使用的 PHP SDK 版本及对应的接口定义。如果当前版本明确支持,可以写成:

$subscriptionResult = Braintree_Subscription::create([
    'planId' => 'some_package',
    'paymentMethodNonce' => $_POST['payment_method_nonce'],
]);

如果接口不支持,或业务还需要处理 makeDefault、去重、验证归属等 Vault 管理操作,可以先调用 Braintree_PaymentMethod::create() 取得 token,再创建 subscription。这个流程更清楚,兼容性也更好。

注意事项

  • nonce 通常只能使用一次,应及时提交给服务端处理,不能作为长期付款标识保存。
  • 长期保存付款方式和执行重复扣款时,应使用 Vault 中的 paymentMethodToken。
  • 不要信任客户端提交的 customer ID。客户身份应根据当前登录会话确定。
  • 创建 payment method 后,应确认它保存在当前客户名下。
  • makeDefault 会影响该客户后续依赖默认付款方式的业务。不要只为创建一次 subscription 就无条件设置它。
  • 如果客户要更换某个现有 subscription 的付款方式,通常应更新该 subscription 的 paymentMethodToken,而不只是修改客户的默认付款方式。
  • 新版 PHP SDK 通常使用命名空间形式,例如 Braintree\Gateway;旧版代码可能使用 Braintree_PaymentMethod。具体写法应与项目当前安装的 SDK 保持一致。

备注:内容仅供参考。