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

如何在React JS中使用Stripe支付,无需跳转返回URL并生成支付令牌?

使用 React + Stripe + Node.js 实现无跳转支付(生成支付令牌后由后端扣费)

注:这里的「支付令牌」对应Stripe官方推荐的PaymentMethod ID,替代已弃用的旧Token API,更安全且符合最新规范。

一、前期准备

  • 注册Stripe账号,获取前端用的Publishable Key和后端用的Secret Key(在Stripe控制台的API密钥页面获取)
  • 安装依赖:
    • 前端项目执行:npm install @stripe/react-stripe-js @stripe/stripe-js
    • 后端项目执行:npm install stripe express

二、React 前端:收集卡片信息并生成PaymentMethod

  1. 全局注入Stripe实例
    用Elements组件包裹支付相关页面,把Stripe实例传入:

    import { loadStripe } from '@stripe/stripe-js';
    import { Elements } from '@stripe/react-stripe-js';
    import PaymentForm from './PaymentForm';
    
    // 替换成你的Publishable Key
    const stripePromise = loadStripe('pk_test_xxxxxx');
    
    function App() {
      return (
        <Elements stripe={stripePromise}>
          <PaymentForm />
        </Elements>
      );
    }
    
    export default App;
    
  2. 构建支付表单,生成PaymentMethod并发送后端
    使用Stripe提供的CardElement安全收集卡片信息,调用createPaymentMethod生成支付令牌,直接传给后端触发扣费:

    import { useState } from 'react';
    import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
    
    export default function PaymentForm() {
      const stripe = useStripe();
      const elements = useElements();
      const [errorMsg, setErrorMsg] = useState(null);
      const [isLoading, setIsLoading] = useState(false);
    
      const handlePay = async (e) => {
        e.preventDefault();
        if (!stripe || !elements) return;
    
        setIsLoading(true);
        setErrorMsg(null);
    
        // 从CardElement提取卡片信息,生成PaymentMethod(支付令牌)
        const { error, paymentMethod } = await stripe.createPaymentMethod({
          type: 'card',
          card: elements.getElement(CardElement),
          // 可选:传入用户账单信息,帮助降低支付失败率
          billing_details: {
            email: 'user@example.com',
            name: 'John Doe'
          },
        });
    
        if (error) {
          setErrorMsg(error.message);
          setIsLoading(false);
          return;
        }
    
        // 把PaymentMethod ID发送到后端
        try {
          const res = await fetch('/api/process-payment', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              paymentMethodId: paymentMethod.id,
              amount: 1000, // 金额单位:分,这里代表10美元
              currency: 'usd'
            }),
          });
    
          const data = await res.json();
          if (data.success) {
            alert('支付成功!');
            // 这里可以添加支付成功后的逻辑,比如跳转到订单页
          } else {
            setErrorMsg(data.error);
          }
        } catch (err) {
          setErrorMsg('网络异常,请稍后重试');
        } finally {
          setIsLoading(false);
        }
      };
    
      return (
        <form onSubmit={handlePay} style={{ maxWidth: '400px', margin: '20px auto' }}>
          <div style={{ marginBottom: '15px' }}>
            <CardElement
              options={{
                style: {
                  base: {
                    fontSize: '16px',
                    padding: '12px 16px',
                    border: '1px solid #ddd',
                    borderRadius: '4px'
                  },
                  invalid: {
                    borderColor: '#ff4444'
                  }
                }
              }}
            />
          </div>
          {errorMsg && <p style={{ color: '#ff4444', margin: '10px 0' }}>{errorMsg}</p>}
          <button
            type="submit"
            disabled={isLoading || !stripe || !elements}
            style={{
              width: '100%',
              padding: '12px',
              backgroundColor: '#0070f3',
              color: 'white',
              border: 'none',
              borderRadius: '4px',
              cursor: isLoading ? 'not-allowed' : 'pointer'
            }}
          >
            {isLoading ? '处理中...' : `支付 $${(1000 / 100).toFixed(2)}`}
          </button>
        </form>
      );
    }
    

三、Node.js 后端:接收PaymentMethod并完成扣费

  1. 初始化服务与Stripe

    const express = require('express');
    const stripe = require('stripe')('sk_test_xxxxxx'); // 替换成你的Secret Key
    const app = express();
    
    // 解析JSON请求体
    app.use(express.json());
    
    // 解决跨域问题(如果前端和后端端口不同)
    app.use((req, res, next) => {
      res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); // 替换成你的前端地址
      res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
      res.header('Access-Control-Allow-Headers', 'Content-Type');
      next();
    });
    
  2. 创建扣费接口
    调用Stripe的paymentIntents.create接口,传入PaymentMethod ID完成扣费(这是Stripe当前推荐的支付流程):

    app.post('/api/process-payment', async (req, res) => {
      const { paymentMethodId, amount, currency } = req.body;
    
      try {
        // 创建并立即确认Payment Intent,完成扣费
        const paymentIntent = await stripe.paymentIntents.create({
          amount: amount,
          currency: currency,
          payment_method: paymentMethodId,
          confirm: true,
          // 自动启用其他支付方式(如Apple Pay、Google Pay),可选
          automatic_payment_methods: { enabled: true }
        });
    
        res.json({
          success: true,
          paymentIntentId: paymentIntent.id,
          status: paymentIntent.status
        });
      } catch (error) {
        // 处理各种支付失败场景:卡片被拒、余额不足、验证失败等
        res.json({
          success: false,
          error: error.message || '支付失败,请检查卡片信息'
        });
      }
    });
    
    // 启动服务
    const PORT = 3001;
    app.listen(PORT, () => {
      console.log(`后端服务运行在 http://localhost:${PORT}`);
    });
    

四、核心注意事项

  • 敏感逻辑必须放后端:绝对不能在前端调用扣费接口,也不能暴露Stripe的Secret Key,否则会导致安全风险
  • PCI合规:使用Stripe的预构建UI组件(如CardElement),卡片信息不会经过你的服务器,大幅降低PCI合规要求
  • 错误处理:要覆盖前端生成PaymentMethod失败、后端扣费失败、网络异常等所有场景,给用户明确提示
  • 金额单位:Stripe要求金额使用最小货币单位(美元用分,人民币用分),避免小数计算误差

内容的提问来源于stack exchange,提问作者Rue Vitale

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 11:42:15