diff --git a/bin/payserver/res/wxconfig@wxconfig.json b/bin/payserver/res/wxconfig@wxconfig.json index 5d1e201..fcd633f 100644 --- a/bin/payserver/res/wxconfig@wxconfig.json +++ b/bin/payserver/res/wxconfig@wxconfig.json @@ -3,6 +3,7 @@ "gameid": "1004", "appid": "wx1e89277247304160", "appkey": "CBP5uFcZP0S2PlcgkBTHxfZEXJft9wMK", + "appsecret": "5834607a498c3225b1d0a608d827c0d6", "zoneid": "1", "offerid": "1450310463" }, @@ -10,7 +11,8 @@ "gameid": "2004", "appid": "wx327f44630205dcd2", "appkey": "CrIjLw1wiExEnNBVqBQqWGMy0yToREC7", + "appsecret": "148509437b331acd15580b279e8511af", "zoneid": "1", "offerid": "1450318181" } -] +] \ No newline at end of file diff --git a/server/payserver/api/v1/ingame/ingame.go b/server/payserver/api/v1/ingame/ingame.go index edda738..775d916 100644 --- a/server/payserver/api/v1/ingame/ingame.go +++ b/server/payserver/api/v1/ingame/ingame.go @@ -12,7 +12,7 @@ import ( "github.com/gin-gonic/gin" ) -type InGameApi struct { +type InGameApi struct { } func (this *InGameApi) PreOrder(c *gin.Context) { @@ -172,7 +172,7 @@ func (this *InGameApi) PayDone(c *gin.Context) { break } - + c.JSON(200, rspObj) } @@ -187,15 +187,6 @@ func (this *InGameApi) OrderInfo(c *gin.Context) { return } - orderModel := new(model.InAppOrder) - if err, found := orderModel.Find(reqJson.AccountId, reqJson.OrderId); err != nil { - f5.RspErr(c, 500, "server internal error") - return - } else if !found { - f5.RspErr(c, 1, "not found") - return - } - strs := strings.Split(reqJson.AccountId, "_") if len(strs) < 3 { f5.RspErr(c, 401, "params error1") @@ -215,59 +206,70 @@ func (this *InGameApi) OrderInfo(c *gin.Context) { return } - status := orderModel.Status + sessionkeytime := q5.SafeToInt64(data["update_time"]) + if service.Wxpay.CheckExpireCache(reqJson.AccountId, sessionkeytime) { + f5.RspErr(c, 402, "session expired") + return + } + + orderModel := new(model.InAppOrder) + if err, found := orderModel.Find(reqJson.AccountId, reqJson.OrderId); err != nil { + f5.RspErr(c, 500, "server internal error") + return + } else if !found { + f5.RspErr(c, 1, "not found") + return + } + rspObj := struct { ErrorCode int32 `json:"errcode"` ErrMsg string `json:"errmsg"` OrderId string `json:"order_id"` GoodsId int32 `json:"goods_id"` Status int32 `json:"status"` - }{} - for status == 0 { + }{ + OrderId: orderModel.OrderId, + GoodsId: orderModel.ItemId, + Status: orderModel.Status, + } + + c.JSON(200, rspObj) + + if orderModel.Status == 0 { gameid := q5.SafeToInt64(strs[1]) openid := strs[2] sessionkey := q5.SafeToString(data["session_key"]) userip := this.getIP(c) balance, errcode, err := service.Wxpay.QueryBalance(openid, gameid, userip, sessionkey) if err != nil { - f5.RspErr(c, 500, "system busy") return } if errcode == constant.WX_ERRCODE_OK { count, err := service.Wxpay.GetGoodsCount(gameid, int64(orderModel.ItemId)) if err != nil { - f5.RspErr(c, 401, "goods error") return } - if balance < count { - break - } - - errcode = service.Wxpay.QueryPay(openid, gameid, userip, sessionkey, int32(count), orderModel.OrderId) - if errcode == constant.WX_ERRCODE_OK { - status = 1 - orderModel.Status = 2 - orderModel.UpdateFields([]string{"status"}) + if balance >= count { + errcode = service.Wxpay.QueryPay(openid, gameid, userip, sessionkey, int32(count), orderModel.OrderId) + if errcode == constant.WX_ERRCODE_OK { + orderModel.Status = 1 + orderModel.UpdateFields([]string{"status"}) + } } } if errcode == constant.WX_ERRCODE_SESSIONERR || errcode == constant.WX_ERRCODE_SIGERR { - rspObj.ErrorCode = 402 - rspObj.ErrMsg = "session overtime" + service.Wxpay.AddExpireInfo(reqJson.AccountId, sessionkeytime) } - - break + } else if orderModel.Status == 1 { + orderModel.Status = 2 + orderModel.UpdateFields([]string{"status"}) } - - rspObj.OrderId = orderModel.OrderId - rspObj.GoodsId = orderModel.ItemId - rspObj.Status = status - c.JSON(200, rspObj) } -func (this *InGameApi) getIP(c *gin.Context) (ip string) { +func (iga *InGameApi) getIP(c *gin.Context) (ip string) { ip = c.Request.Header.Get("X-Real-Ip") if ip == "" { ip = strings.Split(c.Request.Header.Get("X-Forwarded-For"), ",")[0] diff --git a/server/payserver/mtb/mtb.auto_gen.go b/server/payserver/mtb/mtb.auto_gen.go index 3d69131..4efe013 100644 --- a/server/payserver/mtb/mtb.auto_gen.go +++ b/server/payserver/mtb/mtb.auto_gen.go @@ -116,6 +116,7 @@ type Wxconfig struct { gameid int64 appid string appkey string + appsecret string zoneid string offerid string @@ -543,12 +544,20 @@ func (this *Wxconfig) HasAppkey() bool { return (this._flags1_ & (uint64(1) << 3)) > 0 } +func (this *Wxconfig) GetAppsecret() string { + return this.appsecret +} + +func (this *Wxconfig) HasAppsecret() bool { + return (this._flags1_ & (uint64(1) << 4)) > 0 +} + func (this *Wxconfig) GetZoneid() string { return this.zoneid } func (this *Wxconfig) HasZoneid() bool { - return (this._flags1_ & (uint64(1) << 4)) > 0 + return (this._flags1_ & (uint64(1) << 5)) > 0 } func (this *Wxconfig) GetOfferid() string { @@ -556,7 +565,7 @@ func (this *Wxconfig) GetOfferid() string { } func (this *Wxconfig) HasOfferid() bool { - return (this._flags1_ & (uint64(1) << 5)) > 0 + return (this._flags1_ & (uint64(1) << 6)) > 0 } func (this *LoginRedis) GetHost() string { @@ -690,8 +699,9 @@ func (this *Wxconfig) LoadFromKv(kv map[string]interface{}) { f5.ReadMetaTableField(&this.gameid, "gameid", &this._flags1_, 1, kv) f5.ReadMetaTableField(&this.appid, "appid", &this._flags1_, 2, kv) f5.ReadMetaTableField(&this.appkey, "appkey", &this._flags1_, 3, kv) - f5.ReadMetaTableField(&this.zoneid, "zoneid", &this._flags1_, 4, kv) - f5.ReadMetaTableField(&this.offerid, "offerid", &this._flags1_, 5, kv) + f5.ReadMetaTableField(&this.appsecret, "appsecret", &this._flags1_, 4, kv) + f5.ReadMetaTableField(&this.zoneid, "zoneid", &this._flags1_, 5, kv) + f5.ReadMetaTableField(&this.offerid, "offerid", &this._flags1_, 6, kv) } func (this *LoginRedis) LoadFromKv(kv map[string]interface{}) { diff --git a/server/payserver/proto/mt.proto b/server/payserver/proto/mt.proto index e2f01e4..c95bfee 100644 --- a/server/payserver/proto/mt.proto +++ b/server/payserver/proto/mt.proto @@ -95,8 +95,9 @@ message Wxconfig optional int64 gameid = 1; optional string appid = 2; optional string appkey = 3; - optional string zoneid = 4; - optional string offerid = 5; + optional string appsecret = 4; + optional string zoneid = 5; + optional string offerid = 6; } message LoginRedis diff --git a/server/payserver/service/wxpay.go b/server/payserver/service/wxpay.go index d625b50..117efa2 100644 --- a/server/payserver/service/wxpay.go +++ b/server/payserver/service/wxpay.go @@ -11,10 +11,23 @@ import ( "main/mt" "q5" "strings" + "time" ) +type tokenItem struct { + token string + expire int64 +} + +type expireSession struct { + SessionTime int64 + Time int64 +} type wxpay struct { - gamesGoods q5.ConcurrentMap[int64, map[int64]int64] + gamesGoods q5.ConcurrentMap[int64, map[int64]int64] //[gameid, [goodsid]count] + accessTokens q5.ConcurrentMap[int64, tokenItem] //[gameid, tokenitem] + + expireInfo q5.ConcurrentMap[string, *expireSession] //[accountid, expireSession] } type WxQuery struct { @@ -46,6 +59,9 @@ type WxPayRsp struct { func (wp *wxpay) init() { wp.gamesGoods = q5.ConcurrentMap[int64, map[int64]int64]{} + wp.accessTokens = q5.ConcurrentMap[int64, tokenItem]{} + wp.expireInfo = q5.ConcurrentMap[string, *expireSession]{} + mt.Table.Wxconfig.Traverse(func(w *mt.Wxconfig) bool { filename := "../res/gamegoods/" + q5.SafeToString(w.GetGameid()) + ".json" str, err := f5.ReadJsonFile(filename) @@ -61,14 +77,84 @@ func (wp *wxpay) init() { gamegoods[q5.SafeToInt64(data["id"])] = q5.SafeToInt64(data["count"]) } wp.gamesGoods.Store(w.GetGameid(), gamegoods) + wp.accessTokens.Store(w.GetGameid(), tokenItem{}) } return true }) + + wp.gamesGoods.Range(func(gameid int64, value map[int64]int64) bool { + wp.freshAccessToken(gameid) + return true + }) + + go wp.checkExpireInfo() } func (wp *wxpay) unInit() { } +func (wp *wxpay) freshAccessToken(gameid int64) (token string) { + cfg := mt.Table.Wxconfig.GetById(gameid) + params := map[string]string{ + "grant_type": "client_credential", + "appid": cfg.GetAppid(), + "secret": cfg.GetAppsecret(), + } + urls := mt.Table.Config.GetWxUrl() + queryuri := "cgi-bin/token" + for _, wxhost := range urls { + url := "https://" + wxhost + queryuri + sendok := false + f5.GetHttpCliMgr().SendGoStyleRequest( + url, + params, + func(hcr f5.HttpCliResponse) { + if hcr.GetErr() != nil { + return + } + + rspObj := struct { + Token string `json:"access_token"` + Expire int64 `json:"expire_in"` + ErrCode int64 `json:"errcode"` + ErrMsg string `json:"errmsg"` + }{} + + sendok = true + f5.GetSysLog().Debug("wx get access token rsp:%s", hcr.GetRawData()) + if json.Unmarshal([]byte(hcr.GetRawData()), &rspObj) != nil { + return + } + + if rspObj.ErrCode == 0 { + tokenitem, _ := wp.accessTokens.Load(gameid) + tokenitem.token = rspObj.Token + tokenitem.expire = rspObj.Expire + f5.GetApp().GetRealSeconds() + } + + }) + + if sendok { + break + } + } + + return token +} + +func (wp *wxpay) getAccessToken(gameid int64) (token string) { + cfg, exist := wp.accessTokens.Load(gameid) + nowTime := f5.GetApp().GetRealSeconds() + if exist { + if cfg.expire > nowTime+60 { + return cfg.token + } + + return wp.freshAccessToken(gameid) + } + return "" +} + func (wp *wxpay) GetGoodsCount(gameid int64, goodsid int64) (count int64, err error) { goods, ok := wp.gamesGoods.Load(gameid) if !ok { @@ -108,7 +194,7 @@ func (wp *wxpay) QueryBalance(openid string, gameid int64, userip string, sessio queryuri := "/wxa/game/getbalance" query := WxQuery{ - AccessToken: "", + AccessToken: wp.getAccessToken(gameid), Signature: wp.genSHA256Signature(poststr, sessionkey), SigMethod: "hmac_sha256", PaySig: wp.genSHA256Signature(queryuri+"&"+poststr, cfg.GetAppkey()), @@ -121,7 +207,7 @@ func (wp *wxpay) QueryBalance(openid string, gameid int64, userip string, sessio sendRequest := false urls := mt.Table.Config.GetWxUrl() for _, wxhost := range urls { - url := "https://" + wxhost + "/" + queryuri + url := "https://" + wxhost + queryuri f5.GetHttpCliMgr().SendGoStylePost( url, params, @@ -146,11 +232,7 @@ func (wp *wxpay) QueryBalance(openid string, gameid int64, userip string, sessio balance = rspJson.Balance case constant.WX_ERRCODE_BUSY: sendRequest = false - case constant.WX_ERRCODE_SIGERR: - fallthrough - case constant.WX_ERRCODE_PAYSIGERR: - fallthrough - case constant.WX_ERRCODE_PARAMERR: + default: f5.GetSysLog().Info("err msg:%s", rspJson.ErrMsg) } @@ -192,7 +274,7 @@ func (wp *wxpay) QueryPay(openid string, gameid int64, userip string, sessionkey queryuri := "/wxa/game/pay" query := WxQuery{ - AccessToken: "", + AccessToken: wp.getAccessToken(gameid), Signature: wp.genSHA256Signature(poststr, sessionkey), SigMethod: "hmac_sha256", PaySig: wp.genSHA256Signature(queryuri+"&"+poststr, cfg.GetAppkey()), @@ -229,17 +311,7 @@ func (wp *wxpay) QueryPay(openid string, gameid int64, userip string, sessionkey case constant.WX_ERRCODE_OK: case constant.WX_ERRCODE_BUSY: sendRequest = false - case constant.WX_ERRCODE_SIGERR: - fallthrough - case constant.WX_ERRCODE_PAYSIGERR: - fallthrough - case constant.WX_ERRCODE_SAMEBILLNO: - fallthrough - case constant.WX_ERRCODE_NOENOUGH: - fallthrough - case constant.WX_ERRCODE_SESSIONERR: - fallthrough - case constant.WX_ERRCODE_PARAMERR: + default: f5.GetSysLog().Info("err msg:%s", rspJson.ErrMsg) } @@ -252,8 +324,49 @@ func (wp *wxpay) QueryPay(openid string, gameid int64, userip string, sessionkey return wxerrcode } +func (wp *wxpay) AddExpireInfo(accountid string, sessiontime int64) { + info := expireSession{ + SessionTime: sessiontime, + Time: f5.GetApp().GetRealSeconds(), + } + + wp.expireInfo.Store(accountid, &info) +} + +func (wp *wxpay) CheckExpireCache(accountid string, expire int64) bool { + info, exist := wp.expireInfo.Load(accountid) + if !exist { + return false + } + + if (*info).SessionTime < expire { + wp.expireInfo.Delete(accountid) + return false + } + + return true +} + func (wp *wxpay) genSHA256Signature(str string, key string) string { mac := hmac.New(sha256.New, []byte(key)) _, _ = mac.Write([]byte(str)) return strings.ToLower(hex.EncodeToString(mac.Sum(nil))) } + +func (wp *wxpay) checkExpireInfo() { + for { + time.Sleep(time.Minute * 30) + nowTime := f5.GetApp().GetRealSeconds() + deletelist := map[string]int64{} + wp.expireInfo.Range(func(accountid string, value *expireSession) bool { + if value.Time+3600 < nowTime { + deletelist[accountid] = value.Time + } + return true + }) + + for accountid := range deletelist { + wp.expireInfo.Delete(accountid) + } + } +}