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)
|
LockOrder(status.ProviderOrderID)
|
||||||
defer UnlockOrder(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 {
|
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()))
|
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")
|
c.String(http.StatusOK, "fail")
|
||||||
|
|||||||
@ -164,6 +164,11 @@ func WxpayNotify(c *gin.Context) {
|
|||||||
|
|
||||||
LockOrder(status.ProviderOrderID)
|
LockOrder(status.ProviderOrderID)
|
||||||
defer UnlockOrder(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 {
|
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()))
|
logger.LogError(c.Request.Context(), fmt.Sprintf("微信支付 充值处理失败 trade_no=%s client_ip=%s error=%q", status.ProviderOrderID, c.ClientIP(), err.Error()))
|
||||||
wxpayNotifyFail(c, "处理失败")
|
wxpayNotifyFail(c, "处理失败")
|
||||||
|
|||||||
@ -322,6 +322,28 @@ func TestRechargeWaffo_ProcessesAffiliateRebateIdempotently(t *testing.T) {
|
|||||||
assert.Equal(t, int64(1), count)
|
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) {
|
func TestCompleteSubscriptionOrder_RejectsMismatchedPaymentProvider(t *testing.T) {
|
||||||
truncateTables(t)
|
truncateTables(t)
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,8 @@ var (
|
|||||||
ErrPaymentMethodMismatch = errors.New("payment method mismatch")
|
ErrPaymentMethodMismatch = errors.New("payment method mismatch")
|
||||||
ErrTopUpNotFound = errors.New("topup not found")
|
ErrTopUpNotFound = errors.New("topup not found")
|
||||||
ErrTopUpStatusInvalid = errors.New("topup status invalid")
|
ErrTopUpStatusInvalid = errors.New("topup status invalid")
|
||||||
|
ErrTopUpAmountMismatch = errors.New("topup amount mismatch")
|
||||||
|
ErrTopUpCurrencyMismatch = errors.New("topup currency mismatch")
|
||||||
)
|
)
|
||||||
|
|
||||||
type promoCodeBonusInfo struct {
|
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) {
|
func RechargeEpay(tradeNo string, actualPaymentMethod string, callerIp string) (err error) {
|
||||||
if tradeNo == "" {
|
if tradeNo == "" {
|
||||||
return errors.New("未提供支付单号")
|
return errors.New("未提供支付单号")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user