蛋蛋星球-制度模式
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.9 KiB

  1. package wechat
  2. import (
  3. "context"
  4. "crypto/hmac"
  5. "crypto/md5"
  6. "crypto/sha256"
  7. "encoding/hex"
  8. "encoding/json"
  9. "errors"
  10. "fmt"
  11. "hash"
  12. "reflect"
  13. "strings"
  14. "github.com/go-pay/gopay"
  15. "github.com/go-pay/util"
  16. )
  17. // VerifySign 微信同步返回参数验签或异步通知参数验签
  18. // ApiKey:API秘钥值
  19. // signType:签名类型(调用API方法时填写的类型)
  20. // bean:微信同步返回的结构体 wxRsp 或 异步通知解析的结构体 notifyReq,推荐通 BodyMap 验签
  21. // 返回参数ok:是否验签通过
  22. // 返回参数err:其他错误信息,不要根据 error 是否为空来判断验签正确与否,需再单独判断返回的 ok
  23. func VerifySign(apiKey, signType string, bean any) (ok bool, err error) {
  24. if bean == nil {
  25. return false, errors.New("bean is nil")
  26. }
  27. kind := reflect.ValueOf(bean).Kind()
  28. if kind == reflect.Map {
  29. bm := bean.(gopay.BodyMap)
  30. bodySign := bm.GetString("sign")
  31. bm.Remove("sign")
  32. return GetReleaseSign(apiKey, signType, bm) == bodySign, nil
  33. }
  34. bs, err := json.Marshal(bean)
  35. if err != nil {
  36. return false, fmt.Errorf("json.Marshal(%s):%w", string(bs), err)
  37. }
  38. bm := make(gopay.BodyMap)
  39. if err = json.Unmarshal(bs, &bm); err != nil {
  40. return false, fmt.Errorf("json.Marshal(%s):%w", string(bs), err)
  41. }
  42. bodySign := bm.GetString("sign")
  43. bm.Remove("sign")
  44. return GetReleaseSign(apiKey, signType, bm) == bodySign, nil
  45. }
  46. // GetMiniPaySign JSAPI支付,统一下单获取支付参数后,再次计算出小程序用的paySign
  47. // appId:APPID
  48. // nonceStr:随即字符串
  49. // packages:统一下单成功后拼接得到的值
  50. // signType:签名类型
  51. // timeStamp:时间
  52. // ApiKey:API秘钥值
  53. // 微信小程序支付API:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/payment/wx.requestPayment.html
  54. // 微信小程序支付PaySign计算文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3
  55. func GetMiniPaySign(appId, nonceStr, packages, signType, timeStamp, apiKey string) (paySign string) {
  56. var (
  57. buffer strings.Builder
  58. h hash.Hash
  59. )
  60. buffer.WriteString("appId=")
  61. buffer.WriteString(appId)
  62. buffer.WriteString("&nonceStr=")
  63. buffer.WriteString(nonceStr)
  64. buffer.WriteString("&package=")
  65. buffer.WriteString(packages)
  66. buffer.WriteString("&signType=")
  67. buffer.WriteString(signType)
  68. buffer.WriteString("&timeStamp=")
  69. buffer.WriteString(timeStamp)
  70. buffer.WriteString("&key=")
  71. buffer.WriteString(apiKey)
  72. if signType == SignType_HMAC_SHA256 {
  73. h = hmac.New(sha256.New, []byte(apiKey))
  74. } else {
  75. h = md5.New()
  76. }
  77. h.Write([]byte(buffer.String()))
  78. return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
  79. }
  80. // Deprecated
  81. // 微信内H5支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
  82. func GetH5PaySign(appId, nonceStr, packages, signType, timeStamp, apiKey string) (paySign string) {
  83. return GetJsapiPaySign(appId, nonceStr, packages, signType, timeStamp, apiKey)
  84. }
  85. // GetJsapiPaySign JSAPI调起支付,统一下单获取支付参数后,再次计算出微信内H5支付需要用的paySign
  86. // appId:APPID
  87. // nonceStr:随即字符串
  88. // packages:统一下单成功后拼接得到的值
  89. // signType:签名类型
  90. // timeStamp:时间
  91. // ApiKey:API秘钥值
  92. // 文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
  93. func GetJsapiPaySign(appId, nonceStr, packages, signType, timeStamp, apiKey string) (paySign string) {
  94. var (
  95. buffer strings.Builder
  96. h hash.Hash
  97. )
  98. buffer.WriteString("appId=")
  99. buffer.WriteString(appId)
  100. buffer.WriteString("&nonceStr=")
  101. buffer.WriteString(nonceStr)
  102. buffer.WriteString("&package=")
  103. buffer.WriteString(packages)
  104. buffer.WriteString("&signType=")
  105. buffer.WriteString(signType)
  106. buffer.WriteString("&timeStamp=")
  107. buffer.WriteString(timeStamp)
  108. buffer.WriteString("&key=")
  109. buffer.WriteString(apiKey)
  110. if signType == SignType_HMAC_SHA256 {
  111. h = hmac.New(sha256.New, []byte(apiKey))
  112. } else {
  113. h = md5.New()
  114. }
  115. h.Write([]byte(buffer.String()))
  116. paySign = strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
  117. return
  118. }
  119. // GetAppPaySign APP支付,统一下单获取支付参数后,再次计算APP支付所需要的的sign
  120. // appId:APPID
  121. // partnerid:partnerid
  122. // nonceStr:随即字符串
  123. // prepayId:统一下单成功后得到的值
  124. // signType:此处签名方式,务必与统一下单时用的签名方式一致
  125. // timeStamp:时间
  126. // ApiKey:API秘钥值
  127. // 文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12&index=2
  128. func GetAppPaySign(appid, partnerid, noncestr, prepayid, signType, timestamp, apiKey string) (paySign string) {
  129. var (
  130. buffer strings.Builder
  131. h hash.Hash
  132. )
  133. buffer.WriteString("appid=")
  134. buffer.WriteString(appid)
  135. buffer.WriteString("&noncestr=")
  136. buffer.WriteString(noncestr)
  137. buffer.WriteString("&package=Sign=WXPay")
  138. buffer.WriteString("&partnerid=")
  139. buffer.WriteString(partnerid)
  140. buffer.WriteString("&prepayid=")
  141. buffer.WriteString(prepayid)
  142. buffer.WriteString("&timestamp=")
  143. buffer.WriteString(timestamp)
  144. buffer.WriteString("&key=")
  145. buffer.WriteString(apiKey)
  146. if signType == SignType_HMAC_SHA256 {
  147. h = hmac.New(sha256.New, []byte(apiKey))
  148. } else {
  149. h = md5.New()
  150. }
  151. h.Write([]byte(buffer.String()))
  152. paySign = strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
  153. return
  154. }
  155. // Deprecated
  156. // GetParamSign 获取微信支付所需参数里的Sign值(通过支付参数计算Sign值)
  157. // 注意:BodyMap中如无 sign_type 参数,默认赋值 sign_type 为 MD5
  158. // appId:应用ID
  159. // mchId:商户ID
  160. // ApiKey:API秘钥值
  161. // 返回参数 sign:通过Appid、MchId、ApiKey和BodyMap中的参数计算出的Sign值
  162. func GetParamSign(appId, mchId, apiKey string, bm gopay.BodyMap) (sign string) {
  163. bm.Set("appid", appId)
  164. bm.Set("mch_id", mchId)
  165. var (
  166. signType string
  167. h hash.Hash
  168. )
  169. signType = bm.GetString("sign_type")
  170. if signType == gopay.NULL {
  171. bm.Set("sign_type", SignType_MD5)
  172. }
  173. if signType == SignType_HMAC_SHA256 {
  174. h = hmac.New(sha256.New, []byte(apiKey))
  175. } else {
  176. h = md5.New()
  177. }
  178. h.Write([]byte(bm.EncodeWeChatSignParams(apiKey)))
  179. sign = strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
  180. return
  181. }
  182. // Deprecated
  183. // GetSanBoxParamSign 获取微信支付沙箱环境所需参数里的Sign值(通过支付参数计算Sign值)
  184. // 注意:沙箱环境默认 sign_type 为 MD5
  185. // appId:应用ID
  186. // mchId:商户ID
  187. // ApiKey:API秘钥值
  188. // 返回参数 sign:通过Appid、MchId、ApiKey和BodyMap中的参数计算出的Sign值
  189. func GetSanBoxParamSign(ctx context.Context, appId, mchId, apiKey string, bm gopay.BodyMap) (sign string, err error) {
  190. bm.Set("appid", appId)
  191. bm.Set("mch_id", mchId)
  192. bm.Set("sign_type", SignType_MD5)
  193. bm.Set("total_fee", 101)
  194. var (
  195. sandBoxApiKey string
  196. hashMd5 hash.Hash
  197. )
  198. if sandBoxApiKey, err = getSanBoxKey(ctx, mchId, util.RandomString(32), apiKey, SignType_MD5); err != nil {
  199. return
  200. }
  201. hashMd5 = md5.New()
  202. hashMd5.Write([]byte(bm.EncodeWeChatSignParams(sandBoxApiKey)))
  203. sign = strings.ToUpper(hex.EncodeToString(hashMd5.Sum(nil)))
  204. return
  205. }