pay.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package order
  2. import (
  3. "crypto/hmac"
  4. "crypto/md5"
  5. "crypto/sha256"
  6. "encoding/hex"
  7. "encoding/xml"
  8. "errors"
  9. "hash"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/silenceper/wechat/v2/pay/config"
  14. "github.com/silenceper/wechat/v2/util"
  15. )
  16. //https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
  17. var payGateway = "https://api.mch.weixin.qq.com/pay/unifiedorder"
  18. // Order struct extends context
  19. type Order struct {
  20. *config.Config
  21. }
  22. // NewOrder return an instance of order package
  23. func NewOrder(cfg *config.Config) *Order {
  24. order := Order{cfg}
  25. return &order
  26. }
  27. // Params was NEEDED when request unifiedorder
  28. // 传入的参数,用于生成 prepay_id 的必需参数
  29. type Params struct {
  30. TotalFee string
  31. CreateIP string
  32. Body string
  33. OutTradeNo string
  34. OpenID string
  35. TradeType string
  36. SignType string
  37. Detail string
  38. Attach string
  39. GoodsTag string
  40. NotifyURL string
  41. }
  42. // Config 是传出用于 js sdk 用的参数
  43. type Config struct {
  44. Timestamp string `json:"timestamp"`
  45. NonceStr string `json:"nonceStr"`
  46. PrePayID string `json:"prePayId"`
  47. SignType string `json:"signType"`
  48. Package string `json:"package"`
  49. PaySign string `json:"paySign"`
  50. }
  51. // PreOrder 是 unifie order 接口的返回
  52. type PreOrder struct {
  53. ReturnCode string `xml:"return_code"`
  54. ReturnMsg string `xml:"return_msg"`
  55. AppID string `xml:"appid,omitempty"`
  56. MchID string `xml:"mch_id,omitempty"`
  57. NonceStr string `xml:"nonce_str,omitempty"`
  58. Sign string `xml:"sign,omitempty"`
  59. ResultCode string `xml:"result_code,omitempty"`
  60. TradeType string `xml:"trade_type,omitempty"`
  61. PrePayID string `xml:"prepay_id,omitempty"`
  62. CodeURL string `xml:"code_url,omitempty"`
  63. ErrCode string `xml:"err_code,omitempty"`
  64. ErrCodeDes string `xml:"err_code_des,omitempty"`
  65. }
  66. // payRequest 接口请求参数
  67. type payRequest struct {
  68. AppID string `xml:"appid"`
  69. MchID string `xml:"mch_id"`
  70. DeviceInfo string `xml:"device_info,omitempty"`
  71. NonceStr string `xml:"nonce_str"`
  72. Sign string `xml:"sign"`
  73. SignType string `xml:"sign_type,omitempty"`
  74. Body string `xml:"body"`
  75. Detail string `xml:"detail,omitempty"`
  76. Attach string `xml:"attach,omitempty"` // 附加数据
  77. OutTradeNo string `xml:"out_trade_no"` // 商户订单号
  78. FeeType string `xml:"fee_type,omitempty"` // 标价币种
  79. TotalFee string `xml:"total_fee"` // 标价金额
  80. SpbillCreateIP string `xml:"spbill_create_ip"` // 终端IP
  81. TimeStart string `xml:"time_start,omitempty"` // 交易起始时间
  82. TimeExpire string `xml:"time_expire,omitempty"` // 交易结束时间
  83. GoodsTag string `xml:"goods_tag,omitempty"` // 订单优惠标记
  84. NotifyURL string `xml:"notify_url"` // 通知地址
  85. TradeType string `xml:"trade_type"` // 交易类型
  86. ProductID string `xml:"product_id,omitempty"` // 商品ID
  87. LimitPay string `xml:"limit_pay,omitempty"` //
  88. OpenID string `xml:"openid,omitempty"` // 用户标识
  89. SceneInfo string `xml:"scene_info,omitempty"` // 场景信息
  90. }
  91. // BridgeConfig get js bridge config
  92. func (o *Order) BridgeConfig(p *Params) (cfg Config, err error) {
  93. var (
  94. buffer strings.Builder
  95. h hash.Hash
  96. timestamp = strconv.FormatInt(time.Now().Unix(), 10)
  97. )
  98. order, err := o.PrePayOrder(p)
  99. if err != nil {
  100. return
  101. }
  102. buffer.WriteString("appId=")
  103. buffer.WriteString(order.AppID)
  104. buffer.WriteString("&nonceStr=")
  105. buffer.WriteString(order.NonceStr)
  106. buffer.WriteString("&package=")
  107. buffer.WriteString("prepay_id=" + order.PrePayID)
  108. buffer.WriteString("&signType=")
  109. buffer.WriteString(p.SignType)
  110. buffer.WriteString("&timeStamp=")
  111. buffer.WriteString(timestamp)
  112. buffer.WriteString("&key=")
  113. buffer.WriteString(o.Key)
  114. if p.SignType == "MD5" {
  115. h = md5.New()
  116. } else {
  117. h = hmac.New(sha256.New, []byte(o.Key))
  118. }
  119. h.Write([]byte(buffer.String()))
  120. // 签名
  121. cfg.PaySign = strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
  122. cfg.NonceStr = order.NonceStr
  123. cfg.Timestamp = timestamp
  124. cfg.PrePayID = order.PrePayID
  125. cfg.SignType = p.SignType
  126. cfg.Package = "prepay_id=" + order.PrePayID
  127. return
  128. }
  129. // PrePayOrder return data for invoke wechat payment
  130. func (o *Order) PrePayOrder(p *Params) (payOrder PreOrder, err error) {
  131. nonceStr := util.RandomStr(32)
  132. notifyURL := o.NotifyURL
  133. // 签名类型
  134. if p.SignType == "" {
  135. p.SignType = "MD5"
  136. }
  137. // 通知地址
  138. if p.NotifyURL != "" {
  139. notifyURL = p.NotifyURL
  140. }
  141. param := make(map[string]interface{})
  142. param["appid"] = o.AppID
  143. param["body"] = p.Body
  144. param["mch_id"] = o.MchID
  145. param["nonce_str"] = nonceStr
  146. param["out_trade_no"] = p.OutTradeNo
  147. param["spbill_create_ip"] = p.CreateIP
  148. param["total_fee"] = p.TotalFee
  149. param["trade_type"] = p.TradeType
  150. param["openid"] = p.OpenID
  151. param["sign_type"] = p.SignType
  152. param["detail"] = p.Detail
  153. param["attach"] = p.Attach
  154. param["goods_tag"] = p.GoodsTag
  155. param["notify_url"] = notifyURL
  156. bizKey := "&key=" + o.Key
  157. str := util.OrderParam(param, bizKey)
  158. sign := util.MD5Sum(str)
  159. request := payRequest{
  160. AppID: o.AppID,
  161. MchID: o.MchID,
  162. NonceStr: nonceStr,
  163. Sign: sign,
  164. Body: p.Body,
  165. OutTradeNo: p.OutTradeNo,
  166. TotalFee: p.TotalFee,
  167. SpbillCreateIP: p.CreateIP,
  168. NotifyURL: notifyURL,
  169. TradeType: p.TradeType,
  170. OpenID: p.OpenID,
  171. SignType: p.SignType,
  172. Detail: p.Detail,
  173. Attach: p.Attach,
  174. GoodsTag: p.GoodsTag,
  175. }
  176. rawRet, err := util.PostXML(payGateway, request)
  177. if err != nil {
  178. return
  179. }
  180. err = xml.Unmarshal(rawRet, &payOrder)
  181. if err != nil {
  182. return
  183. }
  184. if payOrder.ReturnCode == "SUCCESS" {
  185. // pay success
  186. if payOrder.ResultCode == "SUCCESS" {
  187. err = nil
  188. return
  189. }
  190. err = errors.New(payOrder.ErrCode + payOrder.ErrCodeDes)
  191. return
  192. }
  193. err = errors.New("[msg : xmlUnmarshalError] [rawReturn : " + string(rawRet) + "] [params : " + str + "] [sign : " + sign + "]")
  194. return
  195. }
  196. // PrePayID will request wechat merchant api and request for a pre payment order id
  197. func (o *Order) PrePayID(p *Params) (prePayID string, err error) {
  198. order, err := o.PrePayOrder(p)
  199. if err != nil {
  200. return
  201. }
  202. if order.PrePayID == "" {
  203. err = errors.New("empty prepayid")
  204. }
  205. prePayID = order.PrePayID
  206. return
  207. }