fix(backend): 特惠限次 webhook 兜底——第二笔已支付特惠单只标 paid 不叠加时长
按 pay-contract 看板要求:续期入账前事务内再查本店是否已有另一笔 paid 的 promo_first_month(防绕过前端/并发双买);命中则跳过 entitle、记 ALERT 告警, 回调仍回 SUCCESS(钱已收,退款人工处理)。补对应测试(双买不叠加/正常套餐不受影响)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
@@ -279,8 +279,26 @@ func (s *PayService) settle(outTradeNo, bizCode, amount, tradeNo, channel string
|
||||
if !ok {
|
||||
return ErrUnknownPlan
|
||||
}
|
||||
if err := entitle(tx, p.ShopID, plan); err != nil {
|
||||
return err
|
||||
// 特惠限次 webhook 兜底(防绕过前端/并发双买):入账前事务内再查一次,
|
||||
// 本店已有另一笔已支付特惠单 → 本单只标 paid 不叠加时长,记告警(契约 INTEGRATION-BOARD 要求)
|
||||
entitleOK := true
|
||||
if p.ProductBizCode == PromoBizCode {
|
||||
var dup int64
|
||||
if err := tx.Model(&model.LicensePurchase{}).
|
||||
Where("shop_id = ? AND product_biz_code = ? AND status = ? AND id <> ?",
|
||||
p.ShopID, PromoBizCode, "paid", p.ID).
|
||||
Count(&dup).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if dup > 0 {
|
||||
entitleOK = false
|
||||
log.Printf("[pay] ALERT promo double-claim out_trade_no=%s shop=%d:本店已享受过首月特惠,本单不叠加时长", outTradeNo, p.ShopID)
|
||||
}
|
||||
}
|
||||
if entitleOK {
|
||||
if err := entitle(tx, p.ShopID, plan); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
shopID = p.ShopID
|
||||
return tx.Model(&model.LicensePurchase{}).Where("id = ?", p.ID).Updates(map[string]any{
|
||||
|
||||
Reference in New Issue
Block a user