huawei pay

This commit is contained in:
yangduo 2025-06-21 21:10:59 +08:00
parent 52178e17da
commit 5e40d7287f
16 changed files with 829 additions and 50 deletions

View File

@ -4,5 +4,7 @@
"wx_merchant_id": "1509252791",
"wx_certificate_sn": "3490963E4E2767E6D8EC99CFE406A2CBEB3CD195",
"wx_merchant_api_key": "fgRnUvC5Zu04ir9HQPHWesrsh49ZnfpC",
"wx_merchant_public_key_id": "PUB_KEY_ID_0115092527912025021200388000001392"
"wx_merchant_public_key_id": "PUB_KEY_ID_0115092527912025021200388000001392",
"hw_at_url": "https://oauth-login.cloud.huawei.com/oauth2/v3/token",
"hw_order_host": "orders-drcn.iap.cloud.huawei.com.cn|orders-dra.iap.cloud.huawei.asia|orders-dre.iap.cloud.huawei.eu"
}

View File

@ -0,0 +1,9 @@
[
{
"gameid": "2004",
"clientid": "114061151",
"clientsecret": "bb6ebce4982201802507ac20affdaa084e3b27d30ce4cc85c9e3423349489333",
"notifyurl": "https://game2004api-test.kingsome.cn/6001/webapp/index.php",
"publickey": "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA9wju3RCKPKMG9DXmHiOFJuyjdW8/senrHH/VcFhyWZawpsozF1MeGY83pIE/pqf/lRaQ9+BoN/MJdLwVUJBtjVN6ndAjFejWchGd6PvG+C1MCSdRrJH+V5CT97BS6K3tMHuSlt+jE8iAwFhcc9t63IqfDXAJLBlMLCkp9b2RHX7rTbzTdIBsYFQYMmAAi1LB5mLIk+TECTd7rHMKVztw45X+IO6tkXVgARnI0QCdx3eeRc7yfGdiDg4GGwqBHdP9jYty9bhCw4cFhFhWeG2+E03XsTWQ0t27xHIPf1fET9oawm0GttxFScZsgOGWHpHlBnG0oS9uZ1JkAg1lxp7t3Wn7nF04Qe/jnpoCYwXw++poSDtqyfN3M4eGJemkjwhDBK/cO3/fohQW/jfcmyHMjo5uzd12RETn3iChONqT0l3t5O0RWqP/Cr/1QQY50c4rGGb+gLbD2EWUHP61Gu+QXG4knClg3tcOy8l80imVo7ZoXH8KUdXJJXvphio5EtkvAgMBAAE="
}
]

View File

