fix: IAP续费双重叠加订阅天数
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m43s

早返回路径(existTx!=nil)中的 existSub/orderLinkedSub/singleModeAnchorSub
分支同时调用 extendSubscribeForIAP 更新 expire_time,又通过
syncOrderStatusAndEnqueue(iapExpireAt=0) 入队,导致激活队列再次叠加天数。

修复:移除早返回路径中的 extendSubscribeForIAP 直接写入,改为传入
exp.Unix() 给 syncOrderStatusAndEnqueue,由激活队列的
updateSubscriptionWithIAPExpire 统一处理,前端返回 calcIAPRenewalExpire
估算值,与事务路径行为一致。

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
shanshanzhong 2026-03-31 02:54:52 -07:00
parent 6724e341b8
commit e3cd0643e1

View File

@ -296,58 +296,49 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
switch {
case existSubErr == nil && existSub != nil && existSub.Id > 0:
newExpire, updateErr := l.extendSubscribeForIAP(existSub, exp, accumulateDuration, subscribeId)
if updateErr != nil {
l.Errorw("刷新 IAP 订阅失败", logger.Field("error", updateErr.Error()), logger.Field("subscribeId", existSub.Id))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update iap subscribe failed: %v", updateErr.Error())
}
// 不在此处更新 expire_time由激活队列统一写入避免双重叠加天数
if bindErr := l.bindOrderTradeNo(orderInfo, tradeNo); bindErr != nil {
l.Errorw("写入订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", bindErr.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "bind order trade_no failed: %v", bindErr.Error())
}
if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, 0); syncErr != nil {
if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, exp.Unix()); syncErr != nil {
l.Errorw("同步订单状态失败(existSub)", logger.Field("orderNo", req.OrderNo), logger.Field("error", syncErr.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "sync order status failed: %v", syncErr.Error())
}
l.Infow("事务已处理,刷新订阅到期时间", logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("tier", tier), logger.Field("expiresAt", newExpire.Unix()))
estimatedExpire := l.calcIAPRenewalExpire(existSub.ExpireTime, exp, accumulateDuration)
l.Infow("事务已处理,订阅到期时间由队列更新", logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("tier", tier), logger.Field("estimatedExpiresAt", estimatedExpire.Unix()))
return &types.AttachAppleTransactionResponse{
ExpiresAt: newExpire.Unix(),
ExpiresAt: estimatedExpire.Unix(),
Tier: tier,
}, nil
}
if orderLinkedSub != nil {
newExpire, updateErr := l.extendSubscribeForIAP(orderLinkedSub, exp, accumulateDuration, subscribeId)
if updateErr != nil {
l.Errorw("刷新订单关联订阅失败", logger.Field("error", updateErr.Error()), logger.Field("subscribeId", orderLinkedSub.Id))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update order subscribe failed: %v", updateErr.Error())
}
// 不在此处更新 expire_time由激活队列统一写入避免双重叠加天数
if bindErr := l.bindOrderTradeNo(orderInfo, tradeNo); bindErr != nil {
l.Errorw("写入订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", bindErr.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "bind order trade_no failed: %v", bindErr.Error())
}
if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, 0); syncErr != nil {
if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, exp.Unix()); syncErr != nil {
l.Errorw("同步订单状态失败(orderLinkedSub)", logger.Field("orderNo", req.OrderNo), logger.Field("error", syncErr.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "sync order status failed: %v", syncErr.Error())
}
l.Infow("事务已处理,刷新订单关联订阅到期时间", logger.Field("orderNo", req.OrderNo), logger.Field("userSubscribeId", orderLinkedSub.Id), logger.Field("expiresAt", newExpire.Unix()))
return &types.AttachAppleTransactionResponse{ExpiresAt: newExpire.Unix(), Tier: tier}, nil
estimatedExpire := l.calcIAPRenewalExpire(orderLinkedSub.ExpireTime, exp, accumulateDuration)
l.Infow("事务已处理,订单关联订阅到期时间由队列更新", logger.Field("orderNo", req.OrderNo), logger.Field("userSubscribeId", orderLinkedSub.Id), logger.Field("estimatedExpiresAt", estimatedExpire.Unix()))
return &types.AttachAppleTransactionResponse{ExpiresAt: estimatedExpire.Unix(), Tier: tier}, nil
}
if singleModeAnchorSub != nil {
newExpire, updateErr := l.extendSubscribeForIAP(singleModeAnchorSub, exp, accumulateDuration, subscribeId)
if updateErr != nil {
l.Errorw("刷新单订阅锚点订阅失败", logger.Field("error", updateErr.Error()), logger.Field("subscribeId", singleModeAnchorSub.Id))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update single mode anchor subscribe failed: %v", updateErr.Error())
}
// 不在此处更新 expire_time由激活队列统一写入避免双重叠加天数
if bindErr := l.bindOrderTradeNo(orderInfo, tradeNo); bindErr != nil {
l.Errorw("写入订单交易号失败", logger.Field("orderNo", req.OrderNo), logger.Field("tradeNo", tradeNo), logger.Field("error", bindErr.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "bind order trade_no failed: %v", bindErr.Error())
}
if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, 0); syncErr != nil {
if syncErr := l.syncOrderStatusAndEnqueue(orderInfo, exp.Unix()); syncErr != nil {
l.Errorw("同步订单状态失败(singleModeAnchorSub)", logger.Field("orderNo", req.OrderNo), logger.Field("error", syncErr.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "sync order status failed: %v", syncErr.Error())
}
l.Infow("事务已处理,刷新单订阅锚点到期时间", logger.Field("userSubscribeId", singleModeAnchorSub.Id), logger.Field("expiresAt", newExpire.Unix()))
return &types.AttachAppleTransactionResponse{ExpiresAt: newExpire.Unix(), Tier: tier}, nil
estimatedExpire := l.calcIAPRenewalExpire(singleModeAnchorSub.ExpireTime, exp, accumulateDuration)
l.Infow("事务已处理,单订阅锚点到期时间由队列更新", logger.Field("userSubscribeId", singleModeAnchorSub.Id), logger.Field("estimatedExpiresAt", estimatedExpire.Unix()))
return &types.AttachAppleTransactionResponse{ExpiresAt: estimatedExpire.Unix(), Tier: tier}, nil
}
}