You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Stripe创建客户账单管理静态链接时session_id调用报错问题

问题原因与修复方案

核心报错原因

你遇到的Fatal Error是因为调用\Stripe\Checkout\Session::retrieve()时传入的ID为NULL,Stripe SDK无法识别空的会话ID发起请求。

现有代码的所有问题点

  • 检索Checkout Session的参数逻辑错误:你将从数据库取出的$session_id作为POST参数的键名去取值,而非直接传入会话ID本身,正确写法要么直接传$session_id,要么取表单提交的$_POST['session_id']
  • 前端隐藏输入框未赋值:表单里的session_id隐藏字段value为空,提交表单时不会携带有效会话ID
  • $return_url变量未定义:创建Stripe客户门户会话时没有传入合法的跳转地址,后续会触发额外报错
  • SQL注入风险:直接将$_SESSION["uid"]拼接进SQL语句,没有做参数过滤或预处理,存在数据泄露风险
  • 逻辑冗余:要实现永久的订阅管理入口,无需存储Checkout Session ID,直接给用户表存储Stripe客户ID(customer ID),创建门户会话时直接调用即可,稳定性更高

修复后的代码示例

1. 后端逻辑(create-portal-session.php)

<?php
require 'vendor/autoload.php';
// 替换为你的Stripe密钥
\Stripe\Stripe::setApiKey('sk_test_123');

include('../includes/connection.php');
session_start();
// 校验用户登录状态
if (!isset($_SESSION["uid"])) {
    http_response_code(403);
    exit(json_encode(['error' => '请先登录']));
}
$id = $_SESSION["uid"];
$return_url = 'https://你的域名.com/account'; // 替换为用户跳转回你站点的地址

// 改用预处理语句避免SQL注入
$stmt = $conn->prepare("SELECT stripe_customer_id, checkout_session_id FROM mollby_users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_object();
$stmt->close();

try {
    // 优先用存储的Stripe客户ID创建门户会话,不用依赖Checkout Session
    if (!empty($user->stripe_customer_id)) {
        $customer_id = $user->stripe_customer_id;
    } else {
        // 没有客户ID的情况下再查Checkout Session获取,同时把客户ID存到用户表方便后续使用
        if (empty($user->checkout_session_id)) {
            throw new Exception("未找到有效的订阅记录");
        }
        $checkout_session = \Stripe\Checkout\Session::retrieve($user->checkout_session_id);
        $customer_id = $checkout_session->customer;
        // 把客户ID存到用户表,后续直接用
        $update_stmt = $conn->prepare("UPDATE mollby_users SET stripe_customer_id = ? WHERE id = ?");
        $update_stmt->bind_param("si", $customer_id, $id);
        $update_stmt->execute();
        $update_stmt->close();
    }

    // 创建客户门户会话
    $portal_session = \Stripe\BillingPortal\Session::create([
        'customer' => $customer_id,
        'return_url' => $return_url,
    ]);

    // 跳转到Stripe门户页面
    header("Location: " . $portal_session->url);
    exit();
} catch (Exception $e) {
    http_response_code(500);
    echo json_encode(['error' => $e->getMessage()]);
}
?>

2. 前端账户页代码

<form action="/create-portal-session.php" method="POST">
    <button id="checkout-and-portal-button" type="submit">管理你的订阅信息</button>
</form>

额外优化说明

  • 后续用户完成Stripe Checkout订阅后,直接把返回的customer ID存到你的用户表,不需要再依赖checkout session ID,永久有效
  • 客户门户会话本身是临时的,但只要你存了customer ID,每次用户点击按钮都可以生成新的门户会话,实现永久访问入口的需求

内容的提问来源于stack exchange,提问作者Raphael Flattinger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 08:06:02