PAYATHON 2026

Laravel 如何携带 order id 重定向到另一个 Controller?

支付阿杰

结论

redirect() 会让浏览器发起新的 GET 请求,不会向目标路由发送 POST。由于当前路由使用了 Route::post(),无法匹配这个 GET 请求,因此会抛出 MethodNotAllowedHttpException。

如果 /pay/{orderId} 用来展示或进入支付页面,建议改用 GET 路由,并让路由参数 {orderId} 与 Controller 方法中的 $orderId 保持一致。

推荐写法:使用 GET 路由

OrderController

if (request('payment_method') === 'online') {
    return redirect()->route('payments.pay', [
        'orderId' => $order->id,
    ]);
}

web.php

较新的 Laravel 项目可以使用 Controller 类数组语法:

use App\Http\Controllers\PublicSslCommerzPaymentController;

Route::get('/pay/{orderId}', [PublicSslCommerzPaymentController::class, 'index'])
    ->name('payments.pay');

旧版 Laravel 也可以继续使用字符串语法:

Route::get('/pay/{orderId}', 'PublicSslCommerzPaymentController@index')
    ->name('payments.pay');

PublicSslCommerzPaymentController

use Illuminate\Http\Request;

class PublicSslCommerzPaymentController extends Controller
{
    public function index(Request $request, $orderId)
    {
        // $orderId 就是 URL 中的订单 ID
        $order = Order::findOrFail($orderId);

        // 继续处理支付逻辑
    }
}

路由参数是 {orderId},Controller 中也建议写成 $orderId。虽然某些情况下 Laravel 会按位置注入参数,但使用相同的名称更清楚,也能减少后续维护时的歧义。

使用路由模型绑定

如果项目使用 Order 作为订单模型,可以让 Laravel 根据 ID 自动查询订单:

use App\Models\Order;
use App\Http\Controllers\PublicSslCommerzPaymentController;

Route::get('/pay/{order}', [PublicSslCommerzPaymentController::class, 'index'])
    ->name('payments.pay');

重定向时直接传入订单模型:

return redirect()->route('payments.pay', [
    'order' => $order,
]);

Controller 直接接收 Order:

public function index(Order $order)
{
    // $order 已经是查询到的订单模型
}

找不到对应订单时,Laravel 会返回 404。

如果目标路由必须使用 POST

HTTP 重定向不能把普通的 GET 跳转变成 POST 请求。如果支付接口必须接收 POST,应通过表单提交,而不是调用 redirect():

<form method="POST" action="{{ route('payments.pay', ['orderId' => $order->id]) }}">
    @csrf
    <button type="submit">前往支付</button>
</form>

路由仍然使用 POST:

Route::post('/pay/{orderId}', [PublicSslCommerzPaymentController::class, 'index'])
    ->name('payments.pay');

Controller:

public function index(Request $request, $orderId)
{
    $order = Order::findOrFail($orderId);

    // 处理 POST 请求
}

如果业务要求在创建订单后立即自动提交到支付路由,可以返回一个带有隐藏字段并自动提交的页面:

<form id="payment-form"
      method="POST"
      action="{{ route('payments.pay', ['orderId' => $order->id]) }}">
    @csrf
</form>

<script>
    document.getElementById('payment-form').submit();
</script>

更常见的处理方式是:通过 POST 创建订单,创建成功后重定向到 GET 支付页面,再由支付页面提交后续操作。

注意事项

不要在 Laravel Controller 中手动调用:

session_start();

Laravel 已通过中间件管理 Session。如果需要保存或读取订单 ID,应使用 Laravel 提供的 Session API:

session(['order_id' => $order->id]);

$orderId = session('order_id');

本例已经通过路由参数传递订单 ID,通常不必再写入 Session。

订单 ID 出现在 URL 中,并不代表当前用户有权操作该订单。查询订单时还要验证订单归属和支付状态,例如:

$order = Order::where('id', $orderId)
    ->where('user_id', auth()->id())
    ->firstOrFail();

用于展示支付页面时使用 GET 路由。必须执行 POST 操作时,则通过表单提交,不能依靠 redirect() 发送 POST。

备注:内容仅供参考。