diff --git a/bin/payserver/config/config.json b/bin/payserver/config/config.json index 33bfe5a..9f3a04f 100644 --- a/bin/payserver/config/config.json +++ b/bin/payserver/config/config.json @@ -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", diff --git a/bin/payserver/res/kingsome_pub_key.pem b/bin/payserver/res/kingsome_pub_key.pem new file mode 100644 index 0000000..868b107 --- /dev/null +++ b/bin/payserver/res/kingsome_pub_key.pem @@ -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----- diff --git a/server/payserver/api/v1/ingame/ingame.go b/server/payserver/api/v1/ingame/ingame.go index 9ae20e4..f81f327 100644 --- a/server/payserver/api/v1/ingame/ingame.go +++ b/server/payserver/api/v1/ingame/ingame.go @@ -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"}) } diff --git a/server/payserver/api/v1/mainservice/mainservice.go b/server/payserver/api/v1/mainservice/mainservice.go index 8f36ddc..4a9d786 100644 --- a/server/payserver/api/v1/mainservice/mainservice.go +++ b/server/payserver/api/v1/mainservice/mainservice.go @@ -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, "") } diff --git a/server/payserver/model/inapp_order.go b/server/payserver/model/inapp_order.go index ab4a0d1..1436d3a 100644 --- a/server/payserver/model/inapp_order.go +++ b/server/payserver/model/inapp_order.go @@ -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:支付失败"` } diff --git a/server/payserver/mt/Config.go b/server/payserver/mt/Config.go index 3435a2a..a6c4c48 100644 --- a/server/payserver/mt/Config.go +++ b/server/payserver/mt/Config.go @@ -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 { diff --git a/server/payserver/mtb/mtb.auto_gen.go b/server/payserver/mtb/mtb.auto_gen.go index 48f8496..9344efa 100644 --- a/server/payserver/mtb/mtb.auto_gen.go +++ b/server/payserver/mtb/mtb.auto_gen.go @@ -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{}) { diff --git a/server/payserver/proto/mt.proto b/server/payserver/proto/mt.proto index 434408f..6d6e75f 100644 --- a/server/payserver/proto/mt.proto +++ b/server/payserver/proto/mt.proto @@ -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 diff --git a/server/payserver/service/wxpay_prepare.go b/server/payserver/service/wxpay_prepare.go index 61324d6..611722d 100644 --- a/server/payserver/service/wxpay_prepare.go +++ b/server/payserver/service/wxpay_prepare.go @@ -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) +}