notify_result.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package pay
  2. import (
  3. "fmt"
  4. "github.com/fatih/structs"
  5. "github.com/silenceper/wechat/util"
  6. "github.com/spf13/cast"
  7. "reflect"
  8. "sort"
  9. "strings"
  10. )
  11. // doc: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
  12. // NotifyResult 下单回调
  13. type NotifyResult struct {
  14. ReturnCode *string `xml:"return_code"`
  15. ReturnMsg *string `xml:"return_msg"`
  16. AppID *string `xml:"appid" json:"appid"`
  17. MchID *string `xml:"mch_id"`
  18. DeviceInfo *string `xml:"device_info"`
  19. NonceStr *string `xml:"nonce_str"`
  20. Sign *string `xml:"sign"`
  21. SignType *string `xml:"sign_type"`
  22. ResultCode *string `xml:"result_code"`
  23. ErrCode *string `xml:"err_code"`
  24. ErrCodeDes *string `xml:"err_code_des"`
  25. OpenID *string `xml:"openid"`
  26. IsSubscribe *string `xml:"is_subscribe"`
  27. TradeType *string `xml:"trade_type"`
  28. BankType *string `xml:"bank_type"`
  29. TotalFee *int `xml:"total_fee"`
  30. SettlementTotalFee *int `xml:"settlement_total_fee"`
  31. FeeType *string `xml:"fee_type"`
  32. CashFee *string `xml:"cash_fee"`
  33. CashFeeType *string `xml:"cash_fee_type"`
  34. CouponFee *int `xml:"coupon_fee"`
  35. CouponCount *int `xml:"coupon_count"`
  36. // coupon_type_$n 这里只声明 3 个,如果有更多的可以自己组合
  37. CouponType0 *string `xml:"coupon_type_0"`
  38. CouponType1 *string `xml:"coupon_type_1"`
  39. CouponType2 *string `xml:"coupon_type_2"`
  40. CouponID0 *string `xml:"coupon_id_0"`
  41. CouponID1 *string `xml:"coupon_id_1"`
  42. CouponID2 *string `xml:"coupon_id_2"`
  43. CouponFeed0 *string `xml:"coupon_fee_0"`
  44. CouponFeed1 *string `xml:"coupon_fee_1"`
  45. CouponFeed2 *string `xml:"coupon_fee_2"`
  46. TransactionID *string `xml:"transaction_id"`
  47. OutTradeNo *string `xml:"out_trade_no"`
  48. Attach *string `xml:"attach"`
  49. TimeEnd *string `xml:"time_end"`
  50. }
  51. // NotifyResp 消息通知返回
  52. type NotifyResp struct {
  53. ReturnCode string `xml:"return_code"`
  54. ReturnMsg string `xml:"return_msg"`
  55. }
  56. // VerifySign 验签
  57. func (pcf *Pay) VerifySign(notifyRes NotifyResult) bool {
  58. // STEP1, 转换 struct 为 map,并对 map keys 做排序
  59. resMap := structs.Map(notifyRes)
  60. sortedKeys := make([]string, 0, len(resMap))
  61. for k := range resMap {
  62. sortedKeys = append(sortedKeys, k)
  63. }
  64. sort.Strings(sortedKeys)
  65. // STEP2, 对key=value的键值对用&连接起来,略过空值 & sign
  66. var signStrings string
  67. for _, k := range sortedKeys {
  68. value := fmt.Sprintf("%v", cast.ToString(resMap[k]))
  69. if value != "" && strings.ToLower(k) != "sign" {
  70. signStrings = signStrings + getTagKeyName(k, &notifyRes) + "=" + value + "&"
  71. }
  72. }
  73. // STEP3, 在键值对的最后加上key=API_KEY
  74. signStrings = signStrings + "key=" + pcf.PayKey
  75. // STEP4, 进行MD5签名并且将所有字符转为大写.
  76. sign := util.MD5Sum(signStrings)
  77. if sign != *notifyRes.Sign {
  78. return false
  79. }
  80. return true
  81. }
  82. func getTagKeyName(key string, notifyRes *NotifyResult) string {
  83. s := reflect.TypeOf(notifyRes).Elem()
  84. f, _ := s.FieldByName(key)
  85. name := f.Tag.Get("xml")
  86. return name
  87. }