PAYATHON 2026

basic auth 是否会导致支付回跳后 session 变量丢失?

支付小周

结论

通常不会。Basic Auth 和应用 Session 是两套相互独立的机制:

  • Basic Auth 通过 Authorization 请求头验证访问权限。
  • Session 通常通过 Cookie 中的 Session ID 识别用户。

Basic Auth 不会主动清空 Session 变量。支付回跳后 Session 看似丢失,通常是因为浏览器没有带上原来的 Session Cookie,或者服务端新建了 Session。

常见原因

跨站 POST 受到 SameSite 限制

如果支付网关通过跨站 POST 回跳,而 Session Cookie 设置为 SameSite=Lax 或 SameSite=Strict,浏览器可能不会在这次请求中发送 Cookie。

服务端收不到原有 Cookie,就会创建新的 Session,于是原来的变量看起来全都消失了。

如果需要让跨站 POST 携带 Cookie,可以这样配置:

Set-Cookie: PHPSESSID=...; Path=/; SameSite=None; Secure; HttpOnly

需要注意:

  • 设置 SameSite=None 时,通常还必须设置 Secure。
  • Secure Cookie 只能通过 HTTPS 发送。
  • 浏览器及其隐私策略仍可能限制第三方 Cookie,因此支付流程不能完全依赖 Session。

以 PHP 为例,需要在 session_start() 之前设置 Cookie 参数:

<?php

session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => '',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'None',
]);

session_start();

如果支付平台使用普通的顶层 GET 重定向,SameSite=Lax 通常就够了。只有确认回跳方式和浏览器行为确实需要时,才应改为 None。

回跳地址与原站点不一致

Cookie 会受到域名、路径和 HTTPS 等属性的限制。对 Session Cookie 来说,下面这些地址的作用域可能不同:

https://example.com
https://www.example.com
http://example.com
https://pay.example.com

检查时应关注:

  • 支付前与回跳后的域名是否完全一致;
  • 是否从 HTTPS 回到了 HTTP;
  • Cookie 的 Domain 是否正确;
  • Cookie 的 Path 是否覆盖回跳地址;
  • 是否使用了不同端口或不同开发环境。

例如,Cookie 设置为:

Set-Cookie: PHPSESSID=...; Path=/checkout

而回跳地址是 /payment/callback,浏览器就不会发送这个 Cookie。通常可以把路径设为:

Set-Cookie: PHPSESSID=...; Path=/

回跳请求不是用户浏览器发出的

支付系统通常有两类通知:

  1. 浏览器回跳:支付完成后,由用户浏览器访问商户网站。
  2. 服务端通知:支付网关服务器直接请求商户的 webhook 或 callback。

服务端通知不会携带用户浏览器中的 Session Cookie,也不会自动带上浏览器保存的 Basic Auth 凭证。

如果所谓的 “post back” 实际上来自支付网关服务器,那么无法通过该请求读取用户原来的 Session 是正常的。此时 Basic Auth 还可能导致通知请求收到 401 Unauthorized,除非支付网关支持配置认证信息。

可以通过访问日志确认请求来源,并检查以下请求头是否存在:

Authorization
Cookie
User-Agent
Referer

不要记录完整的认证凭证或 Session ID。调试时确认相应请求头是否存在即可。

Session ID 被重新生成或 Session 存储不一致

还需要排查这些情况:

  • 回跳代码没有执行 session_start();
  • 支付前调用了 session_destroy();
  • 登录或认证逻辑调用了 session_regenerate_id(),但没有正确迁移 Session 数据;
  • Session 已过期;
  • 多台应用服务器没有共享 Session 存储;
  • 回跳请求被路由到另一套环境或另一台机器;
  • Session 文件目录、Redis 或数据库连接配置发生变化;
  • 回跳处理逻辑覆盖了 $_SESSION。

PHP 中可以临时比较支付前后的 Session ID:

<?php

session_start();

error_log('session_id_hash=' . hash('sha256', session_id()));
error_log('session_keys=' . implode(',', array_keys($_SESSION)));

不要把原始 Session ID、支付凭证或完整的 Session 内容写入日志。

推荐的排查步骤

先打开浏览器开发者工具,对比支付前与回跳时的请求:

  1. 查看支付前的 Session Cookie 名称和属性。
  2. 确认回跳请求是否携带同一个 Cookie。
  3. 确认回跳使用 GET 还是跨站 POST。
  4. 对比支付前后的 Session ID。
  5. 检查回跳地址的域名、协议和路径。
  6. 查看应用日志,确认是否发生了 Session 重建、过期或存储读取失败。
  7. 确认请求来自用户浏览器还是支付网关服务器。
  8. 暂时移除 Basic Auth 进行对照测试,但不要把这当成最终修复方案。

如果回跳请求没有带上原来的 Cookie,通常需要检查 Cookie 属性和回跳方式。如果 Cookie 与 Session ID 都没有变化,但数据仍然缺失,则应检查服务端 Session 存储和业务代码。

支付状态不要只保存在 Session 中

支付流程可能持续较长时间,还可能经过多个域名、设备或异步请求。订单号、支付状态及其与用户的关联关系不应只保存在 Session 中,而应写入数据库。

发起支付前创建订单:

<?php

session_start();

$orderId = bin2hex(random_bytes(16));

// 将订单写入数据库,状态设为 pending
createOrder([
    'order_id' => $orderId,
    'user_id' => $_SESSION['user_id'],
    'status' => 'pending',
]);

$state = hash_hmac('sha256', $orderId, PAYMENT_STATE_SECRET);

$paymentUrl = buildPaymentUrl([
    'order_id' => $orderId,
    'state' => $state,
    'return_url' => 'https://example.com/payment/return',
]);

header('Location: ' . $paymentUrl);
exit;

处理回跳时,不要把 Session 当作唯一依据。应先验证返回参数,再查询订单:

<?php

$orderId = $_POST['order_id'] ?? '';
$state = $_POST['state'] ?? '';

$expectedState = hash_hmac('sha256', $orderId, PAYMENT_STATE_SECRET);

if ($orderId === '' || !hash_equals($expectedState, $state)) {
    http_response_code(400);
    exit('Invalid payment response');
}

$order = findOrderById($orderId);

if ($order === null) {
    http_response_code(404);
    exit('Order not found');
}

// 仍需根据支付平台要求验证签名,或通过服务端 API 查询支付结果。

示例中的 state 只能防止订单标识被随意篡改,不能代替支付网关要求的签名验证。支付是否成功,应以经过验签的 webhook 或支付平台服务端 API 的查询结果为准,不能只相信浏览器回跳参数。

Basic Auth 的实际影响

Basic Auth 通常不会造成 Session 变量丢失,但可能影响支付集成的其他环节:

  • 如果用户浏览器已经保存凭证,浏览器回跳可能正常通过认证。
  • 支付网关发出的服务端通知通常不带这些凭证,可能被 Basic Auth 拦截。
  • 某些浏览器在跨域跳转、隐私模式或认证挑战后的行为可能不同,但排查时仍应先确认 Cookie 是否随请求发送。

在开发环境中,可以只对支付通知路径豁免 Basic Auth,再使用支付平台的签名校验保护接口。路径免认证不代表请求可信,也不应使用 IP 白名单代替签名验证,除非支付平台明确提供并要求这种机制。

Basic Auth、用户 Session 和支付回调认证应分别处理。Basic Auth 用于保护开发站点,Session 用于改善用户体验,数据库中的订单记录和支付签名则用于保证支付流程的正确性。

备注:内容仅供参考。