This commit is contained in:
yangduo 2025-02-17 22:13:38 +08:00
parent 7cf416e643
commit c2602bb2dd
9 changed files with 312 additions and 95 deletions

View File

@ -6,6 +6,7 @@
"wx_merchant_id": "1509252791",
"wx_certificate_sn": "3490963E4E2767E6D8EC99CFE406A2CBEB3CD195",
"wx_merchant_api_key": "fgRnUvC5Zu04ir9HQPHWesrsh49ZnfpC",
"wx_merchant_public_key_id": "PUB_KEY_ID_0115092527912025021200388000001392",
"wx_msg_notify_token": "kingsome",
"wx_msg_notify_encoding_aes_key": "2LO2BseGYDjPRK6xSf0Opv3PzWtOq2c3d8NhZHAVYbL",
"wx_nofity_host": "https://payservice-test.kingsome.cn",

View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+qUnB/f1uLPPwHLC844S
svgUMg63IxYNB6lghsk5tTd88r1imXt12svHy3EY+lkStYTB6xY0rHkpbNrdlU+d
DUmjJfmg/jZ9dyBlDgzJlH1UEbFlwWahKj8y8hNP8f5V2W+cQwZwB2pqUyeQQvbK
2lbJcQ8nsnvLvFe1nrlf3Qc79YtL9StIOiQOSMDI/RwYH4xVkFt53WOYG4eNoY7f
Aqg9QMW6gUnHHH8oSQZ7yKBGZ8/Y/DL8k5LQ5DvxabI+vt3khKaDCsarb3eOgXYG
uq2oJgVhOQA6hm3QCA2HpjcRnlKtutBD194cI8ZWN8uZn5zmwEd5Taoy9Fyhgtf0
JQIDAQAB
-----END PUBLIC KEY-----

View File

