PHP支付后偶发登出跳转首页问题求助(生产环境)
支付成功后偶尔自动登出跳转到首页的问题排查与解决方案
核心问题分析
生产环境中出现的偶发登出,本质是支付网关回调/跳转时,用户会话已失效或无法被正确识别:
- 本地环境会话生命周期长,支付流程耗时短,会话不会过期;生产环境会话垃圾回收时间短、用户支付操作耗时久(比如跳转银行页面等待时间长),导致会话过期。
- 支付网关的同步跳转请求可能无法携带完整的会话Cookie(比如跨域场景下SameSite配置问题),导致
pay_response.php判定用户未登录。
具体解决方案
1. 核心逻辑脱离会话依赖,改用订单标识验证
不要依赖$_SESSION判断支付回调的合法性,而是通过支付请求时生成的唯一订单ID关联用户:
修改
payment.php:生成customer_id后,将订单信息存入数据库,关联当前用户:<?php // 优化会话配置 ini_set('session.gc_maxlifetime', 7200); session_set_cookie_params([ 'lifetime' => 7200, 'path' => '/', 'domain' => 'demosite.tripura.gov.in', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax' ]); session_start(); // 前端访问校验 if (!isset($_SESSION["user_mobile"])) { header("Location: index.php"); exit(); } // 响应头设置 header("X-Frame-Options: SAMEORIGIN"); header('X-Content-Type-Options: nosniff'); header("X-XSS-Protection: 1; mode=block"); header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"); header("Referrer-Policy: same-origin"); ?> <html> <head></head> <body> <?php $merchant_id = "xxxx"; $security_id = "xxxx"; $checksum_key = "xxxx"; $customer_id = round(microtime(true) * 10000); $amount = 10.00; $additionaltxt1 = "xxxx"; $user_uid = $_SESSION['user_uid']; // 假设会话中存储用户UID $return_url = 'https://demosite.tripura.gov.in/pay_response.php'; // 插入待支付订单到数据库(需提前创建payment_orders表) $pdo = new PDO("mysql:host=localhost;dbname=your_db", "db_user", "db_pass"); $stmt = $pdo->prepare("INSERT INTO payment_orders (customer_id, user_uid, amount, status, created_at) VALUES (?, ?, ?, 'pending', NOW())"); $stmt->execute([$customer_id, $user_uid, $amount]); // 生成支付字符串 $str = $merchant_id . '|' . $customer_id . '|NA|'.$amount.'|NA|NA|NA|INR|NA|R|'.$security_id . '|NA|NA|F|' . $additionaltxt1 . '|'.$user_uid.'|NA|NA|NA|NA|NA|'.$return_url; $checksum = strtoupper(hash_hmac("sha256", $str, $checksum_key, false)); $payment_string = $str . '|' . $checksum; ?> <form method="POST" action="https://pgi.billdesk.com/pgidsk/PGIMerchantPayment"> <input type="hidden" name="msg" value="<?php echo $payment_string; ?>"> <input type="Submit" class="btn btn-primary btn-md" value='PAY'> </form> </body> </html>修改
pay_response.php:优先通过订单ID验证请求合法性,会话仅用于前端展示:<?php $checksum_key = "xxxx"; // 和payment.php一致 if (!isset($_POST['msg'])) { header("Location: index.php"); exit(); } // 1. 校验回调签名,防止伪造请求 $resp_str = $_POST['msg']; $resp_arr = preg_split("/\|/", $resp_str); $returned_checksum = array_pop($resp_arr); $str_to_verify = implode('|', $resp_arr); $calculated_checksum = strtoupper(hash_hmac("sha256", $str_to_verify, $checksum_key, false)); if ($calculated_checksum !== $returned_checksum) { error_log("非法支付回调: " . $resp_str); header("Location: index.php"); exit(); } // 2. 通过customer_id查询订单 $customer_id = $resp_arr[1]; // 对应请求时传入的customer_id字段索引 $pdo = new PDO("mysql:host=localhost;dbname=your_db", "db_user", "db_pass"); $stmt = $pdo->prepare("SELECT * FROM payment_orders WHERE customer_id = ?"); $stmt->execute([$customer_id]); $order = $stmt->fetch(PDO::FETCH_ASSOC); if (!$order) { error_log("未找到对应订单: " . $customer_id); header("Location: index.php"); exit(); } // 3. 更新订单状态 $response_code = $resp_arr[14]; $status = ($response_code === '000') ? 'success' : 'failed'; $stmt = $pdo->prepare("UPDATE payment_orders SET status = ?, response_msg = ? WHERE customer_id = ?"); $stmt->execute([$status, $resp_str, $customer_id]); // 4. 前端展示:会话存在则正常显示,不存在则提示登录查看 ini_set('session.gc_maxlifetime', 7200); session_set_cookie_params([ 'lifetime' => 7200, 'path' => '/', 'domain' => 'demosite.tripura.gov.in', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax' ]); session_start(); ?> <!doctype html> <html lang="en"> <head></head> <body> <?php if (isset($_SESSION["user_mobile"])): ?> <h3>支付<?= $status === 'success' ? '成功' : '失败' ?></h3> <p>订单号: <?= $customer_id ?></p> <p>金额: <?= $order['amount'] ?> INR</p> <?php else: ?> <h3>支付<?= $status === 'success' ? '成功' : '失败' ?></h3> <p>订单号: <?= $customer_id ?></p> <p>请<a href="index.php">登录</a>查看订单详情</p> <?php endif; ?> </body> </html>
2. 优化生产环境会话配置,防止会话过早过期
在所有需要会话的页面(payment.php、pay_response.php等)的session_start()前添加以下配置,延长会话有效期并增强Cookie安全性:
ini_set('session.gc_maxlifetime', 7200); // 2小时有效期 session_set_cookie_params([ 'lifetime' => 7200, 'path' => '/', 'domain' => 'demosite.tripura.gov.in', 'secure' => true, // HTTPS环境下启用,防止Cookie明文传输 'httponly' => true, // 禁止JS读取Cookie,防范XSS 'samesite' => 'Lax' // 允许跨域跳转时携带Cookie,适配支付网关场景 ]);
3. 启用支付网关异步通知(Webhook)
同步跳转受限于浏览器会话,异步通知是更可靠的支付结果通知方式:
- 联系BillDesk配置异步通知URL(比如
https://demosite.tripura.gov.in/pay_webhook.php),网关会主动向该URL发送支付结果。 - 编写
pay_webhook.php,逻辑和pay_response.php的校验、订单更新部分一致,无需会话验证,仅处理订单状态更新。
内容的提问来源于stack exchange,提问作者user3099225
相关产品推荐
相关产品推荐

