Laravel电商网站基于Stripe实现类Uber Eats支付方式存储功能咨询
嘿,刚好我之前在Laravel项目里实现过类似Uber Eats的支付方式存储功能,完全基于Stripe的合规方案,不用碰任何敏感银行卡信息,给你梳理下核心思路和具体步骤:
Stripe的Customer对象是实现支付方式存储的核心——我们只需要把用户关联到一个Stripe Customer,然后将用户的支付方式(银行卡、PayPal等)关联到这个Customer上。所有敏感的支付信息都由Stripe托管,我们的数据库只存Stripe返回的payment_method_id和stripe_customer_id,完全符合PCI合规要求,不用自己承担存储风险。
我假设你已经用Laravel Cashier或者直接集成了Stripe SDK,下面分步骤来:
1. 准备工作:关联用户与Stripe Customer
首先给你的users表加一个字段stripe_customer_id(字符串类型,可为空),用来存储Stripe侧的Customer ID。
然后在用户注册或者首次使用支付功能时,创建Stripe Customer并关联:
// 在User模型里添加方法 public function createStripeCustomer() { if ($this->stripe_customer_id) { return $this->stripe_customer_id; } $customer = \Stripe\Customer::create([ 'email' => $this->email, 'name' => $this->name, ]); $this->update(['stripe_customer_id' => $customer->id]); return $customer->id; }
如果你用Laravel Cashier的话更简单,直接调用$user->createAsStripeCustomer()就行,Cashier会自动帮你维护stripe_customer_id字段。
2. 前端收集支付方式(关键:绝不碰原始卡号)
必须用Stripe的前端组件(Elements或者Payment Request Button)来收集支付信息,绝对不能把卡号、CVC等信息传到自己的服务器。
举个简单的前端示例(用Stripe.js v3):
<!-- 加载Stripe.js --> <script src="https://js.stripe.com/v3/"></script> <div id="card-element"></div> <button id="save-payment-method">保存支付方式</button> <script> const stripe = Stripe('你的Stripe公钥'); const elements = stripe.elements(); const cardElement = elements.create('card'); cardElement.mount('#card-element'); // 点击保存按钮时处理 document.getElementById('save-payment-method').addEventListener('click', async () => { // 先从后端获取Setup Intent(用来安全初始化支付方式绑定) const setupIntentResponse = await fetch('/api/create-setup-intent', { method: 'POST' }); const setupIntent = await setupIntentResponse.json(); // 确认Setup Intent,获取PaymentMethod const { error, paymentMethod } = await stripe.confirmCardSetup( setupIntent.client_secret, { payment_method: { card: cardElement, billing_details: { name: '用户姓名' } } } ); if (error) { // 处理错误,比如卡片无效、3DS认证失败等 alert(error.message); } else { // 将paymentMethod.id传到后端,关联到用户的Stripe Customer await fetch('/api/save-payment-method', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ payment_method_id: paymentMethod.id }) }); alert('支付方式已保存'); } }); </script>
3. 后端绑定PaymentMethod到Customer
后端接收前端传来的payment_method_id,将其关联到用户的Stripe Customer上:
// 路由:/api/save-payment-method public function savePaymentMethod(Request $request) { $user = auth()->user(); // 确保用户已经有Stripe Customer $user->createStripeCustomer(); // 绑定PaymentMethod到Customer \Stripe\PaymentMethod::retrieve($request->payment_method_id)->attach([ 'customer' => $user->stripe_customer_id, ]); // 可选:设置为默认支付方式 \Stripe\Customer::update($user->stripe_customer_id, [ 'invoice_settings' => [ 'default_payment_method' => $request->payment_method_id, ], ]); return response()->json(['success' => true]); }
用Cashier的话可以简化成:
$user->addPaymentMethod($request->payment_method_id); // 设置默认 $user->updateDefaultPaymentMethod($request->payment_method_id);
4. 使用存储的支付方式发起后续支付
当用户下单时,直接用已存储的PaymentMethod发起支付:
// 方式1:使用默认支付方式 $charge = $user->charge( 1000, // 金额,单位:分(比如10美元就是1000) $user->defaultPaymentMethod()->id ); // 方式2:让用户选择已存储的某个PaymentMethod $paymentMethods = $user->paymentMethods(); // 获取所有已存储的支付方式 $charge = $user->charge(1000, $selectedPaymentMethodId); // 检查支付状态 if ($charge->status === 'succeeded') { // 处理订单逻辑 }
5. 管理支付方式(查看、删除、设默认)
给用户提供管理支付方式的功能:
// 获取所有已存储的支付方式 $paymentMethods = $user->paymentMethods()->map(function ($method) { return [ 'id' => $method->id, 'brand' => $method->card->brand, 'last4' => $method->card->last4, 'exp_month' => $method->card->exp_month, 'exp_year' => $method->card->exp_year, 'is_default' => $method->id === $user->defaultPaymentMethod()?->id ]; }); // 删除某个支付方式 $user->deletePaymentMethod($paymentMethodId); // 设置默认支付方式 $user->updateDefaultPaymentMethod($paymentMethodId);
- 绝对禁止自己处理原始银行卡信息,所有收集流程必须通过Stripe的前端组件,否则会违反PCI合规
- 一定要处理Setup Intent和Payment Intent的错误场景,比如3DS认证需要用户额外验证、卡片过期、余额不足等
- 建议配置Stripe Webhook,监听
payment_method.attached、customer.updated等事件,同步支付方式的状态到自己的数据库 - 测试阶段用Stripe的测试卡号:
4242 4242 4242 4242,过期日期填未来的月份/年份,CVC随便填3位数字
内容的提问来源于stack exchange,提问作者mkirr

