Laravel localhost 支付 callback 端口丢失导致 404
结论
可以在本机测试支付流程,但需要满足两个条件:
- 支付平台接受
localhost和非标准端口作为回调地址。 - 回调由浏览器跳转触发,而不是由支付平台的服务器主动请求。
当前代码不应把完整 URL 传给 url()。建议正确配置 APP_URL,为回调路由命名,再用 route() 生成包含 :8000 的完整地址。
推荐配置
先在 .env 中设置应用地址:
APP_URL=http://localhost:8000
如果修改后没有生效,请清理 Laravel 的配置缓存:
php artisan optimize:clear
然后定义命名路由:
use Illuminate\Support\Facades\Route;
Route::get('/buy', function () {
try {
$gateway = \Gateway::zarinpal();
$gateway->setCallback(route('payment.callback'));
$gateway->price(1000)->ready();
$refId = $gateway->refId();
$transID = $gateway->transactionId();
// 保存 $refId、$transID、金额和订单编号
return $gateway->redirect();
} catch (\Exception $e) {
abort(500, $e->getMessage());
}
});
Route::match(['get', 'post'], '/payment/callback', function () {
try {
$gateway = \Gateway::verify();
$trackingCode = $gateway->trackingCode();
$refId = $gateway->refId();
$cardNumber = $gateway->cardNumber();
return response()->json([
'card_number' => $cardNumber,
'tracking_code' => $trackingCode,
'reference_id' => $refId,
]);
} catch (\RetryException $e) {
abort(409, $e->getMessage());
} catch (\PortNotFoundException $e) {
abort(404, $e->getMessage());
} catch (\InvalidRequestException $e) {
abort(400, $e->getMessage());
} catch (\NotFoundTransactionException $e) {
abort(404, $e->getMessage());
} catch (\Exception $e) {
abort(500, $e->getMessage());
}
})->name('payment.callback');
生成的回调地址应该是:
http://localhost:8000/payment/callback
可以暂时用下面的代码检查实际提交给支付组件的地址:
dd(route('payment.callback'));
不要这样调用 url()
原代码写成了:
$gateway->setCallback(
url('http://localhost:8000/PaymentSuccessfulController')
);
url() 通常用于把应用内路径转换为完整 URL,例如:
url('/payment/callback')
如果已经有完整地址,可以直接传入:
$gateway->setCallback(
'http://localhost:8000/payment/callback'
);
不过,命名路由更方便维护。部署到测试环境或生产环境后,也不必在业务代码中修改域名。
为什么地址会变成 http://localhost/...
浏览器访问:
http://localhost/PaymentSuccessfulController
相当于访问本机 HTTP 服务的默认端口 80。而 php artisan serve 通常监听 8000 端口,所以这个请求不会进入当前的 Laravel 应用,最终会返回 404。
如果 route('payment.callback') 已经生成了带 :8000 的地址,但从支付页面返回时端口仍然消失,问题通常出在以下某个环节:
- 支付 SDK 错误地解析或规范化了 callback URL。
- SDK 从自己的配置文件中读取了另一个 callback 地址。
- 支付平台不接受
localhost或非标准端口。 - 支付平台后台设置的返回地址覆盖了代码提交的地址。
- 发起支付请求时,实际传给平台的 callback 参数已经丢失了端口。
这时需要检查 SDK 最终发出的请求参数或日志,确认提交给支付平台的 callback 究竟是哪一个地址。
支付平台无法访问 localhost 时怎么办
如果回调只是支付结束后的浏览器重定向,而且支付操作和 Laravel 服务都在同一台电脑上运行,那么 localhost:8000 有可能正常工作。
如果支付平台会从自己的服务器请求 callback,localhost 就一定不可用。因为支付平台所访问的 localhost 指向它自己的服务器,而不是开发者的电脑。此时需要使用公网可访问的 HTTPS 地址,例如测试服务器或本地隧道,并将 APP_URL 改为该地址:
APP_URL=https://your-test-domain.example
支付平台是否接受 localhost、HTTP 地址或自定义端口,应以该平台当前的回调地址规则为准。
还需要注意的事项
如果支付平台通过 POST 请求 web.php 中的回调路由,Laravel 的 CSRF 中间件可能会拒绝该请求。应根据所用的 Laravel 版本,只为支付回调路径配置 CSRF 排除,不要关闭整个站点的 CSRF 防护。
另外,成功进入 callback 不代表订单已经支付成功。处理回调时还需要:
- 使用
transaction_id或平台返回的交易标识进行服务端验证。 - 核对订单金额、订单号和支付状态。
- 防止同一笔交易被重复处理。
- 在数据库事务中更新订单。
- 不要只根据地址栏参数将订单标记为已支付。
先用 route('payment.callback') 确认 Laravel 生成的地址包含端口。如果提交时地址正确,返回时端口仍被删除,就需要检查支付 SDK 或平台对 callback URL 的限制,并改用公网测试地址。
备注:内容仅供参考。
