如何在 PHP 中读取 POST webhook 通知内容
支付老李
结论
这个请求使用 application/x-www-form-urlencoded 编码,PHP 通常会自动解析其中的表单字段。Webhook 的 JSON 内容保存在 $_POST['data'] 中,整个 $_POST 并不是 JSON。
取出字符串后,使用 json_decode() 将它解析为 PHP 数组:
<?php
$json = $_POST['data'] ?? null;
if ($json === null) {
http_response_code(400);
exit('Missing data parameter');
}
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
exit('Invalid JSON: ' . json_last_error_msg());
}
$event = $data['event'] ?? null;
$paymentId = $data['payment']['id'] ?? null;
$status = $data['payment']['status'] ?? null;
http_response_code(200);
echo 'OK';
为什么直接读取 $_POST 没有得到预期结果
$_POST 是一个数组。按照这个请求的表单结构,其内容大致如下:
[
'data' => '{ "event": "PAYMENT_UPDATED", "payment": { ... } }'
]
因此,下面的代码取得的是整个表单数组:
$datasrc = $_POST;
真正需要解析的 JSON 字符串在 data 字段中:
$datasrc = $_POST['data'];
如果使用 echo $_POST; 检查内容,PHP 只会输出 Array 或产生数组转换警告,看起来就像没有收到数据。调试时可以改用:
var_dump($_POST);
或者:
print_r($_POST);
完整的接收与解析示例
<?php
header('Content-Type: application/json; charset=UTF-8');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
$json = $_POST['data'] ?? null;
if (!is_string($json) || trim($json) === '') {
http_response_code(400);
echo json_encode(['error' => 'Missing data parameter']);
exit;
}
$payload = json_decode($json, true);
if (!is_array($payload)) {
http_response_code(400);
echo json_encode([
'error' => 'Invalid JSON',
'details' => json_last_error_msg(),
]);
exit;
}
$event = $payload['event'] ?? '';
$payment = $payload['payment'] ?? [];
if ($event === 'PAYMENT_UPDATED') {
$paymentId = $payment['id'] ?? null;
$paymentStatus = $payment['status'] ?? null;
// 在这里执行业务处理。
}
http_response_code(200);
echo json_encode(['received' => true]);
json_decode($json, true) 的第二个参数为 true 时,JSON 对象会转换为关联数组。如果需要对象,可以省略这个参数:
$payload = json_decode($json);
echo $payload->event;
echo $payload->payment->id;
如果 $_POST 确实为空
只要请求头和请求体符合描述,PHP 就应当填充 $_POST['data']。如果当前运行环境没有正常解析表单正文,可以读取原始请求体,再用 parse_str() 解析:
<?php
$rawBody = file_get_contents('php://input');
$form = [];
parse_str($rawBody, $form);
$json = $form['data'] ?? null;
if ($json === null) {
http_response_code(400);
exit('Missing data parameter');
}
$payload = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
exit('Invalid JSON: ' . json_last_error_msg());
}
不要把整个原始请求体直接传给 json_decode()。这个请求发送的是 URL 编码表单,不是纯 JSON,原始内容通常类似:
data=%7B%22event%22%3A%22PAYMENT_UPDATED%22%2C...
只有当发送方把 Content-Type 改为 application/json,并在请求体中直接发送 JSON 对象时,才应这样处理:
$rawBody = file_get_contents('php://input');
$payload = json_decode($rawBody, true);
注意事项
charset=UTF-8不会影响$_POST读取表单字段。- 不建议使用
$_REQUEST,因为它可能混合 GET、POST 和 Cookie 数据,难以确认数据来源。 - 不要根据示例中的
payment.status、金额或付款编号直接更新业务数据。应先按照 webhook 服务方的规则验证签名或其他认证信息。 - Webhook 可能重复投递,处理时最好根据事件 ID 或业务对象 ID 做幂等控制。
- 生产环境中不要把完整的付款数据、密钥或签名写入响应或公开日志。
- 正常接收并处理请求后,应尽快返回
2xx状态码,避免发送方因超时而重复投递。
备注:内容仅供参考。
