2024-12-25 14:29:50 +08:00

373 lines
8.6 KiB
Go

package service
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"f5"
"main/constant"
"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] //[gameid, [goodsid]count]
accessTokens q5.ConcurrentMap[int64, tokenItem] //[gameid, tokenitem]
expireInfo q5.ConcurrentMap[string, *expireSession] //[accountid, expireSession]
}
type WxQuery struct {
AccessToken string `json:"access_token"`
Signature string `json:"signature"`
SigMethod string `json:"sig_method"`
PaySig string `json:"pay_sig"`
}
type WxBalanceRsp struct {
ErrCode int32 `json:"errcode"`
ErrMsg string `json:"errmsg"`
Balance int64 `json:"balance"`
GiftBalance int64 `json:"present_balance"`
SumSave int64 `json:"sum_save"`
SumGift int64 `json:"sum_present"`
SumBalance int64 `json:"sum_balance"`
SumCost int64 `json:"sum_cost"`
FirstSave bool `json:"first_save"`
}
type WxPayRsp struct {
ErrCode int32 `json:"errcode"`
ErrMsg string `json:"errmsg"`
BillNo string `json:"bill_no"`
Balance int64 `json:"balance"`
UsedGift int64 `json:"used_present_amount"`
}
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)
if err != nil {
return true
}
data := []map[string]interface{}{}
if json.Unmarshal([]byte(str), &data) != nil {
f5.GetSysLog().Error("parse [%s] error.", filename)
} else {
gamegoods := map[int64]int64{}
for _, data := range data {
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 {
return 0, errors.New("no game")
}
count, ok = (*goods)[goodsid]
if !ok {
return 0, errors.New("no goods")
}
return count, nil
}
func (wp *wxpay) QueryBalance(openid string, gameid int64, userip string, sessionkey string) (balance int64, wxerrcode int32, err error) {
cfg := mt.Table.Wxconfig.GetById(gameid)
postbody := struct {
OpenId string `json:"openid"`
OfferId string `json:"offer_id"`
Ts int64 `json:"ts"`
ZoneId string `json:"zone_id"`
Env int32 `json:"env"`
UserIp string `json:"user_ip"`
}{
OpenId: openid,
OfferId: cfg.GetOfferid(),
Ts: f5.GetApp().GetRealSeconds(),
ZoneId: cfg.GetZoneid(),
UserIp: userip,
}
if !f5.IsOnlineEnv() {
postbody.Env = 1
}
poststr := q5.EncodeJson(postbody)
queryuri := "/wxa/game/getbalance"
query := WxQuery{
AccessToken: wp.getAccessToken(gameid),
Signature: wp.genSHA256Signature(poststr, sessionkey),
SigMethod: "hmac_sha256",
PaySig: wp.genSHA256Signature(queryuri+"&"+poststr, cfg.GetAppkey()),
}
params := map[string]string{}
data, _ := json.Marshal(query)
json.Unmarshal(data, &params)
sendRequest := false
urls := mt.Table.Config.GetWxUrl()
for _, wxhost := range urls {
url := "https://" + wxhost + queryuri
f5.GetHttpCliMgr().SendGoStylePost(
url,
params,
"Content-Type: application/json",
poststr,
func(rsp f5.HttpCliResponse) {
if rsp.GetErr() != nil {
return
}
sendRequest = true
rspJson := WxBalanceRsp{}
f5.GetSysLog().Debug("wx balance rsp:%s", rsp.GetRawData())
err = q5.DecodeJson(rsp.GetRawData(), &rspJson)
if err != nil {
return
}
wxerrcode = rspJson.ErrCode
switch rspJson.ErrCode {
case constant.WX_ERRCODE_OK:
balance = rspJson.Balance
case constant.WX_ERRCODE_BUSY:
sendRequest = false
default:
f5.GetSysLog().Info("err msg:%s", rspJson.ErrMsg)
}
})
if sendRequest {
break
}
}
return balance, wxerrcode, err
}
func (wp *wxpay) QueryPay(openid string, gameid int64, userip string, sessionkey string, amount int32, billno string) (wxerrcode int32) {
cfg := mt.Table.Wxconfig.GetById(gameid)
postbody := struct {
OpenId string `json:"openid"`
OfferId string `json:"offer_id"`
Ts int64 `json:"ts"`
ZoneId string `json:"zone_id"`
Env int32 `json:"env"`
UserIp string `json:"user_ip"`
Amount int32 `json:"amount"`
BillNo string `json:"bill_no"`
}{
OpenId: openid,
OfferId: cfg.GetOfferid(),
Ts: f5.GetApp().GetRealSeconds(),
ZoneId: cfg.GetZoneid(),
UserIp: userip,
Amount: amount,
BillNo: billno,
}
if !f5.IsOnlineEnv() {
postbody.Env = 1
}
poststr := q5.EncodeJson(postbody)
queryuri := "/wxa/game/pay"
query := WxQuery{
AccessToken: wp.getAccessToken(gameid),
Signature: wp.genSHA256Signature(poststr, sessionkey),
SigMethod: "hmac_sha256",
PaySig: wp.genSHA256Signature(queryuri+"&"+poststr, cfg.GetAppkey()),
}
params := map[string]string{}
data, _ := json.Marshal(query)
json.Unmarshal(data, &params)
sendRequest := false
urls := mt.Table.Config.GetWxUrl()
for _, wxhost := range urls {
url := "https://" + wxhost + queryuri
f5.GetHttpCliMgr().SendGoStylePost(
url,
params,
"Content-Type: application/json",
poststr,
func(rsp f5.HttpCliResponse) {
if rsp.GetErr() != nil {
return
}
sendRequest = true
rspJson := WxPayRsp{}
f5.GetSysLog().Debug("wx pay rsp:%s", rsp.GetRawData())
err := q5.DecodeJson(rsp.GetRawData(), &rspJson)
if err != nil {
return
}
wxerrcode = rspJson.ErrCode
switch rspJson.ErrCode {
case constant.WX_ERRCODE_OK:
case constant.WX_ERRCODE_BUSY:
sendRequest = false
default:
f5.GetSysLog().Info("err msg:%s", rspJson.ErrMsg)
}
})
if sendRequest {
break
}
}
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)
}
}
}