如何添加Stripe订单确认功能并保留活动注册表单数据?
Stripe活动报名支付流程修复方案
问题根源
- 表单监听ID不匹配:表单ID是
registrationForm,但JS代码里监听的是mainForm,导致表单提交时JS逻辑根本没触发,直接走了表单默认的POST跳转,checkout.php里的POST处理逻辑没执行,表单数据没存进session。 - 前后端响应不匹配:
checkout.php的POST请求只返回{"success":true},但前端代码期望拿到带id的session对象,所以前端不会跳转到Stripe支付页面,反而直接提交到checkout.php的GET请求,此时session里没有表单数据,后续支付成功后自然拿不到数据。 - Session传递数据不可靠:依赖用户浏览器session传递表单数据,支付完成后跳转回站点时,可能因为会话过期、跨域(Stripe跳转时上下文丢失)导致session数据丢失,更可靠的方式是把表单数据绑定到Stripe的Checkout Session中。
修复步骤
1. 前端代码修正
- 修正表单监听的ID,移除表单默认的
action和method属性,避免默认跳转 - 统一Stripe实例,不要重复初始化
修改后的表单代码片段:
<form id="registrationForm"> <!-- 移除action和method --> <!-- 原表单字段保持不变 --> <script> // 统一初始化Stripe实例 const stripe = Stripe("pk_test_51OWirXLdOaRkelq7e41bzMCHiHQQ8DM58BMQ1OOsep64ge470Kf45kjHaav6DSDH3blukq8vozEQK1Dfk4H80bqC000HPnvHxo"); // 监听正确的表单ID document.getElementById('registrationForm').addEventListener('submit', function (event) { event.preventDefault(); // 显示加载提示 document.getElementById('loadingMessage').style.display = 'block'; fetch('checkout.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ nome: document.getElementById('nome').value, cognome: document.getElementById('cognome').value, email: document.getElementById('email').value, telefono: document.getElementById('telefono').value, discoteca: document.getElementById('SerataNome').value, faiTavolo: document.getElementById('faiTavolo').value, saltaCoda: document.getElementById('saltaCoda').value, quantitaPersone: document.getElementById('quantitaPersone').value, ricordaWhatsapp: document.getElementById('ricordaWhatsapp').checked, archiviazioneDati: document.getElementById('archiviazioneDati').value, invioPromozioni: document.getElementById('invioPromozioni').value, }), }) .then(response => response.json()) .then(result => { if (result.sessionId) { stripe.redirectToCheckout({ sessionId: result.sessionId }); } else { alert('Errore durante la creazione della sessione di checkout.'); document.getElementById('loadingMessage').style.display = 'none'; } }) .catch(error => { console.error('Error:', error); alert('Si è verificato un errore durante il pagamento.'); document.getElementById('loadingMessage').style.display = 'none'; }); }); </script> <button type="submit" id="submitBtn">PAGA</button> </form>
2. 后端checkout.php修正
- 仅在POST请求时创建Checkout Session,把表单数据存入Session的metadata(同时保留session备份)
- 返回Checkout Session的ID给前端,让前端跳转
修改后的checkout.php代码:
<?php require_once 'init.php'; require_once 'config.php'; $stripe_secret_key = "sk_test_*****5"; \Stripe\Stripe::setApiKey($stripe_secret_key); session_start(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 获取表单数据 $formData = json_decode(file_get_contents("php://input"), true); // 存到session备份 $_SESSION['form_data'] = $formData; // 创建Checkout Session,把表单数据存入metadata $checkout_session = \Stripe\Checkout\Session::create([ 'mode' => 'payment', 'success_url' => 'https://www.chesifastasera.com/vendor/stripe-php-master/processaQrcode.php?session_id={CHECKOUT_SESSION_ID}', 'cancel_url' => 'https://www.chesifastasera.com/你的取消页面URL', 'customer_email' => $formData['email'], // 预填用户邮箱 'line_items' => [ [ 'quantity' => (int)$formData['quantitaPersone'], 'price_data' => [ 'currency' => 'EUR', 'unit_amount' => 20000, // 注意这里是分,20000=200EUR 'product_data' => [ 'name' => 'SerataTest - ' . $formData['discoteca'] ] ] ] ], 'metadata' => $formData, // 把所有表单数据存入metadata ]); // 返回session ID给前端 echo json_encode(['sessionId' => $checkout_session->id]); exit(); } // GET请求直接返回错误(因为表单提交应该走POST) http_response_code(405); echo json_encode(['error' => 'Metodo non consentito']); ?>
3. processaQrcode.php处理逻辑
- 从Checkout Session中获取表单数据(优先),或者从session中获取
- 处理数据(比如生成二维码、存储到数据库等)
示例代码:
<?php require_once 'init.php'; require_once 'config.php'; $stripe_secret_key = "sk_test_*****5"; \Stripe\Stripe::setApiKey($stripe_secret_key); session_start(); // 获取Checkout Session ID $sessionId = $_GET['session_id'] ?? ''; $formData = []; if ($sessionId) { // 从Stripe获取Session数据,拿到metadata里的表单信息 try { $session = \Stripe\Checkout\Session::retrieve($sessionId); // 验证支付是否成功 if ($session->payment_status === 'paid') { $formData = $session->metadata; } else { // 支付未完成,跳转回错误页面 header("Location: https://www.chesifastasera.com/支付失败页面"); exit(); } } catch (\Exception $e) { // 从session备份获取数据 $formData = $_SESSION['form_data'] ?? []; } } else { $formData = $_SESSION['form_data'] ?? []; } // 检查是否有表单数据 if (empty($formData)) { header("Location: https://www.chesifastasera.com/表单数据丢失页面"); exit(); } // 处理表单数据,比如生成二维码、存储到数据库等 // 调用你的业务逻辑,比如: // include '你的二维码生成逻辑.php'; // generateQrcode($formData); // 清空session数据,避免重复处理 unset($_SESSION['form_data']); // 跳转成功页面 header("Location: https://www.chesifastasera.com/支付成功页面"); ?>
4. 更可靠的方案:使用Stripe Webhook
依赖前端跳转可能存在用户中途关闭页面的情况,导致processaQrcode.php没执行。建议配置Stripe Webhook,当支付成功时自动触发你的后端逻辑:
- 在Stripe Dashboard中创建Webhook,监听
checkout.session.completed事件 - 设置Webhook URL为你的后端处理地址(比如
https://www.chesifastasera.com/webhook.php) - 在
webhook.php中验证Stripe签名,然后获取Session的metadata,调用processaQrcode.php的逻辑
示例webhook.php:
<?php require_once 'init.php'; require_once 'config.php'; $stripe_secret_key = "sk_test_*****5"; $webhook_secret = "whsec_你的webhook密钥"; \Stripe\Stripe::setApiKey($stripe_secret_key); $payload = @file_get_contents('php://input'); $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE']; $event = null; try { $event = \Stripe\Webhook::constructEvent( $payload, $sig_header, $webhook_secret ); } catch(\UnexpectedValueException $e) { // 无效payload http_response_code(400); exit(); } catch(\Stripe\Exception\SignatureVerificationException $e) { // 无效签名 http_response_code(400); exit(); } // 处理checkout.session.completed事件 if ($event->type === 'checkout.session.completed') { $session = $event->data->object; // 验证支付状态 if ($session->payment_status === 'paid') { $formData = $session->metadata; // 调用processaQrcode的逻辑 include 'processaQrcode.php'; // 假设processaQrcode.php里有处理函数 processQrcodeData($formData); } } http_response_code(200); ?>
内容的提问来源于stack exchange,提问作者Alessio Torregrossa
相关产品推荐
相关产品推荐

