← ClaudeAtlas

pay-notifylisted

Use when implementing 支付异步回调, WeChat/Alipay notify, 验签, 金额校验, notify 返回 SUCCESS. Do not use user JWT on callbacks. Unique-key mechanics also in backend-idempotent.
1398281322-a11y/java-backend-guardrails · ★ 2 · AI & Automation · score 73
Install: claude install-skill 1398281322-a11y/java-backend-guardrails
# 支付异步回调 ## When to Invoke 微信/支付宝/聚合支付 **异步通知**、return_url 与 notify 的区别、回调重试。资金面试第一题。 ## When NOT 用户点支付、预下单 → `pay-trade`。丢回调去查单 → `pay-query`。重复付了两笔 → `pay-duplicate`。通用幂等表结构可再读 `idempotent`,本文件管支付回调流水线。 ## 风险(面试考点) 渠道收不到成功应答会重试(微信约 24h 内多次)。不幂等 = 重复入账、重复发货。 回调 **不是用户浏览器**,不能验 JWT。必须 **验签 → 校验商户号/金额/币种/单号 → 落唯一流水 → 推状态**。验签失败直接拒绝,不要落业务。 在回调线程里调渠道退款、发券、RPC:超时会导致渠道以为失败再打,把线程池打满。回调只做短事务,重活丢 MQ。 return_url(同步跳回)只展示,**不能当入账依据**。入账只认异步 notify 或主动查单。 金额单位:渠道常用 **分**。本地订单是元会错 100 倍,必须统一成分比较。 ## 方案选型(轻量优先) ```text 1. 验签(官方 SDK,禁止手写拼串除非渠道要求) 2. 校验 mchId、out_trade_no、amount、currency 与本地支付单一致 3. INSERT 回调流水 uk(channel, transaction_id 或 notify_id) 冲突 → 已处理,仍返回 SUCCESS 4. 支付单 PAYING/CREATED → SUCCESS(条件更新) 5. 发 MQ:改订单、发货、发券(消费者幂等) 6. HTTP 返回渠道要求的 SUCCESS/XML ``` 业务处理失败要返回 FAIL 让渠道重试 **仅当还没落成功流水**。已经 SUCCESS 的重复通知必须 SUCCESS,否则会重试到天荒。 ## 默认方案 ```java public String notify(HttpServletRequest req) { if (!wxPay.verifyNotifySign(req)) return "FAIL"; // 不落业务 WxNotify n = parse(req); PayTrade t = tradeMapper.selectByOutNo(n.getOutTradeNo()); if (t == null || !t.getMchId().equals(n.getMchId()) || t.getAmountFen() != n.getAmountFen()) { log.error("回调与本地不一致"); return "FAIL"; } try { logMapper.insert(new PayNotifyLog(n.getTransactionId(), n.getRaw())); } catch (DuplicateKeyException e) { return "SUCCESS"; } int rows = tradeMapper.update(null, Wrappers.<PayTrade>lambdaUpdate() .set(PayTrade::getStatus,