pay.go 6.1 KB

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