wx prepare pay

This commit is contained in:
yangduo 2025-02-11 15:35:52 +08:00
parent 2cbcb7f46f
commit f13e6554e2
10 changed files with 392 additions and 19 deletions

View File

@ -2,5 +2,10 @@
"gameapi_url": "https://game2006api-test.kingsome.cn",
"wx_url": "api.weixin.qq.com|api2.weixin.qq.com|sh.api.weixin.qq.com|sz.api.weixin.qq.com|hk.api.weixin.qq.com",
"wx_notify_token": "dV93f4FwSGMwkYcvsRHD8egdW5egPMhF",
"wx_notify_encoding_aes_key": "t7zDjlqSow7OY4s61q8wp4EabjWnUtTSi5w0KM48O1K"
"wx_notify_encoding_aes_key": "t7zDjlqSow7OY4s61q8wp4EabjWnUtTSi5w0KM48O1K",
"wx_merchant_id": "1509252791",
"wx_certificate_sn": "1234",
"wx_merchant_api_key": "123",
"wx_msg_notify_token": "AcKipXgrCmNTFM9PA4CvemJxm38vxu3J",
"wx_msg_notify_encoding_aes_key": "U6HmUjnj135RX57PivS9rCz0cHiSi02BPJT89fymwDd"
}

View File

