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

React+Express集成Stripe:支付成功后前后端反馈方案咨询

新版Stripe Checkout支付结果同步方案(无需WebSocket)

核心思路

新版Stripe Checkout无需依赖WebSocket,可通过前端跳转成功页后主动查询后端状态,或Webhook更新后端订单状态+前端页面加载时验证两种方式,实现支付结果的同步并执行对应逻辑。


方案一:前端跳转成功页后主动查询会话状态

1. 修改后端Checkout会话创建逻辑,传递会话ID到成功页

更新后端/checkout接口的success_url,将Stripe会话ID作为查询参数附加到跳转链接:

app.post("/checkout", async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ["card"],
      mode: "payment",
      line_items: req.body.items.map(({ id, quantity }) => {
        const storeItem = storeItems.get(id)
        return {
            price_data: {
            currency: "usd",
            product_data: {
              name: storeItem.name,
            },
            unit_amount: storeItem.priceInCents,
          },
          quantity: quantity,
        }
      }),
      // 新增会话ID作为查询参数
      success_url: "http://localhost:3000/success?session_id={CHECKOUT_SESSION_ID}",
      cancel_url: "http://localhost:3000/cancel",
    })

    res.json({ url: session.url })

  } catch (e) {
    res.status(500).json({ error: e.message })
  }
})

2. 后端新增查询会话状态的接口

通过Stripe API查询会话的支付状态,返回给前端:

app.get("/checkout/session-status", async (req, res) => {
  const { session_id } = req.query;
  try {
    const session = await stripe.checkout.sessions.retrieve(session_id);
    res.json({
      status: session.payment_status, // 状态值:paid/unpaid/no_payment_required
      orderId: session.metadata?.orderId // 若存入元数据可返回订单ID
    });
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

3. 前端创建Success页面并查询状态

在成功页加载时,从URL提取会话ID并调用后端接口验证支付状态,执行对应逻辑:

// Success.jsx
import React, { useEffect, useState } from "react";

export default function Success() {
  const [paymentStatus, setPaymentStatus] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const sessionId = urlParams.get("session_id");
    
    if (sessionId) {
      fetch(`http://localhost:4242/checkout/session-status?session_id=${sessionId}`)
        .then(res => res.json())
        .then(data => {
          if (data.status === "paid") {
            setPaymentStatus("success");
            // 执行支付成功逻辑:更新订单、显示成功提示、跳转个人中心等
            console.log("支付成功,执行后续操作");
          } else {
            setPaymentStatus("failed");
            console.log("支付未完成,请重试");
          }
        })
        .catch(err => {
          setError(err.message);
          console.log("查询支付状态失败");
        });
    }
  }, []);

  if (error) return <div>查询失败:{error}</div>;
  if (!paymentStatus) return <div>正在验证支付状态...</div>;

  return (
    <div>
      {paymentStatus === "success" ? (
        <div>
          <h1>支付成功!</h1>
          <p>您的订单已确认,感谢购买</p>
        </div>
      ) : (
        <div>
          <h1>支付未完成</h1>
          <p>请返回重新支付</p>
        </div>
      )}
    </div>
  );
}

方案二:Webhook更新后端状态,前端加载时验证订单

1. Webhook中处理支付成功事件并保存状态

修改后端Webhook逻辑,当Stripe推送checkout.session.completed事件时,将订单状态存入数据库(示例用内存模拟):

// 模拟数据库存储订单状态
const orderStatusMap = new Map();

app.post('/webhook', bodyParser.raw({type: 'application/json'}), async (request, response) => {
  // 生产环境必须验证Stripe签名,防止伪造请求
  // const sig = request.headers['stripe-signature'];
  // let event;
  // try {
  //   event = stripe.webhooks.constructEvent(request.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
  // } catch (err) {
  //   return response.status(400).send(`Webhook Error: ${err.message}`);
  // }

  const event = request.body;

  switch (event.type) {
    case 'checkout.session.completed':
      const session = event.data.object;
      const orderId = session.metadata?.orderId;
      if (orderId) {
        orderStatusMap.set(orderId, "paid");
        console.log(`订单${orderId}支付成功`);
      }
      break;
    default:
      console.log(`未处理事件类型:${event.type}`);
  }

  response.json({received: true});
});

2. 后端新增查询订单状态的接口

app.get("/order/status", async (req, res) => {
  const { orderId } = req.query;
  if (!orderId) {
    return res.status(400).json({ error: "缺少orderId参数" });
  }
  const status = orderStatusMap.get(orderId);
  res.json({ status: status || "unknown" });
});

3. 前端传递订单ID并在成功页查询

修改前端handleCheckout,生成订单ID并传递给后端,同时缓存到localStorage:

const handleCheckout = () => {
  // 生成唯一订单ID(实际项目从后端获取)
  const orderId = `order_${Date.now()}`;
  
  fetch("http://localhost:4242/checkout", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      items: [
        { id: 1, quantity: 5 },
        { id: 2, quantity: 2 },
      ],
      orderId: orderId // 传递订单ID到后端
    }),
  })
  .then(res => {
    if (res.ok) return res.json();
    return res.json().then(json => Promise.reject(json));
  })
  .then(({ url }) => {
    localStorage.setItem("currentOrderId", orderId);
    window.location = url;
  })
  .catch(e => console.log(e.error));
}

在Success页面查询订单状态:

// Success.jsx
import React, { useEffect, useState } from "react";

export default function Success() {
  const [paymentStatus, setPaymentStatus] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const orderId = localStorage.getItem("currentOrderId");
    if (orderId) {
      fetch(`http://localhost:4242/order/status?orderId=${orderId}`)
        .then(res => res.json())
        .then(data => {
          if (data.status === "paid") {
            setPaymentStatus("success");
            console.log("支付成功");
            localStorage.removeItem("currentOrderId"); // 清理缓存
          } else {
            setPaymentStatus("pending");
            console.log("订单状态待确认");
          }
        })
        .catch(err => {
          setError(err.message);
        });
    }
  }, []);

  // 渲染逻辑同方案一
}

注意事项

  1. 生产环境必须验证Webhook签名:方案二中的签名验证代码已注释,生产环境必须启用,避免恶意请求。
  2. 状态持久化:示例用内存存储订单状态,实际项目需存入数据库(如MySQL、MongoDB)。
  3. 会话ID安全性:会话ID可公开传递,但只有后端能通过Stripe API密钥验证其有效性。

内容的提问来源于stack exchange,提问作者Bryam Loaiza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:01:14