wx notify
This commit is contained in:
parent
6f039a0d8d
commit
9e35fe6e5e
@ -1,10 +1,17 @@
|
|||||||
package mainservice
|
package mainservice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/json"
|
||||||
"f5"
|
"f5"
|
||||||
|
"io"
|
||||||
"main/constant"
|
"main/constant"
|
||||||
"main/service"
|
"main/service"
|
||||||
|
"payserver/model"
|
||||||
|
"payserver/mt"
|
||||||
"q5"
|
"q5"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -36,3 +43,186 @@ func (this *MainServiceApi) RefreshToken(c *gin.Context) {
|
|||||||
service.Wxpay.GetAccessTokenList(&rspObj.Data)
|
service.Wxpay.GetAccessTokenList(&rspObj.Data)
|
||||||
c.JSON(200, rspObj)
|
c.JSON(200, rspObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *MainServiceApi) WxTNotify(c *gin.Context) {
|
||||||
|
f5.GetSysLog().Debug("wx 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{constant.WX_NOTIFY_TOKEN, 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(m.Sum(nil))
|
||||||
|
|
||||||
|
f5.GetSysLog().Debug("wx sign:%s, %s", sign, signature)
|
||||||
|
|
||||||
|
if sign != signature {
|
||||||
|
c.String(200, "wrong")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.String(200, echostr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *MainServiceApi) WxNotifyPurchase(c *gin.Context) {
|
||||||
|
f5.GetSysLog().Debug("wx notify purchase:%s", c.Request.URL.RawQuery)
|
||||||
|
|
||||||
|
signature := c.Query("signature")
|
||||||
|
timestamp := c.Query("timestamp")
|
||||||
|
nonce := c.Query("nonce")
|
||||||
|
|
||||||
|
strs := []string{constant.WX_NOTIFY_TOKEN, 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(m.Sum(nil))
|
||||||
|
|
||||||
|
f5.GetSysLog().Debug("wx sign:%s, %s", sign, signature)
|
||||||
|
if sign != signature {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rspObj := struct {
|
||||||
|
ErrorCode int32 `json:"ErrCode"`
|
||||||
|
ErrMsg string `json:"ErrMsg"`
|
||||||
|
}{
|
||||||
|
ErrorCode: 99999,
|
||||||
|
ErrMsg: "internal error",
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_signature := c.Query("msg_signature")
|
||||||
|
if msg_signature != "" {
|
||||||
|
postObj := struct {
|
||||||
|
Encrypt string `json:"Encrypt"`
|
||||||
|
ToUserName string `json:"ToUserName"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
if err := c.ShouldBindJSON(&postObj); err != nil {
|
||||||
|
rspObj.ErrorCode = 401
|
||||||
|
rspObj.ErrMsg = "post data error"
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
smsg, appid := service.Wxpay.DecryptMsg(msg_signature, timestamp, nonce, postObj.Encrypt)
|
||||||
|
if smsg == nil || appid == nil || len(smsg) == 0 || len(appid) == 0 {
|
||||||
|
rspObj.ErrorCode = 402
|
||||||
|
rspObj.ErrMsg = "decrypt data error"
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f5.GetSysLog().Debug("wx decrypt msg:%s", smsg)
|
||||||
|
|
||||||
|
wxnotifyobj := service.WxPurchaseNotify{}
|
||||||
|
if json.Unmarshal(smsg, &wxnotifyobj) != nil {
|
||||||
|
rspObj.ErrorCode = 403
|
||||||
|
rspObj.ErrMsg = "unmarshal data error"
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gameid := int64(0)
|
||||||
|
appkey := ""
|
||||||
|
notifyurl := ""
|
||||||
|
mt.Table.Wxconfig.Traverse(func(w *mt.Wxconfig) bool {
|
||||||
|
if w.GetAppid() == string(appid) {
|
||||||
|
gameid = w.GetGameid()
|
||||||
|
appkey = w.GetAppkey()
|
||||||
|
notifyurl = w.GetNotifyurl()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if appkey == "" {
|
||||||
|
f5.GetSysLog().Error("wx app config error:%s", appid)
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
oristr := wxnotifyobj.Event + "&" + wxnotifyobj.MiniGame.Payload
|
||||||
|
sig := service.Wxpay.GenSHA256Signature(oristr, appkey)
|
||||||
|
if sig != wxnotifyobj.MiniGame.PayEventSig {
|
||||||
|
f5.GetSysLog().Error("pay event sig error:%s, %s, %s", appid, sig, wxnotifyobj.MiniGame.PayEventSig)
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadobj := new(service.WxPayload)
|
||||||
|
if json.Unmarshal([]byte(wxnotifyobj.MiniGame.Payload), &payloadobj) != nil {
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
envpass := true
|
||||||
|
if f5.IsOnlineEnv() {
|
||||||
|
if payloadobj.Env != 0 {
|
||||||
|
f5.GetSysLog().Error("notify test info to prod url")
|
||||||
|
envpass = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if payloadobj.Env != 1 {
|
||||||
|
f5.GetSysLog().Error("notify prod info to test url")
|
||||||
|
envpass = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !envpass {
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
orderModel := new(model.InAppOrder)
|
||||||
|
if err, found := orderModel.FindByOrderId(payloadobj.OutTradeNo); err != nil {
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
} else if !found {
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if orderModel.Status > 1 {
|
||||||
|
rspObj.ErrorCode = 0
|
||||||
|
rspObj.ErrMsg = "Success"
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rediskey := "ls:accountid:" + orderModel.AccountId
|
||||||
|
str, err := service.Redis.Get(constant.LOGIN_REDIS, rediskey)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := map[string]interface{}{}
|
||||||
|
if json.Unmarshal([]byte(str), &data) != nil {
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
openid := q5.SafeToString(data["openid"])
|
||||||
|
if openid != payloadobj.OpenId {
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
orderModel.GameId = int32(gameid)
|
||||||
|
f5.GetSysLog().Debug("notify url:%s, %s", appid, notifyurl)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, rspObj)
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -56,3 +56,12 @@ const (
|
|||||||
const (
|
const (
|
||||||
GLOBAL_SALT = "f3a6a9a5-217a-4079-ab99-b5d69b8212be"
|
GLOBAL_SALT = "f3a6a9a5-217a-4079-ab99-b5d69b8212be"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WX_NOTIFY_TOKEN = "dV93f4FwSGMwkYcvsRHD8egdW5egPMhF" //必须32位
|
||||||
|
WX_NOTIFY_ENCODING_AES_KEY = "H60uFIXjyd431hLVhlsKyus3U28RVIzWncey424DqpY"
|
||||||
|
WX_AESKEY_SIZE = 32
|
||||||
|
WX_ENCODING_KEY_SIZE = 43
|
||||||
|
WX_RANDENCRYPT_STRLEN = 16
|
||||||
|
WX_KMSG_LEN = 4
|
||||||
|
)
|
||||||
|
@ -23,7 +23,7 @@ type InAppOrder struct {
|
|||||||
TryCount int32 `gorm:"column:try_count;comment:补单次数"`
|
TryCount int32 `gorm:"column:try_count;comment:补单次数"`
|
||||||
Price int32 `gorm:"column:price;comment:price"`
|
Price int32 `gorm:"column:price;comment:price"`
|
||||||
IP string `gorm:"column:ipv4;comment:ipv4地址"`
|
IP string `gorm:"column:ipv4;comment:ipv4地址"`
|
||||||
Status int32 `gorm:"column:status;comment:0: 新添加订单 1:已经完成订单"`
|
Status int32 `gorm:"column:status;comment:0: 新添加订单 1:已支付 2:已发货"`
|
||||||
ConfirmTime int32 `gorm:"column:confirmtime;comment:GameServer订单确认时间"`
|
ConfirmTime int32 `gorm:"column:confirmtime;comment:GameServer订单确认时间"`
|
||||||
CreateTime int32 `gorm:"column:createtime;<-:create"`
|
CreateTime int32 `gorm:"column:createtime;<-:create"`
|
||||||
ModifyTime int32 `gorm:"column:modifytime"`
|
ModifyTime int32 `gorm:"column:modifytime"`
|
||||||
|
@ -119,6 +119,7 @@ type Wxconfig struct {
|
|||||||
appsecret string
|
appsecret string
|
||||||
zoneid string
|
zoneid string
|
||||||
offerid string
|
offerid string
|
||||||
|
notifyurl string
|
||||||
|
|
||||||
_flags1_ uint64
|
_flags1_ uint64
|
||||||
_flags2_ uint64
|
_flags2_ uint64
|
||||||
@ -568,6 +569,14 @@ func (this *Wxconfig) HasOfferid() bool {
|
|||||||
return (this._flags1_ & (uint64(1) << 6)) > 0
|
return (this._flags1_ & (uint64(1) << 6)) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Wxconfig) GetNotifyurl() string {
|
||||||
|
return this.notifyurl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Wxconfig) HasNotifyurl() bool {
|
||||||
|
return (this._flags1_ & (uint64(1) << 7)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (this *LoginRedis) GetHost() string {
|
func (this *LoginRedis) GetHost() string {
|
||||||
return this.host
|
return this.host
|
||||||
}
|
}
|
||||||
@ -702,6 +711,7 @@ func (this *Wxconfig) LoadFromKv(kv map[string]interface{}) {
|
|||||||
f5.ReadMetaTableField(&this.appsecret, "appsecret", &this._flags1_, 4, kv)
|
f5.ReadMetaTableField(&this.appsecret, "appsecret", &this._flags1_, 4, kv)
|
||||||
f5.ReadMetaTableField(&this.zoneid, "zoneid", &this._flags1_, 5, kv)
|
f5.ReadMetaTableField(&this.zoneid, "zoneid", &this._flags1_, 5, kv)
|
||||||
f5.ReadMetaTableField(&this.offerid, "offerid", &this._flags1_, 6, kv)
|
f5.ReadMetaTableField(&this.offerid, "offerid", &this._flags1_, 6, kv)
|
||||||
|
f5.ReadMetaTableField(&this.notifyurl, "notifyurl", &this._flags1_, 7, kv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *LoginRedis) LoadFromKv(kv map[string]interface{}) {
|
func (this *LoginRedis) LoadFromKv(kv map[string]interface{}) {
|
||||||
|
@ -98,6 +98,7 @@ message Wxconfig
|
|||||||
optional string appsecret = 4;
|
optional string appsecret = 4;
|
||||||
optional string zoneid = 5;
|
optional string zoneid = 5;
|
||||||
optional string offerid = 6;
|
optional string offerid = 6;
|
||||||
|
optional string notifyurl = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LoginRedis
|
message LoginRedis
|
||||||
|
@ -11,4 +11,6 @@ func (this *MainServiceRouter) InitRouter() {
|
|||||||
api := v1.ApiGroupApp.MainServiceApiGroup
|
api := v1.ApiGroupApp.MainServiceApiGroup
|
||||||
f5.GetApp().GetGinEngine().GET("/api/service/refresh",
|
f5.GetApp().GetGinEngine().GET("/api/service/refresh",
|
||||||
api.RefreshToken)
|
api.RefreshToken)
|
||||||
|
f5.GetApp().GetGinEngine().GET("/wx/tnotify",
|
||||||
|
api.WxTNotify)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"f5"
|
"f5"
|
||||||
"main/constant"
|
"main/constant"
|
||||||
"main/mt"
|
"main/mt"
|
||||||
"q5"
|
"q5"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,6 +54,50 @@ type WxPayRsp struct {
|
|||||||
UsedGift int64 `json:"used_present_amount"`
|
UsedGift int64 `json:"used_present_amount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WxQueryOrderRsp struct {
|
||||||
|
ErrCode int32 `json:"errcode"`
|
||||||
|
ErrMsg string `json:"errmsg"`
|
||||||
|
ProductId string `json:"product_id"` //道具id
|
||||||
|
PayState int32 `json:"pay_state"` //1:未支付;2:已支付
|
||||||
|
DeliverState int32 `json:"deliver_state"` //1:未发货;2:已发货
|
||||||
|
PayTime int64 `json:"pay_finish_time"` //支付完成时间
|
||||||
|
TrandNo string `json:"out_trade_no"` //用户订单号
|
||||||
|
MchOrderNo string `json:"mch_order_no"` //微信支付商户单
|
||||||
|
Trans string `json:"transaction_id"` //微信支付订单号
|
||||||
|
}
|
||||||
|
|
||||||
|
type WxPurchaseNotify struct {
|
||||||
|
ToUserName string `json:"ToUserName"` //小游戏原始ID
|
||||||
|
FromUserName string `json:"FromUserName"` //该事件消息的openid,道具发货场景固定为微信官方的openid
|
||||||
|
CreateTime int64 `json:"CreateTime"` //消息发送时间
|
||||||
|
MsgType string `json:"MsgType"` //消息类型,道具发货场景固定为:event
|
||||||
|
Event string `json:"Event"` //事件类型 商城道具场景固定为:minigame_h5_goods_deliver_notify 道具直购(游戏内)场景固定为:minigame_game_pay_goods_deliver_notify
|
||||||
|
MiniGame struct {
|
||||||
|
Payload string `json:"Payload"` // 携带的具体内容,格式为json,具体内容如下表格Payload(因为这里需要对消息内容统一签名,所以统一把消息内容设计成json格式)
|
||||||
|
PayEventSig string `json:"PayEventSig"` //见https://docs.qq.com/doc/DVVZZdHFsYkttYmxl(PayEventSig)
|
||||||
|
} `json:"MiniGame"` //道具直购发货参数
|
||||||
|
}
|
||||||
|
|
||||||
|
type WxPayload struct {
|
||||||
|
OpenId string `json:"OpenId"` //接收道具的玩家openid
|
||||||
|
Env int32 `json:"Env"` //环境配置 0:现网环境(也叫正式环境) 1:沙箱环境
|
||||||
|
OutTradeNo string `json:"OutTradeNo"` // 订单号
|
||||||
|
GoodsInfo struct {
|
||||||
|
ProductId string `json:"ProductId"` //游戏道具id标识
|
||||||
|
Quantity int64 `json:"Quantity"` //购买道具数量
|
||||||
|
ZoneId string `json:"ZoneId"` //分区
|
||||||
|
OrigPrice int64 `json:"OrigPrice"` //物品原始价格 (单位:分)
|
||||||
|
ActualPrice int64 `json:"ActualPrice"` //物品实际支付价格(单位:分)
|
||||||
|
Attach string `json:"Attach"` //透传数据
|
||||||
|
OrderSource int64 `json:"OrderSource"` // 1 游戏内 2 商城下单 3 商城测试下单
|
||||||
|
} `json:"GoodsInfo"` //发货道具
|
||||||
|
WeChatPayInfo struct {
|
||||||
|
MchOrderNo string `json:"MchOrderNo"` // 微信支付商户单号
|
||||||
|
TransactionId string `json:"TransactionId"` // 交易单号(微信支付订单号)
|
||||||
|
} `json:"WeChatPayInfo"` //微信支付信息(仅微信支付渠道)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (wp *wxpay) init() {
|
func (wp *wxpay) init() {
|
||||||
wp.gamesGoods = q5.ConcurrentMap[int64, map[int64]int64]{}
|
wp.gamesGoods = q5.ConcurrentMap[int64, map[int64]int64]{}
|
||||||
wp.accessTokens = q5.ConcurrentMap[int64, TokenInfo]{}
|
wp.accessTokens = q5.ConcurrentMap[int64, TokenInfo]{}
|
||||||
@ -333,6 +373,82 @@ func (wp *wxpay) QueryPay(openid string, gameid int64, userip string, sessionkey
|
|||||||
return wxerrcode
|
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, ¶ms)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
func (wp *wxpay) AddExpireInfo(accountid string, sessiontime int64) {
|
func (wp *wxpay) AddExpireInfo(accountid string, sessiontime int64) {
|
||||||
info := expireSession{
|
info := expireSession{
|
||||||
SessionTime: sessiontime,
|
SessionTime: sessiontime,
|
||||||
@ -356,12 +472,6 @@ func (wp *wxpay) CheckExpireCache(accountid string, expire int64) bool {
|
|||||||
return true
|
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) GetAccessTokenList(data *[]TokenInfo) {
|
func (wp *wxpay) GetAccessTokenList(data *[]TokenInfo) {
|
||||||
wp.accessTokens.Range(func(key int64, value TokenInfo) bool {
|
wp.accessTokens.Range(func(key int64, value TokenInfo) bool {
|
||||||
*data = append(*data, value)
|
*data = append(*data, value)
|
||||||
|
135
server/payserver/service/wxpaybase.go
Normal file
135
server/payserver/service/wxpaybase.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"main/constant"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
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) DecryptMsg(sMsgSignature string, sTimeStamp string, sNonce string, sEncryptMsg string) (sMsg []byte, msgappid []byte) {
|
||||||
|
|
||||||
|
// 2.validate signature
|
||||||
|
if !wp.ValidateSignature(sMsgSignature, sTimeStamp, sNonce, sEncryptMsg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//3.decode base64
|
||||||
|
sAesData, err := base64.StdEncoding.DecodeString(sEncryptMsg)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//4.decode aes
|
||||||
|
sAesKey := wp.GenAesKeyFromEncodingKey(constant.WX_NOTIFY_ENCODING_AES_KEY)
|
||||||
|
if sAesKey == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sNoEncryptData := wp.AES_CBCDecrypt(sAesData, sAesKey)
|
||||||
|
|
||||||
|
// 5. remove kRandEncryptStrLen str
|
||||||
|
if len(sNoEncryptData) <= constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
netlenbyte := sNoEncryptData[constant.WX_RANDENCRYPT_STRLEN : constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN]
|
||||||
|
buf := bytes.NewReader(netlenbyte)
|
||||||
|
iMsgLen := int(0) //ntohl(iNetLen);
|
||||||
|
binary.Read(buf, binary.BigEndian, &iMsgLen)
|
||||||
|
if len(sNoEncryptData) <= constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN+iMsgLen {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sMsg = sNoEncryptData[constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN : constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN+iMsgLen]
|
||||||
|
|
||||||
|
//6. validate appid
|
||||||
|
msgappid = sNoEncryptData[constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN+iMsgLen:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wp *wxpay) ValidateSignature(sMsgSignature string, sTimeStamp string, sNonce string, sEncryptMsg string) bool {
|
||||||
|
sSignature := wp.ComputeSignature(constant.WX_NOTIFY_TOKEN, sTimeStamp, sNonce, sEncryptMsg)
|
||||||
|
if sSignature == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return sMsgSignature == sSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wp *wxpay) ComputeSignature(sToken string, sTimeStamp string, sNonce string, sMessage string) string {
|
||||||
|
if sToken == "" || sNonce == "" || sMessage == "" || sTimeStamp == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort
|
||||||
|
strs := []string{}
|
||||||
|
strs = append(strs, sToken)
|
||||||
|
strs = append(strs, sTimeStamp)
|
||||||
|
strs = append(strs, sNonce)
|
||||||
|
strs = append(strs, sMessage)
|
||||||
|
sort.Strings(strs)
|
||||||
|
sStr := strs[0] + strs[1] + strs[2] + strs[3]
|
||||||
|
|
||||||
|
//compute
|
||||||
|
sha1crypto := sha1.New()
|
||||||
|
_, err := sha1crypto.Write([]byte(sStr))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(sha1crypto.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wp *wxpay) GenAesKeyFromEncodingKey(sEncodingKey string) string {
|
||||||
|
if len(sEncodingKey) != len(constant.WX_NOTIFY_ENCODING_AES_KEY) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
sBase64 := sEncodingKey + "="
|
||||||
|
data, err := base64.StdEncoding.DecodeString(sBase64)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wp *wxpay) AES_CBCDecrypt(sSource []byte, sKey string) []byte {
|
||||||
|
if len(sSource) < constant.WX_AESKEY_SIZE || len(sSource)%constant.WX_AESKEY_SIZE != 0 {
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
key := []byte(sKey)
|
||||||
|
if len(sKey) > constant.WX_AESKEY_SIZE {
|
||||||
|
key = key[0:constant.WX_AESKEY_SIZE]
|
||||||
|
}
|
||||||
|
return aesDecryptCBC(sSource, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func aesDecryptCBC(encrypted []byte, key []byte) (decrypted []byte) {
|
||||||
|
block, _ := aes.NewCipher(key) // 分组秘钥
|
||||||
|
blockSize := block.BlockSize() // 获取秘钥块的长度
|
||||||
|
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) // 加密模式
|
||||||
|
decrypted = make([]byte, len(encrypted)) // 创建数组
|
||||||
|
blockMode.CryptBlocks(decrypted, encrypted) // 解密
|
||||||
|
decrypted = pkcs5UnPadding(decrypted) // 去除补全码
|
||||||
|
return decrypted
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkcs5UnPadding(origData []byte) []byte {
|
||||||
|
length := len(origData)
|
||||||
|
unpadding := int(origData[length-1])
|
||||||
|
return origData[:(length - unpadding)]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user