paid.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package notify
  2. import (
  3. "fmt"
  4. "reflect"
  5. "sort"
  6. "strings"
  7. "github.com/fatih/structs"
  8. "github.com/spf13/cast"
  9. "github.com/silenceper/wechat/v2/util"
  10. )
  11. // doc: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
  12. // PaidResult 下单回调
  13. type PaidResult 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. TradeState *string `xml:"trade_state"`
  29. BankType *string `xml:"bank_type"`
  30. TotalFee *int `xml:"total_fee"`
  31. SettlementTotalFee *int `xml:"settlement_total_fee"`
  32. FeeType *string `xml:"fee_type"`
  33. CashFee *string `xml:"cash_fee"`
  34. CashFeeType *string `xml:"cash_fee_type"`
  35. CouponFee *int `xml:"coupon_fee"`
  36. CouponCount *int `xml:"coupon_count"`
  37. // coupon_type_$n 这里只声明 3 个,如果有更多的可以自己组合
  38. CouponType0 *string `xml:"coupon_type_0"`
  39. CouponType1 *string `xml:"coupon_type_1"`
  40. CouponType2 *string `xml:"coupon_type_2"`
  41. CouponID0 *string `xml:"coupon_id_0"`
  42. CouponID1 *string `xml:"coupon_id_1"`
  43. CouponID2 *string `xml:"coupon_id_2"`
  44. CouponFee0 *string `xml:"coupon_fee_0"`
  45. CouponFee1 *string `xml:"coupon_fee_1"`
  46. CouponFee2 *string `xml:"coupon_fee_2"`
  47. TransactionID *string `xml:"transaction_id"`
  48. OutTradeNo *string `xml:"out_trade_no"`
  49. Attach *string `xml:"attach"`
  50. TimeEnd *string `xml:"time_end"`
  51. }
  52. // PaidResp 消息通知返回
  53. type PaidResp struct {
  54. ReturnCode string `xml:"return_code"`
  55. ReturnMsg string `xml:"return_msg"`
  56. }
  57. // PaidVerifySign 支付成功结果验签
  58. func (notify *Notify) PaidVerifySign(notifyRes PaidResult) bool {
  59. // STEP1, 转换 struct 为 map,并对 map keys 做排序
  60. resMap := structs.Map(notifyRes)
  61. sortedKeys := make([]string, 0, len(resMap))
  62. for k := range resMap {
  63. sortedKeys = append(sortedKeys, k)
  64. }
  65. sort.Strings(sortedKeys)
  66. // STEP2, 对 key=value 的键值对用&连接起来,略过空值 & sign
  67. var signStrings string
  68. for _, k := range sortedKeys {
  69. value := fmt.Sprintf("%v", cast.ToString(resMap[k]))
  70. if value != "" && strings.ToLower(k) != "sign" {
  71. signStrings = signStrings + getTagKeyName(k, &notifyRes) + "=" + value + "&"
  72. }
  73. }
  74. // STEP3, 在键值对的最后加上 key=API_KEY
  75. signStrings = signStrings + "key=" + notify.Key
  76. // STEP4, 根据 SignType 计算出签名
  77. var signType string
  78. if notifyRes.SignType != nil {
  79. signType = *notifyRes.SignType
  80. }
  81. sign, err := util.CalculateSign(signStrings, signType, notify.Key)
  82. if err != nil {
  83. return false
  84. }
  85. if sign != *notifyRes.Sign {
  86. return false
  87. }
  88. return true
  89. }
  90. func getTagKeyName(key string, notifyRes *PaidResult) string {
  91. s := reflect.TypeOf(notifyRes).Elem()
  92. f, _ := s.FieldByName(key)
  93. name := f.Tag.Get("xml")
  94. return name
  95. }