Stripe支付异常:无法存储数据到数据库,且出现Token重复使用报错
Stripe支付错误排查与修复
错误原因分析
- Token重复使用:Stripe支付令牌(
tok_xxx)是一次性凭证,当你用它创建Customer后,令牌会立即失效。如果同一令牌被重复提交到后端,就会触发You cannot use a Stripe token more than once错误。你能在Stripe仪表盘看到测试金额,说明第一次请求已成功创建Charge,但后续重复执行了相同逻辑,导致报错。 - 数据库未存储数据:你的代码仅处理了Stripe的API调用,完全没有数据库插入逻辑;且致命错误会直接中断代码执行,即使第一次请求成功,也没机会把数据写入数据库。
修复步骤
1. 解决Token重复使用问题
- 前端防重复提交:给支付按钮添加置灰逻辑,用户点击后立即禁用按钮,避免多次提交同一令牌。
- 后端幂等性处理:给Stripe API请求添加
idempotency_key参数,确保同一请求只会被执行一次。用你生成的$orderID作为幂等键即可。 - 确保令牌单次使用:前端每次发起支付请求时,必须通过Stripe.js/Elements重新生成新令牌,禁止复用旧令牌。
2. 修复数据库存储问题
- 在Stripe Charge创建成功后,添加数据库插入代码,将订单、用户、支付相关信息写入数据库。
- 用
try-catch包裹Stripe API调用,捕获错误并记录日志,避免致命错误中断流程。
修改后的代码示例
// set API Key $stripe = array( "SecretKey"=>"sk_test_51PNnHTGMcptyt0mOGyt8G6VmAvXhlJ7ojBpzdlI1Ey2JLrDghSZ2Nz5UszwSJr9madoDm", "PublishableKey"=>"pk_test_51PNnHTGMcptyt0mOSgd8HrIaMr0mj0DM6lyd7W2EqlnzL3L4CHKJ0Yf2EPd9Qid8dOeGXNcwK8j" ); \Stripe\Stripe::setApiKey($stripe['SecretKey']); // Generate Unique order ID $orderID = strtoupper(str_replace('.','',uniqid('', true))); // 准备幂等键 $idempotencyKey = $orderID; try { // Add customer to stripe - 加入幂等键 $customer = \Stripe\Customer::create(array( 'email' => $email, 'source' => $token, 'name' => $name, 'description'=>$des ), array('idempotency_key' => $idempotencyKey)); // Convert price to cents $itemPrice = ($price*100); $currency = "usd"; // Charge a credit or a debit card - 加入幂等键 $charge = \Stripe\Charge::create(array( 'amount' => $itemPrice, 'currency' => $currency, 'customer' => $customer->id, 'description' => $des ), array('idempotency_key' => $idempotencyKey)); // -------------------------- // 数据库插入逻辑示例(根据你的表结构调整) // -------------------------- $pdo = new PDO('mysql:host=localhost;dbname=your_database', 'db_user', 'db_password'); $stmt = $pdo->prepare("INSERT INTO orders (order_id, email, stripe_customer_id, stripe_charge_id, amount, currency, description) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([ $orderID, $email, $customer->id, $charge->id, $price, // 存储原金额(非分单位) $currency, $des ]); // 支付成功跳转 header("Location: success.php?order=$orderID"); exit; } catch (\Stripe\Exception\ApiErrorException $e) { // 记录Stripe错误日志 error_log('Stripe Error: ' . $e->getMessage()); // 返回错误提示 header("Location: payment_error.php?msg=" . urlencode($e->getMessage())); exit; } catch (PDOException $e) { // 记录数据库错误日志 error_log('DB Error: ' . $e->getMessage()); header("Location: payment_error.php?msg=订单存储失败"); exit; }
额外注意事项
- 禁止将Stripe Secret Key暴露在前端或公开代码中,仅在后端环境使用。
- 测试时使用Stripe官方测试卡号(如
4242 4242 4242 4242),避免使用真实卡信息。 - 建议配置Stripe Webhook接收支付状态通知,确保后端流程中断时,仍能同步支付结果到数据库。
内容的提问来源于stack exchange,提问作者Nazmul Hossain Pappu
相关产品推荐
相关产品推荐

