From 3a790dea4b0bf222288b1196b7925afa55280cd7 Mon Sep 17 00:00:00 2001 From: linquan <349589071@qq.com> Date: Fri, 30 May 2025 09:21:46 +0800 Subject: [PATCH] gettransactionId --- api/v1/game/advertisement.go | 49 ++++++++ api/v1/game/notice.go | 18 +++ api/v1/game/order.go | 37 +++++- go.mod | 4 + go.sum | 8 ++ internal/controller/game_order.go | 5 + internal/controller/game_pub.go | 20 +++ internal/serviceGame/advertisement.go | 14 +++ .../serviceGame/internal/advertisement.go | 42 +++++++ .../internal/order_get_transaction_id.go | 117 ++++++++++++++++++ internal/serviceGame/notice.go | 21 ++++ internal/serviceGame/order.go | 16 ++- sub_siphoto_pkcs8.pem | 3 + 13 files changed, 350 insertions(+), 4 deletions(-) create mode 100644 internal/serviceGame/internal/order_get_transaction_id.go create mode 100644 sub_siphoto_pkcs8.pem diff --git a/api/v1/game/advertisement.go b/api/v1/game/advertisement.go index 57821e8..0261492 100644 --- a/api/v1/game/advertisement.go +++ b/api/v1/game/advertisement.go @@ -46,3 +46,52 @@ type DeepADReq struct { type DeepADRes struct { g.Meta `mime:"application/json"` } + +type ATHAReq struct { + g.Meta `path:"/attributionHA" tags:"ad" method:"get" summary:"广告投放"` + Platform string `p:"platform"` + Idfv string `p:"idfv"` + AndroidId string `p:"android_id"` + PackageName string `p:"package_name"` + CustomerActiveTime string `p:"customer_active_time"` +} + +type HugeAmount struct { + Idfa string `p:"idfa"` + Time string `p:"time"` + Ip string `p:"ip"` + OrgId string `p:"org_id"` + OrgName string `p:"org_name"` + GameId string `p:"game_id"` + GameName string `p:"game_name"` + AdsetId string `p:"adset_id"` + AdsetNet string `p:"adset_net"` + DeviceBrand string `p:"device_brand"` + DeviceModel string `p:"device_model"` + CreativeId string `p:"creative_id"` + ConversionType string `p:"conversion_type"` + Device string `p:"device"` + OAID string `p:"OAID"` + Callback string `p:"callback"` + TapProjectId string `p:"tap_track_id"` + TapTrackId string `p:"tap_project_id"` + ANID string `p:"ANID"` + IMEI string `p:"IMEI"` +} + +type ATHARes struct { + g.Meta `mime:"application/json"` + Code int `json:"code"` +} + +type CSHAReq struct { + g.Meta `path:"/attributionHA" tags:"ad" method:"get" summary:"广告投放"` + EventType string `p:"event_type"` + Platform string `p:"platform"` + Id string `p:"id"` +} + +type CSHARes struct { + g.Meta `mime:"application/json"` + Code int `json:"code"` +} diff --git a/api/v1/game/notice.go b/api/v1/game/notice.go index c8adb1f..42ee036 100644 --- a/api/v1/game/notice.go +++ b/api/v1/game/notice.go @@ -141,6 +141,24 @@ type GetGameNoticeRes struct { TimeStamp int32 `json:"timeStamp"` } +type GetGameNoticeListReq struct { + g.Meta `path:"/gamenotice/list" tags:"公告" method:"get" summary:"维护公告"` + Channel string `p:"channel"` + ServerId int `p:"serverId"` +} + +type Notice struct { + Status int `json:"status"` + Content string `json:"content"` + TimeStamp int32 `json:"timeStamp"` + NoticeType int32 `json:"noticeType"` +} + +type GetGameNoticeListRes struct { + g.Meta `mime:"application/json"` + List []Notice `json:"notices"` +} + type GetGameNoticeLogReq struct { g.Meta `path:"/notice/log" tags:"公告" method:"post" summary:"历史公告"` PageSize int `json:"pageSize"` diff --git a/api/v1/game/order.go b/api/v1/game/order.go index 435debd..7d04069 100644 --- a/api/v1/game/order.go +++ b/api/v1/game/order.go @@ -232,7 +232,7 @@ type CheckRechargeSignReq struct { type CheckRechargeSignRes struct { g.Meta `mime:"application/json"` - List []int `json:"list"` + List []int `json:"CfgIds"` } // 插入充值情况 @@ -241,13 +241,12 @@ type AddRechargeSignReq struct { Id int64 `p:"unitId"` ServerId int32 `p:"server"` Config int32 `p:"config"` - Time int32 `p:"time"` + Time int64 `p:"time"` Token string `p:"token"` } type AddRechargeSignRes struct { g.Meta `mime:"application/json"` - List []int `json:"list"` } // 重置充值情况 @@ -258,3 +257,35 @@ type ResetRechargeSignReq struct { type ResetRechargeSignRes struct { g.Meta `mime:"application/json"` } + +// 获取订单号 +type GetTransactionIdReq struct { + g.Meta `path:"/order/getTransactionId" tags:"订单" method:"get" summary:"获取订单号"` + Order string `p:"order"` +} + +// 订单查询响应结构体 +type SignedTransaction struct { + TransactionID string `json:"transactionId"` // 苹果交易唯一标识 + OriginalTxID string `json:"originalTransactionId"` // 原始交易号(用于续订跟踪) + BundleId string `json:"bundleId"` // bundleId + Quantity int `json:"quantity"` // bundleId + Price int `json:"price"` // bundleId + Type string `json:"type"` // bundleId + InAppOwnershipType string `json:"inAppOwnershipType"` // bundleId + SignedDate int64 `json:"signedDate"` // bundleId + TransactionReason string `json:"transactionReason"` // bundleId + Storefront string `json:"storefront"` // bundleId + StorefrontId string `json:"storefrontId"` // bundleId + Currency string `json:"currency"` // bundleId + AppTransactionId string `json:"appTransactionId"` // bundleId + ProductID string `json:"productId"` // 商品ID(如:com.game.diamond100) + PurchaseDate int64 `json:"purchaseDate"` // 购买时间戳(毫秒) + OriginalPurchaseDate int64 `json:"originalPurchaseDate"` // 购买时间戳(毫秒) + Environment string `json:"environment"` // 环境类型(Production/Sandbox) +} + +type GetTransactionIdRes struct { + g.Meta `mime:"application/json"` + Order *SignedTransaction `json:"order"` +} diff --git a/go.mod b/go.mod index 4d00380..922d688 100644 --- a/go.mod +++ b/go.mod @@ -23,9 +23,11 @@ require ( require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect + github.com/SermoDigital/jose v0.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/clbanning/mxj v1.8.4 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/cristalhq/base64 v0.1.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -38,6 +40,7 @@ require ( github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/google/go-querystring v1.0.0 // indirect @@ -70,4 +73,5 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect ) diff --git a/go.sum b/go.sum index 8c82fa2..64f1212 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= +github.com/SermoDigital/jose v0.9.1 h1:atYaHPD3lPICcbK1owly3aPm0iaJGSGPi0WD4vLznv8= +github.com/SermoDigital/jose v0.9.1/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= github.com/casbin/casbin/v2 v2.42.0 h1:EA0aE5PZnFSYY6WulzTScOo4YO6xrGAAZkXRLs8p2ME= github.com/casbin/casbin/v2 v2.42.0/go.mod h1:sEL80qBYTbd+BPeL4iyvwYzFT3qwLaESq5aFKVLbLfA= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -18,6 +20,8 @@ github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5P github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/cristalhq/base64 v0.1.2 h1:edsefYyYDiac7Ytdh2xdaiiSSJzcI2f0yIkdGEf1qY0= +github.com/cristalhq/base64 v0.1.2/go.mod h1:sy4+2Hale2KbtSqkzpdMeYTP/IrB+HCvxVHWsh2VSYk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -66,6 +70,8 @@ github.com/gogf/gf/v2 v2.2.4 h1:Y2c0F4dDDYlQswHPtoGJ0l9kvQjE0a9jmM02qCAZoqo= github.com/gogf/gf/v2 v2.2.4/go.mod h1:thvkyb43RWUu/m05sRm4CbH9r7t7/FrW2M56L9Ystwk= github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= @@ -298,6 +304,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/controller/game_order.go b/internal/controller/game_order.go index 4e86892..81159aa 100644 --- a/internal/controller/game_order.go +++ b/internal/controller/game_order.go @@ -90,3 +90,8 @@ func (c *orderController) ResetRechargeSign(ctx context.Context, req *game.Reset res, err = serviceGame.GameOrder().ResetRechargeSign(ctx, req) return } + +func (c *orderController) GetTransactionId(ctx context.Context, req *game.GetTransactionIdReq) (res *game.GetTransactionIdRes, err error) { + res, err = serviceGame.GameOrder().GetTransactionId(ctx, req) + return +} diff --git a/internal/controller/game_pub.go b/internal/controller/game_pub.go index c8af31a..2c3e81e 100644 --- a/internal/controller/game_pub.go +++ b/internal/controller/game_pub.go @@ -25,6 +25,11 @@ func (c *pubController) Get(ctx context.Context, req *game.GetGameNoticeReq) (re return } +func (c *pubController) GetList(ctx context.Context, req *game.GetGameNoticeListReq) (res *game.GetGameNoticeListRes, err error) { + res, err = serviceGame.GameNotice().GetGameNoticeList(ctx, req) + return +} + func (c *pubController) GetUpdateUrl(ctx context.Context, req *game.GetGameUpdateUrlReq) (res *game.GetGameUpdateUrlRes, err error) { config := new(entity.SysConfig) res = new(game.GetGameUpdateUrlRes) @@ -215,3 +220,18 @@ func (c *pubController) AddRechargeSign(ctx context.Context, req *game.AddRechar res, err = serviceGame.GameOrder().AddRechargeSign(ctx, req) return } + +func (c *pubController) GetTransactionId(ctx context.Context, req *game.GetTransactionIdReq) (res *game.GetTransactionIdRes, err error) { + res, err = serviceGame.GameOrder().GetTransactionId(ctx, req) + return +} + +func (c *pubController) AttributionHugeAmount(ctx context.Context, req *game.ATHAReq) (res *game.ATHARes, err error) { + res, err = serviceGame.Advertisement().AttributionHugeAmount(ctx, req) + return +} + +func (c *pubController) ConversionHugeAmount(ctx context.Context, req *game.CSHAReq) (res *game.CSHARes, err error) { + res, err = serviceGame.Advertisement().ConversionHugeAmount(ctx, req) + return +} diff --git a/internal/serviceGame/advertisement.go b/internal/serviceGame/advertisement.go index b43e8e2..4058018 100644 --- a/internal/serviceGame/advertisement.go +++ b/internal/serviceGame/advertisement.go @@ -9,6 +9,8 @@ import ( type IAdvertisement interface { Advertise(ctx context.Context, req *game.ADReq) (res *game.ADRes, err error) DeepAdvertise(ctx context.Context, req *game.DeepADReq) (res *game.DeepADRes, err error) + AttributionHugeAmount(ctx context.Context, req *game.ATHAReq) (res *game.ATHARes, err error) + ConversionHugeAmount(ctx context.Context, req *game.CSHAReq) (res *game.CSHARes, err error) } type advertisementImpl struct { @@ -31,3 +33,15 @@ func (g *advertisementImpl) DeepAdvertise(ctx context.Context, req *game.DeepADR res, err = internal.DeepAdvertise(ctx, req) return } + +func (g *advertisementImpl) AttributionHugeAmount(ctx context.Context, req *game.ATHAReq) (res *game.ATHARes, err error) { + res = new(game.ATHARes) + res, err = internal.AttributionHugeAmount(ctx, req) + return +} + +func (g *advertisementImpl) ConversionHugeAmount(ctx context.Context, req *game.CSHAReq) (res *game.CSHARes, err error) { + res = new(game.CSHARes) + res, err = internal.ConversionHugeAmount(ctx, req) + return +} diff --git a/internal/serviceGame/internal/advertisement.go b/internal/serviceGame/internal/advertisement.go index 7ece9ee..1bd3e67 100644 --- a/internal/serviceGame/internal/advertisement.go +++ b/internal/serviceGame/internal/advertisement.go @@ -84,3 +84,45 @@ func sendMsg(ctx context.Context, url string, eventType, amount int32) { g.Log().Info(ctx, "sendMsg - res: ", string(bytes)) return } + +func sendMsgHugeAmount(ctx context.Context, url, platform, idfv, androidId, packageName string) { + //url := "https://analytics.oceanengine.com/sdk/app/attribution" + + g.Client().SetHeader("Content-Type", "application/json;charset=UTF-8") + data := map[string]string{ + "platform": platform, // ios或android + "idfv": idfv, // 仅ios需要 + "android_id": androidId, // 仅android需要 + "package_name": packageName, + "customer_active_time": fmt.Sprint(time.Now().UnixMicro()), // 毫秒时间戳,客户激活归因时间点 + } + bytes, _ := g.Client().Post(ctx, url, data) + g.Log().Info(ctx, "sendMsg - res: ", bytes) + return +} + +func AttributionHugeAmount(ctx context.Context, req *game.ATHAReq) (res *game.ATHARes, err error) { + log.Printf("Advertise: %s", gjson.MustEncodeString(req)) + //g.Try(ctx, func(ctx context.Context) { + // model := dao.Advertisement.Ctx(ctx) + // _, err = model.Insert(do.Advertisement{Idfa: req.Idfa, Time: req.Time, Ip: req.Ip, OrgId: req.OrgId, OrgName: req.OrgName, + // GameId: req.GameId, GameName: req.GameName, AdsetId: req.AdsetId, AdsetNet: req.AdsetNet, DeviceBrand: req.DeviceBrand, + // DeviceModel: req.DeviceModel, CreativeId: req.CreativeId, ConversionType: req.ConversionType, Device: req.Device, + // OAID: req.OAID, Callback: req.Callback, TapProjectId: req.TapProjectId, TapTrackId: req.TapTrackId, IMEI: req.IMEI, + // ANID: req.ANID}) + //}) + return +} + +func ConversionHugeAmount(ctx context.Context, req *game.CSHAReq) (res *game.CSHARes, err error) { + log.Printf("Advertise: %s", gjson.MustEncodeString(req)) + //g.Try(ctx, func(ctx context.Context) { + // model := dao.Advertisement.Ctx(ctx) + // _, err = model.Insert(do.Advertisement{Idfa: req.Idfa, Time: req.Time, Ip: req.Ip, OrgId: req.OrgId, OrgName: req.OrgName, + // GameId: req.GameId, GameName: req.GameName, AdsetId: req.AdsetId, AdsetNet: req.AdsetNet, DeviceBrand: req.DeviceBrand, + // DeviceModel: req.DeviceModel, CreativeId: req.CreativeId, ConversionType: req.ConversionType, Device: req.Device, + // OAID: req.OAID, Callback: req.Callback, TapProjectId: req.TapProjectId, TapTrackId: req.TapTrackId, IMEI: req.IMEI, + // ANID: req.ANID}) + //}) + return +} diff --git a/internal/serviceGame/internal/order_get_transaction_id.go b/internal/serviceGame/internal/order_get_transaction_id.go new file mode 100644 index 0000000..fbb50a1 --- /dev/null +++ b/internal/serviceGame/internal/order_get_transaction_id.go @@ -0,0 +1,117 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/SermoDigital/jose" + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/golang-jwt/jwt/v5" + "io" + "log" + "net/http" + "strings" + "time" + "tyj_admin/api/v1/game" +) + +// Apple JWT 认证配置 +type AppleAPIConfig struct { + IssuerID string `json:"issuer_id"` // 苹果开发者团队ID(格式:57246542-96fe-1a63e053-0824d011072a) + BundleID string `json:"bundle_id"` // 应用Bundle ID(如:com.example.game) + KeyID string `json:"key_id"` // 苹果API密钥ID(格式:2X9R4HXF34) + PrivateKey []byte `json:"private_key"` // 从苹果开发者平台下载的.p8私钥文件内容 +} + +// 订单查询响应结构体 +type AppleOrderResponse struct { + Status int `json:"status"` // 状态码(0表示成功) + SignedTransactions []string `json:"signedTransactions"` +} + +func LoadP8File() []byte { + return []byte(`-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg2XqPEHgWj2cUnO2GoPfIeEcAc0tJsTehvNNNBTGf4KigCgYIKoZIzj0DAQehRANCAAT6IdBMPYuNAQYuZsYi3EkflniotI/KJa6ELt1ednywlOpuwgNOn2WXONmDzzVVMJqQjD/6FSJ4jH7fRtP+Eci6 +-----END PRIVATE KEY-----`) +} + +func GenerateAppleJWT(config AppleAPIConfig) (string, error) { + // 使用ES256算法签名 + key, _ := jwt.ParseECPrivateKeyFromPEM(config.PrivateKey) + token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{ + "iss": config.IssuerID, + "iat": time.Now().Unix(), + "exp": time.Now().Add(5 * time.Minute).Unix(), // 有效期5分钟 + "aud": "appstoreconnect-v1", + "bid": config.BundleID, + }) + token.Header["kid"] = config.KeyID + return token.SignedString(key) +} + +func QueryAppleOrder(orderID string) (*game.SignedTransaction, error) { + config := AppleAPIConfig{ + IssuerID: "b8b82821-922e-4b43-a3cf-d293020a70d1", + BundleID: "com.XiamenAvatar.PeachValley", + KeyID: "J4RAWQBLHF", + PrivateKey: LoadP8File(), + } + + // 构造API地址 + baseURL := "https://api.storekit.itunes.apple.com/inApps/v1/lookup/" + if isSandboxOrder(orderID) { // 根据订单号自动判断环境 + baseURL = strings.Replace(baseURL, "storekit", "storekit-sandbox", 1) + } + url := baseURL + orderID + + // 生成JWT令牌 + authToken, err := GenerateAppleJWT(config) + if err != nil { + return nil, fmt.Errorf("JWT生成失败: %v", err) + } + + log.Printf("authToken %v", gjson.MustEncodeString(authToken)) + // 创建HTTP请求 + req, _ := http.NewRequest("GET", url, nil) + req.Header.Set("Authorization", "Bearer "+authToken) + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{Timeout: 15 * time.Second} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("API请求失败: %v", err) + } + defer resp.Body.Close() + + // 解析响应 + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("苹果接口异常[%d]: %s", resp.StatusCode, string(body)) + } + var result AppleOrderResponse + if err = json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + if result.Status != 0 { + return nil, fmt.Errorf("订单查询失败,状态码:%d", result.Status) + } + + data, err := DecodeJWSTransaction([]byte(result.SignedTransactions[0])) + log.Printf("result %v", gjson.MustEncodeString(data)) + return data, err +} + +func DecodeJWSTransaction(jwsToken []byte) (inappOrder *game.SignedTransaction, err error) { + parts := bytes.Split(jwsToken, []byte{'.'}) + // todo: parts[0] 为header 用作验证jwt, 暂未验证 + dec, err := jose.Base64Decode(parts[1]) + log.Println("解码64", string(dec)) + err = json.Unmarshal(dec, &inappOrder) + return +} + +// 根据订单号特征判断沙盒环境(测试订单通常以特定前缀开头) +func isSandboxOrder(orderID string) bool { + return strings.HasPrefix(orderID, "SANDBOX_") || + strings.HasPrefix(orderID, "TEST_") +} diff --git a/internal/serviceGame/notice.go b/internal/serviceGame/notice.go index bc7ac09..5f83ca6 100644 --- a/internal/serviceGame/notice.go +++ b/internal/serviceGame/notice.go @@ -4,8 +4,10 @@ import ( "context" "errors" "fmt" + "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" + "log" "time" "tyj_admin/api/v1/game" "tyj_admin/internal/consts" @@ -25,6 +27,7 @@ type IGameNotice interface { DelNoticeList(ctx context.Context, req *game.DelNoticeListReq) (res *game.DelNoticeListRes, err error) PopNotice(ctx context.Context, req *game.PopReq) (res *game.PopRes, err error) GetGameNotice(ctx context.Context, req *game.GetGameNoticeReq) (res *game.GetGameNoticeRes, err error) + GetGameNoticeList(ctx context.Context, req *game.GetGameNoticeListReq) (res *game.GetGameNoticeListRes, err error) GetGameNoticeLog(ctx context.Context, req *game.GetGameNoticeLogReq) (res *game.GetGameNoticeLogRes, err error) GetNoticeModel(ctx context.Context, req *game.GetNoticeModelReq) (res *game.GetNoticeModelRes, err error) AddNoticeModel(ctx context.Context, req *game.AddNoticeModelReq) (res *game.AddNoticeModelRes, err error) @@ -163,6 +166,24 @@ func (c *gameNoticeImpl) GetGameNotice(ctx context.Context, req *game.GetGameNot return } +func (c *gameNoticeImpl) GetGameNoticeList(ctx context.Context, req *game.GetGameNoticeListReq) (res *game.GetGameNoticeListRes, err error) { + res = new(game.GetGameNoticeListRes) + g.Try(ctx, func(ctx context.Context) { + req1 := new(game.GetGameNoticeReq) + req1.NoticeType = consts.Notice_Type_Login + req1.Channel = req.Channel + res1, _ := c.GetGameNotice(ctx, req1) + res.List = append(res.List, game.Notice{Content: res1.Content, Status: res1.Status, TimeStamp: res1.TimeStamp, NoticeType: consts.Notice_Type_Login}) + + req1.NoticeType = consts.Notice_Type_Preview + req1.Channel = req.Channel + res2, _ := c.GetGameNotice(ctx, req1) + res.List = append(res.List, game.Notice{Content: res2.Content, Status: res2.Status, TimeStamp: res2.TimeStamp, NoticeType: consts.Notice_Type_Preview}) + log.Printf("GetGameNotice res: %s", gjson.MustEncodeString(res.List)) + }) + return +} + func (c *gameNoticeImpl) GetGameNoticeLog(ctx context.Context, req *game.GetGameNoticeLogReq) (res *game.GetGameNoticeLogRes, err error) { res = new(game.GetGameNoticeLogRes) g.Try(ctx, func(ctx context.Context) { diff --git a/internal/serviceGame/order.go b/internal/serviceGame/order.go index b9d5074..817c442 100644 --- a/internal/serviceGame/order.go +++ b/internal/serviceGame/order.go @@ -39,6 +39,7 @@ type IGameOrder interface { CheckRechargeSign(ctx context.Context, req *game.CheckRechargeSignReq) (res *game.CheckRechargeSignRes, err error) AddRechargeSign(ctx context.Context, req *game.AddRechargeSignReq) (res *game.AddRechargeSignRes, err error) ResetRechargeSign(ctx context.Context, req *game.ResetRechargeSignReq) (res *game.ResetRechargeSignRes, err error) + GetTransactionId(ctx context.Context, req *game.GetTransactionIdReq) (res *game.GetTransactionIdRes, err error) } type gameOrderImpl struct { @@ -554,7 +555,7 @@ func (o gameOrderImpl) AddRechargeSign(ctx context.Context, req *game.AddRecharg } if count > 0 { log.Printf("req: %s, AddRechargeSign: count:%v", gjson.MustEncodeString(req), count) - return nil, errors.New("ok") + return nil, errors.New("is in list") } _, err = dao.GameRechargeLevel.Ctx(ctx).Insert(do.GameRechargeLevel{UnitId: req.Id, Server: req.ServerId, Config: req.Config}) @@ -579,3 +580,16 @@ func (o gameOrderImpl) ResetRechargeSign(ctx context.Context, req *game.ResetRec log.Printf("ResetRechargeSign: %v", rows) return } + +func (o gameOrderImpl) GetTransactionId(ctx context.Context, req *game.GetTransactionIdReq) (res *game.GetTransactionIdRes, err error) { + res = new(game.GetTransactionIdRes) + + // 查询订单(示例订单号) + order, err := internal.QueryAppleOrder(req.Order) + if err != nil { + log.Printf("查询失败: %v", err) + return + } + res.Order = order + return +} diff --git a/sub_siphoto_pkcs8.pem b/sub_siphoto_pkcs8.pem new file mode 100644 index 0000000..5b7ec18 --- /dev/null +++ b/sub_siphoto_pkcs8.pem @@ -0,0 +1,3 @@ +`-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg2XqPEHgWj2cUnO2GoPfIeEcAc0tJsTehvNNNBTGf4KigCgYIKoZIzj0DAQehRANCAAT6IdBMPYuNAQYuZsYi3EkflniotI/KJa6ELt1ednywlOpuwgNOn2WXONmDzzVVMJqQjD/6FSJ4jH7fRtP+Eci6 +-----END PRIVATE KEY-----` \ No newline at end of file