Repository navigation
feat(wechatpay): 微信支付 v3 Native 直连插件(对标 #12.2 微信侧) - #6
Merged
Merged
Conversation
补上 12.2 的微信半边。与支付宝那半边的结构差异决定了这不是「照着抄」:
- 支付宝把 `sign` 放在表单里;微信是 `Authorization: WECHATPAY2-SHA256-RSA2048 ...` 头;
- 支付宝的应答是表单文本;微信是 JSON + **响应头签名**,要用原始报文验;
- 支付宝回调用自己的签名串;微信回调是 **AES-256-GCM 密文**,要用 APIv3 密钥解。
## 三个必然踩到的坑
1. 请求签名串 `METHOD\nURL\nTIMESTAMP\nNONCE\nBODY\n` —— 末尾那个换行属于签名内容。
少一个就是 401 `SIGN_ERROR`,而错误码不会告诉你是哪一段错了。
2. 应答/回调签名串只有三段 `TIMESTAMP\nNONCE\nBODY\n`,与请求的不同;且必须用**原始字节**。
所以回调用 `kind: 'raw'` 路由拿未解析流 —— 内核 JSON 解析会重新序列化,字段顺序/
空格/转义一变,签名就永远验不过(这不是偶发,是必然)。
3. `ciphertext` 是 base64 的 `密文 || 16 字节认证标签`,不是纯密文;整段喂给 decipher
会在 `final()` 抛「unable to authenticate data」。
## 设计取舍
- **应答无签名即拒绝**:`code_url` 决定用户把钱扫给谁,伪造的应答等于劫持订单。
- **回调时间戳 5 分钟容差**:微信会重推,但重推必须带新签名;旧报文原样重放不能通过。
- **非 SUCCESS 状态也回 `{"code":"SUCCESS"}`**:`CLOSED`/`PROCESSING` 不该结算,
但回 FAIL 会让微信一直重推。
- **验签/解密失败回 401 + `FAIL`**:这类报文不可信,必须让它重试。
- **AppID 用常量时间比较**。
- **密钥留空 = 保持不变**:后台不回显密钥材料,改个商户号不该逼管理员重贴三份密钥。
- **API 基址只允许 https 域名**(可配置是为了测试,那就必须按 SSRF 入口来防)。
## 验证
`pnpm test` **37 例**:协议 24(签名串形状、Authorization 头、裸 base64 私钥、应答验签、
篡改/错密钥/过期时间戳拒绝、AES-GCM 往返与篡改拒绝、错误密钥拒绝、金额单位是分、
description 截断 127、错误体解析、配置校验、SSRF 校验、常量时间 AppID 比较)
+ 接线 13(未配置不给支付方式、下单并验应答签名、**未签名应答被拒**、CNY 之外不出网、
原始报文回调结算、非 SUCCESS 不结算、篡改回 401 FAIL、AppID 不符拒绝、金额缺失拒绝、
密钥留空继承、坏配置不落盘)。
`pnpm typecheck` / `pnpm build` 通过;`frontend/` 配置页(四语言)构建出
`frontend/dist/index.js`;`install-into-kernel.mjs` 成功装入内核 `data/plugins`。
## 未完成
- **未用真实商户号跑过真单**:需 mchid + 商户 API 证书序列号 + 私钥 + APIv3 密钥 +
平台公钥(或平台证书)+ 可公网回调的 https 域名。
- **平台证书自动轮换未做**:现在直接配平台公钥/证书内容;微信平台证书会轮换,
生产上还需要定时下载 `/v3/certificates`(用 APIv3 密钥解密)并替换。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
对标 #12.2 的微信半边(支付宝半边已在 #5 合入)。
与支付宝那半边不是「照着抄」
signAuthorization: WECHATPAY2-SHA256-RSA2048 ...头三个必然踩到的坑
METHOD\nURL\nTIMESTAMP\nNONCE\nBODY\n—— 末尾换行属于签名内容,少一个就是 401
SIGN_ERROR,且错误码不说是哪段错了。TIMESTAMP\nNONCE\nBODY\n,且必须用原始字节。所以回调用
kind: 'raw'路由拿未解析流:内核 JSON 解析会重新序列化,字段顺序/空格/转义一变,签名永远验不过。
ciphertext是 base64 的密文 || 16B 认证标签,整段喂给 decipher 会在final()抛错。设计取舍
code_url决定用户把钱扫给谁,伪造应答等于劫持订单SUCCESS:CLOSED/PROCESSING不该结算,但回 FAIL 会招致无限重推FAIL:报文不可信,必须让它重试验证
pnpm test37 例:协议 24 + 接线 13,含「未签名应答被拒」「篡改回 401」「AppID 不符拒绝」「密钥留空继承」「坏配置不落盘」。
typecheck/build通过,配置页(四语言)构建产物齐全,install-into-kernel.mjs成功装入内核data/plugins。未完成
/v3/certificates解密替换)按 TRACK §四,本 PR 只能说「驱动已实现,未联调」。