PAYATHON 2026

如何在 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 状态码,避免发送方因超时而重复投递。

备注:内容仅供参考。