@ -142,6 +142,13 @@ func (this *MainServiceApi) WxNotifyPurchase(c *gin.Context) {
return
}
if wxnotifyobj.MiniGame.IsMock {
rspObj.ErrorCode = 0
rspObj.ErrMsg = "Success"
c.JSON(200, rspObj)
return
}
payloadobj := new(service.WxPayload)
if json.Unmarshal([]byte(wxnotifyobj.MiniGame.Payload), &payloadobj) != nil {
c.JSON(200, rspObj)
@ -166,13 +173,6 @@ func (this *MainServiceApi) WxNotifyPurchase(c *gin.Context) {
return
}
if wxnotifyobj.MiniGame.IsMock {
rspObj.ErrorCode = 0
rspObj.ErrMsg = "Success"
c.JSON(200, rspObj)
return
}
orderModel := new(model.InAppOrder)
if err, found := orderModel.FindByOrderId(payloadobj.OutTradeNo); err != nil {
c.JSON(200, rspObj)
@ -261,3 +261,55 @@ func (this *MainServiceApi) WxNotifyPurchase(c *gin.Context) {
c.JSON(200, rspObj)
}
func (this *MainServiceApi) WxMsgTNotify(c *gin.Context) {
f5.GetSysLog().Debug("wx msg test notify:%s", c.Request.URL.RawQuery)
signature := c.Query("signature")
timestamp := c.Query("timestamp")
nonce := c.Query("nonce")
echostr := c.Query("echostr")
strs := []string{mt.Table.Config.GetWxMsgNotifyToken(), timestamp, nonce}
sort.Strings(strs)
sb := strings.Builder{}
sb.WriteString(strs[0])
sb.WriteString(strs[1])
sb.WriteString(strs[2])
m := sha1.New()
io.WriteString(m, sb.String())
sign := string(hex.EncodeToString(m.Sum(nil)))
f5.GetSysLog().Debug("wx t msg sign:%s, %s", sign, signature)
if sign != signature {
c.String(200, "wrong")
return
}
c.String(200, echostr)
}
func (this *MainServiceApi) WxMsgNotify(c *gin.Context) {
f5.GetSysLog().Debug("wx msg notify:%s", c.Request.URL.RawQuery)
signature := c.Query("signature")
timestamp := c.Query("timestamp")
nonce := c.Query("nonce")
echostr := c.Query("echostr")
strs := []string{mt.Table.Config.GetWxMsgNotifyToken(), timestamp, nonce}
sort.Strings(strs)
sb := strings.Builder{}
sb.WriteString(strs[0])
sb.WriteString(strs[1])
sb.WriteString(strs[2])
m := sha1.New()
io.WriteString(m, sb.String())
sign := string(hex.EncodeToString(m.Sum(nil)))
f5.GetSysLog().Debug("wx msg sign:%s, %s", sign, signature)
if sign != signature {
c.String(200, "wrong")
return
}
c.String(200, echostr)
}

View File

@ -9,6 +9,7 @@ require f5 v1.0.0
require jccommon v1.0.0
require (
github.com/wechatpay-apiv3/wechatpay-go v0.2.20 // indirect
golang.org/x/sync v0.6.0 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.2 // indirect
)

View File

@ -1,3 +1,4 @@
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/auth0/go-jwt-middleware/v2 v2.2.0/go.mod h1:BFCz+RF+1szSkrGNJLYn2ng2PtfzBiKR6fynTvS2A/k=
github.com/auth0/go-jwt-middleware/v2 v2.2.1 h1:pqxEIwlCztD0T9ZygGfOrw4NK/F9iotnCnPJVADKbkE=
github.com/auth0/go-jwt-middleware/v2 v2.2.1/go.mod h1:CSi0tuu0QrALbWdiQZwqFL8SbBhj4e2MJzkvNfjY0Us=
@ -78,6 +79,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/wechatpay-apiv3/wechatpay-go v0.2.20 h1:gS8oFn1bHGnyapR2Zb4aqTV6l4kJWgbtqjCq6k1L9DQ=
github.com/wechatpay-apiv3/wechatpay-go v0.2.20/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=

View File

@ -15,25 +15,45 @@ type ConfigTable struct {
selfConf *Config
}
func (this *ConfigTable) GetGameApiUrl() string {
return this.selfConf.GetGameapiUrl()
func (ct *ConfigTable) GetGameApiUrl() string {
return ct.selfConf.GetGameapiUrl()
}
func (this *ConfigTable) GetWxUrl() []string {
return strings.Split(this.selfConf.GetWxUrl(), "|")
func (ct *ConfigTable) GetWxUrl() []string {
return strings.Split(ct.selfConf.GetWxUrl(), "|")
}
func (this *ConfigTable) GetWxNotifyToken() string {
return this.selfConf.GetWxNotifyToken()
func (ct *ConfigTable) GetWxNotifyToken() string {
return ct.selfConf.GetWxNotifyToken()
}
func (this *ConfigTable) GetWxNotifyEncodingAesKey() string {
return this.selfConf.GetWxNotifyEncodingAesKey()
func (ct *ConfigTable) GetWxNotifyEncodingAesKey() string {
return ct.selfConf.GetWxNotifyEncodingAesKey()
}
func (this *ConfigTable) PostInit1() {
this.selfConf = this.GetById(int64(0))
if this.selfConf == nil {
func (ct *ConfigTable) GetWxMerchantId() string {
return ct.selfConf.GetWxMerchantId()
}
func (ct *ConfigTable) GetWxCertificateSn() string {
return ct.selfConf.GetWxCertificateSn()
}
func (ct *ConfigTable) GetWxMerchantApiKey() string {
return ct.selfConf.GetWxMerchantApiKey()
}
func (ct *ConfigTable) GetWxMsgNotifyToken() string {
return ct.selfConf.GetWxMsgNotifyToken()
}
func (ct *ConfigTable) GetWxMsgNotifyEncodingAesKey() string {
return ct.selfConf.GetWxMsgNotifyEncodingAesKey()
}
func (ct *ConfigTable) PostInit1() {
ct.selfConf = ct.GetById(int64(0))
if ct.selfConf == nil {
panic("无法读取config.json")
}
}

View File

@ -70,6 +70,11 @@ type Config struct {
wx_url string
wx_notify_token string
wx_notify_encoding_aes_key string
wx_merchant_id string
wx_certificate_sn string
wx_merchant_api_key string
wx_msg_notify_token string
wx_msg_notify_encoding_aes_key string
_flags1_ uint64
_flags2_ uint64
@ -419,6 +424,46 @@ func (this *Config) HasWxNotifyEncodingAesKey() bool {
return (this._flags1_ & (uint64(1) << 4)) > 0
}
func (this *Config) GetWxMerchantId() string {
return this.wx_merchant_id
}
func (this *Config) HasWxMerchantId() bool {
return (this._flags1_ & (uint64(1) << 5)) > 0
}
func (this *Config) GetWxCertificateSn() string {
return this.wx_certificate_sn
}
func (this *Config) HasWxCertificateSn() bool {
return (this._flags1_ & (uint64(1) << 6)) > 0
}
func (this *Config) GetWxMerchantApiKey() string {
return this.wx_merchant_api_key
}
func (this *Config) HasWxMerchantApiKey() bool {
return (this._flags1_ & (uint64(1) << 7)) > 0
}
func (this *Config) GetWxMsgNotifyToken() string {
return this.wx_msg_notify_token
}
func (this *Config) HasWxMsgNotifyToken() bool {
return (this._flags1_ & (uint64(1) << 8)) > 0
}
func (this *Config) GetWxMsgNotifyEncodingAesKey() string {
return this.wx_msg_notify_encoding_aes_key
}
func (this *Config) HasWxMsgNotifyEncodingAesKey() bool {
return (this._flags1_ & (uint64(1) << 9)) > 0
}
func (this *RechargeCurrency) GetCurrencyName() string {
return this.currency_name
}
@ -695,6 +740,11 @@ func (this *Config) LoadFromKv(kv map[string]interface{}) {
f5.ReadMetaTableField(&this.wx_url, "wx_url", &this._flags1_, 2, kv)
f5.ReadMetaTableField(&this.wx_notify_token, "wx_notify_token", &this._flags1_, 3, kv)
f5.ReadMetaTableField(&this.wx_notify_encoding_aes_key, "wx_notify_encoding_aes_key", &this._flags1_, 4, kv)
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_msg_notify_token, "wx_msg_notify_token", &this._flags1_, 8, kv)
f5.ReadMetaTableField(&this.wx_msg_notify_encoding_aes_key, "wx_msg_notify_encoding_aes_key", &this._flags1_, 9, kv)
}
func (this *RechargeCurrency) LoadFromKv(kv map[string]interface{}) {

View File

@ -59,6 +59,11 @@ message Config
optional string wx_url = 2;
optional string wx_notify_token = 3;
optional string wx_notify_encoding_aes_key = 4;
optional string wx_merchant_id = 5;
optional string wx_certificate_sn = 6;
optional string wx_merchant_api_key = 7;
optional string wx_msg_notify_token = 8;
optional string wx_msg_notify_encoding_aes_key = 9;
}
message RechargeCurrency

View File

@ -15,4 +15,8 @@ func (this *MainServiceRouter) InitRouter() {
api.WxTNotify)
f5.GetApp().GetGinEngine().POST("/wx/tnotify",
api.WxNotifyPurchase)
f5.GetApp().GetGinEngine().GET("/wx/msgnotify",
api.WxMsgTNotify)
f5.GetApp().GetGinEngine().POST("/wx/msgnotify",
api.WxMsgNotify)
}

View File

@ -1,6 +1,7 @@
package service
import (
"context"
"encoding/json"
"errors"
"f5"
@ -8,6 +9,8 @@ import (
"main/mt"
"q5"
"time"
"github.com/wechatpay-apiv3/wechatpay-go/core"
)
type TokenInfo struct {
@ -25,6 +28,8 @@ type wxpay struct {
accessTokens q5.ConcurrentMap[int64, TokenInfo] //[gameid, TokenInfo]
expireInfo q5.ConcurrentMap[string, *expireSession] //[accountid, expireSession]
refreshflag bool
ctx context.Context
client *core.Client
}
type WxQuery struct {

View File

@ -0,0 +1,228 @@
package service
import (
"context"
"f5"
"fmt"
"main/mt"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/services/certificates"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
"github.com/wechatpay-apiv3/wechatpay-go/utils"
)
func (wp *wxpay) initMch() {
var (
mchID string = mt.Table.Config.GetWxMerchantId() // 商户号
mchCertificateSerialNumber string = mt.Table.Config.GetWxCertificateSn() // 商户证书序列号
mchAPIv3Key string = mt.Table.Config.GetWxMerchantApiKey() // 商户APIv3密钥
)
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
mchPrivateKey, err := utils.LoadPrivateKeyWithPath("../res/apiclient_key.pem")
if err != nil {
f5.GetSysLog().Alert("load merchant private key error")
}
wp.ctx = context.Background()
// 使用商户私钥等初始化 client并使它具有自动定时获取微信支付平台证书的能力
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
}
wp.client, err = core.NewClient(wp.ctx, opts...)
if err != nil {
f5.GetSysLog().Alert("new wechat pay client err:%s", err)
}
// 发送请求,以下载微信支付平台证书为例
// https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay5_1.shtml
svc := certificates.CertificatesApiService{Client: wp.client}
resp, result, err := svc.DownloadCertificates(wp.ctx)
f5.GetSysLog().Debug("status=%d resp=%s", result.Response.StatusCode, resp)
}
func (wp *wxpay) GetPrepareid(openid string, gameid int64, userip string, orderid string) (balance int64, wxerrcode int32, err error) {
svc := jsapi.JsapiApiService{Client: wp.client}
// 得到prepay_id以及调起支付所需的参数和签名
cfg := mt.Table.Wxconfig.GetById(gameid)
resp, result, err := svc.PrepayWithRequestPayment(wp.ctx,
jsapi.PrepayRequest{
Appid: core.String(cfg.GetAppid()),
Mchid: core.String(mt.Table.Config.GetWxMerchantId()),
Description: core.String(""),
OutTradeNo: core.String(orderid),
Attach: core.String(userip),
NotifyUrl: core.String("https://www.weixin.qq.com/wxpay/pay.php"),
Amount: &jsapi.Amount{
Total: core.Int64(100),
},
Payer: &jsapi.Payer{
Openid: core.String(openid),
},
},
)
if err == nil {
f5.GetSysLog().Debug("%s, %s", fmt.Sprintln(resp), result.Response.Status)
}
return
}
// 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)
// wp.checkErrorCode(rspJson.ErrCode)
// }
// })
// if sendRequest {
// break
// }
// }
// return wxerrcode
// }
// func (wp *wxpay) QueryPurchase(openid string, gameid int64, userip string, sessionkey string, amount int32, tradeno string) (wxerrcode int32) {
// cfg := mt.Table.Wxconfig.GetById(gameid)
// postbody := struct {
// OpenId string `json:"openid"`
// Ts int64 `json:"ts"`
// Env int32 `json:"env"`
// TradeNo string `json:"out_trade_no"`
// BizId int32 `json:"biz_id"`
// OfferId string `json:"offer_id"`
// }{
// OpenId: openid,
// Ts: f5.GetApp().GetRealSeconds(),
// BizId: 2, //1代币 2道具直购
// TradeNo: tradeno,
// OfferId: cfg.GetOfferid(),
// }
// if !f5.IsOnlineEnv() {
// postbody.Env = 1
// }
// poststr := q5.EncodeJson(postbody)
// queryuri := "/wxa/game/queryorderinfo"
// 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 := WxQueryOrderRsp{}
// f5.GetSysLog().Debug("wx query order 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)
// wp.checkErrorCode(rspJson.ErrCode)
// }
// })
// if sendRequest {
// break
// }
// }
// return wxerrcode
// }