如何在 PHP 中获取在线 webhook 生成的 JSON
结论
不要从 RequestBin 网页抓取 JSON。RequestBin 是用来查看和调试 HTTP 请求的临时接收端。正确的处理方式是将支付平台的 webhook 地址配置为自己的 PHP 接口,再通过 php://input 读取原始请求体,并用 json_decode() 解析。
如果 webhook 已经发送到 RequestBin,只能通过该服务的请求详情页面或官方 API 查询历史数据。不同 RequestBin 服务使用的接口可能不同,不能直接把网页地址当成 JSON 接口。
在 PHP 中接收原始 JSON
假设 webhook 地址是:
https://example.com/webhooks/payment.php
可以在 payment.php 中这样处理请求:
<?php
declare(strict_types=1);
$rawBody = file_get_contents('php://input');
if ($rawBody === false || $rawBody === '') {
http_response_code(400);
exit('Empty request body');
}
try {
$payload = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
http_response_code(400);
exit('Invalid JSON');
}
// 示例:安全读取字段
$eventId = $payload['id'] ?? null;
$status = $payload['status'] ?? null;
$amount = $payload['amount'] ?? null;
// 在这里验证签名、检查事件类型并执行业务逻辑
http_response_code(200);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(
['received' => true],
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
这里的 $rawBody 是 webhook 发来的原始 JSON 字符串,$payload 是解析后的 PHP 关联数组。使用 JSON_THROW_ON_ERROR 后,JSON 解析失败时会抛出异常,不会被静默忽略。
排查问题时可以记录 $rawBody,但不要记录完整银行卡号、验证码、访问令牌或其他敏感支付信息。
配置支付平台
在支付服务后台,将 webhook URL 从 RequestBin 地址改为自己的 HTTPS 地址:
https://example.com/webhooks/payment.php
配置完成后,支付服务会直接向 PHP 接口发送请求,不需要再经过 RequestBin。
如果仍需使用 RequestBin 调试,可以先用它确认请求头、请求方法和请求体结构,再按相同格式开发接收接口。
不要直接抓取 RequestBin 页面
下面这种做法通常不可取:
$html = file_get_contents('https://requestbin.example/some-page');
这个地址返回的可能是 HTML 管理页面,而不是原始 JSON。页面也可能依赖登录状态、JavaScript 或临时令牌,其结构还可能发生变化。
只有所用服务明确提供官方 API 时,才应通过 API 获取已捕获的请求。例如,调用方式可能类似:
<?php
$apiUrl = 'https://requestbin.example/api/requests/REQUEST_ID';
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer YOUR_API_TOKEN',
],
]);
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($response === false) {
throw new RuntimeException(curl_error($ch));
}
curl_close($ch);
if ($statusCode !== 200) {
throw new RuntimeException("RequestBin API returned HTTP {$statusCode}");
}
$data = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
这只是一个通用示例。具体的 API 地址、认证方式和响应结构,应以实际使用的 RequestBin 服务文档为准。
确认请求确实是 JSON
有些支付平台发送的不是 JSON,而是表单数据。可以通过请求头中的 Content-Type 判断:
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
if (str_contains($contentType, 'application/json')) {
$rawBody = file_get_contents('php://input');
$payload = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
} elseif (str_contains($contentType, 'application/x-www-form-urlencoded')) {
$payload = $_POST;
} else {
http_response_code(415);
exit('Unsupported content type');
}
RequestBin 中显示的 JSON 有时只是它对请求信息的结构化展示,并不表示 webhook 的原始请求体一定是 JSON。判断时需要同时查看 Content-Type 和 raw body。
支付 webhook 的必要防护
不能只根据请求体中的 status 字段确认支付结果并直接入账。正式环境至少需要处理以下事项:
- 按照支付平台文档验证 webhook 签名。
- 使用原始请求体进行签名验证,不要先修改或重新编码 JSON。
- 检查订单号、金额、币种和商户标识是否符合预期。
- 根据事件 ID 或支付流水号进行幂等处理,避免重复通知造成重复入账。
- 只接受 HTTPS 请求。
- 尽快返回成功状态码,耗时业务可以放入队列异步处理。
- 不要使用 RequestBin 接收真实的生产支付数据,因为请求中可能含有敏感信息。
RequestBin 适合用来观察 webhook 的实际格式,正式的 JSON 接收和处理应由 PHP 接口完成。
备注:内容仅供参考。
