fix(topup): reject uncreditable orders before payment (#6845)

* fix(topup): reject uncreditable orders before payment

* fix(topup): align zero-ratio Stripe validation

* fix(topup): mirror settlement conversions in validation
This commit is contained in:
Shawn Wang
2026-08-14 17:01:32 +08:00
committed by GitHub
parent e5efc73cdb
commit 2a0ce3475c
6 changed files with 251 additions and 0 deletions
+66
View File
@@ -187,6 +187,66 @@ func getMinTopup() int64 {
return int64(minTopup)
}
func getTopUpQuota(amount int64) (int, error) {
quota := decimal.NewFromInt(amount)
if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
quotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
quota = decimal.NewFromInt(quota.Div(quotaPerUnit).IntPart()).Mul(quotaPerUnit)
} else {
quota = quota.Mul(decimal.NewFromFloat(common.QuotaPerUnit))
}
return common.QuotaFromDecimalStrict(quota)
}
func getMaxTopUpAmount() int64 {
if common.QuotaPerUnit <= 0 {
return 0
}
quotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
maxStoredAmount := decimal.NewFromInt(common.MaxQuota - 1).
Div(quotaPerUnit).
Floor()
if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
return maxStoredAmount.Add(decimal.NewFromInt(1)).
Mul(quotaPerUnit).
Ceil().
Sub(decimal.NewFromInt(1)).
IntPart()
}
return maxStoredAmount.IntPart()
}
func validateCreditedQuota(quota decimal.Decimal) error {
value, err := common.QuotaFromDecimalStrict(quota)
if err != nil {
return errors.New("充值额度超出系统可表示范围")
}
if value <= 0 {
return errors.New("充值额度必须大于 0")
}
return nil
}
func validateTopUpQuota(amount int64) error {
quota, err := getTopUpQuota(amount)
if err == nil && quota > 0 {
return nil
}
maxAmount := getMaxTopUpAmount()
if maxAmount > 0 && amount > maxAmount {
return fmt.Errorf("单笔充值数量不能大于 %d", maxAmount)
}
return errors.New("充值数量无效")
}
func rejectInvalidTopUpQuota(c *gin.Context, amount int64) bool {
if err := validateTopUpQuota(amount); err != nil {
c.JSON(http.StatusOK, gin.H{"message": "error", "data": err.Error()})
return true
}
return false
}
func RequestEpay(c *gin.Context) {
var req EpayRequest
err := c.ShouldBindJSON(&req)
@@ -198,6 +258,9 @@ func RequestEpay(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getMinTopup())})
return
}
if rejectInvalidTopUpQuota(c, req.Amount) {
return
}
id := c.GetInt("id")
group, err := model.GetUserGroup(id, true)
@@ -412,6 +475,9 @@ func RequestAmount(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getMinTopup())})
return
}
if rejectInvalidTopUpQuota(c, req.Amount) {
return
}
id := c.GetInt("id")
group, err := model.GetUserGroup(id, true)
if err != nil {