Braintree Drop-in UI 如何为 recurring billing 选择付款方式
结论
Drop-in UI 可以用于 recurring billing。创建 subscription 时,客户无需先修改 Vault 中的 default payment method。
处理流程如下:
- 客户通过 Drop-in UI 选择已有付款方式,或输入新的付款方式。
- 客户端取得
paymentMethodNonce。 - 服务端使用该 nonce 创建或保存 payment method,并取得对应的
paymentMethodToken。 - 使用该 token 创建 subscription。
- 业务确有需要时,再将该 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 保持一致。
备注:内容仅供参考。
