Laravel集成Stripe时支付失败但订单仍创建问题排查
Stripe支付失败仍创建订单的问题排查与修复
核心问题定位
支付失败却生成订单,本质是订单创建逻辑和支付结果验证的执行顺序错误,或者支付结果验证环节缺失/失效。
常见错误场景及修复方案
1. Laravel端:订单创建在支付验证之前
错误示例
如果你的Payment Controller先执行订单保存,再验证支付状态,就会导致支付失败后订单已经入库:
public function store(Request $request) { // 直接创建订单,未验证支付 $order = Order::create($request->all()); // 后续才验证支付,失败时订单已存在 $paymentIntent = \Stripe\PaymentIntent::retrieve($request->payment_intent_id); if ($paymentIntent->status !== 'succeeded') { return response()->json(['error' => '支付失败'], 400); } return response()->json(['success' => true]); }
修正方案
先通过Stripe API验证支付状态,确认成功后再创建订单:
public function store(Request $request) { // 1. 优先验证Stripe支付结果 try { $paymentIntent = \Stripe\PaymentIntent::retrieve($request->payment_intent_id); if ($paymentIntent->status !== 'succeeded') { return response()->json(['error' => '支付未完成'], 400); } } catch (\Exception $e) { return response()->json(['error' => '支付验证失败:'.$e->getMessage()], 400); } // 2. 支付成功后再创建订单 $order = Order::create($request->all()); return response()->json(['success' => true, 'order' => $order]); }
2. Laravel端:Webhook支付失败事件未正确处理
如果依赖Stripe Webhook同步支付状态,可能存在以下问题:
- 未监听
payment_intent.payment_failed事件 - Webhook签名验证失败导致事件未处理,订单已提前标记为完成
排查与修复
- 登录Stripe Dashboard查看Webhook日志,确认失败事件是否成功发送到你的Laravel服务
- 确保Webhook路由正确验证签名并处理失败事件:
public function handleWebhook(Request $request) { $payload = $request->getContent(); $sigHeader = $request->header('Stripe-Signature'); $webhookSecret = config('services.stripe.webhook_secret'); try { $event = \Stripe\Webhook::constructEvent( $payload, $sigHeader, $webhookSecret ); } catch (\Exception $e) { return response()->json(['error' => $e->getMessage()], 400); } // 处理支付失败事件,更新订单状态 if ($event->type === 'payment_intent.payment_failed') { $paymentIntent = $event->data->object; $order = Order::where('payment_intent_id', $paymentIntent->id)->first(); if ($order) { $order->update(['status' => 'failed']); } } return response()->json(['success' => true]); }
注意:用Webhook模式时,订单创建后需默认标记为pending,待Webhook确认支付成功后再改为paid,避免直接设置为已支付状态。
3. Flutter端:未判断支付结果就发起订单请求
错误示例
如果Flutter代码在支付失败时仍调用了Laravel的订单创建接口,也会导致无效订单生成:
Stripe.instance.confirmPayment( paymentIntentClientSecret: clientSecret, data: PaymentMethodCard( number: cardNumber, expMonth: expMonth, expYear: expYear, cvc: cvc, ), ).then((value) { // 未判断支付状态就发送订单请求 http.post(Uri.parse('https://your-api.com/orders'), body: orderData); }).catchError((error) { print(error); });
修正方案
仅在支付成功时才调用订单创建接口:
try { final paymentResult = await Stripe.instance.confirmPayment( paymentIntentClientSecret: clientSecret, data: PaymentMethodCard( number: cardNumber, expMonth: expMonth, expYear: expYear, cvc: cvc, ), ); if (paymentResult.status == PaymentIntentsStatus.Succeeded) { // 支付成功后才提交订单 await http.post(Uri.parse('https://your-api.com/orders'), body: { ...orderData, 'payment_intent_id': paymentResult.paymentIntentId, }); } else { print('支付失败:${paymentResult.status}'); } } catch (e) { print('支付错误:$e'); }
关键验证原则
- 永远不要信任前端传来的支付成功状态,必须在Laravel端通过Stripe API或Webhook二次验证
- 订单状态需区分
pending、paid、failed三种状态,避免直接创建为已支付状态 - 确保Stripe API调用时捕获异常(比如网络错误、无效的payment_intent_id)
内容的提问来源于stack exchange,提问作者Pramod Yadav
相关产品推荐
相关产品推荐

