pay.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package pay
  2. import (
  3. "encoding/json"
  4. "encoding/xml"
  5. "errors"
  6. "fmt"
  7. "github.com/silenceper/wechat/context"
  8. "github.com/silenceper/wechat/util"
  9. )
  10. var payGateway = "https://api.mch.weixin.qq.com/pay/unifiedorder"
  11. // Pay struct extends context
  12. type Pay struct {
  13. *context.Context
  14. }
  15. // Params was NEEDED when request unifiedorder
  16. // 传入的参数,用于生成 prepay_id 的必需参数
  17. type Params struct {
  18. TotalFee string
  19. CreateIP string
  20. Body string
  21. Attach string
  22. OutTradeNo string
  23. OpenID string
  24. }
  25. // Config 是传出用于 jsdk 用的参数
  26. type Config struct {
  27. Timestamp int64
  28. NonceStr string
  29. PrePayID string
  30. SignType string
  31. Sign string
  32. }
  33. // notifyResult 是 支付成功后回调返回
  34. type NotifyResult struct {
  35. AppID string `xml:"appid,omitempty"`
  36. Attach string `xml:"attach,omitempty"`
  37. BankType string `xml:"bank_type,omitempty"`
  38. FeeType string `xml:"fee_type,omitempty"`
  39. IsSubscribe string `xml:"is_subscribe,omitempty"`
  40. MchId string `xml:"mch_id,omitempty"`
  41. NonceStr string `xml:"nonce_str,omitempty"`
  42. Openid string `xml:"openid,omitempty"`
  43. OutTradeNo string `xml:"out_trade_no,omitempty"`
  44. ResultCode string `xml:"result_code,omitempty"`
  45. ReturnCode string `xml:"return_code,omitempty"`
  46. Sign string `xml:"sign,omitempty"`
  47. SubMchId string `xml:"sub_mch_id,omitempty"`
  48. TimeEnd string `xml:"time_end,omitempty"`
  49. TotalFee string `xml:"total_fee,omitempty"`
  50. CouponFee string `xml:"coupon_fee,omitempty"`
  51. CouponCount string `xml:"coupon_count,omitempty"`
  52. CouponType string `xml:"coupon_type,omitempty"`
  53. CouponId string `xml:"coupon_id,omitempty"`
  54. TradeType string `xml:"trade_type,omitempty"`
  55. TransactionId string `xml:"transaction_id,omitempty"`
  56. }
  57. // notifyResult 是 支付成功后回调返回
  58. type NotifyResponse struct {
  59. ReturnCode string `xml:"return_code"`
  60. ReturnMsg string `xml:"return_msg"`
  61. }
  62. // payResult 是 unifie order 接口的返回
  63. type payResult struct {
  64. ReturnCode string `xml:"return_code"`
  65. ReturnMsg string `xml:"return_msg"`
  66. AppID string `xml:"appid,omitempty"`
  67. MchID string `xml:"mch_id,omitempty"`
  68. NonceStr string `xml:"nonce_str,omitempty"`
  69. Sign string `xml:"sign,omitempty"`
  70. ResultCode string `xml:"result_code,omitempty"`
  71. TradeType string `xml:"trade_type,omitempty"`
  72. PrePayID string `xml:"prepay_id,omitempty"`
  73. CodeURL string `xml:"code_url,omitempty"`
  74. ErrCode string `xml:"err_code,omitempty"`
  75. ErrCodeDes string `xml:"err_code_des,omitempty"`
  76. }
  77. //payRequest 接口请求参数
  78. type payRequest struct {
  79. AppID string `xml:"appid"`
  80. MchID string `xml:"mch_id"`
  81. DeviceInfo string `xml:"device_info,omitempty"`
  82. NonceStr string `xml:"nonce_str"`
  83. Sign string `xml:"sign"`
  84. SignType string `xml:"sign_type,omitempty"`
  85. Body string `xml:"body"`
  86. Detail string `xml:"detail,omitempty"`
  87. Attach string `xml:"attach,omitempty"` //附加数据
  88. OutTradeNo string `xml:"out_trade_no"` //商户订单号
  89. FeeType string `xml:"fee_type,omitempty"` //标价币种
  90. TotalFee string `xml:"total_fee"` //标价金额
  91. SpbillCreateIP string `xml:"spbill_create_ip"` //终端IP
  92. TimeStart string `xml:"time_start,omitempty"` //交易起始时间
  93. TimeExpire string `xml:"time_expire,omitempty"` //交易结束时间
  94. GoodsTag string `xml:"goods_tag,omitempty"` //订单优惠标记
  95. NotifyURL string `xml:"notify_url"` //通知地址
  96. TradeType string `xml:"trade_type"` //交易类型
  97. ProductID string `xml:"product_id,omitempty"` //商品ID
  98. LimitPay string `xml:"limit_pay,omitempty"` //
  99. OpenID string `xml:"openid,omitempty"` //用户标识
  100. SceneInfo string `xml:"scene_info,omitempty"` //场景信息
  101. }
  102. // NewPay return an instance of Pay package
  103. func NewPay(ctx *context.Context) *Pay {
  104. pay := Pay{Context: ctx}
  105. return &pay
  106. }
  107. // PrePayID will request wechat merchant api and request for a pre payment order id
  108. func (pcf *Pay) PrePayID(p *Params) (prePayID string, err error) {
  109. nonceStr := util.RandomStr(32)
  110. tradeType := "JSAPI"
  111. //tradeType := "MWEB"
  112. template := "appid=%s&body=%s&mch_id=%s&nonce_str=%s&notify_url=%s&openid=%s&out_trade_no=%s&spbill_create_ip=%s&total_fee=%s&trade_type=%s&key=%s"
  113. str := fmt.Sprintf(template, pcf.AppID, p.Body, pcf.PayMchID, nonceStr, pcf.PayNotifyURL, p.OpenID, p.OutTradeNo, p.CreateIP, p.TotalFee, tradeType, pcf.PayKey)
  114. sign := util.MD5Sum(str)
  115. request := payRequest{
  116. AppID: pcf.AppID,
  117. MchID: pcf.PayMchID,
  118. NonceStr: nonceStr,
  119. Sign: sign,
  120. Body: p.Body,
  121. OutTradeNo: p.OutTradeNo,
  122. TotalFee: p.TotalFee,
  123. SpbillCreateIP: p.CreateIP,
  124. NotifyURL: pcf.PayNotifyURL,
  125. TradeType: tradeType,
  126. OpenID: p.OpenID,
  127. }
  128. rawRet, err := util.PostXML(payGateway, request)
  129. if err != nil {
  130. return "", errors.New(err.Error() + " parameters : " + str)
  131. }
  132. payRet := payResult{}
  133. err = xml.Unmarshal(rawRet, &payRet)
  134. s, _ := json.Marshal(payRet)
  135. fmt.Println("return pay msg:", string(s))
  136. if err != nil {
  137. return "", errors.New(err.Error())
  138. }
  139. if payRet.ReturnCode == "SUCCESS" {
  140. //pay success
  141. if payRet.ResultCode == "SUCCESS" {
  142. return payRet.PrePayID, nil
  143. }
  144. return "", errors.New(payRet.ErrCode + payRet.ErrCodeDes)
  145. }
  146. return "", errors.New("[msg : xmlUnmarshalError] [rawReturn : " + string(rawRet) + "] [params : " + str + "] [sign : " + sign + "]")
  147. }
  148. type PayResult struct {
  149. ReturnCode string `xml:"return_code"`
  150. ReturnMsg string `xml:"return_msg"`
  151. AppID string `xml:"appid,omitempty"`
  152. MchID string `xml:"mch_id,omitempty"`
  153. NonceStr string `xml:"nonce_str,omitempty"`
  154. Sign string `xml:"sign,omitempty"`
  155. ResultCode string `xml:"result_code,omitempty"`
  156. TradeType string `xml:"trade_type,omitempty"`
  157. PrePayID string `xml:"prepay_id,omitempty"`
  158. CodeURL string `xml:"code_url,omitempty"`
  159. ErrCode string `xml:"err_code,omitempty"`
  160. ErrCodeDes string `xml:"err_code_des,omitempty"`
  161. }
  162. func (pcf *Pay) PreNative(p *Params) (*PayResult, error) {
  163. nonceStr := util.RandomStr(32)
  164. tradeType := "NATIVE"
  165. template := "appid=%s&attach=%s&body=%s&mch_id=%s&nonce_str=%s&notify_url=%s&out_trade_no=%s&spbill_create_ip=%s&total_fee=%s&trade_type=%s&key=%s"
  166. str := fmt.Sprintf(template, pcf.AppID, p.Attach, p.Body, pcf.PayMchID, nonceStr, pcf.PayNotifyURL, p.OutTradeNo, p.CreateIP, p.TotalFee, tradeType, pcf.PayKey)
  167. sign := util.MD5Sum(str)
  168. request := payRequest{
  169. AppID: pcf.AppID,
  170. MchID: pcf.PayMchID,
  171. NonceStr: nonceStr,
  172. Sign: sign,
  173. Body: p.Body,
  174. OutTradeNo: p.OutTradeNo,
  175. TotalFee: p.TotalFee,
  176. SpbillCreateIP: p.CreateIP,
  177. NotifyURL: pcf.PayNotifyURL,
  178. TradeType: tradeType,
  179. OpenID: p.OpenID,
  180. Attach: p.Attach,
  181. }
  182. rawRet, err := util.PostXML(payGateway, request)
  183. if err != nil {
  184. return nil, errors.New(err.Error() + " parameters : " + str)
  185. }
  186. payRet := PayResult{}
  187. err = xml.Unmarshal(rawRet, &payRet)
  188. s, _ := json.Marshal(payRet)
  189. fmt.Println("return pay msg:", string(s))
  190. if err != nil {
  191. return nil, errors.New(err.Error())
  192. }
  193. if payRet.ReturnCode == "SUCCESS" {
  194. //pay success
  195. if payRet.ResultCode == "SUCCESS" {
  196. return &payRet, nil
  197. }
  198. return nil, errors.New(payRet.ErrCode + payRet.ErrCodeDes)
  199. }
  200. return nil, errors.New("[msg : xmlUnmarshalError] [rawReturn : " + string(rawRet) + "] [params : " + str + "] [sign : " + sign + "]")
  201. }