@ -70,11 +70,17 @@ func (this *InGameApi) ServerPreOrder(c *gin.Context) {
return
}
data, ret := this.checkSessionData(c, accountId)
if !ret {
return
}
nowTime := int32(f5.GetApp().GetRealSeconds())
order := new(model.InAppOrder)
order.AccountId = accountId
order.OrderId = q5.ToString(f5.GetApp().NewLockNodeUuid())
order.ItemId = q5.SafeToInt32(goodsid)
order.GameId = q5.SafeToInt32(data["gameid"])
order.IP = c.Query("user_ip")
order.CreateTime = nowTime
@ -438,38 +444,7 @@ func (this *InGameApi) QueryPay(c *gin.Context) {
c.JSON(200, rspObj)
if orderModel.Status == 0 {
// gameid := q5.SafeToInt64(data["gameid"])
// openid := q5.SafeToString(data["openid"])
// userip := this.getIP(c)
// sessionkey := q5.SafeToString(data["session_key"])
// balance, errcode, err := service.Wxpay.QueryBalance(openid, gameid, userip, sessionkey)
// if err != nil {
// return
// }
// if errcode == constant.WX_ERRCODE_OK {
// count, err := service.Wxpay.GetGoodsCount(gameid, int64(orderModel.ItemId))
// if err != nil {
// return
// }
// if balance >= count {
// errcode = service.Wxpay.QueryPay(openid, gameid, userip, sessionkey, int32(balance), orderModel.OrderId)
// if errcode == constant.WX_ERRCODE_OK {
// orderModel.Status = 1
// orderModel.SpAmount = int32(count)
// orderModel.SpOthers = int32(balance - count)
// orderModel.UpdateFields([]string{"status", "sp_amount", "sp_others"})
// }
// }
// }
// if errcode == constant.WX_ERRCODE_SESSIONERR || errcode == constant.WX_ERRCODE_SIGERR {
// sessionkeytime := q5.SafeToInt64(data["update_time"])
// service.Wxpay.AddExpireInfo(reqJson.AccountId, sessionkeytime)
// }
} else if orderModel.Status == 1 {
if orderModel.Status == 1 {
orderModel.Status = 2
orderModel.UpdateFields([]string{"status"})
}

View File

@ -461,9 +461,19 @@ func (this *MainServiceApi) WxClickNotify(c *gin.Context) {
func (this *MainServiceApi) WxPayNotify(c *gin.Context) {
f5.GetSysLog().Debug("wx pay notify:%s", c.Request.URL.RawQuery)
payserial := c.GetHeader("Wechatpay-Serial") //验签的微信支付平台证书序列号/微信支付公钥ID
failrspobj := struct {
Code string `json:"code"`
Msg string `json:"message"`
}{
Code: "FAIL",
Msg: "失败",
}
payserial := c.GetHeader("Wechatpay-Serial") //验签的微信支付平台证书序列号/微信支付公钥ID
if mt.Table.Config.GetWxCertificateSn() != payserial {
f5.GetSysLog().Alert("!!!!!!!!!!cerificate Serial error")
c.JSON(501, failrspobj)
return
}
@ -472,74 +482,271 @@ func (this *MainServiceApi) WxPayNotify(c *gin.Context) {
paynonce := c.GetHeader("Wechatpay-Nonce") //验签的随机字符串
nowtime := f5.GetApp().GetRealSeconds()
paytime := q5.SafeToInt64(paytimestamp)
if paytime < nowtime - 60 || paytime > nowtime + 60 {
if paytime < nowtime-60 || paytime > nowtime+60 {
c.JSON(501, failrspobj)
return
}
rawdata, err := c.GetRawData()
if err != nil {
c.JSON(501, failrspobj)
return
}
f5.GetSysLog().Debug("wx pay post data:%s", rawdata)
rawstr := fmt.Sprintf("%s\n%s\n%s", paytimestamp, paynonce, rawdata)
if !service.Wxpay.VerifyPaySign(rawstr, paysign) {
c.JSON(501, failrspobj)
return
}
rspObj := struct {
ErrorCode int32 `json:"ErrCode"`
ErrMsg string `json:"ErrMsg"`
}{
ErrorCode: 99999,
ErrMsg: "internal error",
postbodyObj := struct {
ID string `json:"id"`
CreateTime string `json:"create_time"`
ResourceType string `json:"resource_type"`
EventType string `json:"event_type"`
Summary string `json:"summary"`
Resource struct {
OT string `json:"original_type"`
Algorithm string `json:"algorithm"`
CT string `json:"ciphertext"`
AD string `json:"associated_data"`
Nonce string `json:"nonce"`
} `json:"resource"`
}{}
if json.Unmarshal(rawdata, &postbodyObj) != nil {
f5.GetSysLog().Debug("unmarshal wx pay post data errpr")
c.JSON(501, failrspobj)
return
}
msg_signature := c.Query("msg_signature")
if msg_signature != "" {
// postObj := struct {
// ToUserName string `json:"ToUserName"` //小程序的原始 ID
// Encrypt string `json:"Encrypt"`
// }{}
// if err := c.ShouldBindJSON(&postObj); err != nil {
// f5.GetSysLog().Debug("post data error ")
// c.JSON(200, rspObj)
// return
// }
// smsg, appid := service.Wxpay.DecryptMsg(msg_signature, timestamp, nonce, postObj.Encrypt, mt.Table.Config.GetWxMsgNotifyToken(), mt.Table.Config.GetWxMsgNotifyEncodingAesKey())
// if len(smsg) == 0 || len(appid) == 0 {
// f5.GetSysLog().Debug("decrypt msg data error")
// c.JSON(200, rspObj)
// return
// }
// wxmsg := struct {
// ToUserName string `json:"ToUserName"` //小程序的原始 ID
// FromUserName string `json:"FromUserName"` //发送者的 openid
// CreateTime int64 `json:"CreateTime"`
// MsgType string `json:"MsgType"`
// Content string `json:"Content"`
// MediaId string `json:"MediaId"`
// PicUrl string `json:"PicUrl"`
// MsgId int64 `json:"MsgId"`
// Event string `json:"Event"`
// SessionFrom string `json:"SessionFrom"`
// }{}
// f5.GetSysLog().Debug("wx msg notify decrypted data:%s", smsg)
// if err := json.Unmarshal(smsg, &wxmsg); err != nil {
// f5.GetSysLog().Debug("decrypt msg data error")
// c.JSON(200, rspObj)
// return
// }
// if wxmsg.MsgType != "event" || wxmsg.Event != "user_enter_tempsession" || wxmsg.SessionFrom != "prepare_pay_from_game" {
// c.JSON(200, rspObj)
// return
// }
res := postbodyObj.Resource
decryptedData, err := service.Wxpay.DecryptPaydata(res.AD, res.Nonce, res.CT)
if err != nil {
f5.GetSysLog().Debug("decrypte pay data error:%s, %s, %s", res.AD, res.Nonce, res.CT)
c.JSON(501, failrspobj)
return
}
c.JSON(200, rspObj)
f5.GetSysLog().Debug("pay decripted data:%s", decryptedData)
/*
{
"transaction_id":"1217752501201407033233368018",
"amount":{
"payer_total":100,
"total":100,
"currency":"CNY",
"payer_currency":"CNY"
},
"mchid":"1230000109",
"trade_state":"SUCCESS",
"bank_type":"CMC",
"promotion_detail":[
{
"amount":100,
"wechatpay_contribute":0,
"coupon_id":"109519",
"scope":"GLOBAL",
"merchant_contribute":0,
"name":"单品惠-6",
"other_contribute":0,
"currency":"CNY",
"stock_id":"931386",
"goods_detail":[
{
"goods_remark":"商品备注信息",
"quantity":1,
"discount_amount":1,
"goods_id":"M1006",
"unit_price":100
},
{
"goods_remark":"商品备注信息",
"quantity":1,
"discount_amount":1,
"goods_id":"M1006",
"unit_price":100
}
]
},
{
"amount":100,
"wechatpay_contribute":0,
"coupon_id":"109519",
"scope":"GLOBAL",
"merchant_contribute":0,
"name":"单品惠-6",
"other_contribute":0,
"currency":"CNY",
"stock_id":"931386",
"goods_detail":[
{
"goods_remark":"商品备注信息",
"quantity":1,
"discount_amount":1,
"goods_id":"M1006",
"unit_price":100
},
{
"goods_remark":"商品备注信息",
"quantity":1,
"discount_amount":1,
"goods_id":"M1006",
"unit_price":100
}
]
}
],
"success_time":"2018-06-08T10:34:56+08:00",
"payer":{
"openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
},
"out_trade_no":"1217752501201407033233368018",
"appid":"wxd678efh567hg6787",
"trade_state_desc":"支付成功",
"trade_type":"APP",
"attach":"自定义数据",
"scene_info":{
"device_id":"013467007045764"
}
}
*/
resObj := struct {
TransId string `json:"transaction_id"`
Amount struct {
PayerTotal int64 `json:"payer_total"`
Total int64 `json:"total"`
} `json:"amount"`
MchId string `json:"mchid"`
TradeState string `json:"trade_state"`
Payer struct {
Openid string `json:"openid"`
} `json:"payer"`
OrderId string `json:"out_trade_no"`
AppId string `json:"appid"`
SceneInfo struct {
DeviceId string `json:"device_id"`
} `json:"scene_info"`
}{}
err = json.Unmarshal([]byte(decryptedData), &resObj)
if err != nil {
f5.GetSysLog().Debug("unmarshal resObj err:%s", err.Error())
c.JSON(501, failrspobj)
return
}
if resObj.TradeState != "SUCCESS" {
c.JSON(501, failrspobj)
return
}
if resObj.OrderId == "" ||
resObj.AppId == "" ||
resObj.MchId != mt.Table.Config.GetWxMerchantId() {
c.JSON(501, failrspobj)
return
}
gameid := int64(0)
notifyurl := ""
mt.Table.Wxconfig.Traverse(func(w *mt.Wxconfig) bool {
if w.GetAppid() == string(resObj.AppId) {
gameid = w.GetGameid()
notifyurl = w.GetNotifyurl()
return false
}
return true
})
if gameid == 0 {
c.JSON(501, failrspobj)
return
}
orderModel := new(model.InAppOrder)
if err, found := orderModel.FindByOrderId(resObj.OrderId); err != nil || !found {
c.JSON(501, failrspobj)
return
}
if orderModel.Status > 1 {
c.String(200, "")
return
}
rediskey := "ls:accountid:" + orderModel.AccountId
sessionstr, err := service.Redis.Get(constant.LOGIN_REDIS, rediskey)
if err != nil {
c.JSON(501, failrspobj)
return
}
data := map[string]interface{}{}
if json.Unmarshal([]byte(sessionstr), &data) != nil {
c.JSON(501, failrspobj)
return
}
openid := q5.SafeToString(data["openid"])
if openid != resObj.Payer.Openid {
c.JSON(501, failrspobj)
return
}
f5.GetSysLog().Debug("notify url:%d, %s", gameid, notifyurl)
if len(notifyurl) > 0 {
goodsidstr := q5.SafeToString(orderModel.ItemId)
totalamountstr := q5.SafeToString(resObj.Amount.Total)
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 {
orderModel.Status = 1
}
})
} else {
orderModel.Status = 1
}
orderModel.SpOrderId = resObj.TransId
count, _ := service.Wxpay.GetGoodsCount(gameid, int64(orderModel.ItemId))
orderModel.SpAmount = int32(count)
orderModel.UpdateFields([]string{"status", "sp_orderid", "sp_amount"})
c.String(200, "")
}

View File

@ -29,8 +29,8 @@ type InAppOrder struct {
ModifyTime int32 `gorm:"column:modifytime"`
SpOrderId string `gorm:"column:sp_orderid;comment:平台的订单id"`
SpAmount int32 `gorm:"column:sp_amount;comment:sp_amount"`
SpOthers int32 `gorm:"column:sp_others;comment:补单金额"`
SpAmount int32 `gorm:"column:sp_amount;comment:充值获得游戏货币"`
SpOthers int32 `gorm:"column:sp_others;comment:游戏货币补单"`
SpConfirm int32 `gorm:"column:sp_confirmtime;comment:平台确认时间"`
SpResult int32 `gorm:"column:sp_pay_result;comment:0: 未确认 1: 支付成功 -1:支付失败"`
}

View File

@ -63,6 +63,10 @@ func (ct *ConfigTable) GetWxLinkDescription() string {
return ct.selfConf.GetWxLinkDescription()
}
func (ct *ConfigTable) GetWxMerchantPublicKeyId() string {
return ct.selfConf.GetWxMerchantPublicKeyId()
}
func (ct *ConfigTable) PostInit1() {
ct.selfConf = ct.GetById(int64(0))
if ct.selfConf == nil {

View File

@ -78,6 +78,7 @@ type Config struct {
wx_nofity_host string
wx_link_title string
wx_link_description string
wx_merchant_public_key_id string
_flags1_ uint64
_flags2_ uint64
@ -491,6 +492,14 @@ func (this *Config) HasWxLinkDescription() bool {
return (this._flags1_ & (uint64(1) << 12)) > 0
}
func (this *Config) GetWxMerchantPublicKeyId() string {
return this.wx_merchant_public_key_id
}
func (this *Config) HasWxMerchantPublicKeyId() bool {
return (this._flags1_ & (uint64(1) << 13)) > 0
}
func (this *RechargeCurrency) GetCurrencyName() string {
return this.currency_name
}
@ -775,6 +784,7 @@ func (this *Config) LoadFromKv(kv map[string]interface{}) {
f5.ReadMetaTableField(&this.wx_nofity_host, "wx_nofity_host", &this._flags1_, 10, kv)
f5.ReadMetaTableField(&this.wx_link_title, "wx_link_title", &this._flags1_, 11, kv)
f5.ReadMetaTableField(&this.wx_link_description, "wx_link_description", &this._flags1_, 12, kv)
f5.ReadMetaTableField(&this.wx_merchant_public_key_id, "wx_merchant_public_key_id", &this._flags1_, 13, kv)
}
func (this *RechargeCurrency) LoadFromKv(kv map[string]interface{}) {

View File

@ -67,6 +67,7 @@ message Config
optional string wx_nofity_host = 10;
optional string wx_link_title = 11;
optional string wx_link_description = 12;
optional string wx_merchant_public_key_id = 13;
}
message RechargeCurrency

View File

@ -20,9 +20,10 @@ import (
func (wp *wxpay) initMch() {
var (
mchID string = mt.Table.Config.GetWxMerchantId() // 商户号
mchCertificateSerialNumber string = mt.Table.Config.GetWxCertificateSn() // 商户证书序列号
mchAPIv3Key string = mt.Table.Config.GetWxMerchantApiKey() // 商户APIv3密钥
mchID string = mt.Table.Config.GetWxMerchantId() // 商户号
mchCertificateSerialNumber string = mt.Table.Config.GetWxCertificateSn() // 商户证书序列号
// mchAPIv3Key string = mt.Table.Config.GetWxMerchantApiKey() // 商户APIv3密钥
mchPubKeyId string = mt.Table.Config.GetWxMerchantPublicKeyId()
)
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
@ -30,11 +31,16 @@ func (wp *wxpay) initMch() {
if err != nil {
f5.GetSysLog().Alert("load merchant private key error")
}
mchPublicKey, err := utils.LoadPublicKeyWithPath("../res/kingsome_pub_key.pem")
if err != nil {
f5.GetSysLog().Alert("load merchant public key error")
}
wp.ctx = context.Background()
// 使用商户私钥等初始化 client并使它具有自动定时获取微信支付平台证书的能力
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
// option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
option.WithWechatPayPublicKeyAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchPubKeyId, mchPublicKey),
}
wp.client, err = core.NewClient(wp.ctx, opts...)
if err != nil {
@ -50,7 +56,7 @@ func (wp *wxpay) initMch() {
go wp.checkGameMediaId()
wp.payhtmlstr, err = f5.ReadJsonFile("../config/payhtml.template")
if err != nil {
f5.GetSysLog().Error("%s", err.Error())
}
@ -102,7 +108,7 @@ func (wp *wxpay) GenThumburl(gameid int64) (thumburl string) {
thumburl = fmt.Sprintf("https://%s/cgi-bin/media/get?access_token=%s&type=image&media_id=%s", urls[0], token, mediaid)
}
return
return
}
func (wp *wxpay) GenSendWxMsgUrl(gameid int64) (msgurl string) {
@ -112,7 +118,7 @@ func (wp *wxpay) GenSendWxMsgUrl(gameid int64) (msgurl string) {
msgurl = fmt.Sprintf("https://%s/cgi-bin/message/custom/send?access_token=%s", urls[0], token)
}
return
return
}
func (wp *wxpay) GetPrepayInfoStr(openid string, gameid int64, userip string, orderid string, goodsid int64) (rspstr string) {
@ -164,3 +170,7 @@ func (wp *wxpay) VerifyPaySign(rawdata string, signature string) bool {
return true
}
func (wp *wxpay) DecryptPaydata(associatedData string, nonce string, clipherdata string) (plaindata string, err error) {
return utils.DecryptAES256GCM(mt.Table.Config.GetWxMerchantApiKey(), associatedData, nonce, clipherdata)
}