wechat_params.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package gopay
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "crypto/md5"
  6. "crypto/sha256"
  7. "encoding/hex"
  8. "encoding/json"
  9. "sort"
  10. "strconv"
  11. "strings"
  12. )
  13. type requestBody map[string]string
  14. //获取参数
  15. func (w requestBody) Get(key string) string {
  16. if w == nil {
  17. return ""
  18. }
  19. ws := w[key]
  20. return ws
  21. }
  22. //设置参数
  23. func (w requestBody) Set(key string, value string) {
  24. w[key] = value
  25. }
  26. //删除参数
  27. func (w requestBody) Remove(key string) {
  28. delete(w, key)
  29. }
  30. // 必选.
  31. // Appid: 微信支付分配的公众账号ID(企业号corpid即为此appId).
  32. // MchId: 微信支付分配的商户号.
  33. // NonceStr: 随机字符串,长度要求在32位以内(如不写,go-pay将为你随机生成).
  34. // Body: 商品简单描述.
  35. // OutTradeNo: 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一.
  36. // TotalFee: 订单总金额,单位为分.
  37. // SpbillCreateIp: 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP.
  38. // NotifyUrl: 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数.
  39. // TradeType: 交易类型:(JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付,不同trade_type决定了调起支付的方式).
  40. //
  41. // 非必选.
  42. // DeviceInfo: 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB".
  43. // SignType: 签名类型,默认为MD5,支持HMAC-SHA256和MD5.
  44. // Detail: 商品详细描述,对于使用单品优惠的商户,字段必须按照规范上传.
  45. // Attach: 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用.
  46. // FeeType: 符合ISO 4217标准的三位字母代码,默认人民币:CNY.
  47. // TimeStart: 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010.
  48. // TimeExpire: 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。订单失效时间是针对订单号而言的,由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期,所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id.
  49. // GoodsTag: 订单优惠标记,使用代金券或立减优惠功能时需要的参数.
  50. // ProductId: trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,商户自行定义.
  51. // LimitPay: 上传此参数 no_credit 可限制用户不能使用信用卡支付.
  52. // Openid: 用户标识: trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识.
  53. // Receipt: Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效.
  54. // SceneInfo: 该字段常用于线下活动时的场景信息上报,支持上报实际门店信息,商户也可以按需求自己上报相关信息。该字段为JSON对象数据,对象格式为{"store_info":{"id": "门店ID","name": "名称","area_code": "编码","address": "地址" }}.
  55. type WeChatPayParams struct {
  56. NonceStr string `xml:"nonce_str"`
  57. Body string `xml:"body"`
  58. OutTradeNo string `xml:"out_trade_no"`
  59. TotalFee int `xml:"total_fee"`
  60. SpbillCreateIp string `xml:"spbill_create_ip"`
  61. NotifyUrl string `xml:"notify_url"`
  62. TradeType string `xml:"trade_type"`
  63. DeviceInfo string `xml:"device_info"`
  64. SignType string `xml:"sign_type"`
  65. Detail string `xml:"detail"`
  66. Attach string `xml:"attach"`
  67. FeeType string `xml:"fee_type"`
  68. TimeStart string `xml:"time_start"`
  69. TimeExpire string `xml:"time_expire"`
  70. GoodsTag string `xml:"goods_tag"`
  71. ProductId string `xml:"product_id"`
  72. LimitPay string `xml:"limit_pay"`
  73. Openid string `xml:"openid"`
  74. Receipt string `xml:"receipt"`
  75. SceneInfo *StoreInfo `xml:"scene_info"`
  76. }
  77. //StoreInfo: SceneInfo 的字段信息
  78. type StoreInfo struct {
  79. Id string `json:"id"` // 门店唯一标识
  80. Name string `json:"name"` // 门店名称
  81. AreaCode string `json:"area_code"` // 门店所在地行政区划码,详细见《最新县及县以上行政区划代码》
  82. Address string `json:"address"` // 门店详细地址
  83. }
  84. //获取请求支付的参数
  85. func (w *WeChatPayParams) getRequestBody(appId, mchId string, params *WeChatPayParams) requestBody {
  86. reqs := make(requestBody)
  87. reqs.Set("appid", appId)
  88. reqs.Set("mch_id", mchId)
  89. reqs.Set("nonce_str", params.NonceStr)
  90. reqs.Set("body", params.Body)
  91. reqs.Set("out_trade_no", params.OutTradeNo)
  92. reqs.Set("total_fee", strconv.Itoa(params.TotalFee))
  93. reqs.Set("spbill_create_ip", params.SpbillCreateIp)
  94. reqs.Set("notify_url", params.NotifyUrl)
  95. reqs.Set("trade_type", params.TradeType)
  96. if params.DeviceInfo != "" {
  97. reqs.Set("device_info", params.DeviceInfo)
  98. }
  99. if params.SignType != "" {
  100. reqs.Set("sign_type", params.SignType)
  101. } else {
  102. reqs.Set("sign_type", "MD5")
  103. }
  104. if params.Detail != "" {
  105. reqs.Set("detail", params.Detail)
  106. }
  107. if params.Attach != "" {
  108. reqs.Set("attach", params.Attach)
  109. }
  110. if params.FeeType != "" {
  111. reqs.Set("fee_type", params.FeeType)
  112. }
  113. if params.TimeStart != "" {
  114. reqs.Set("time_start", params.TimeStart)
  115. }
  116. if params.TimeExpire != "" {
  117. reqs.Set("time_expire", params.TimeExpire)
  118. }
  119. if params.GoodsTag != "" {
  120. reqs.Set("goods_tag", params.GoodsTag)
  121. }
  122. if params.ProductId != "" {
  123. reqs.Set("product_id", params.ProductId)
  124. }
  125. if params.LimitPay != "" {
  126. reqs.Set("limit_pay", params.LimitPay)
  127. }
  128. if params.Openid != "" {
  129. reqs.Set("openid", params.Openid)
  130. }
  131. if params.Receipt != "" {
  132. reqs.Set("receipt", params.Receipt)
  133. }
  134. if params.SceneInfo != nil {
  135. marshal, _ := json.Marshal(params.SceneInfo)
  136. reqs.Set("scene_info", string(marshal))
  137. }
  138. return reqs
  139. }
  140. //获取Sign签名和请求支付的参数
  141. func getSignAndRequestBody(appId, mchId string, secretKey string, param *WeChatPayParams) (sign string, reqs requestBody) {
  142. reqs = param.getRequestBody(appId, mchId, param)
  143. signStr := getSignString(secretKey, reqs)
  144. //fmt.Println("signStr:", signStr)
  145. var hashSign []byte
  146. if param.SignType == WX_SignType_MD5 {
  147. hash := md5.New()
  148. hash.Write([]byte(signStr))
  149. hashSign = hash.Sum(nil)
  150. } else {
  151. hash := hmac.New(sha256.New, []byte(secretKey))
  152. hash.Write([]byte(signStr))
  153. hashSign = hash.Sum(nil)
  154. }
  155. sign = strings.ToUpper(hex.EncodeToString(hashSign))
  156. return
  157. }
  158. //获取根据Key排序后的请求参数字符串
  159. func getSignString(secretKey string, wxReq requestBody) string {
  160. keyList := make([]string, 0)
  161. for k := range wxReq {
  162. keyList = append(keyList, k)
  163. }
  164. sort.Strings(keyList)
  165. buffer := new(bytes.Buffer)
  166. for _, k := range keyList {
  167. buffer.WriteString(k)
  168. buffer.WriteString("=")
  169. buffer.WriteString(wxReq[k])
  170. buffer.WriteString("&")
  171. }
  172. buffer.WriteString("key=")
  173. buffer.WriteString(secretKey)
  174. return buffer.String()
  175. }
  176. //获取SanboxKey
  177. func getSanBoxSignKey() {
  178. //url:= "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey"
  179. }