PHP callback 重定向时如何向 Paysera 返回 OK
结论
Paysera 的 callback 是服务器之间的通知,请求必须直接返回 HTTP 200,响应正文只能是:
OK
同一个 HTTP 响应无法同时返回 OK 和执行 Location 重定向。调用:
header('Location: ...');
等于要求 Paysera 的服务器访问另一个页面。如果 Paysera 跟随重定向,最终收到的是目标 WordPress 页面生成的 HTML 和 JavaScript,而不是 OK,因此会把 callback 判定为失败。
callback 应先完成订单状态更新,再输出 OK 并立即结束程序。如果确实需要调用另一个 URL,应由 callback 在服务器端发起请求,不能使用浏览器重定向。
推荐方案:直接更新订单状态
把 update_status() 改成普通的业务函数,不再发送 Location 响应:
<?php
try {
$response = WebToPay::checkResponse($_GET, [
'projectid' => '######',
'sign_password' => '#################',
]);
if ($response['test'] !== '0') {
throw new Exception('Testing, real payment was not made');
}
if ($response['type'] !== 'macro') {
throw new Exception('Only macro payment callbacks are accepted');
}
$orderId = $response['orderid'];
$amount = $response['amount'];
$currency = $response['currency'];
// 必须根据数据库中的订单重新核对,不能只相信回调参数。
// 1. 查询订单是否存在
// 2. 核对金额和币种
// 3. 如果订单尚未支付,更新订单状态
update_order_status($orderId, $amount, $currency);
http_response_code(200);
header('Content-Type: text/plain; charset=UTF-8');
echo 'OK';
exit;
} catch (Throwable $e) {
http_response_code(400);
header('Content-Type: text/plain; charset=UTF-8');
// 对外不建议返回详细异常,以免泄露内部信息。
echo 'ERROR';
exit;
}
function update_order_status($orderId, $amount, $currency)
{
// 在此处调用 WordPress/WooCommerce API 或更新数据库。
// 该操作必须具备幂等性,因为 Paysera 可能重复发送 callback。
}
输出 OK 后要立即 exit,否则 WordPress 可能继续渲染主题模板、Gravity Forms 脚本、emoji 脚本或其他页面内容。
如果必须访问另一个 URL
如果 /uzsakymo-uzskaitymas 已经包含订单更新逻辑,callback 可以从服务器端调用它。在 WordPress 环境中,可以使用 wp_remote_post():
function update_status(array $response): void
{
$result = wp_remote_post(
'https://www.example.lt/uzsakymo-uzskaitymas',
[
'timeout' => 10,
'body' => [
'orderid' => $response['orderid'],
'amount' => $response['amount'],
'currency' => $response['currency'],
],
]
);
if (is_wp_error($result)) {
throw new RuntimeException(
'Status update request failed: ' . $result->get_error_message()
);
}
$statusCode = wp_remote_retrieve_response_code($result);
if ($statusCode < 200 || $statusCode >= 300) {
throw new RuntimeException(
'Status update endpoint returned HTTP ' . $statusCode
);
}
}
验证 Paysera callback 后调用该函数:
update_status($response);
http_response_code(200);
header('Content-Type: text/plain; charset=UTF-8');
echo 'OK';
exit;
更稳妥的设计是把订单更新代码提取为 PHP 函数或服务,供 callback 和其他页面直接调用。通过 HTTP 请求自己的网站,会带来超时、认证、重试和重复处理等问题。
用户付款后的页面跳转应单独处理
Paysera callback 与用户浏览器的返回页面属于两种不同的请求:
- callback URL:供 Paysera 服务器通知支付结果,需要返回
OK。 - accept/return URL:供用户付款后跳转到结果页。
- cancel URL:供用户取消付款后返回。
如果需要让付款用户看到 /uzsakymo-uzskaitymas 页面,应将它配置为用户返回地址,而不是在 callback 中重定向。
不能根据用户返回页面确认付款成功,因为用户可能关闭浏览器、刷新页面或伪造请求。订单是否支付成功,仍应以通过签名验证的 callback 为准。
注意事项
- 不要直接使用未经验证的
$_GET['data']更新订单。应先调用 Paysera 提供的签名校验方法,再使用解析后的$response。 - callback 可能重复到达,因此更新逻辑必须具备幂等性。已支付的订单再次收到同一通知时,不应重复扣减库存、发送邮件或记账。
- 必须核对订单号、实际金额、币种和订单当前状态。
- callback 响应中不能包含 WordPress 页面、调试输出、PHP warning、BOM 或额外空白。
- 不要先
echo 'OK'再重定向。即使某些环境允许继续发送 header,最终响应也不是 Paysera 要求的纯OK。 - 如果服务器端的状态更新失败,不应返回
OK,否则 Paysera 可能认为通知已经处理成功,不再重试。具体的重试行为应以当前 Paysera 文档为准。
备注:内容仅供参考。
