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

如何防止Laravel 9表单因重复点击提交按钮产生重复提交?

解决Laravel表单重复提交导致重复交易的方案

以下是几种实用的解决思路,可单独或组合使用:

1. 前端拦截:点击后立即禁用提交按钮

用户点击提交按钮后,立即禁用按钮并修改提示文本,阻止重复点击。修改你的Blade模板:

<form id="create-offline-payment-form" action="{{route ('user.cart.pay')}}" method="post">
     @csrf
     <input type="hidden" value="{{$shop_id}}" id="shop-id" name="shop_id">
     <button id="payment" type="submit" class="btn">پرداخت آفلاین</button>
</form>

<script>
document.getElementById('create-offline-payment-form').addEventListener('submit', function() {
    const submitBtn = document.getElementById('payment');
    submitBtn.disabled = true;
    submitBtn.textContent = 'در حال پردازش...'; // 可选,给用户反馈
});
</script>

这个方法能快速拦截前端误操作,但无法防范用户绕过前端(比如手动提交请求),建议配合后端校验。

2. 后端一次性令牌校验

生成仅能使用一次的令牌,提交后立即失效,彻底阻止重复提交:

步骤1:在渲染表单的控制器方法中生成令牌

use Illuminate\Support\Str;

// 假设这是显示购物车支付表单的方法
public function showPaymentForm()
{
    $shop_id = ...; // 你的业务逻辑获取shop_id
    $paymentToken = Str::random(40);
    session(['payment_token' => $paymentToken]);
    return view('user.cart.payment', compact('shop_id', 'paymentToken'));
}

步骤2:在Blade表单中添加令牌字段

<form id="create-offline-payment-form" action="{{route ('user.cart.pay')}}" method="post">
     @csrf
     <input type="hidden" value="{{$shop_id}}" id="shop-id" name="shop_id">
     <input type="hidden" value="{{$paymentToken}}" name="payment_token">
     <button id="payment" type="submit" class="btn">پرداخت آفلاین</button>
</form>

步骤3:在支付处理方法中校验并销毁令牌

public function payCart(Request $request)
{
    // 验证令牌存在
    $request->validate([
        'payment_token' => 'required|string',
    ]);

    // 校验令牌是否匹配
    if ($request->payment_token !== session('payment_token')) {
        return back()->withErrors(['msg' => 'درخواست تکراری است.']);
    }

    // 立即销毁令牌,防止重复使用
    session()->forget('payment_token');

    // 创建交易记录
    Transaction::create([
        'seller_id' => Auth::id(),
        'amount' =>  $this->cartSum(),
    ]);

    return redirect()->route('user.cart.paid');
}

3. 基于业务逻辑的幂等性校验

结合购物车/用户的业务场景,校验是否已存在重复交易:

修改payCart方法,添加重复校验逻辑:

public function payCart(Request $request)
{
    // 校验当前用户是否在短时间内创建过相同金额的交易
    $recentTransaction = Transaction::where('seller_id', Auth::id())
        ->where('amount', $this->cartSum())
        ->where('created_at', '>=', now()->subMinutes(5)) // 5分钟内的相同交易视为重复
        ->first();

    if ($recentTransaction) {
        // 直接跳转到支付成功页,避免重复创建
        return redirect()->route('user.cart.paid');
    }

    // 创建交易记录
    Transaction::create([
        'seller_id' => Auth::id(),
        'amount' =>  $this->cartSum(),
    ]);

    return redirect()->route('user.cart.paid');
}

如果你的Transaction表关联了购物车(有cart_id字段),可以将校验条件改为where('cart_id', $cartId),这样能精准判断该购物车是否已生成交易。

4. 强化Post/Redirect/Get (PRG)模式

你当前已经使用PRG模式(提交后重定向到成功页),这能避免用户刷新成功页时重复提交,但无法阻止提交过程中的多次点击。将PRG与前面的前端禁用或后端令牌方案结合,就能彻底解决问题。


内容的提问来源于stack exchange,提问作者aref razavi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 19:50:49