Skip to content

雷鸟跑腿 状态回调验签说明 ​

版本:1.0
日期:2026-10-23
适用:雷鸟跑腿 订单状态回调(X-Sign)

1. 概述 ​

订单状态变更时,我方向三方平台配置的 callbackUrl 发送 POST 请求,并在请求头携带 X-Callback-Id、X-Timestamp、X-Sign。

  • X-Sign 算法:HMAC-SHA256(appSecret, 签名原文),结果小写 hex。
  • appSecret 由平台注册时分配(platform_config.app_secret)。
  • 签名覆盖:回调 ID、订单 ID、状态、时间戳,以及请求体的 SHA-256。

2. 请求 ​

Method: POST
Content-Type: application/json

请求头:

Header说明
X-Callback-Id回调ID,与 body.callbackId 一致,用于幂等去重
X-TimestampUnix 时间戳(秒)
X-SignHMAC-SHA256 签名(小写 hex)

请求体字段:

字段类型可空说明
callbackIdString否回调ID
orderIdString否我方内部订单ID(字符串)
orderNoString否我方内部订单号
thirdOrderNoString是三方订单号(原样回传)
statusInteger否订单状态码
statusDescString否状态描述
riderNameString是骑手姓名,未接单时为 null
riderPhoneString是骑手电话,未接单时为 null
callbackTimeString否回调时间 yyyy-MM-dd HH:mm:ss

状态码:

状态码说明
0待支付
1已取消
2已支付待接单
3已接单待到店
4已到店待取件
5已取件配送中
6已送达
7已完成(已评价)
8异常
9已退款
10返还中
11已返还

回调可能推送 0~11 中任意状态,请勿只处理 1~7。

3. 签名原文 ​

callbackId{callbackId}orderId{orderId}status{status}timestamp{timestamp}bodyHash{bodyHash}

生成步骤:

  1. 对收到的原始 body 字节做 SHA-256,转小写 hex,得到 bodyHash。
  2. 取请求头 X-Callback-Id、X-Timestamp 和 body 中的 status(整数转字符串)。
  3. 按上述模板拼接(无分隔符、无 URL 编码)。
  4. HMAC-SHA256(appSecret, 拼接串),转小写 hex,与 X-Sign 比较(常量时间比较)。

4. 完整样例 ​

请求头

Content-Type: application/json
X-Callback-Id: CB_12345_3_1698888888000
X-Timestamp: 1761200000
X-Sign: 3e532b8bc09c6d5178cc3b464118834b1409279c63bad27e632246d0135aa966

Body(原始字符)

json
{"callbackId":"CB_12345_3_1698888888000","orderId":"12345","orderNo":"ORD202610230001","thirdOrderNo":"THIRD202610230001","status":3,"statusDesc":"已接单待到店","riderName":"张三","riderPhone":"13800000000","callbackTime":"2026-10-23 12:00:00"}

中间值

appSecret = test_app_secret_please_replace   (示例值,实际以平台分配为准)
bodyHash  = 5fbb38de8e07f4aa63f89c17144647caf4eb96a3fabb7e282445b2629e3684ac
timestamp = 1761200000

签名原文

callbackIdCB_12345_3_1698888888000orderId12345status3timestamp1761200000bodyHash5fbb38de8e07f4aa63f89c17144647caf4eb96a3fabb7e282445b2629e3684ac

X-Sign

3e532b8bc09c6d5178cc3b464118834b1409279c63bad27e632246d0135aa966

5. 验签代码示例 ​

Java ​

java
String bodyHash = sha256Hex(rawBodyBytes);                    // 小写 hex
String plain = "callbackId" + callbackId
        + "orderId" + orderId
        + "status" + status
        + "timestamp" + timestamp
        + "bodyHash" + bodyHash;
String expected = hmacSha256Hex(appSecret, plain);            // 小写 hex
boolean ok = java.security.MessageDigest.isEqual(
        expected.getBytes(java.nio.charset.StandardCharsets.UTF_8),
        sign.getBytes(java.nio.charset.StandardCharsets.UTF_8));

Node.js ​

js
const crypto = require('crypto');
const bodyHash = crypto.createHash('sha256').update(rawBody).digest('hex');
const plain = `callbackId${callbackId}orderId${orderId}status${status}`
            + `timestamp${timestamp}bodyHash${bodyHash}`;
const expected = crypto.createHmac('sha256', appSecret).update(plain, 'utf8').digest('hex');
const ok = expected.length === sign.length
        && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sign));

PHP ​

php
$bodyHash = hash('sha256', $rawBody);
$plain = "callbackId{$callbackId}orderId{$orderId}status{$status}"
       . "timestamp{$timestamp}bodyHash{$bodyHash}";
$expected = hash_hmac('sha256', $plain, $appSecret);
$ok = hash_equals($expected, $sign);

Python ​

python
import hashlib, hmac

body_hash = hashlib.sha256(raw_body).hexdigest()
plain = (f"callbackId{callback_id}orderId{order_id}status{status}"
         f"timestamp{timestamp}bodyHash{body_hash}")
expected = hmac.new(app_secret.encode(), plain.encode(), hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, sign)

6. 响应、重试与幂等 ​

  • 返回 HTTP 2xx(建议 body {"code":0})即视为成功;我方只判断 HTTP 状态码。
  • 失败重试:最多 5 次,间隔 1 / 5 / 15 / 30 / 60 分钟;首次推送后超过 1 小时停止。
  • 超过重试次数后仅记录失败日志,不再推送。
  • 按 X-Callback-Id 幂等去重;重试时 callbackId 不变,但 X-Timestamp 会重新生成,X-Sign 随之变化,请每次重新验签。

7. 常见问题 ​

  1. bodyHash 必须基于原始字节:不要反序列化后重新序列化再哈希,字段顺序、空格、null 都会变化。
  2. 统一 UTF-8;签名结果均为小写 hex。
  3. 时间戳为秒级;建议校验 |now - timestamp| ≤ 300s 防重放。
  4. 签名参数只有 4 个:callbackId、orderId、status、timestamp,不要加入其他 body 字段。
  5. bodyHash 只拼接一次,位于原文末尾。
  6. X-Callback-Id 与 body.callbackId 应一致,可作一致性校验。