Skip to content

回调通知

支付终态平台主动 POST 你的 notify_url(创建时快照):

X-App-Id / X-Timestamp / X-Nonce / X-Signature   // 与请求签名同一套,用 app_secret 验签
{"event_type":"payment.succeeded","payment_no":"PAY...","out_trade_no":"...","amount":1000,"paid_at":"..."}
// closed 事件为 closed_at + closed_reason(expired|manual)

应答约定

  • HTTP 2xx = 成功;否则重试梯度 1m→5m→30m→2h→6h 共 5 次后置 dead(平台人工兜底)。
  • 单次超时 5s——请快速应答,业务处理异步化。
  • at-least-once:可能重复投递,请按 payment_no + event_type 幂等处理。
  • 极端时序:可能先收 closed 再收 succeeded(渠道扣款晚于关单的补单),以查单为准

回调验签(必须)

收到回调后,用你的 app_secret 对收到的请求按签名串规则重新计算 X-Signature,constant-time 比较;验证通过才处理业务。

Go

go
func verifyNotify(secret string, r *http.Request, body []byte) (bool, error) {
    raw := r.Method + "\n" + r.URL.Path + "\n" + r.URL.RawQuery + "\n" +
           r.Header.Get("X-Timestamp") + "\n" + r.Header.Get("X-Nonce") + "\n" + string(body)
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(raw))
    want := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(want), []byte(r.Header.Get("X-Signature"))), nil
}

PHP

php
function verifyNotify(string $secret, array $headers, string $body): bool {
    $raw = implode("\n", [$headers['REQUEST_METHOD'] ?? 'POST', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?? '',
                     $_SERVER['QUERY_STRING'] ?? '', $headers['X-Timestamp'] ?? '', $headers['X-Nonce'] ?? ''])
           . "\n" . $body;
    $sig = hash_hmac('sha256', $raw, $secret);
    return hash_equals($sig, strtolower($headers['X-Signature'] ?? ''));
}

X-Timestamp 与本地时差 > 5 分钟直接丢弃(防重放),X-Nonce 建议记录 8h 去重(稳健对接方在验签后统一处理)。

外发事件 event_type

事件触发说明
payment.succeeded支付成功paid_at
payment.closed订单关闭closed_at + closed_reason(expired/manual)
payment.refunded退款成功refunded_at + refund_amount

订单状态字典见查单 · 状态字典