fix: add quota limit check to prevent subscription bypass
- Add quota check in preCreateOrderLogic for order preview - Move quota check inside transaction in purchaseLogic to prevent race condition - Add quota check in activateOrderLogic as final safeguard when creating subscription - Add quota check in redeemCodeLogic when redeeming codes for new subscriptions
This commit is contained in:
parent
2a1ae2e1cc
commit
7d4a19c9a3
@ -55,6 +55,25 @@ func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (r
|
||||
l.Errorw("[PreCreateOrder] Database query error", logger.Field("error", err.Error()), logger.Field("subscribe_id", req.SubscribeId))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribe error: %v", err.Error())
|
||||
}
|
||||
|
||||
// check subscribe plan quota limit
|
||||
if sub.Quota > 0 {
|
||||
userSub, err := l.svcCtx.UserModel.QueryUserSubscribe(l.ctx, u.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[PreCreateOrder] Database query error", logger.Field("error", err.Error()), logger.Field("user_id", u.Id))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user subscription error: %v", err.Error())
|
||||
}
|
||||
var count int64
|
||||
for _, v := range userSub {
|
||||
if v.SubscribeId == req.SubscribeId {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count >= sub.Quota {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeQuotaLimit), "quota limit")
|
||||
}
|
||||
}
|
||||
|
||||
var discount float64 = 1
|
||||
if sub.Discount != "" {
|
||||
var dis []types.SubscribeDiscount
|
||||
|
||||
@ -87,19 +87,6 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeOutOfStock), "subscribe out of stock")
|
||||
}
|
||||
|
||||
// check subscribe plan limit
|
||||
if sub.Quota > 0 {
|
||||
var count int64
|
||||
for _, v := range userSub {
|
||||
if v.SubscribeId == req.SubscribeId {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
if count >= sub.Quota {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeQuotaLimit), "quota limit")
|
||||
}
|
||||
}
|
||||
|
||||
var discount float64 = 1
|
||||
if sub.Discount != "" {
|
||||
var dis []types.SubscribeDiscount
|
||||
@ -195,6 +182,24 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
|
||||
}
|
||||
// Database transaction
|
||||
err = l.svcCtx.DB.Transaction(func(db *gorm.DB) error {
|
||||
// check subscribe plan quota limit inside transaction to prevent race condition
|
||||
if sub.Quota > 0 {
|
||||
var currentUserSub []user.Subscribe
|
||||
if e := db.Model(&user.Subscribe{}).Where("user_id = ?", u.Id).Find(¤tUserSub).Error; e != nil {
|
||||
l.Errorw("[Purchase] Database query error", logger.Field("error", e.Error()), logger.Field("user_id", u.Id))
|
||||
return e
|
||||
}
|
||||
var count int64
|
||||
for _, v := range currentUserSub {
|
||||
if v.SubscribeId == req.SubscribeId {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count >= sub.Quota {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SubscribeQuotaLimit), "quota limit")
|
||||
}
|
||||
}
|
||||
|
||||
// update user deduction && Pre deduction ,Return after canceling the order
|
||||
if orderInfo.GiftAmount > 0 {
|
||||
// update user deduction && Pre deduction ,Return after canceling the order
|
||||
|
||||
@ -160,6 +160,24 @@ func (l *RedeemCodeLogic) RedeemCode(req *types.RedeemCodeRequest) (resp *types.
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update subscribe error: %v", err.Error())
|
||||
}
|
||||
} else {
|
||||
// Check quota limit before creating new subscribe
|
||||
if subscribePlan.Quota > 0 {
|
||||
var count int64
|
||||
if err := tx.Model(&user.Subscribe{}).Where("user_id = ? AND subscribe_id = ?", u.Id, redemptionCode.SubscribePlan).Count(&count).Error; err != nil {
|
||||
l.Errorw("[RedeemCode] Count user subscribe failed", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count user subscribe error: %v", err.Error())
|
||||
}
|
||||
if count >= subscribePlan.Quota {
|
||||
l.Infow("[RedeemCode] Subscribe quota limit exceeded",
|
||||
logger.Field("user_id", u.Id),
|
||||
logger.Field("subscribe_id", redemptionCode.SubscribePlan),
|
||||
logger.Field("quota", subscribePlan.Quota),
|
||||
logger.Field("current_count", count),
|
||||
)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SubscribeQuotaLimit), "subscribe quota limit exceeded")
|
||||
}
|
||||
}
|
||||
|
||||
// Create new subscribe
|
||||
expireTime, traffic, err := calculateSubscribeTimeAndTraffic(redemptionCode.UnitTime, redemptionCode.Quantity, subscribePlan.Traffic)
|
||||
if err != nil {
|
||||
|
||||
@ -342,6 +342,24 @@ func (l *ActivateOrderLogic) createUserSubscription(ctx context.Context, orderIn
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
// Check quota limit before creating subscription (final safeguard)
|
||||
if sub.Quota > 0 {
|
||||
var count int64
|
||||
if err := l.svc.DB.Model(&user.Subscribe{}).Where("user_id = ? AND subscribe_id = ?", orderInfo.UserId, orderInfo.SubscribeId).Count(&count).Error; err != nil {
|
||||
logger.WithContext(ctx).Error("Count user subscribe failed", logger.Field("error", err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
if count >= sub.Quota {
|
||||
logger.WithContext(ctx).Infow("Subscribe quota limit exceeded",
|
||||
logger.Field("user_id", orderInfo.UserId),
|
||||
logger.Field("subscribe_id", orderInfo.SubscribeId),
|
||||
logger.Field("quota", sub.Quota),
|
||||
logger.Field("current_count", count),
|
||||
)
|
||||
return nil, fmt.Errorf("subscribe quota limit exceeded")
|
||||
}
|
||||
}
|
||||
|
||||
if err := l.svc.UserModel.InsertSubscribe(ctx, userSub); err != nil {
|
||||
logger.WithContext(ctx).Error("Insert user subscribe failed", logger.Field("error", err.Error()))
|
||||
return nil, err
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user