fix: validate direct payment callback amounts

Check direct payment callback currency, provider, and paid amount before settling Alipay or WxPay top-ups.
This commit is contained in:
zizi 2026-05-20 15:49:08 +08:00
parent 03e440b005
commit b4ee983d1d
4 changed files with 56 additions and 0 deletions

View File

@ -179,6 +179,11 @@ func AlipayNotify(c *gin.Context) {
LockOrder(status.ProviderOrderID)
defer UnlockOrder(status.ProviderOrderID)
if err := model.ValidateTopUpPaymentAmount(status.ProviderOrderID, model.PaymentProviderAlipay, status.Amount, status.Currency); err != nil {
logger.LogWarn(c.Request.Context(), fmt.Sprintf("支付宝 异步通知金额校验失败 trade_no=%s amount=%d currency=%s client_ip=%s error=%q", status.ProviderOrderID, status.Amount, status.Currency, c.ClientIP(), err.Error()))
c.String(http.StatusOK, "fail")
return
}
if err := model.RechargeAlipay(status.ProviderOrderID, c.ClientIP()); err != nil {
logger.LogError(c.Request.Context(), fmt.Sprintf("支付宝 充值处理失败 trade_no=%s client_ip=%s error=%q", status.ProviderOrderID, c.ClientIP(), err.Error()))
c.String(http.StatusOK, "fail")

View File

@ -164,6 +164,11 @@ func WxpayNotify(c *gin.Context) {
LockOrder(status.ProviderOrderID)
defer UnlockOrder(status.ProviderOrderID)
if err := model.ValidateTopUpPaymentAmount(status.ProviderOrderID, model.PaymentProviderWxpay, status.Amount, status.Currency); err != nil {
logger.LogWarn(c.Request.Context(), fmt.Sprintf("微信支付 异步通知金额校验失败 trade_no=%s amount=%d currency=%s client_ip=%s error=%q", status.ProviderOrderID, status.Amount, status.Currency, c.ClientIP(), err.Error()))
wxpayNotifyFail(c, "金额校验失败")
return
}
if err := model.RechargeWxpay(status.ProviderOrderID, c.ClientIP()); err != nil {
logger.LogError(c.Request.Context(), fmt.Sprintf("微信支付 充值处理失败 trade_no=%s client_ip=%s error=%q", status.ProviderOrderID, c.ClientIP(), err.Error()))
wxpayNotifyFail(c, "处理失败")

View File

@ -322,6 +322,28 @@ func TestRechargeWaffo_ProcessesAffiliateRebateIdempotently(t *testing.T) {
assert.Equal(t, int64(1), count)
}
func TestValidateTopUpPaymentAmount(t *testing.T) {
truncateTables(t)
insertUserForPaymentGuardTest(t, 186, 0)
topUp := &TopUp{
UserId: 186,
Amount: 2,
Money: 12.34,
TradeNo: "direct-pay-amount-guard",
PaymentMethod: PaymentMethodAlipay,
PaymentProvider: PaymentProviderAlipay,
Status: common.TopUpStatusPending,
CreateTime: time.Now().Unix(),
}
require.NoError(t, topUp.Insert())
require.NoError(t, ValidateTopUpPaymentAmount("direct-pay-amount-guard", PaymentProviderAlipay, 1234, "CNY"))
require.ErrorIs(t, ValidateTopUpPaymentAmount("direct-pay-amount-guard", PaymentProviderAlipay, 1233, "CNY"), ErrTopUpAmountMismatch)
require.ErrorIs(t, ValidateTopUpPaymentAmount("direct-pay-amount-guard", PaymentProviderAlipay, 1234, "USD"), ErrTopUpCurrencyMismatch)
require.ErrorIs(t, ValidateTopUpPaymentAmount("direct-pay-amount-guard", PaymentProviderWxpay, 1234, "CNY"), ErrPaymentMethodMismatch)
}
func TestCompleteSubscriptionOrder_RejectsMismatchedPaymentProvider(t *testing.T) {
truncateTables(t)

View File

@ -52,6 +52,8 @@ var (
ErrPaymentMethodMismatch = errors.New("payment method mismatch")
ErrTopUpNotFound = errors.New("topup not found")
ErrTopUpStatusInvalid = errors.New("topup status invalid")
ErrTopUpAmountMismatch = errors.New("topup amount mismatch")
ErrTopUpCurrencyMismatch = errors.New("topup currency mismatch")
)
type promoCodeBonusInfo struct {
@ -235,6 +237,28 @@ func UpdatePendingTopUpStatus(tradeNo string, expectedPaymentProvider string, ta
})
}
func ValidateTopUpPaymentAmount(tradeNo string, expectedPaymentProvider string, paidAmountCents int, currency string) error {
if tradeNo == "" {
return ErrTopUpNotFound
}
if strings.ToUpper(strings.TrimSpace(currency)) != "CNY" {
return ErrTopUpCurrencyMismatch
}
topUp := GetTopUpByTradeNo(tradeNo)
if topUp == nil {
return ErrTopUpNotFound
}
if expectedPaymentProvider != "" && topUp.PaymentProvider != expectedPaymentProvider {
return ErrPaymentMethodMismatch
}
expectedAmountCents := int(decimal.NewFromFloat(topUp.Money).Mul(decimal.NewFromInt(100)).Round(0).IntPart())
if paidAmountCents != expectedAmountCents {
return ErrTopUpAmountMismatch
}
return nil
}
func RechargeEpay(tradeNo string, actualPaymentMethod string, callerIp string) (err error) {
if tradeNo == "" {
return errors.New("未提供支付单号")