@ -3,6 +3,7 @@ package ingame
import (
"encoding/json"
"f5"
"fmt"
"main/constant"
"main/model"
"main/service"
@ -332,7 +333,7 @@ func (iga *InGameApi) OrderInfo(c *gin.Context) {
}
if errcode == constant.WX_ERRCODE_OK {
count, err := service.Wxpay.GetGoodsCount(gameid, int64(orderModel.ItemId))
count, err := service.GetGoodsCount(gameid, int64(orderModel.ItemId))
if err != nil {
return
}
@ -531,3 +532,61 @@ func (iga *InGameApi) isNopayChannel(accountId string) (ret bool) {
return strs[0] == "6000"
}
func (iga *InGameApi) HwPayDone(c *gin.Context) {
accountId := c.Query("account_id")
goodsid := c.Query("goods_id")
sessionid := c.Query("session_id")
orderid := c.Query("order_id")
token := c.Query("token")
sign := c.Query("sign")
if !strings.EqualFold(q5.Md5Str(accountId+goodsid+constant.GLOBAL_SALT+sessionid+orderid+token), strings.ToLower(sign)) {
f5.RspErr(c, 401, "sign err")
return
}
orderModel := new(model.InAppOrder)
if err, found := orderModel.Find(accountId, orderid); err != nil {
f5.RspErr(c, 500, "server internal error")
return
} else if !found {
f5.RspErr(c, 1, "order not found")
return
}
if orderModel.AccountId != accountId ||
orderModel.ItemId != q5.SafeToInt32(goodsid) {
f5.RspErr(c, 1, "order not found 2")
return
}
rspObj := struct {
ErrorCode int32 `json:"errcode"`
ErrMsg string `json:"errmsg"`
OrderId string `json:"order_id"`
State int32 `json:"state"` // 0 未支付1 已支付2 已发货
}{
OrderId: orderid,
}
oldstate := orderModel.Status
updatefields := []string{"status"}
if orderModel.Status > 0 {
rspObj.State = orderModel.Status
if orderModel.Status == 1 {
orderModel.Status = 2
}
} else {
productid := fmt.Sprintf("hw%s", goodsid) // hw productid="hw"+shopid
price := int64(0)
orderModel.Status, orderModel.SpOrderId, price = service.Hwpay.QueryOrder(int64(orderModel.GameId), token, productid, &orderid)
orderModel.Price = int32(price)
updatefields = append(updatefields, []string{"sp_orderid", "price"}...)
}
if oldstate != orderModel.Status {
orderModel.UpdateFields(updatefields)
}
c.JSON(0, rspObj)
}

View File

@ -21,6 +21,10 @@ import (
type MainServiceApi struct {
}
func (this *MainServiceApi) Self(c *gin.Context) {
c.String(200, c.Param("gameid"))
}
func (this *MainServiceApi) RefreshToken(c *gin.Context) {
reqtime := q5.SafeToInt64(c.Query("time"))
reqsign := c.Query("sign")
@ -42,7 +46,13 @@ func (this *MainServiceApi) RefreshToken(c *gin.Context) {
ErrMsg string `json:"errmsg"`
}{}
reftype := q5.SafeToInt64(c.Query("type"))
switch reftype {
case 0:
service.Wxpay.GetAccessTokenList(&rspObj.Data)
case 1:
service.Hwpay.GetAccessTokenList(&rspObj.Data)
}
c.JSON(200, rspObj)
}
@ -203,7 +213,7 @@ func (this *MainServiceApi) WxNotifyPurchase(c *gin.Context) {
return
}
if orderModel.Status > 1 {
if orderModel.Status > 0 {
rspObj.ErrorCode = 0
rspObj.ErrMsg = "Success"
c.JSON(200, rspObj)
@ -395,7 +405,7 @@ func (this *MainServiceApi) WxMsgNotify(c *gin.Context) {
}
thumburl := service.Wxpay.GenThumburl(int64(orderModel.GameId))
itemname, _ := service.Wxpay.GetGoodsName(int64(orderModel.GameId), int64(orderModel.ItemId))
itemname, _ := service.GetGoodsName(int64(orderModel.GameId), int64(orderModel.ItemId))
// 发给客服系统的clicklink
clickurl := fmt.Sprintf("https://%s/wx/clicknotify?orderid=%s", cfg.GetWxPayNotifyHost(), orderid)
@ -703,7 +713,7 @@ func (this *MainServiceApi) WxPayNotify(c *gin.Context) {
return
}
if orderModel.Status > 1 {
if orderModel.Status > 0 {
c.String(200, "")
return
}
@ -774,7 +784,136 @@ func (this *MainServiceApi) WxPayNotify(c *gin.Context) {
}
})
} else {
count, _ := service.Wxpay.GetGoodsCount(gameid, int64(orderModel.ItemId))
count, _ := service.GetGoodsCount(gameid, int64(orderModel.ItemId))
orderModel.SpAmount = int32(count)
fields = append(fields, "sp_amount")
}
orderModel.UpdateFields(fields)
c.String(200, "")
}
func (this *MainServiceApi) HwPayNotify(c *gin.Context) {
gameid := q5.SafeToInt64(c.Param("gameid"))
if gameid == 0 {
c.String(501, "")
return
}
cfg := mt.Table.Hwconfig.GetById(gameid)
if cfg == nil {
f5.GetSysLog().Warning("error gameid cfg :%d", gameid)
return
}
rawdata, err := c.GetRawData()
if err != nil {
c.String(501, "")
return
}
f5.GetSysLog().Debug("hw pay notify post data:%s", rawdata)
rsp := service.HwpayNotifyObj{}
if json.Unmarshal(rawdata, &rsp) != nil {
c.String(501, "")
return
}
if rsp.AppId != cfg.GetClientid() ||
rsp.ET != "ORDER" {
c.String(200, "")
return
}
orderrspobj := service.HwpayOrderNotifyObj{}
if json.Unmarshal(rawdata, &orderrspobj) != nil {
c.String(501, "")
return
}
if orderrspobj.Type != 1 {
c.String(200, "")
return
}
orderid := ""
state, sporderid, orderprice := service.Hwpay.QueryOrder(gameid, orderrspobj.PT, orderrspobj.PId, &orderid)
if state != 1 {
c.String(200, "")
return
}
orderModel := new(model.InAppOrder)
if err, found := orderModel.FindByOrderId(orderid); err != nil || !found {
c.String(200, "")
return
}
if orderModel.Status > 0 {
c.String(200, "")
return
}
productid := fmt.Sprintf("hw%d", orderModel.ItemId)
if productid != orderrspobj.PId {
c.String(200, "")
return
}
if !service.Hwpay.ConfirmOrder(gameid, orderrspobj.PT, orderrspobj.PId) {
c.String(501, "")
return
}
notifyurl := cfg.GetNotifyurl()
f5.GetSysLog().Debug("hw notify game url:%d, %s", gameid, notifyurl)
fields := []string{"status", "sp_orderid"}
orderModel.SpOrderId = sporderid
orderModel.Status = 1
if len(notifyurl) > 0 {
goodsidstr := q5.SafeToString(orderModel.ItemId)
totalamountstr := q5.SafeToString(orderprice)
nowtimestr := q5.SafeToString(f5.GetApp().GetRealSeconds())
originstr := "account_id=" + orderModel.AccountId
originstr += "&goodsid=" + goodsidstr
originstr += "&orderid=" + orderModel.OrderId
originstr += "&amount=" + totalamountstr
originstr += ":" + nowtimestr + constant.NOFITY_GAMESERVER_SALT
params := map[string]string{
"c": "Recharge",
"a": "purchaseNotify",
"account_id": orderModel.AccountId,
"orderid": orderModel.OrderId,
"timestamp": nowtimestr,
"goodsid": goodsidstr,
"amount": totalamountstr,
"sign": q5.Md5Str(originstr),
}
f5.GetHttpCliMgr().SendGoStyleRequest(
notifyurl,
params,
func(hcr f5.HttpCliResponse) {
if hcr.GetErr() != nil {
return
}
gamerspObj := struct {
ErrCode int64 `json:"errcode"`
ErrMsg string `json:"errmsg"`
}{}
f5.GetSysLog().Debug("get game rsp:%s", hcr.GetRawData())
if json.Unmarshal([]byte(hcr.GetRawData()), &gamerspObj) != nil {
return
}
if gamerspObj.ErrCode == 0 {
orderModel.Status = 2
}
})
} else {
count, _ := service.GetGoodsCount(gameid, int64(orderModel.ItemId))
orderModel.SpAmount = int32(count)
fields = append(fields, "sp_amount")
}

View File

@ -39,6 +39,14 @@ func (ct *ConfigTable) GetWxMerchantPublicKeyId() string {
return ct.selfConf.GetWxMerchantPublicKeyId()
}
func (ct *ConfigTable) GetHwAtUrl() string {
return ct.selfConf.GetHwAtUrl()
}
func (ct *ConfigTable) GetHwOrderHosts() []string {
return strings.Split(ct.selfConf.GetHwOrderHost(), "|")
}
func (ct *ConfigTable) PostInit1() {
ct.selfConf = ct.GetById(int64(0))
if ct.selfConf == nil {

View File

@ -0,0 +1,14 @@
package mt
import (
"f5"
"main/mtb"
)
type Hwconfig struct {
mtb.Hwconfig
}
type HwconfigTable struct {
f5.IdMetaTable[Hwconfig]
}

View File

@ -13,6 +13,7 @@ type table struct {
ConfDb *ConfDbTable
Wxconfig *WxconfigTable
LoginRedis *LoginRedisTable
Hwconfig *HwconfigTable
}
var Table = f5.New(func(this *table) {
@ -55,4 +56,9 @@ var Table = f5.New(func(this *table) {
this.FileName = "../config/login.redis.json"
this.PrimKey = ""
})
this.Hwconfig = f5.New(func(this *HwconfigTable) {
this.FileName = "../res/hwconfig@hwconfig.json"
this.PrimKey = "gameid"
})
})

View File

@ -72,6 +72,8 @@ type Config struct {
wx_certificate_sn string
wx_merchant_api_key string
wx_merchant_public_key_id string
hw_at_url string
hw_order_host string
_flags1_ uint64
_flags2_ uint64
@ -148,6 +150,17 @@ type LoginRedis struct {
_flags2_ uint64
}
type Hwconfig struct {
gameid int64
clientid string
clientsecret string
notifyurl string
publickey string
_flags1_ uint64
_flags2_ uint64
}
func (this *PayServerCluster) GetInstanceId() int32 {
return this.instance_id
}
@ -441,7 +454,23 @@ func (this *Config) GetWxMerchantPublicKeyId() string {
}
func (this *Config) HasWxMerchantPublicKeyId() bool {
return (this._flags1_ & (uint64(1) << 13)) > 0
return (this._flags1_ & (uint64(1) << 8)) > 0
}
func (this *Config) GetHwAtUrl() string {
return this.hw_at_url
}
func (this *Config) HasHwAtUrl() bool {
return (this._flags1_ & (uint64(1) << 9)) > 0
}
func (this *Config) GetHwOrderHost() string {
return this.hw_order_host
}
func (this *Config) HasHwOrderHost() bool {
return (this._flags1_ & (uint64(1) << 10)) > 0
}
func (this *RechargeCurrency) GetCurrencyName() string {
@ -724,6 +753,46 @@ func (this *LoginRedis) HasMaxIdleConns() bool {
return (this._flags1_ & (uint64(1) << 6)) > 0
}
func (this *Hwconfig) GetGameid() int64 {
return this.gameid
}
func (this *Hwconfig) HasGameid() bool {
return (this._flags1_ & (uint64(1) << 1)) > 0
}
func (this *Hwconfig) GetClientid() string {
return this.clientid
}
func (this *Hwconfig) HasClientid() bool {
return (this._flags1_ & (uint64(1) << 2)) > 0
}
func (this *Hwconfig) GetClientsecret() string {
return this.clientsecret
}
func (this *Hwconfig) HasClientsecret() bool {
return (this._flags1_ & (uint64(1) << 3)) > 0
}
func (this *Hwconfig) GetNotifyurl() string {
return this.notifyurl
}
func (this *Hwconfig) HasNotifyurl() bool {
return (this._flags1_ & (uint64(1) << 4)) > 0
}
func (this *Hwconfig) GetPublickey() string {
return this.publickey
}
func (this *Hwconfig) HasPublickey() bool {
return (this._flags1_ & (uint64(1) << 5)) > 0
}
func (this *PayServerCluster) LoadFromKv(kv map[string]interface{}) {
f5.ReadMetaTableField(&this.instance_id, "instance_id", &this._flags1_, 1, kv)
@ -777,7 +846,9 @@ func (this *Config) LoadFromKv(kv map[string]interface{}) {
f5.ReadMetaTableField(&this.wx_merchant_id, "wx_merchant_id", &this._flags1_, 5, kv)
f5.ReadMetaTableField(&this.wx_certificate_sn, "wx_certificate_sn", &this._flags1_, 6, kv)
f5.ReadMetaTableField(&this.wx_merchant_api_key, "wx_merchant_api_key", &this._flags1_, 7, kv)
f5.ReadMetaTableField(&this.wx_merchant_public_key_id, "wx_merchant_public_key_id", &this._flags1_, 13, kv)
f5.ReadMetaTableField(&this.wx_merchant_public_key_id, "wx_merchant_public_key_id", &this._flags1_, 8, kv)
f5.ReadMetaTableField(&this.hw_at_url, "hw_at_url", &this._flags1_, 9, kv)
f5.ReadMetaTableField(&this.hw_order_host, "hw_order_host", &this._flags1_, 10, kv)
}
func (this *RechargeCurrency) LoadFromKv(kv map[string]interface{}) {
@ -832,3 +903,11 @@ func (this *LoginRedis) LoadFromKv(kv map[string]interface{}) {
f5.ReadMetaTableField(&this.max_open_conns, "max_open_conns", &this._flags1_, 5, kv)
f5.ReadMetaTableField(&this.max_idle_conns, "max_idle_conns", &this._flags1_, 6, kv)
}
func (this *Hwconfig) LoadFromKv(kv map[string]interface{}) {
f5.ReadMetaTableField(&this.gameid, "gameid", &this._flags1_, 1, kv)
f5.ReadMetaTableField(&this.clientid, "clientid", &this._flags1_, 2, kv)
f5.ReadMetaTableField(&this.clientsecret, "clientsecret", &this._flags1_, 3, kv)
f5.ReadMetaTableField(&this.notifyurl, "notifyurl", &this._flags1_, 4, kv)
f5.ReadMetaTableField(&this.publickey, "publickey", &this._flags1_, 5, kv)
}

View File

@ -60,7 +60,9 @@ message Config
optional string wx_merchant_id = 5;
optional string wx_certificate_sn = 6;
optional string wx_merchant_api_key = 7;
optional string wx_merchant_public_key_id = 13;
optional string wx_merchant_public_key_id = 8;
optional string hw_at_url = 9;
optional string hw_order_host = 10;
}
message RechargeCurrency
@ -121,3 +123,12 @@ message LoginRedis
optional int32 max_open_conns = 5;
optional int32 max_idle_conns = 6;
}
message Hwconfig
{
optional int64 gameid = 1;
optional string clientid = 2;
optional string clientsecret = 3;
optional string notifyurl = 4;
optional string publickey = 5;
}

View File

@ -25,4 +25,6 @@ func (this *IngameRouter) InitRouter() {
api.InGameApi.PreOrder)
f5.GetApp().GetGinEngine().POST("/api/ingame/querypay",
api.InGameApi.QueryPay)
f5.GetApp().GetGinEngine().POST("/api/ingame/hwpaid",
api.InGameApi.HwPayDone)
}

View File

@ -9,6 +9,8 @@ type MainServiceRouter struct{}
func (this *MainServiceRouter) InitRouter() {
api := v1.ApiGroupApp.MainServiceApiGroup
f5.GetApp().GetGinEngine().GET("/self/:gameid",
api.Self)
f5.GetApp().GetGinEngine().GET("/api/service/refresh",
api.RefreshToken)
f5.GetApp().GetGinEngine().GET("/wx/purnotify/:gameid",
@ -23,4 +25,6 @@ func (this *MainServiceRouter) InitRouter() {
api.WxClickNotify)
f5.GetApp().GetGinEngine().POST("/wx/paynotify/:gameid",
api.WxPayNotify)
f5.GetApp().GetGinEngine().POST("/hw/paynotify/:gameid",
api.HwPayNotify)
}

View File

@ -8,6 +8,7 @@ import (
var _serviceMgr = new(serviceMgr)
var Wxpay = new(wxpay)
var Redis = new(redisMap)
var Hwpay = new(hwpay)
func init() {
global.RegModule(constant.SERVICE_MGR_MODULE_IDX, _serviceMgr)

View File

@ -0,0 +1,413 @@
package service
import (
"bytes"
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"f5"
"fmt"
"io"
"main/mt"
"net/http"
"q5"
"time"
)
type hwpay struct {
accessTokens q5.ConcurrentMap[int64, TokenInfo] //[gameid, TokenInfo]
refreshflag bool
client http.Client
}
type HwVerifyOrderRsp struct {
RspCode string `json:"responseCode"` //"0":OK;其它,错误
RspMsg string `json:"responseMessage"`
PTokenData string `json:"purchaseTokenData"` //支付信息
Sig string `json:"dataSignature"` //签名
SigAlg string `json:"signatureAlgorithm"` //签名算法
}
type InappPurchaseDetails struct {
AppId int64 `json:"applicationId"` //应用id
AutoRenew bool `json:"autoRenewing"` //是否自动更新非订阅型始终为false
OrderId string `json:"orderId"` //订单id
Kind int64 `json:"kind"` //商品类别
PKGName string `json:"packageName"` //包名
ProdId string `json:"productId"` //商品id
ProdName string `json:"productName"` //商品名
PurTime int64 `json:"purchaseTime"` //购买时间
State int64 `json:"purchaseState"` //订单状态 0 已购买
PayLoad string `json:"developerPayload"` //保留信息约定为游戏内orderid
ConsumeState int64 `json:"consumptionState"` //消耗状态 0 未消耗 1 已消耗
Price int64 `json:"price"` //商品价格 分
}
type HwpayNotifyObj struct {
V string `json:"version"` //"0":OK;其它,错误
ET string `json:"eventType"` //"ORDER" "SUBSCRIPTION"
Time int64 `json:"notifyTime"` //ms
AppId string `json:"applicationId"` //appid
}
type HwpayOrderNotifyObj struct {
V string `json:"version"` //"0":OK;其它,错误
Type int64 `json:"notificationType"` //1 支付成功 2 退款成功
PT string `json:"purchaseToken"` //"ORDER" "SUBSCRIPTION"
PId string `json:"productId"` //hw productid
}
func (hp *hwpay) init() {
hp.accessTokens = q5.ConcurrentMap[int64, TokenInfo]{}
mt.Table.Hwconfig.Traverse(func(w *mt.Hwconfig) bool {
hp.accessTokens.Store(w.GetGameid(), TokenInfo{})
return true
})
hp.refreshflag = true
go hp.checkAccessToken()
hp.client = http.Client{
Timeout: time.Second * 5,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
},
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
},
},
}
}
func (hp *hwpay) unInit() {
}
func (hp *hwpay) freshAccessToken(gameid int64) (token string) {
cfg := mt.Table.Hwconfig.GetById(gameid)
params := map[string]string{
"grant_type": "client_credentials",
"client_id": cfg.GetClientid(),
"client_secret": cfg.GetClientsecret(),
}
url := mt.Table.Config.GetHwAtUrl()
f5.GetHttpCliMgr().SendGoStyleRequest(
url,
params,
func(hcr f5.HttpCliResponse) {
if hcr.GetErr() != nil {
return
}
rspObj := struct {
Token string `json:"access_token"`
Expire int64 `json:"expires_in"`
ErrCode int64 `json:"error"`
ErrMsg string `json:"error_description"`
}{}
f5.GetSysLog().Debug("get hw access token rsp:%s", hcr.GetRawData())
if json.Unmarshal([]byte(hcr.GetRawData()), &rspObj) != nil {
return
}
if rspObj.ErrCode == 0 {
tokenitem, _ := hp.accessTokens.Load(gameid)
tokenitem.GameId = gameid
tokenitem.Token = rspObj.Token
tokenitem.Expire = rspObj.Expire + f5.GetApp().GetRealSeconds()
hp.accessTokens.Store(gameid, *tokenitem)
}
})
return token
}
func (hp *hwpay) getAccessToken(gameid int64) (token string) {
cfg, exist := hp.accessTokens.Load(gameid)
nowTime := f5.GetApp().GetRealSeconds()
if exist {
if cfg.Expire > nowTime {
if cfg.Expire < nowTime+3 && !hp.refreshflag {
hp.refreshflag = true
}
return cfg.Token
}
if !hp.refreshflag {
hp.refreshflag = true
}
return ""
}
return ""
}
// return 0 unpaid, 1 paid, 2 consumed
func (hp *hwpay) QueryOrder(gameid int64, purchaseToken string, product_id string, orderid *string) (state int32, sporderid string, orderprice int64) {
bodyMap := map[string]string{"purchaseToken": purchaseToken, "productId": product_id}
hosts := mt.Table.Config.GetHwOrderHosts()
queryuri := "/applications/purchases/tokens/verify"
cfg := mt.Table.Hwconfig.GetById(gameid)
if cfg == nil {
return
}
for _, hwhost := range hosts {
url := "https://" + hwhost + queryuri
bodybytes, err := hp.sendRequest(gameid, url, bodyMap)
if err != nil {
continue
}
rsp := HwVerifyOrderRsp{}
f5.GetSysLog().Debug("get hw verify order rsp:%s", bodybytes)
if json.Unmarshal(bodybytes, &rsp) != nil {
return
}
if rsp.RspCode != "0" {
return
}
if hp.verifyRsaSign(rsp.PTokenData, rsp.Sig, cfg.GetPublickey()) != nil {
return
}
purdetails := InappPurchaseDetails{}
if json.Unmarshal([]byte(rsp.PTokenData), &purdetails) != nil {
return
}
if q5.SafeToString(purdetails.AppId) != cfg.GetClientid() {
return
}
if orderid == nil || (len(*orderid) > 0 && purdetails.PayLoad != *orderid) {
return
}
if purdetails.ProdId != product_id {
return
}
if purdetails.State != 0 {
return
}
if purdetails.ConsumeState != 0 {
state = 2
return
}
sporderid = purdetails.OrderId
orderprice = purdetails.Price
if len(*orderid) > 0 {
// confirm order
url = "https://" + hwhost + "/applications/v2/purchases/confirm"
confirmbytes, err := hp.sendRequest(gameid, url, bodyMap)
if err != nil {
return
}
f5.GetSysLog().Debug("get hw confirm order rsp:%s", confirmbytes)
confirmrsp := struct {
RspCode string `json:"responseCode"` //"0":OK;其它,错误
RspMsg string `json:"responseMessage"`
}{}
if json.Unmarshal(confirmbytes, &confirmrsp) != nil {
return
}
if confirmrsp.RspCode == "0" {
state = 1
}
} else {
*orderid = purdetails.PayLoad
state = 1
}
return
}
return
}
// call queryorder first and its return is 1
func (hp *hwpay) ConfirmOrder(gameid int64, purchaseToken string, product_id string) (ret bool) {
bodyMap := map[string]string{"purchaseToken": purchaseToken, "productId": product_id}
hosts := mt.Table.Config.GetHwOrderHosts()
queryuri := "/applications/v2/purchases/confirm"
cfg := mt.Table.Hwconfig.GetById(gameid)
if cfg == nil {
return
}
for _, hwhost := range hosts {
url := "https://" + hwhost + queryuri
confirmbytes, err := hp.sendRequest(gameid, url, bodyMap)
if err != nil {
continue
}
f5.GetSysLog().Debug("get hw confirm order rsp:%s", confirmbytes)
confirmrsp := struct {
RspCode string `json:"responseCode"` //"0":OK;其它,错误
RspMsg string `json:"responseMessage"`
}{}
if json.Unmarshal(confirmbytes, &confirmrsp) != nil {
return
}
return confirmrsp.RspCode == "0"
}
return
}
func (hp *hwpay) GetAccessTokenList(data *[]TokenInfo) {
hp.accessTokens.Range(func(key int64, value TokenInfo) bool {
*data = append(*data, value)
return true
})
}
func (hp *hwpay) checkAccessToken() {
for {
time.Sleep(time.Second)
// if f5.IsOnlineEnv() {
gamesGoods.Range(func(gameid int64, value map[int64]GoodsInfo) bool {
cfg, ok := hp.accessTokens.Load(gameid)
if !ok {
return true
}
if hp.refreshflag || (cfg != nil && cfg.Expire <= f5.GetApp().GetRealSeconds()) {
hp.freshAccessToken(gameid)
}
return true
})
// } else {
// if !hp.refreshflag {
// hp.accessTokens.Range(func(key int64, value TokenInfo) bool {
// if value.Expire <= f5.GetApp().GetRealSeconds() {
// hp.refreshflag = true
// return false
// }
// return true
// })
// if !hp.refreshflag {
// continue
// }
// }
// url := "https://payservice.kingsome.cn/api/service/refresh"
// nowtimestr := q5.SafeToString(f5.GetApp().GetRealSeconds())
// params := map[string]string{
// "type": "1",
// "time": nowtimestr,
// "sign": Wxpay.GenSHA256Signature(nowtimestr+constant.GLOBAL_SALT, constant.GLOBAL_SALT),
// }
// f5.GetHttpCliMgr().SendGoStyleRequest(
// url,
// params,
// func(hcr f5.HttpCliResponse) {
// if hcr.GetErr() != nil {
// return
// }
// rspObj := struct {
// Data []TokenInfo `json:"data"`
// ErrCode int64 `json:"errcode"`
// ErrMsg string `json:"errmsg"`
// }{}
// f5.GetSysLog().Debug("hp get online payservice rsp:%s", hcr.GetRawData())
// if json.Unmarshal([]byte(hcr.GetRawData()), &rspObj) != nil {
// return
// }
// if rspObj.ErrCode == 0 {
// for _, dataitem := range rspObj.Data {
// hp.accessTokens.Store(dataitem.GameId, dataitem)
// }
// }
// })
// }
hp.refreshflag = false
}
}
func (hp *hwpay) buildAuthorization(gameid int64) (string, error) {
token := hp.getAccessToken(gameid)
if token == "" {
return "", errors.New("")
}
oriString := fmt.Sprintf("APPAT:%s", token)
var authString = base64.StdEncoding.EncodeToString([]byte(oriString))
var authHeaderString = fmt.Sprintf("Basic %s", authString)
return authHeaderString, nil
}
func (hp *hwpay) sendRequest(gameid int64, url string, bodyMap map[string]string) ([]byte, error) {
authHeader, err := hp.buildAuthorization(gameid)
if err != nil {
return nil, err
}
bodyString, err := json.Marshal(bodyMap)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", url, bytes.NewReader(bodyString))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
req.Header.Set("Authorization", authHeader)
response, err := hp.client.Do(req)
if err != nil {
return nil, err
}
defer response.Body.Close()
bodyBytes, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
return bodyBytes, nil
}
func (hp *hwpay) verifyRsaSign(content string, sign string, publicKey string) error {
publicKeyByte, err := base64.StdEncoding.DecodeString(publicKey)
if err != nil {
return err
}
pub, err := x509.ParsePKIXPublicKey(publicKeyByte)
if err != nil {
return err
}
hashed := sha256.Sum256([]byte(content))
signature, err := base64.StdEncoding.DecodeString(sign)
if err != nil {
return err
}
return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hashed[:], signature)
}

View File

@ -1,14 +1,78 @@
package service
import (
"encoding/json"
"errors"
"f5"
"main/mt"
"q5"
)
var gamesGoods q5.ConcurrentMap[int64, map[int64]GoodsInfo] //[gameid, [goodsid]goodsinfo]
type serviceMgr struct {
}
func (this *serviceMgr) Init() {
gamesGoods = q5.ConcurrentMap[int64, map[int64]GoodsInfo]{}
Wxpay.init()
Redis.init()
Hwpay.init()
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]GoodsInfo{}
for _, data := range data {
gamegoods[q5.SafeToInt64(data["id"])] = GoodsInfo{
Name: q5.SafeToString(data["name"]),
Count: q5.SafeToInt64(data["count"]),
Price: q5.SafeToInt64(data["price"]) * 100,
}
}
gamesGoods.Store(w.GetGameid(), gamegoods)
}
return true
})
}
func (this *serviceMgr) UnInit() {
Wxpay.unInit()
Redis.unInit()
Hwpay.unInit()
}
func GetGoodsCount(gameid int64, goodsid int64) (count int64, err error) {
goods, ok := gamesGoods.Load(gameid)
if !ok {
return 0, errors.New("no game")
}
info, ok := (*goods)[goodsid]
if !ok {
return 0, errors.New("no goods")
}
return info.Count, nil
}
func GetGoodsName(gameid int64, goodsid int64) (name string, err error) {
goods, ok := gamesGoods.Load(gameid)
if !ok {
return "", errors.New("no game")
}
info, ok := (*goods)[goodsid]
if !ok {
return "", errors.New("no goods")
}
return info.Name, nil
}

View File

@ -4,7 +4,6 @@ import (
"context"
"crypto/rsa"
"encoding/json"
"errors"
"f5"
"main/constant"
"main/mt"
@ -30,7 +29,6 @@ type mediaidInfo struct {
time int64
}
type wxpay struct {
gamesGoods q5.ConcurrentMap[int64, map[int64]GoodsInfo] //[gameid, [goodsid]goodsinfo]
accessTokens q5.ConcurrentMap[int64, TokenInfo] //[gameid, TokenInfo]
expireInfo q5.ConcurrentMap[string, *expireSession] //[accountid, expireSession]
mediaInfo q5.ConcurrentMap[int64, mediaidInfo] //[gameid, mediainfo]
@ -120,7 +118,6 @@ type GoodsInfo struct {
}
func (wp *wxpay) init() {
wp.gamesGoods = q5.ConcurrentMap[int64, map[int64]GoodsInfo]{}
wp.accessTokens = q5.ConcurrentMap[int64, TokenInfo]{}
wp.expireInfo = q5.ConcurrentMap[string, *expireSession]{}
@ -142,7 +139,6 @@ func (wp *wxpay) init() {
Price: q5.SafeToInt64(data["price"]) * 100,
}
}
wp.gamesGoods.Store(w.GetGameid(), gamegoods)
wp.accessTokens.Store(w.GetGameid(), TokenInfo{})
}
return true
@ -229,34 +225,6 @@ func (wp *wxpay) getAccessToken(gameid int64) (token string) {
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")
}
info, ok := (*goods)[goodsid]
if !ok {
return 0, errors.New("no goods")
}
return info.Count, nil
}
func (wp *wxpay) GetGoodsName(gameid int64, goodsid int64) (name string, err error) {
goods, ok := wp.gamesGoods.Load(gameid)
if !ok {
return "", errors.New("no game")
}
info, ok := (*goods)[goodsid]
if !ok {
return "", errors.New("no goods")
}
return info.Name, 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 {
@ -543,7 +511,7 @@ func (wp *wxpay) checkAccessToken() {
time.Sleep(time.Second)
if f5.IsOnlineEnv() {
wp.gamesGoods.Range(func(gameid int64, value map[int64]GoodsInfo) bool {
gamesGoods.Range(func(gameid int64, value map[int64]GoodsInfo) bool {
cfg, _ := wp.accessTokens.Load(gameid)
if wp.refreshflag || (cfg != nil && cfg.Expire <= f5.GetApp().GetRealSeconds()) {
wp.freshAccessToken(gameid)

View File

@ -187,7 +187,7 @@ func (wp *wxpay) GetPrepayInfoStr(openid string, gameid int64, userip string, or
svc := jsapi.JsapiApiService{Client: wp.client}
// 得到prepay_id以及调起支付所需的参数和签名
cfg := mt.Table.Wxconfig.GetById(gameid)
goods, ok := wp.gamesGoods.Load(gameid)
goods, ok := gamesGoods.Load(gameid)
if !ok {
return
}