From abf6a43b8c809e344a059cfe6d121bed118e1a2c Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Fri, 10 Jul 2026 14:14:01 +0800 Subject: [PATCH] =?UTF-8?q?docs(v2):=20P2=20=E8=AE=A1=E5=88=92=20settle=20?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=AE=B5=E5=90=8C=E6=AD=A5=20T5=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D(=E7=9E=AC=E6=97=B6=E8=AF=BB=E5=BA=93=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E2=86=92SettleFailed)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u --- .../plans/2026-07-10-pay-v2-p2-pipeline.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/plans/2026-07-10-pay-v2-p2-pipeline.md b/docs/superpowers/plans/2026-07-10-pay-v2-p2-pipeline.md index 4c29e5c..b51a832 100644 --- a/docs/superpowers/plans/2026-07-10-pay-v2-p2-pipeline.md +++ b/docs/superpowers/plans/2026-07-10-pay-v2-p2-pipeline.md @@ -1321,7 +1321,9 @@ func (g *Gateway) Settle(ctx context.Context, ev *provider.PaidEvent) (SettleRes if errors.Is(err, store.ErrAttemptNotFound) { return SettleNotFound, nil } - return SettleNotFound, err + // 读库瞬时失败是可重试态(SettleFailed),不能与"查无此单"(终态,ack 即弃) + // 混淆——否则按 result 决定 ack 的调用方会让渠道停止重投,已付单永不入账。 + return SettleFailed, err } // 金额/币种核对:币种须一致,实付须 ≥ 应收(允许 crypto 多付,拒少付)。 if ev.PaidCurrency != att.Currency || ev.PaidAmountMinor < att.AmountMinor { @@ -2158,13 +2160,19 @@ func (h *GatewayHandler) Callback(c *gin.Context) { res, err := h.g.HandleCallback(c.Request.Context(), method, provider.CallbackInput{ Raw: raw, Headers: headers, }) - if err != nil { - // 验签失败等:回 400 让渠道按策略重投(或人工排障)。 + // 按 SettleResult(而非只看 err)分终态/可重试:amount_mismatch 是终态—— + // 重投同一回调永远解决不了,必须回 200 让渠道停投(差错单归 P6 对账/人工); + // SettleFailed/验签失败才是可重试态,回 400 让渠道重投。 + switch { + case err == nil: + c.JSON(http.StatusOK, gin.H{"result": string(res)}) + case res == gateway.SettleAmountMismatch: + log.Printf("[v2 callback] method=%s 金额/币种不符(终态,已受理停投): %v", method, err) + c.JSON(http.StatusOK, gin.H{"result": string(res)}) + default: log.Printf("[v2 callback] method=%s result=%s err=%v", method, res, err) util.RespondError(c, http.StatusBadRequest, "callback_failed", "回调处理失败") - return } - c.JSON(http.StatusOK, gin.H{"result": string(res)}) } func (h *GatewayHandler) writeCreateErr(c *gin.Context, action, method string, err error) {