家庭组逻辑导致支付失败
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 8m11s
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 8m11s
This commit is contained in:
parent
9f4d71770b
commit
ce6351368d
@ -17,7 +17,7 @@ Logger: # 日志配置
|
|||||||
MySQL:
|
MySQL:
|
||||||
Addr: 127.0.0.1:3306 # MySQL地址
|
Addr: 127.0.0.1:3306 # MySQL地址
|
||||||
Username: root # MySQL用户名 (与创建的用户一致)
|
Username: root # MySQL用户名 (与创建的用户一致)
|
||||||
Password: password # MySQL密码 (换成之前生成的随机密码)
|
Password: rootpassword # MySQL密码 (换成之前生成的随机密码)
|
||||||
Dbname: ppanel # MySQL数据库名 (与脚本创建的数据库一致)
|
Dbname: ppanel # MySQL数据库名 (与脚本创建的数据库一致)
|
||||||
Config: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
Config: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||||
MaxIdleConns: 10
|
MaxIdleConns: 10
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/hibiken/asynq"
|
"github.com/hibiken/asynq"
|
||||||
commonLogic "github.com/perfect-panel/server/internal/logic/common"
|
commonLogic "github.com/perfect-panel/server/internal/logic/common"
|
||||||
iapmodel "github.com/perfect-panel/server/internal/model/iap/apple"
|
iapmodel "github.com/perfect-panel/server/internal/model/iap/apple"
|
||||||
|
ordermodel "github.com/perfect-panel/server/internal/model/order"
|
||||||
"github.com/perfect-panel/server/internal/model/subscribe"
|
"github.com/perfect-panel/server/internal/model/subscribe"
|
||||||
"github.com/perfect-panel/server/internal/model/user"
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
"github.com/perfect-panel/server/internal/svc"
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
@ -61,6 +62,21 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
|
|||||||
var existTx *iapmodel.Transaction
|
var existTx *iapmodel.Transaction
|
||||||
existTx, _ = iapmodel.NewModel(l.svcCtx.DB, l.svcCtx.Redis).FindByOriginalId(l.ctx, txPayload.OriginalTransactionId)
|
existTx, _ = iapmodel.NewModel(l.svcCtx.DB, l.svcCtx.Redis).FindByOriginalId(l.ctx, txPayload.OriginalTransactionId)
|
||||||
l.Infow("幂等等检查", logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("exists", existTx != nil && existTx.Id > 0))
|
l.Infow("幂等等检查", logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("exists", existTx != nil && existTx.Id > 0))
|
||||||
|
var orderInfo *ordermodel.Order
|
||||||
|
if req.OrderNo != "" {
|
||||||
|
ord, orderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||||
|
switch {
|
||||||
|
case orderErr == nil && ord != nil && ord.Id > 0:
|
||||||
|
if ord.UserId != 0 && ord.UserId != u.Id {
|
||||||
|
l.Errorw("订单与当前用户不匹配", logger.Field("orderNo", req.OrderNo), logger.Field("orderUserId", ord.UserId), logger.Field("userId", u.Id))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "order owner mismatch")
|
||||||
|
}
|
||||||
|
orderInfo = ord
|
||||||
|
case orderErr != nil && !errors.Is(orderErr, gorm.ErrRecordNotFound):
|
||||||
|
l.Errorw("查询订单失败", logger.Field("orderNo", req.OrderNo), logger.Field("error", orderErr.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find order error: %v", orderErr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 解析 Apple 商品ID中的单位与数量:支持 dayN / monthN / yearN
|
// 解析 Apple 商品ID中的单位与数量:支持 dayN / monthN / yearN
|
||||||
var parsedUnit string
|
var parsedUnit string
|
||||||
@ -152,15 +168,13 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
|
|||||||
}
|
}
|
||||||
if subscribeId == 0 {
|
if subscribeId == 0 {
|
||||||
// fallback from order_no if provided
|
// fallback from order_no if provided
|
||||||
if req.OrderNo != "" {
|
if orderInfo != nil && orderInfo.Id > 0 {
|
||||||
if ord, e := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo); e == nil && ord != nil && ord.Id != 0 {
|
duration = orderInfo.Quantity
|
||||||
duration = ord.Quantity
|
subscribeId = orderInfo.SubscribeId
|
||||||
subscribeId = ord.SubscribeId
|
|
||||||
l.Infow("使用订单信息回退", logger.Field("orderNo", req.OrderNo), logger.Field("durationDays", duration), logger.Field("subscribeId", subscribeId))
|
l.Infow("使用订单信息回退", logger.Field("orderNo", req.OrderNo), logger.Field("durationDays", duration), logger.Field("subscribeId", subscribeId))
|
||||||
} else {
|
} else if req.OrderNo != "" {
|
||||||
l.Infow("订单信息不可用,尝试请求参数回退", logger.Field("orderNo", req.OrderNo))
|
l.Infow("订单信息不可用,尝试请求参数回退", logger.Field("orderNo", req.OrderNo))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// final fallback: use request fields
|
// final fallback: use request fields
|
||||||
if duration <= 0 {
|
if duration <= 0 {
|
||||||
duration = req.DurationDays
|
duration = req.DurationDays
|
||||||
@ -179,25 +193,49 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
|
|||||||
}
|
}
|
||||||
exp := iapapple.CalcExpire(txPayload.PurchaseDate, duration)
|
exp := iapapple.CalcExpire(txPayload.PurchaseDate, duration)
|
||||||
l.Infow("计算订阅到期时间", logger.Field("expireAt", exp), logger.Field("expireUnix", exp.Unix()))
|
l.Infow("计算订阅到期时间", logger.Field("expireAt", exp), logger.Field("expireUnix", exp.Unix()))
|
||||||
|
var orderLinkedSub *user.Subscribe
|
||||||
|
if orderInfo != nil && orderInfo.SubscribeToken != "" {
|
||||||
|
orderSub, subErr := l.svcCtx.UserModel.FindOneSubscribeByToken(l.ctx, orderInfo.SubscribeToken)
|
||||||
|
switch {
|
||||||
|
case subErr == nil && orderSub != nil && orderSub.Id > 0:
|
||||||
|
if orderSub.UserId != u.Id {
|
||||||
|
l.Errorw("订单订阅与当前用户不匹配", logger.Field("orderNo", orderInfo.OrderNo), logger.Field("orderSubUserId", orderSub.UserId), logger.Field("userId", u.Id))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "order subscribe owner mismatch")
|
||||||
|
}
|
||||||
|
orderLinkedSub = orderSub
|
||||||
|
subscribeId = orderSub.SubscribeId
|
||||||
|
l.Infow("IAP 绑定命中订单订阅", logger.Field("orderNo", orderInfo.OrderNo), logger.Field("userSubscribeId", orderSub.Id), logger.Field("subscribeToken", orderInfo.SubscribeToken))
|
||||||
|
case subErr != nil && !errors.Is(subErr, gorm.ErrRecordNotFound):
|
||||||
|
l.Errorw("查询订单订阅失败", logger.Field("orderNo", orderInfo.OrderNo), logger.Field("subscribeToken", orderInfo.SubscribeToken), logger.Field("error", subErr.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find order subscribe error: %v", subErr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var singleModeAnchorSub *user.Subscribe
|
||||||
|
if l.svcCtx.Config.Subscribe.SingleModel && orderLinkedSub == nil {
|
||||||
|
anchorSub, anchorErr := findSingleModeMergeTarget(l.ctx, l.svcCtx, u.Id, subscribeId)
|
||||||
|
switch {
|
||||||
|
case errors.Is(anchorErr, commonLogic.ErrSingleModePlanMismatch):
|
||||||
|
l.Errorw("单订阅模式下 IAP 套餐不匹配", logger.Field("userId", u.Id), logger.Field("orderNo", req.OrderNo), logger.Field("iapSubscribeId", subscribeId))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SingleSubscribePlanMismatch), "single subscribe mode plan mismatch")
|
||||||
|
case anchorErr == nil && anchorSub != nil && anchorSub.Id > 0:
|
||||||
|
singleModeAnchorSub = anchorSub
|
||||||
|
subscribeId = anchorSub.SubscribeId
|
||||||
|
l.Infow("IAP 绑定命中单订阅锚点", logger.Field("userSubscribeId", anchorSub.Id), logger.Field("subscribeId", anchorSub.SubscribeId))
|
||||||
|
case errors.Is(anchorErr, gorm.ErrRecordNotFound):
|
||||||
|
case anchorErr != nil:
|
||||||
|
l.Errorw("查询单订阅锚点失败", logger.Field("userId", u.Id), logger.Field("error", anchorErr.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find single mode anchor subscribe error: %v", anchorErr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if existTx != nil && existTx.Id > 0 {
|
if existTx != nil && existTx.Id > 0 {
|
||||||
token := fmt.Sprintf("iap:%s", txPayload.OriginalTransactionId)
|
existSub, err := l.findIAPSubscribeByOriginalTransactionID(txPayload.OriginalTransactionId)
|
||||||
existSub, err := l.svcCtx.UserModel.FindOneSubscribeByToken(l.ctx, token)
|
|
||||||
switch {
|
switch {
|
||||||
case err == nil && existSub != nil && existSub.Id > 0:
|
case err == nil && existSub != nil && existSub.Id > 0:
|
||||||
newExpire := existSub.ExpireTime
|
newExpire, updateErr := l.extendSubscribeForIAP(existSub, exp, subscribeId)
|
||||||
if exp.After(newExpire) {
|
if updateErr != nil {
|
||||||
existSub.ExpireTime = exp
|
l.Errorw("刷新 IAP 订阅失败", logger.Field("error", updateErr.Error()), logger.Field("subscribeId", existSub.Id))
|
||||||
newExpire = exp
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update iap subscribe failed: %v", updateErr.Error())
|
||||||
}
|
|
||||||
if subscribeId > 0 && existSub.SubscribeId != subscribeId {
|
|
||||||
existSub.SubscribeId = subscribeId
|
|
||||||
}
|
|
||||||
existSub.Status = 1
|
|
||||||
existSub.FinishedAt = nil
|
|
||||||
if err := l.svcCtx.UserModel.UpdateSubscribe(l.ctx, existSub); err != nil {
|
|
||||||
l.Errorw("刷新 IAP 订阅失败", logger.Field("error", err.Error()), logger.Field("subscribeId", existSub.Id))
|
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update iap subscribe failed: %v", err.Error())
|
|
||||||
}
|
}
|
||||||
l.Infow("事务已处理,刷新订阅到期时间", logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("tier", tier), logger.Field("expiresAt", newExpire.Unix()))
|
l.Infow("事务已处理,刷新订阅到期时间", logger.Field("originalTransactionId", txPayload.OriginalTransactionId), logger.Field("tier", tier), logger.Field("expiresAt", newExpire.Unix()))
|
||||||
return &types.AttachAppleTransactionResponse{
|
return &types.AttachAppleTransactionResponse{
|
||||||
@ -205,9 +243,27 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
|
|||||||
Tier: tier,
|
Tier: tier,
|
||||||
}, nil
|
}, nil
|
||||||
case err != nil && !errors.Is(err, gorm.ErrRecordNotFound):
|
case err != nil && !errors.Is(err, gorm.ErrRecordNotFound):
|
||||||
l.Errorw("查询 IAP 订阅失败", logger.Field("error", err.Error()), logger.Field("token", token))
|
l.Errorw("查询 IAP 订阅失败", logger.Field("error", err.Error()), logger.Field("originalTransactionId", txPayload.OriginalTransactionId))
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find iap subscribe error: %v", err.Error())
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find iap subscribe error: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
if orderLinkedSub != nil {
|
||||||
|
newExpire, updateErr := l.extendSubscribeForIAP(orderLinkedSub, exp, 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())
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if singleModeAnchorSub != nil {
|
||||||
|
newExpire, updateErr := l.extendSubscribeForIAP(singleModeAnchorSub, exp, 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())
|
||||||
|
}
|
||||||
|
l.Infow("事务已处理,刷新单订阅锚点到期时间", logger.Field("userSubscribeId", singleModeAnchorSub.Id), logger.Field("expiresAt", newExpire.Unix()))
|
||||||
|
return &types.AttachAppleTransactionResponse{ExpiresAt: newExpire.Unix(), Tier: tier}, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sum := sha256.Sum256([]byte(req.SignedTransactionJWS))
|
sum := sha256.Sum256([]byte(req.SignedTransactionJWS))
|
||||||
@ -230,7 +286,21 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
|
|||||||
}
|
}
|
||||||
l.Infow("写入事务表成功", logger.Field("id", iapTx.Id))
|
l.Infow("写入事务表成功", logger.Field("id", iapTx.Id))
|
||||||
}
|
}
|
||||||
// insert user_subscribe
|
merged := false
|
||||||
|
if orderLinkedSub != nil {
|
||||||
|
if _, e := l.extendSubscribeForIAP(orderLinkedSub, exp, subscribeId, tx); e != nil {
|
||||||
|
l.Errorw("更新订单关联订阅失败", logger.Field("error", e.Error()), logger.Field("userSubscribeId", orderLinkedSub.Id))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
merged = true
|
||||||
|
} else if singleModeAnchorSub != nil {
|
||||||
|
if _, e := l.extendSubscribeForIAP(singleModeAnchorSub, exp, subscribeId, tx); e != nil {
|
||||||
|
l.Errorw("更新单订阅锚点失败", logger.Field("error", e.Error()), logger.Field("userSubscribeId", singleModeAnchorSub.Id))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
merged = true
|
||||||
|
}
|
||||||
|
if !merged {
|
||||||
userSub := user.Subscribe{
|
userSub := user.Subscribe{
|
||||||
UserId: u.Id,
|
UserId: u.Id,
|
||||||
SubscribeId: subscribeId,
|
SubscribeId: subscribeId,
|
||||||
@ -248,10 +318,15 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
l.Infow("写入用户订阅成功", logger.Field("userId", u.Id), logger.Field("subscribeId", subscribeId), logger.Field("expireUnix", exp.Unix()))
|
l.Infow("写入用户订阅成功", logger.Field("userId", u.Id), logger.Field("subscribeId", subscribeId), logger.Field("expireUnix", exp.Unix()))
|
||||||
|
}
|
||||||
// optional: mark related order as paid and enqueue activation
|
// optional: mark related order as paid and enqueue activation
|
||||||
if req.OrderNo != "" {
|
if req.OrderNo != "" {
|
||||||
orderInfo, e := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
if orderInfo == nil {
|
||||||
if e != nil {
|
if ord, e := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo); e == nil && ord != nil && ord.Id > 0 {
|
||||||
|
orderInfo = ord
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if orderInfo == nil {
|
||||||
// do not fail transaction if order not found; just continue
|
// do not fail transaction if order not found; just continue
|
||||||
l.Infow("订单不存在或查询失败,跳过订单状态更新", logger.Field("orderNo", req.OrderNo))
|
l.Infow("订单不存在或查询失败,跳过订单状态更新", logger.Field("orderNo", req.OrderNo))
|
||||||
return nil
|
return nil
|
||||||
@ -286,3 +361,40 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
|
|||||||
Tier: tier,
|
Tier: tier,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *AttachTransactionLogic) findIAPSubscribeByOriginalTransactionID(originalTransactionID string) (*user.Subscribe, error) {
|
||||||
|
if originalTransactionID == "" {
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
candidates := []string{fmt.Sprintf("iap:%s", originalTransactionID), originalTransactionID}
|
||||||
|
for _, token := range candidates {
|
||||||
|
sub, err := l.svcCtx.UserModel.FindOneSubscribeByToken(l.ctx, token)
|
||||||
|
if err == nil && sub != nil && sub.Id > 0 {
|
||||||
|
return sub, nil
|
||||||
|
}
|
||||||
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *AttachTransactionLogic) extendSubscribeForIAP(userSub *user.Subscribe, exp time.Time, subscribeId int64, tx ...*gorm.DB) (time.Time, error) {
|
||||||
|
if userSub == nil {
|
||||||
|
return time.Time{}, errors.New("user subscribe is nil")
|
||||||
|
}
|
||||||
|
newExpire := userSub.ExpireTime
|
||||||
|
if exp.After(newExpire) {
|
||||||
|
newExpire = exp
|
||||||
|
}
|
||||||
|
userSub.ExpireTime = newExpire
|
||||||
|
if subscribeId > 0 && userSub.SubscribeId != subscribeId {
|
||||||
|
userSub.SubscribeId = subscribeId
|
||||||
|
}
|
||||||
|
userSub.Status = 1
|
||||||
|
userSub.FinishedAt = nil
|
||||||
|
if err := l.svcCtx.UserModel.UpdateSubscribe(l.ctx, userSub, tx...); err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
return newExpire, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package apple
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -153,6 +154,26 @@ SIRDAVLcWemp0fMlnfDE4EHmqcD58arEJWsr3aWEhc4BHocOUIGjko0cVWGchrFa
|
|||||||
// But for now, we only rely on appAccountToken or just skip order linking.
|
// But for now, we only rely on appAccountToken or just skip order linking.
|
||||||
|
|
||||||
exp := iapapple.CalcExpire(txp.PurchaseDate, m.DurationDays)
|
exp := iapapple.CalcExpire(txp.PurchaseDate, m.DurationDays)
|
||||||
|
if l.svcCtx.Config.Subscribe.SingleModel {
|
||||||
|
anchorSub, anchorErr := findSingleModeMergeTarget(l.ctx, l.svcCtx, u.Id, m.SubscribeId)
|
||||||
|
switch {
|
||||||
|
case errors.Is(anchorErr, commonLogic.ErrSingleModePlanMismatch):
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SingleSubscribePlanMismatch), "single subscribe mode plan mismatch")
|
||||||
|
case anchorErr == nil && anchorSub != nil && anchorSub.Id > 0:
|
||||||
|
if exp.After(anchorSub.ExpireTime) {
|
||||||
|
anchorSub.ExpireTime = exp
|
||||||
|
}
|
||||||
|
anchorSub.Status = 1
|
||||||
|
anchorSub.FinishedAt = nil
|
||||||
|
if err := l.svcCtx.UserModel.UpdateSubscribe(l.ctx, anchorSub, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case errors.Is(anchorErr, gorm.ErrRecordNotFound):
|
||||||
|
case anchorErr != nil:
|
||||||
|
return anchorErr
|
||||||
|
}
|
||||||
|
}
|
||||||
userSub := user.Subscribe{
|
userSub := user.Subscribe{
|
||||||
UserId: u.Id,
|
UserId: u.Id,
|
||||||
SubscribeId: m.SubscribeId,
|
SubscribeId: m.SubscribeId,
|
||||||
@ -161,7 +182,7 @@ SIRDAVLcWemp0fMlnfDE4EHmqcD58arEJWsr3aWEhc4BHocOUIGjko0cVWGchrFa
|
|||||||
Traffic: 0,
|
Traffic: 0,
|
||||||
Download: 0,
|
Download: 0,
|
||||||
Upload: 0,
|
Upload: 0,
|
||||||
Token: txp.OriginalTransactionId,
|
Token: fmt.Sprintf("iap:%s", txp.OriginalTransactionId),
|
||||||
UUID: uuid.New().String(),
|
UUID: uuid.New().String(),
|
||||||
Status: 1,
|
Status: 1,
|
||||||
}
|
}
|
||||||
|
|||||||
65
internal/logic/public/iap/apple/singleModeHelper.go
Normal file
65
internal/logic/public/iap/apple/singleModeHelper.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package apple
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
commonLogic "github.com/perfect-panel/server/internal/logic/common"
|
||||||
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func findSingleModeMergeTarget(ctx context.Context, svcCtx *svc.ServiceContext, userId int64, subscribeId int64) (*user.Subscribe, error) {
|
||||||
|
anchorSub, err := svcCtx.UserModel.FindSingleModeAnchorSubscribe(ctx, userId)
|
||||||
|
switch {
|
||||||
|
case err == nil && anchorSub != nil && anchorSub.Id > 0:
|
||||||
|
if subscribeId > 0 && anchorSub.SubscribeId != subscribeId {
|
||||||
|
return nil, commonLogic.ErrSingleModePlanMismatch
|
||||||
|
}
|
||||||
|
return anchorSub, nil
|
||||||
|
case err != nil && !errors.Is(err, gorm.ErrRecordNotFound):
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userSubs, queryErr := svcCtx.UserModel.QueryUserSubscribe(ctx, userId, 0, 1, 2, 3, 5)
|
||||||
|
if queryErr != nil {
|
||||||
|
return nil, queryErr
|
||||||
|
}
|
||||||
|
var candidate *user.SubscribeDetails
|
||||||
|
for _, item := range userSubs {
|
||||||
|
if item == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if subscribeId > 0 && item.SubscribeId != subscribeId {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if candidate == nil ||
|
||||||
|
item.ExpireTime.After(candidate.ExpireTime) ||
|
||||||
|
(item.ExpireTime.Equal(candidate.ExpireTime) && item.UpdatedAt.After(candidate.UpdatedAt)) ||
|
||||||
|
(item.ExpireTime.Equal(candidate.ExpireTime) && item.UpdatedAt.Equal(candidate.UpdatedAt) && item.Id > candidate.Id) {
|
||||||
|
candidate = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if candidate == nil {
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
return &user.Subscribe{
|
||||||
|
Id: candidate.Id,
|
||||||
|
UserId: candidate.UserId,
|
||||||
|
OrderId: candidate.OrderId,
|
||||||
|
SubscribeId: candidate.SubscribeId,
|
||||||
|
StartTime: candidate.StartTime,
|
||||||
|
ExpireTime: candidate.ExpireTime,
|
||||||
|
FinishedAt: candidate.FinishedAt,
|
||||||
|
Traffic: candidate.Traffic,
|
||||||
|
Download: candidate.Download,
|
||||||
|
Upload: candidate.Upload,
|
||||||
|
Token: candidate.Token,
|
||||||
|
UUID: candidate.UUID,
|
||||||
|
Status: candidate.Status,
|
||||||
|
Note: candidate.Note,
|
||||||
|
CreatedAt: candidate.CreatedAt,
|
||||||
|
UpdatedAt: candidate.UpdatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user