pay.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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. ErrCode string `xml:"err_code,omitempty"`
  59. ErrCodeDes string `xml:"err_code_des,omitempty"`
  60. }
  61. // payRequest 接口请求参数
  62. type payRequest struct {
  63. AppID string `xml:"appid"`
  64. MchID string `xml:"mch_id"`
  65. DeviceInfo string `xml:"device_info,omitempty"`
  66. NonceStr string `xml:"nonce_str"`
  67. Sign string `xml:"sign"`
  68. SignType string `xml:"sign_type,omitempty"`
  69. Body string `xml:"body"`
  70. Detail string `xml:"detail,omitempty"`
  71. Attach string `xml:"attach,omitempty"` // 附加数据
  72. OutTradeNo string `xml:"out_trade_no"` // 商户订单号
  73. FeeType string `xml:"fee_type,omitempty"` // 标价币种
  74. TotalFee string `xml:"total_fee"` // 标价金额
  75. SpbillCreateIP string `xml:"spbill_create_ip"` // 终端IP
  76. TimeStart string `xml:"time_start,omitempty"` // 交易起始时间
  77. TimeExpire string `xml:"time_expire,omitempty"` // 交易结束时间
  78. GoodsTag string `xml:"goods_tag,omitempty"` // 订单优惠标记
  79. NotifyURL string `xml:"notify_url"` // 通知地址
  80. TradeType string `xml:"trade_type"` // 交易类型
  81. ProductID string `xml:"product_id,omitempty"` // 商品ID
  82. LimitPay string `xml:"limit_pay,omitempty"` //
  83. OpenID string `xml:"openid,omitempty"` // 用户标识
  84. SceneInfo string `xml:"scene_info,omitempty"` // 场景信息
  85. XMLName struct{} `xml:"xml"`
  86. }
  87. // BridgeConfig get js bridge config
  88. func (o *Order) BridgeConfig(p *Params) (cfg Config, err error) {
  89. var (
  90. buffer strings.Builder
  91. timestamp = strconv.FormatInt(time.Now().Unix(), 10)
  92. )
  93. order, err := o.PrePayOrder(p)
  94. if err != nil {
  95. return
  96. }
  97. buffer.WriteString("appId=")
  98. buffer.WriteString(order.AppID)
  99. buffer.WriteString("&nonceStr=")
  100. buffer.WriteString(order.NonceStr)
  101. buffer.WriteString("&package=")
  102. buffer.WriteString("prepay_id=" + order.PrePayID)
  103. buffer.WriteString("&signType=")
  104. buffer.WriteString(p.SignType)
  105. buffer.WriteString("&timeStamp=")
  106. buffer.WriteString(timestamp)
  107. buffer.WriteString("&key=")
  108. buffer.WriteString(o.Key)
  109. sign, err := util.CalculateSign(buffer.String(), p.SignType, o.Key)
  110. if err != nil {
  111. return
  112. }
  113. // 签名
  114. cfg.PaySign = sign
  115. cfg.NonceStr = order.NonceStr
  116. cfg.Timestamp = timestamp
  117. cfg.PrePayID = order.PrePayID
  118. cfg.SignType = p.SignType
  119. cfg.Package = "prepay_id=" + order.PrePayID
  120. return
  121. }
  122. // PrePayOrder return data for invoke wechat payment
  123. func (o *Order) PrePayOrder(p *Params) (payOrder PreOrder, err error) {
  124. nonceStr := util.RandomStr(32)
  125. notifyURL := o.NotifyURL
  126. // 签名类型
  127. if p.SignType == "" {
  128. p.SignType = util.SignTypeMD5
  129. }
  130. // 通知地址
  131. if p.NotifyURL != "" {
  132. notifyURL = p.NotifyURL
  133. }
  134. param := make(map[string]string)
  135. param["appid"] = o.AppID
  136. param["body"] = p.Body
  137. param["mch_id"] = o.MchID
  138. param["nonce_str"] = nonceStr
  139. param["out_trade_no"] = p.OutTradeNo
  140. param["spbill_create_ip"] = p.CreateIP
  141. param["total_fee"] = p.TotalFee
  142. param["trade_type"] = p.TradeType
  143. param["openid"] = p.OpenID
  144. param["sign_type"] = p.SignType
  145. param["detail"] = p.Detail
  146. param["attach"] = p.Attach
  147. param["goods_tag"] = p.GoodsTag
  148. param["notify_url"] = notifyURL
  149. sign, err := util.ParamSign(param, o.Key)
  150. if err != nil {
  151. return
  152. }
  153. request := payRequest{
  154. AppID: o.AppID,
  155. MchID: o.MchID,
  156. NonceStr: nonceStr,
  157. Sign: sign,
  158. Body: p.Body,
  159. OutTradeNo: p.OutTradeNo,
  160. TotalFee: p.TotalFee,
  161. SpbillCreateIP: p.CreateIP,
  162. NotifyURL: notifyURL,
  163. TradeType: p.TradeType,
  164. OpenID: p.OpenID,
  165. SignType: p.SignType,
  166. Detail: p.Detail,
  167. Attach: p.Attach,
  168. GoodsTag: p.GoodsTag,
  169. }
  170. rawRet, err := util.PostXML(payGateway, request)
  171. if err != nil {
  172. return
  173. }
  174. err = xml.Unmarshal(rawRet, &payOrder)
  175. if err != nil {
  176. return
  177. }
  178. if payOrder.ReturnCode == "SUCCESS" {
  179. // pay success
  180. if payOrder.ResultCode == "SUCCESS" {
  181. err = nil
  182. return
  183. }
  184. err = errors.New(payOrder.ErrCode + payOrder.ErrCodeDes)
  185. return
  186. }
  187. err = errors.New("[msg : xmlUnmarshalError] [rawReturn : " + string(rawRet) + "] [sign : " + sign + "]")
  188. return
  189. }
  190. // PrePayID will request wechat merchant api and request for a pre payment order id
  191. func (o *Order) PrePayID(p *Params) (prePayID string, err error) {
  192. order, err := o.PrePayOrder(p)
  193. if err != nil {
  194. return
  195. }
  196. if order.PrePayID == "" {
  197. err = errors.New("empty prepayid")
  198. }
  199. prePayID = order.PrePayID
  200. return
  201. }