feat(iap/apple): 从Apple商品ID解析购买数量并匹配订阅折扣
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 6m8s

添加从Apple商品ID中解析购买数量(天数)的逻辑,并基于订阅列表的折扣配置进行匹配。当商品ID包含"day"时,提取后续数字作为购买数量,然后查找对应数量的订阅折扣配置。
This commit is contained in:
shanshanzhong 2025-12-17 18:48:57 -08:00
parent 9944ab7b8a
commit 5d7ca4b9bd

View File

@ -6,11 +6,14 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"github.com/google/uuid"
"github.com/hibiken/asynq"
iapmodel "github.com/perfect-panel/server/internal/model/iap/apple"
"github.com/perfect-panel/server/internal/model/subscribe"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
@ -54,18 +57,67 @@ func (l *AttachTransactionLogic) Attach(req *types.AttachAppleTransactionRequest
var existTx *iapmodel.Transaction
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))
pm, _ := iapapple.ParseProductMap(l.svcCtx.Config.Site.CustomData)
m, ok := pm.Items[txPayload.ProductId]
l.Infow("商品映射解析", logger.Field("items_count", len(pm.Items)), logger.Field("命中映射", ok))
// 从 Apple 商品ID中解析购买数量天数形如 com.hifastvpn.plan.day7 -> 7
var parsedQuantity int64
if idx := strings.Index(strings.ToLower(txPayload.ProductId), "day"); idx >= 0 {
sub := txPayload.ProductId[idx+3:]
for i := 0; i < len(sub); i++ {
if sub[i] < '0' || sub[i] > '9' {
sub = sub[:i]
break
}
}
if q, e := strconv.ParseInt(sub, 10, 64); e == nil && q > 0 {
parsedQuantity = q
}
}
l.Infow("商品映射解析", logger.Field("productId", txPayload.ProductId), logger.Field("解析数量", parsedQuantity))
// 基于订阅列表的折扣配置做匹配UnitTime=Day 且 Discount.quantity == parsedQuantity
var duration int64
var tier string
var subscribeId int64
if ok {
duration = m.DurationDays
tier = m.Tier
subscribeId = m.SubscribeId
l.Infow("命中商品映射", logger.Field("productId", txPayload.ProductId), logger.Field("durationDays", duration), logger.Field("tier", tier), logger.Field("subscribeId", subscribeId))
if parsedQuantity > 0 {
_, subs, e := l.svcCtx.SubscribeModel.FilterList(l.ctx, &subscribe.FilterParams{
Page: 1,
Size: 9999,
Show: true,
Sell: true,
DefaultLanguage: true,
})
if e == nil && len(subs) > 0 {
for _, item := range subs {
if !strings.EqualFold(item.UnitTime, "Day") {
continue
}
var discounts []types.SubscribeDiscount
if item.Discount != "" {
_ = json.Unmarshal([]byte(item.Discount), &discounts)
}
for _, d := range discounts {
if int64(d.Quantity) == parsedQuantity {
duration = parsedQuantity
subscribeId = item.Id
tier = item.Name
l.Infow("订阅映射命中", logger.Field("subscribeId", subscribeId), logger.Field("name", tier), logger.Field("durationDays", duration))
break
}
}
if subscribeId > 0 {
break
}
}
} else {
l.Infow("订阅列表为空或查询失败", logger.Field("error", func() string {
if e != nil {
return e.Error()
}
return ""
}()))
}
}
if subscribeId == 0 {
// fallback from order_no if provided
if req.OrderNo != "" {
if ord, e := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo); e == nil && ord != nil && ord.Id != 0 {