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

Laravel Cashier实现用户注册时同步创建Stripe订阅的问题

解决方案:Laravel注册+Stripe订阅一步完成

你当前的核心问题是流程顺序错误:不能先创建用户再生成Setup Intent,因为此时表单已经提交,无法将client_secret回传给前端完成Stripe支付验证。正确的逻辑是:先在表单展示阶段生成Setup Intent,让前端完成支付方式确认后,再把注册信息+支付凭证一起提交给后端,最终创建用户和订阅。

步骤1:合并注册与支付表单

把Laravel默认注册表单和支付表单整合,让用户一次性填写所有信息:

<form id="payment-form" action="{{ route('register') }}" method="POST">
    @csrf
    <input type="hidden" name="plan" id="plan" value="{{ $plan->id }}">

    <!-- 注册信息 -->
    <div class="row">
        <div class="col-xl-4 col-lg-4">
            <div class="form-group">
                <label>First Name</label>
                <input type="text" name="first_name" class="form-control" required>
            </div>
        </div>
        <div class="col-xl-4 col-lg-4">
            <div class="form-group">
                <label>Last Name</label>
                <input type="text" name="last_name" class="form-control" required>
            </div>
        </div>
        <div class="col-xl-4 col-lg-4">
            <div class="form-group">
                <label>Email</label>
                <input type="email" name="email" class="form-control" required>
            </div>
        </div>
        <div class="col-xl-6 col-lg-6">
            <div class="form-group">
                <label>Password</label>
                <input type="password" name="password" class="form-control" required>
            </div>
        </div>
        <div class="col-xl-6 col-lg-6">
            <div class="form-group">
                <label>Confirm Password</label>
                <input type="password" name="password_confirmation" class="form-control" required>
            </div>
        </div>
    </div>

    <!-- 支付信息 -->
    <div class="row">
        <div class="col-xl-4 col-lg-4">
            <div class="form-group">
                <label>Name on Card</label>
                <input type="text" id="card-holder-name" class="form-control" required>
            </div>
        </div>
        <div class="col-xl-4 col-lg-4">
            <div class="form-group">
                <label>Card Details</label>
                <div id="card-element"></div>
            </div>
        </div>
        <div class="col-xl-12 col-lg-12">
            <hr>
            <button type="submit" class="btn btn-primary" id="card-button" data-secret="{{ $setupIntent->client_secret }}">Register & Subscribe</button>
        </div>
    </div>
</form>

步骤2:在表单展示阶段生成Setup Intent

修改注册页面的控制器方法,直接调用Stripe API生成Setup Intent(无需提前创建用户):

// App/Http/Controllers/Auth/RegisteredUserController.php
use Stripe\Stripe;
use Stripe\SetupIntent;

public function create()
{
    Stripe::setApiKey(config('cashier.secret'));
    
    // 生成Setup Intent,用于收集支付方式
    $setupIntent = SetupIntent::create([
        'payment_method_types' => ['card'],
    ]);
    
    $plan = Plan::find(request()->query('plan')); // 假设通过URL参数传递计划ID
    return view('auth.register', compact('setupIntent', 'plan'));
}

步骤3:前端完成Stripe支付验证

添加Stripe JS代码,在表单提交前验证支付方式,获取payment_method凭证:

<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('{{ config('cashier.key') }}');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;

cardButton.addEventListener('click', async (e) => {
    e.preventDefault();
    
    // 确认支付方式,获取payment_method
    const { setupIntent, error } = await stripe.confirmCardSetup(
        clientSecret,
        {
            payment_method: {
                card: cardElement,
                billing_details: { name: cardHolderName.value }
            }
        }
    );
    
    if (error) {
        alert(error.message); // 处理错误提示
        return;
    }
    
    // 将payment_method添加到表单并提交
    const form = document.getElementById('payment-form');
    const paymentMethodInput = document.createElement('input');
    paymentMethodInput.type = 'hidden';
    paymentMethodInput.name = 'payment_method';
    paymentMethodInput.value = setupIntent.payment_method;
    form.appendChild(paymentMethodInput);
    form.submit();
});
</script>

步骤4:后端创建用户并完成订阅

修改store方法,接收payment_method参数,创建用户后直接完成订阅:

// App/Http/Controllers/Auth/RegisteredUserController.php
public function store(Request $request)
{
    $request->validate([
        'first_name' => ['required', 'string', 'max:255'],
        'last_name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
        'password' => ['required', 'string', 'min:8', 'confirmed'],
        'plan' => ['required', 'exists:plans,id'],
        'payment_method' => ['required', 'string'],
    ]);

    $plan = Plan::find($request->plan);
    $userID = uniqid('u-');

    // 创建用户
    $user = User::create([
        'unique_id' => $userID,
        'first_name' => $request->first_name,
        'last_name' => $request->last_name,
        'name' => $request->first_name . ' ' . $request->last_name,
        'email' => $request->email,
        'password' => Hash::make($request->password),
        'user_role' => 'customer',
        'status' => 'active', // 订阅成功后直接激活
        'show_in_directory' => $request->has('show_in_directory') ? 'on' : 'off',
    ]);

    event(new Registered($user));
    Auth::login($user);

    // 使用payment_method创建订阅
    try {
        $user->newSubscription('default', $plan->stripe_plan)
            ->create($request->payment_method, [
                'email' => $user->email,
            ]);
        return view('subscription_success');
    } catch (\Exception $e) {
        // 订阅失败时删除用户,避免无效数据
        $user->delete();
        return back()->withErrors(['payment' => $e->getMessage()]);
    }
}

关键说明

  1. 移除了原代码中硬编码的密码,改为用户自主输入,保证安全性
  2. 流程调整为:展示表单→生成Setup Intent→前端验证支付→提交所有信息→后端创建用户+订阅
  3. 订阅失败时自动删除用户,避免数据库中存在未完成支付的无效账户

内容的提问来源于stack exchange,提问作者John Hubler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 08:30:35