如何使用Stripe PHP通过Card ID或Customer ID完成二次扣费?
解决Stripe二次支付复用已有Customer/Card ID的问题
我来帮你搞定这个二次支付复用客户和卡片的问题!核心问题在于你现在每次调用Stripe_Customer::create()都会生成新客户,咱们只要调整逻辑,先复用已有客户、绑定并存储支付方式,二次支付时直接用存储的信息发起扣费就行。下面分步骤给你讲具体实现:
一、首次支付时:创建/复用Customer,绑定并存储支付方式
1. 先检查数据库中是否已有当前用户的Stripe Customer ID
别每次都新建客户,先从你的数据库里查询当前登录用户(通过$_SESSION['userEmail']关联)是否已经有对应的Stripe Customer ID:
// 假设你的用户表是users,字段stripe_customer_id用来存储Customer ID $userEmail = $_SESSION['userEmail']; // 用你自己的数据库查询逻辑获取已有Customer ID $existingCustomerId = $db->query("SELECT stripe_customer_id FROM users WHERE email = ?", [$userEmail])->fetchColumn();
2. 创建或复用Customer,绑定支付方式
如果没有已有Customer ID,再创建新的;如果有,直接用已有的。然后把用户选择的支付方式(Card/PaymentMethod)附加到这个Customer上:
// 接收前端从Stripe模态框传来的支付方式ID(新版是payment_method,旧版是source) $paymentMethodId = $_POST['payment_method_id']; if (!$existingCustomerId) { // 首次创建Customer并绑定支付方式 $customer = Stripe_Customer::create([ 'email' => $userEmail, 'payment_method' => $paymentMethodId, 'invoice_settings' => [ 'default_payment_method' => $paymentMethodId, // 可选:设置默认支付方式 ], ]); $existingCustomerId = $customer->id; // 把新的Customer ID保存到你的用户表中 $db->query("UPDATE users SET stripe_customer_id = ? WHERE email = ?", [$existingCustomerId, $userEmail]); } else { // 已有Customer,直接附加新的支付方式(如果用户绑定了新卡) Stripe_PaymentMethod::attach( $paymentMethodId, ['customer' => $existingCustomerId] ); // 可选:将新绑定的卡片设为默认 Stripe_Customer::update( $existingCustomerId, ['invoice_settings' => ['default_payment_method' => $paymentMethodId]] ); } // 把支付方式的ID、卡片后四位等信息保存到数据库(方便用户后续选择) $db->query("INSERT INTO user_payment_methods (user_email, stripe_customer_id, stripe_payment_method_id, card_last4) VALUES (?, ?, ?, ?)", [ $userEmail, $existingCustomerId, $paymentMethodId, $_POST['card_last4'] // 前端传来的卡片后四位,比如"4242" ]);
二、二次支付时:用已有Customer/PaymentMethod发起扣费
当用户选择已绑定的卡片(比如你展示的visa****4242),从数据库取出对应的stripe_customer_id和stripe_payment_method_id,然后发起支付:
方式1:用PaymentIntent(Stripe最新推荐方案)
// 从数据库取出用户选中的支付方式信息 $selectedPaymentMethod = $db->query("SELECT stripe_customer_id, stripe_payment_method_id FROM user_payment_methods WHERE user_email = ? AND card_last4 = ?", [$userEmail, '4242'])->fetch(); // 创建并确认支付Intent $paymentIntent = Stripe_PaymentIntent::create([ 'amount' => 1000, // 金额单位为分,这里代表10美元 'currency' => 'usd', 'customer' => $selectedPaymentMethod['stripe_customer_id'], 'payment_method' => $selectedPaymentMethod['stripe_payment_method_id'], 'confirm' => true, // 直接确认支付 'off_session' => true, // 如果是用户不在当前页面的无会话支付 ]); // 处理支付结果 if ($paymentIntent->status === 'succeeded') { // 支付成功,更新你的订单状态等业务逻辑 } else { // 处理支付失败,比如提示用户卡片过期、余额不足等 }
方式2:用Charge(旧版API,适配你当前的Stripe_Customer类)
如果你还在使用旧版Stripe API,可以用Charge接口:
$charge = Stripe_Charge::create([ 'amount' => 1000, 'currency' => 'usd', 'customer' => $selectedPaymentMethod['stripe_customer_id'], // 自动关联客户的默认支付方式 // 或者指定具体的旧版Card ID(source) // 'source' => $selectedCardId, ]);
关键注意点
- 禁止重复创建Customer:这是你之前的核心问题,一定要先查询数据库是否已有当前用户的Customer ID,避免每次支付都生成新客户。
- 完整存储支付信息:把Customer ID、PaymentMethod/Card ID、卡片后四位都存在数据库,既方便用户选择卡片,也能确保二次支付时能准确调用。
- API版本兼容:如果你的Stripe PHP库是较新版本,建议优先使用
PaymentMethod和PaymentIntent,这是Stripe目前推荐的标准方案。
内容的提问来源于stack exchange,提问作者Hashaam zahid




