diff --git a/go.mod b/go.mod index ac3bab8..ee34604 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,14 @@ go 1.13 require ( github.com/astaxie/beego v1.7.1 github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a + github.com/fatih/structs v1.1.0 github.com/gin-gonic/gin v1.1.4 github.com/golang/protobuf v0.0.0-20161117033126-8ee79997227b // indirect github.com/gomodule/redigo v2.0.1-0.20180627144507-2cd21d9966bf+incompatible github.com/kr/pretty v0.1.0 github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739 // indirect github.com/mattn/go-isatty v0.0.0-20161123143637-30a891c33c7c // indirect + github.com/spf13/cast v1.3.1 github.com/stretchr/testify v1.4.0 // indirect golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/net v0.0.0-20191125084936-ffdde1057850 // indirect diff --git a/go.sum b/go.sum index 9345625..16b424d 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,9 @@ github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a h1:k5TuEkqEYCR github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/gin-gonic/gin v1.1.4 h1:XLaCFbU39SSGRQrEeP7Z7mM3lvRqC4vE5tEaVdLDdSE= github.com/gin-gonic/gin v1.1.4/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= github.com/golang/protobuf v0.0.0-20161117033126-8ee79997227b h1:fE/yi9pibxGEc0gSJuEShcsBXE2d5FW3OudsjE9tKzQ= @@ -21,7 +24,10 @@ github.com/mattn/go-isatty v0.0.0-20161123143637-30a891c33c7c h1:YHHK/dEmr2Jo1cW github.com/mattn/go-isatty v0.0.0-20161123143637-30a891c33c7c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= diff --git a/pay/notify_result.go b/pay/notify_result.go index db63ef4..2b2317b 100644 --- a/pay/notify_result.go +++ b/pay/notify_result.go @@ -2,36 +2,57 @@ package pay import ( "fmt" + "github.com/fatih/structs" "github.com/silenceper/wechat/util" + "github.com/spf13/cast" + "reflect" "sort" + "strings" ) -// Base 公用参数 -type Base struct { - AppID string `xml:"appid"` - MchID string `xml:"mch_id"` - NonceStr string `xml:"nonce_str"` - Sign string `xml:"sign"` -} +// doc: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8 // NotifyResult 下单回调 type NotifyResult struct { - Base - ReturnCode string `xml:"return_code"` - ReturnMsg string `xml:"return_msg"` - ResultCode string `xml:"result_code"` - OpenID string `xml:"openid"` - IsSubscribe string `xml:"is_subscribe"` - TradeType string `xml:"trade_type"` - BankType string `xml:"bank_type"` - TotalFee int `xml:"total_fee"` - FeeType string `xml:"fee_type"` - CashFee int `xml:"cash_fee"` - CashFeeType string `xml:"cash_fee_type"` - TransactionID string `xml:"transaction_id"` - OutTradeNo string `xml:"out_trade_no"` - Attach string `xml:"attach"` - TimeEnd string `xml:"time_end"` + ReturnCode *string `xml:"return_code"` + ReturnMsg *string `xml:"return_msg"` + + AppID *string `xml:"appid" json:"appid"` + MchID *string `xml:"mch_id"` + DeviceInfo *string `xml:"device_info"` + NonceStr *string `xml:"nonce_str"` + Sign *string `xml:"sign"` + SignType *string `xml:"sign_type"` + ResultCode *string `xml:"result_code"` + ErrCode *string `xml:"err_code"` + ErrCodeDes *string `xml:"err_code_des"` + OpenID *string `xml:"openid"` + IsSubscribe *string `xml:"is_subscribe"` + TradeType *string `xml:"trade_type"` + BankType *string `xml:"bank_type"` + TotalFee *int `xml:"total_fee"` + SettlementTotalFee *int `xml:"settlement_total_fee"` + FeeType *string `xml:"fee_type"` + CashFee *string `xml:"cash_fee"` + CashFeeType *string `xml:"cash_fee_type"` + CouponFee *int `xml:"coupon_fee"` + CouponCount *int `xml:"coupon_count"` + + // coupon_type_$n 这里只声明 3 个,如果有更多的可以自己组合 + CouponType0 *string `xml:"coupon_type_0"` + CouponType1 *string `xml:"coupon_type_1"` + CouponType2 *string `xml:"coupon_type_2"` + CouponID0 *string `xml:"coupon_id_0"` + CouponID1 *string `xml:"coupon_id_1"` + CouponID2 *string `xml:"coupon_id_2"` + CouponFeed0 *string `xml:"coupon_fee_0"` + CouponFeed1 *string `xml:"coupon_fee_1"` + CouponFeed2 *string `xml:"coupon_fee_2"` + + TransactionID *string `xml:"transaction_id"` + OutTradeNo *string `xml:"out_trade_no"` + Attach *string `xml:"attach"` + TimeEnd *string `xml:"time_end"` } // NotifyResp 消息通知返回 @@ -42,46 +63,38 @@ type NotifyResp struct { // VerifySign 验签 func (pcf *Pay) VerifySign(notifyRes NotifyResult) bool { - // 封装map 请求过来的 map - resMap := make(map[string]interface{}) - // base - resMap["appid"] = notifyRes.AppID - resMap["mch_id"] = notifyRes.MchID - resMap["nonce_str"] = notifyRes.NonceStr - // NotifyResult - resMap["return_code"] = notifyRes.ReturnCode - resMap["result_code"] = notifyRes.ResultCode - resMap["openid"] = notifyRes.OpenID - resMap["is_subscribe"] = notifyRes.IsSubscribe - resMap["trade_type"] = notifyRes.TradeType - resMap["bank_type"] = notifyRes.BankType - resMap["total_fee"] = notifyRes.TotalFee - resMap["fee_type"] = notifyRes.FeeType - resMap["cash_fee"] = notifyRes.CashFee - resMap["transaction_id"] = notifyRes.TransactionID - resMap["out_trade_no"] = notifyRes.OutTradeNo - resMap["attach"] = notifyRes.Attach - resMap["time_end"] = notifyRes.TimeEnd - // 支付key + // STEP1, 转换 struct 为 map,并对 map keys 做排序 + resMap := structs.Map(notifyRes) + sortedKeys := make([]string, 0, len(resMap)) for k := range resMap { sortedKeys = append(sortedKeys, k) } sort.Strings(sortedKeys) - // STEP2, 对key=value的键值对用&连接起来,略过空值 + + // STEP2, 对key=value的键值对用&连接起来,略过空值 & sign var signStrings string for _, k := range sortedKeys { - value := fmt.Sprintf("%v", resMap[k]) - if value != "" { - signStrings = signStrings + k + "=" + value + "&" + value := fmt.Sprintf("%v", cast.ToString(resMap[k])) + if value != "" && strings.ToLower(k) != "sign" { + signStrings = signStrings + getTagKeyName(k, ¬ifyRes) + "=" + value + "&" } } + // STEP3, 在键值对的最后加上key=API_KEY signStrings = signStrings + "key=" + pcf.PayKey + // STEP4, 进行MD5签名并且将所有字符转为大写. sign := util.MD5Sum(signStrings) - if sign != notifyRes.Sign { + if sign != *notifyRes.Sign { return false } return true } + +func getTagKeyName(key string, notifyRes *NotifyResult) string { + s := reflect.TypeOf(notifyRes).Elem() + f, _ := s.FieldByName(key) + name := f.Tag.Get("xml") + return name +}