代码 微信支付 APIv3 请求签名报 401/SIGN_ERROR:5 行签名串与 Authorization 头逐行排查

2026-10-09 09:01:33

微信支付 APIv3 请求签名报 401/SIGN_ERROR:5 行签名串与 Authorization 头逐行排查

官方文档:

  • 签名生成:https://pay.weixin.qq.com/doc/global/v3/zh/4012354988
  • 请求参数带 Body 如何计算签名:https://pay.weixin.qq.com/doc/v3/merchant/4012365336
  • 签名验签(排障与 SIGN_ERROR detail):https://pay.weixin.qq.com/doc/global/v3/zh/4012355060
  • 签名验签工具:https://pay.weixin.qq.com/doc/v3/merchant/4012365352
  • 合作伙伴排障:https://pay.weixin.qq.com/doc/v3/partner/4012365875
  • 开发指引 / APP 调起支付:https://pay.weixin.qq.com/doc/global/v3/zh/4012354124

一、先分清方向:请求签名 5 行,回调验签 3 行

APIv3 所有接口交互都要对「接口请求」做 SHA256withRSA 签名,对「响应 / 回调」做验签。请求侧是自己签名发给微信,回调侧是验微信发来的签名。两者签名串格式不同:请求签名串 5 行,应答 / 回调验签串 3 行。拼错方向是最常见的低级错误。

二、请求签名串:5 行,每行以 \n 结尾(包括最后一行)

1 HTTP请求方法\n
2 URL\n
3 请求时间戳\n
4 请求随机串\n
5 请求报文主体\n
  • 第 2 行 URL 要去掉域名,只保留 /v3/... 路径;有查询参数时末尾要带 ? 和查询串。例:/v3/pay/transactions/out-trade-no/Tencentwechatpay0000457?mchid=1900006891
  • 第 3 行是秒级时间戳(格林威治 1970-01-01 起的总秒数)。微信会拒绝很久以前发起的请求,Authorization 里的 timestamp 与发起请求时间不得超过 5 分钟。
  • 第 5 行:POST/PUT 用真实发送的 JSON 报文,且必须是一行;GET 时 body 为空,第五行是空行加换行,也就是随机串后出现两个 \n。

三、算签名

用商户 API 证书私钥 apiclient_key.pem 对签名串做 SHA256withRSA,结果 Base64 得到 signature。

命令行示例(GET 空 body):

$ echo -n -e "GET\n/v3/certificates\n1554208460\n593BEC0C930BF1AFEB40B4A08C8FB242\n" | openssl dgst -sha256 -sign apiclient_key.pem | openssl base64 -A

四、Authorization 头

Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900007291",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="...",timestamp="1554208460",serial_no="408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB"
  • 认证类型目前仅 WECHATPAY2-SHA256-RSA2048。
  • serial_no 是商户 API 证书序列号(apiclient_cert.pem),不是平台证书序列号,别混用;mchid、apiclient_key.pem、apiclient_cert.pem 序列号必须一一对应。
  • nonce_str、timestamp 必须与计算签名时用的值完全一致。

五、官方列出的常见签名失败原因

  • 签名串最后一行没有附加换行符;GET 空 body 少了一个换行。
  • 手工拼接的 URL 和实际请求发送的不一致,建议用 HTTP 库 / URL 对象取 URL。
  • 签名和设置 Authorization 头时用了前后两个时间戳。
  • 签名和设置 Authorization 头时用了前后两个不同的随机串。
  • 签名和请求时用了前后两次序列化的 JSON 作为请求体。字段顺序、空格、中文编码不同都会导致签名不匹配。
  • 请求方法必须大写 GET/POST/PUT,不能小写。
  • URL 不带域名,是 /v3/certificates 而不是 https://api.mch.weixin.qq.com/v3/certificates;不要出现多余的 //。
  • 代码里 \n 要真换行,不能是字面字符 "\n"。

六、其他常见 400 / 401 报错

  • 400:Accept 和 User-Agent 必须都设置,缺一不可。Content-Type: application/json,Accept: application/json;APIv3 很可能拒绝无 User-Agent 的请求。
  • 401 Unauthorized / SIGN_ERROR:Authorization 值格式错误时会提示「请检查上送的 Authorization,目前仅支持 WECHATPAY2-SHA256-RSA2048」;timestamp 与发起请求时间不得超过 5 分钟。

SIGN_ERROR 的 detail 对比法

验签失败会在应答 detail 里给出 sign_information:method、url、truncated_sign_message(微信侧实际用的签名串,换行显示成 \n)、sign_message_length(签名串字节长度)。把这两个值和自己在程序里拼的对比,定位最快。官方示例:

{"code":"SIGN_ERROR","message":"错误的签名,验签失败","detail":{"field":"signature","issue":"sign not match","location":"authorization","sign_information":{"method":"GET","url":"/payscore/user-service-state?service_id=500001&appid=wx...&openid=...","truncated_sign_message":"GET\n/payscore/user-service-state?service_id=500001&appid=...&openid=...\n1559194069\n18a427e78d2344e1a71156a2690cc4d6\n\n","sign_message_length":157}}}

sign_message_length 会随 url 长度变化。注意 GET 的签名串末尾是两个 \n。

七、回调方向:验签串 3 行

回调 / 应答验签串是 3 行:

应答时间戳\n
应答随机串\n
应答报文主体\n

回调 HTTP 头有 Wechatpay-Timestamp、Wechatpay-Nonce、Wechatpay-Signature、Wechatpay-Serial 四个。某些代理 / CDN 会过滤这些扩展头,取不到就先原样打日志,确认四个头都在。

八、APP 调起支付签名:另一套,4 行

签名串 4 行:应用 id、时间戳、随机字符串、预支付交易会话 ID,用商户私钥 SHA256withRSA + Base64 得到 paySign。

$ echo -n -e "wx8888888888888888\n1414561699\n5K8264ILTKch16CQ2502SI8ZNMTM67VS\nWX1217752501201407033233368018\n" | openssl dgst -sha256 -sign apiclient_key.pem | openssl base64 -A

九、工具

官方提供签名 / 验签工具,可以模拟生成请求签名并校验,用来确认「算法没错,是参数不一致」。如果工具生成的签名正确但接口仍报错,就是实际请求里的证书、签名信息或 Body 与工具中参与签名的参数不一致。

推荐文章

程序员茄子在线接单