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:
parent
03e440b005
commit
b4ee983d1d
@ -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")
|
||||
|
||||
@ -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, "处理失败")
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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("未提供支付单号")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user