PAYATHON 2026

Laravel 支付回调出现 MethodNotAllowedHttpException

支付阿杰

结论

MethodNotAllowedHttpException 通常与支付验证代码无关。它表示 callback 请求使用的 HTTP 方法与 Laravel 路由允许的方法不一致。

例如,支付平台向回调地址发送 POST 请求,但路由只注册了 GET:

Route::get('/payment/callback', [PaymentController::class, 'callback']);

Laravel 会在请求进入控制器前将其拒绝,所以控制器中的 $_POST、SOAP 调用等代码都不会执行。

正确配置回调路由

如果支付平台通过 POST 发送回调,路由也应注册为 POST:

use App\Http\Controllers\PaymentController;
use Illuminate\Support\Facades\Route;

Route::post('/payment/callback', [PaymentController::class, 'callback'])
    ->name('payment.callback');

如果支付服务可能使用 GET 或 POST,确认服务商协议后,可以同时允许这两种方法:

Route::match(['get', 'post'], '/payment/callback', [
    PaymentController::class,
    'callback',
])->name('payment.callback');

不要为了省事直接使用 Route::any()。回调接口只需开放支付服务实际使用的请求方法。

使用 Laravel 的 Request 读取参数

在 Laravel 控制器中,应注入 Illuminate\Http\Request,不要直接访问 $_POST:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use SoapClient;

class PaymentController extends Controller
{
    public function callback(Request $request)
    {
        $state = $request->input('State');
        $refNum = $request->input('RefNum');

        if ($state !== 'OK' || empty($refNum)) {
            return response('Transaction Failed', 400);
        }

        $merchantCode = config('services.sep.merchant_code');

        $soapClient = new SoapClient(
            'https://verify.sep.ir/Payments/ReferencePayment.asmx?WSDL'
        );

        $result = $soapClient->VerifyTransaction($refNum, $merchantCode);

        if ($result <= 0) {
            return response('Transaction Failed', 400);
        }

        return response(
            "Transaction Successful\nRef: {$refNum}\nRes: {$result}"
        );
    }
}

$request->input() 能读取表单参数和查询字符串参数,也便于进行验证、测试和后续维护。

检查 CSRF 中间件

支付平台属于站外服务,无法携带 Laravel 页面生成的 CSRF token。如果 callback 路由位于启用了 CSRF 校验的 web 中间件组中,还要根据当前 Laravel 项目的配置方式,将该路径排除在 CSRF 校验之外。

不同 Laravel 版本的配置位置不同。某些版本可以在应用配置中排除:

->withMiddleware(function ($middleware) {
    $middleware->validateCsrfTokens(except: [
        'payment/callback',
    ]);
})

较早的项目通常在 CSRF 中间件的 $except 中配置:

protected $except = [
    'payment/callback',
];

CSRF 校验失败通常会显示 419 Page Expired 或 token mismatch,而不是 MethodNotAllowedHttpException。因此,先修正路由方法,再处理 CSRF 问题。

如何确认实际请求方法

使用下面的命令检查已注册的路由:

php artisan route:list

找到 callback 路径,确认其 Method 是否包含支付平台实际发送的 POST 或 GET。

还要注意以下几种情况:

  • 浏览器跳转到回调页时通常使用 GET。
  • 支付平台服务器发送交易结果时通常使用 POST。
  • 部分支付服务会分别提供浏览器返回地址和服务器通知地址,最好为这两个地址单独定义路由。

例如:

Route::get('/payment/return', [PaymentController::class, 'returnPage']);
Route::post('/payment/callback', [PaymentController::class, 'callback']);

注意事项

不能只根据 State === 'OK' 判断支付成功。该字段来自外部请求,可能被伪造。必须调用支付服务的验证接口,并核对订单号、商户号、支付金额、交易状态以及该交易是否已经处理。

回调还应采用幂等设计。支付平台可能重复发送同一笔通知,因此需要使用 RefNum 或服务商提供的唯一交易号,避免重复入账。SOAP 请求也可能超时或抛出 SoapFault。生产环境中应捕获异常并记录日志,不要直接向客户端输出内部错误信息。

备注:内容仅供参考。