pay.go 6.4 KB

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