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

Laravel Inertia React集成Stripe支付遇上下文错误求解决方案

Laravel Inertia React 集成Stripe支付网关报错解决方案

报错信息

无法找到Elements上下文;您需要将调用useStripe()的应用部分包裹在提供者中

问题场景

在Laravel + Inertia + React项目中集成Stripe订阅支付,点击「选择方案」按钮触发支付流程时出现上述报错,核心代码包含首页组件、定价卡片组件和后端控制器。


解决方案

1. 全局注入Stripe Elements上下文

useStripe()和useElements()必须在<Elements>组件的上下文内使用,因此需要在包含PricingCard的父组件中添加该提供者。

修改Welcome组件:

import Navbar from "@/Components/Wesite/Navbar";
import Carousal from "@/Components/Wesite/Carousel";
import Cards from "@/Components/Wesite/Cards";
import { Link, Head, usePage } from "@inertiajs/react";
import PricingCard from "@/Components/Wesite/Pricing";
import FooterBrand from "@/Components/Wesite/Footer";
import { useState } from "react";
// 新增Stripe依赖
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";

// 替换为你的Stripe发布密钥
const stripePromise = loadStripe('pk_test_your_publishable_key_here');

export default function Welcome(props) {
    const { image, memberships } = usePage().props;
    const { class_3, class_4, class_5, phone, message, location } = image;
    const [membershipCards, setMemberShipCards] = useState(memberships);

    return (
        <>
            <Head title="Home" />
            <div className="h-screen  bg-gray-900">
                {/* 省略其他代码 */}
                <section className="w-full bg-gray-900">
                    <div className="text-center p-10">
                        <div className="uppercase text-theme-orange text-2xl font-[700]">
                            OUR PLAN
                        </div>
                        <div className="uppercase text-white text-[32px] font-[600]">
                            CHOOSE YOUR PRICING PLAN
                        </div>
                    </div>
                    <div className="container mx-auto p-10">
                        <div className="w-full flex gap-3 flex-grow justify-evenly flex-wrap">
                            {/* 用Elements包裹所有PricingCard */}
                            <Elements stripe={stripePromise}>
                                {membershipCards.map((item, index) => (
                                    <PricingCard
                                        key={index}
                                        id={item.id}
                                        name={item.name}
                                        price={item.price}
                                        description={item.description}
                                        duration={item.duration}
                                    />
                                ))}
                            </Elements>
                        </div>
                    </div>
                </section>
                {/* 省略其他代码 */}
            </div>
        </>
    );
}

2. 优化前端支付逻辑

修正表单提交行为、添加加载状态、支持自定义持卡人姓名,提升用户体验:

修改PricingCard组件:

import { CardElement, useStripe, useElements } from "@stripe/react-stripe-js";
import axios from "axios";
import { Card } from "flowbite-react";
import { useState } from "react";

let PricingCard = ({ id, name, price, description, duration }) => {
    const stripe = useStripe();
    const elements = useElements();
    const [cardHolderName, setCardHolderName] = useState('');
    const [loading, setLoading] = useState(false);

    const handleSubscription = async (e) => {
        e.preventDefault();
        if (!stripe || !elements || !cardHolderName) return;

        setLoading(true);
        try {
            const { setupIntent, error } = await stripe.confirmCardSetup({
                payment_method: {
                    card: elements.getElement(CardElement),
                    billing_details: { name: cardHolderName }
                },
            });

            if (error) throw new Error(error.message);

            const response = await axios.post("/member/buy-subscription", {
                payment_method: setupIntent.payment_method,
                price_id: id
            });

            if (response.data.success) {
                alert("订阅成功!");
                // 可添加页面跳转或状态更新逻辑
            } else {
                throw new Error(response.data.error);
            }
        } catch (err) {
            alert(`操作失败: ${err.message}`);
            console.error(err);
        } finally {
            setLoading(false);
        }
    };

    return (
        <Card>
            <h5 className="mb-4 text-xl font-medium text-gray-500 dark:text-gray-400">
                {name}
            </h5>
            <div className="flex items-baseline text-gray-900 dark:text-white">
                <span className="text-3xl font-semibold">$</span>
                <span className="text-5xl font-extrabold tracking-tight">{price}</span>
                <span className="ml-1 text-xl font-normal text-gray-500 dark:text-gray-400">
                    /{duration} month
                </span>
            </div>
            <div className="my-7 space-y-5">
                <div className="w-[300px]">{description}</div>
            </div>

            <form onSubmit={handleSubscription} className="space-y-4">
                <div>
                    <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                        持卡人姓名
                    </label>
                    <input
                        type="text"
                        value={cardHolderName}
                        onChange={(e) => setCardHolderName(e.target.value)}
                        className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-theme-orange"
                        required
                    />
                </div>
                <div>
                    <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                        银行卡信息
                    </label>
                    <CardElement 
                        options={{ 
                            style: { 
                                base: { fontSize: "16px", color: "#424770" },
                                invalid: { color: "#9e2146" }
                            } 
                        }} 
                    />
                </div>
                <button
                    className="inline-flex w-full justify-center rounded-lg bg-theme-orange px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-theme-orange/90 focus:outline-none focus:ring-4 focus:ring-cyan-200 dark:focus:ring-cyan-900"
                    type="submit"
                    disabled={!stripe || loading || !cardHolderName}
                >
                    {loading ? "处理中..." : "选择方案"}
                </button>
            </form>
        </Card>
    );
};

export default PricingCard;

3. 修正后端订阅逻辑

Laravel Cashier创建订阅的参数格式有误,调整为正确的调用方式:

修改MemberController:

<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class MemberController extends Controller
{
    public function buySubScription(Request $request)
    {
        if (!Auth::check()) {
            return response()->json(['error' => '请先登录'], 401);
        }

        $request->validate([
            'price_id' => 'required|string',
            'payment_method' => 'required|string',
        ]);

        $user = auth()->user();
        try {
            // 关联支付方式到用户
            $user->addPaymentMethod($request->payment_method);
            
            // 创建订阅
            $subscription = $user->newSubscription('default', $request->price_id)
                ->create($request->payment_method);

            return response()->json(['success' => true, 'subscription' => $subscription]);
        } catch (Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }
}

4. 关键配置检查

  • 确保已安装Laravel Cashier:composer require laravel/cashier,并完成迁移和Stripe密钥配置
  • 在.env文件中添加Stripe密钥:
    STRIPE_KEY=pk_test_your_publishable_key
    STRIPE_SECRET=sk_test_your_secret_key
    
  • 确认memberships数据中的id字段对应Stripe平台的价格ID(格式为price_xxx)

内容的提问来源于stack exchange,提问作者Hassan Khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 09:45:00