
Payment Callback Safety
当代码涉及支付回调、webhook 通知、notify_url、微信支付/支付宝回调处理时触发。防止回调伪造、重放攻击、金额篡改等支付安全问题。
Install
npx skills add doccker/cc-use-exp --skill payment-callback-safety -g -a claude-code -y- Repository
- doccker/cc-use-exp1.1k
- Category
- Engineering
- Updated
- 2mo ago
- License
- See repository
- Context cost
- Mediumabout 1,621 tokens
- Runs scripts
- No, instructions only
What it does
当代码涉及支付回调、webhook 通知、notify_url、微信支付/支付宝回调处理时触发。防止回调伪造、重放攻击、金额篡改等支付安全问题。
支付回调安全规范
当系统涉及第三方支付回调(微信支付、支付宝等)或 webhook 通知时,防止伪造、篡改和重放攻击。
陷阱 #1: 回调未验证平台签名
场景: 收到支付回调后直接解密/解析并处理,未验证请求确实来自支付平台
问题根因
支付回调地址暴露在公网,任何人都可以伪造请求。如果不验签,攻击者可以构造假的"支付成功"通知。
错误示例
// ❌ 错误: 直接解密处理,未验签
@PostMapping("/payment/callback/wechat")
public String wechatCallback(@RequestBody String body) {
JsonNode json = objectMapper.readTree(body);
// 直接尝试解密 resource
String decrypted = decryptAesGcm(ciphertext, apiV3Key, nonce, aad);
processPayment(decrypted); // 谁发来的都处理
return "{\"code\":\"SUCCESS\"}";
}
正确做法
// ✅ 正确: 先验签,再解密,再处理
@PostMapping("/payment/callback/wechat")
public String wechatCallback(
@RequestBody String body,
@RequestHeader("Wechatpay-Signature") String signature,
@RequestHeader("Wechatpay-Timestamp") String timestamp,
@RequestHeader("Wechatpay-Nonce") String nonce,
@RequestHeader("Wechatpay-Serial") String serial) {
// 1. 用微信支付平台公钥验签(不是商户私钥)
String message = timestamp + "\n" + nonce + "\n" + body + "\n";
PublicKey platformKey = loadPlatformPublicKey(serial);
if (!verifySignature(message, signature, platformKey)) {
return "{\"code\":\"FAIL\",\"message\":\"验签失败\"}";
}
// 2. 验签通过后再解密和处理
String decrypted = decryptAesGcm(ciphertext, apiV3Key, nonce, aad);
processPayment(decrypted);
return "{\"code\":\"SUCCESS\"}";
}
关键区分
| 密钥/证书 | 用途 | 持有方 |
|---|---|---|
| 商户私钥 | 商户请求微信时签名 | 商户 |
| 商户 API 证书 | 商户身份标识 | 商户 |
| 微信支付平台公钥/证书 | 验证微信回调签名 | 微信平台签发,商户持有公钥 |
| API V3 Key | 解密回调通知体 | 商户 |
检查清单
- 回调入口是否在解密前先验证平台签名
- 验签使用的是平台公钥/证书,而非商户私钥
- 是否按
Wechatpay-Serial匹配对应平台证书(支持证书轮换) - 验签失败是否直接拒绝,不继续处理
陷阱 #2: 未做防重放校验
场景: 攻击者截获一份真实的支付成功回调,重复发送给系统
问题根因
合法的旧回调报文签名仍然有效,仅靠验签无法防止重放。
错误示例
// ❌ 错误: 验签通过就直接处理,不检查是否重复
if (verifySignature(message, signature, platformKey)) {
processPayment(decrypted); // 同一笔单可能被处理多次
}
