pay.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package pay
  2. import (
  3. "errors"
  4. "crypto/md5"
  5. "encoding/xml"
  6. "fmt"
  7. "strings"
  8. "github.com/silenceper/wechat/context"
  9. "github.com/silenceper/wechat/util"
  10. )
  11. var payGateway = "https://api.mch.weixin.qq.com/pay/unifiedorder"
  12. // Pay struct extends context
  13. type Pay struct {
  14. *context.Context
  15. }
  16. // 传入的参数,用于生成 prepay_id 的必需参数
  17. // PayParams was NEEDED when request unifiedorder
  18. type PayParams struct {
  19. TotalFee string
  20. CreateIP string
  21. Body string
  22. OutTradeNo string
  23. OpenID string
  24. }
  25. // PayConfig 是传出用于 jsdk 用的参数
  26. type PayConfig struct {
  27. Timestamp int64
  28. NonceStr string
  29. PrePayID string
  30. SignType string
  31. Sign string
  32. }
  33. // payResult 是 unifie order 接口的返回
  34. type payResult struct {
  35. ReturnCode string `xml:"return_code"`
  36. ReturnMsg string `xml:"return_msg"`
  37. AppID string `xml:"appid,omitempty"`
  38. MchID string `xml:"mch_id,omitempty"`
  39. NonceStr string `xml:"nonce_str,omitempty"`
  40. Sign string `xml:"sign,omitempty"`
  41. ResultCode string `xml:"result_code,omitempty"`
  42. TradeType string `xml:"trade_type,omitempty"`
  43. PrePayID string `xml:"prepay_id,omitempty"`
  44. CodeURL string `xml:"code_url,omitempty"`
  45. ErrCode string `xml:"err_code,omitempty"`
  46. ErrCodeDes string `xml:"err_code_des,omitempty"`
  47. }
  48. //payRequest 接口请求参数
  49. type payRequest struct {
  50. AppID string `xml:"appid"`
  51. MchID string `xml:"mch_id"`
  52. NotifyUrl string `xml:"notify_url"` //通知地址
  53. DeviceInfo string `xml:"device_info,omitempty"`
  54. NonceStr string `xml:"nonce_str"`
  55. Sign string `xml:"sign"`
  56. SignType string `xml:"sign_type,omitempty"`
  57. Body string `xml:"body"`
  58. Detail string `xml:"detail,omitempty"`
  59. Attach string `xml:"attach,omitempty"` //附加数据
  60. OutTradeNo string `xml:"out_trade_no"` //商户订单号
  61. FeeType string `xml:"fee_type,omitempty"` //标价币种
  62. TotalFee string `xml:"total_fee"` //标价金额
  63. SpbillCreateIp string `xml:"spbill_create_ip"` //终端IP
  64. TimeStart string `xml:"time_start,omitempty"` //交易起始时间
  65. TimeExpire string `xml:"time_expire,omitempty"` //交易结束时间
  66. GoodsTag string `xml:"goods_tag,omitempty"` //订单优惠标记
  67. TradeType string `xml:"trade_type"` //交易类型
  68. ProductId string `xml:"product_id,omitempty"` //商品ID
  69. LimitPay string `xml:"limit_pay,omitempty"` //
  70. OpenID string `xml:"openid,omitempty"` //用户标识
  71. SceneInfo string `xml:"scene_info,omitempty"` //场景信息
  72. }
  73. // NewPay return an instance of Pay package
  74. func NewPay(ctx *context.Context) *Pay {
  75. pay := Pay{Context: ctx}
  76. return &pay
  77. }
  78. // PrePayId will request wechat merchant api and request for a pre payment order id
  79. func (pcf *Pay) PrePayId(p *PayParams) (prePayID string, err error) {
  80. nonceStr := util.RandomStr(32)
  81. pType := "JSAPI"
  82. template := "appid=%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"
  83. str := fmt.Sprintf(template, pcf.AppID, p.Body, pcf.PayMchID, nonceStr, pcf.PayNotifyURL, p.OutTradeNo, p.CreateIP, p.TotalFee, pType)
  84. str += pcf.PayKey
  85. sum := md5.Sum([]byte(str))
  86. signature := string(sum[:])
  87. sign := strings.ToUpper(signature)
  88. request := payRequest{
  89. AppID: pcf.AppID,
  90. MchID: pcf.PayMchID,
  91. NotifyUrl: pcf.PayNotifyURL,
  92. NonceStr: util.RandomStr(32),
  93. Sign: sign,
  94. Body: p.Body,
  95. OutTradeNo: p.OutTradeNo,
  96. TotalFee: p.TotalFee,
  97. SpbillCreateIp: p.CreateIP,
  98. OpenID: p.OpenID,
  99. }
  100. rawRet, err := util.PostXML(payGateway, request)
  101. if err != nil {
  102. return "", err
  103. }
  104. payRet := payResult{}
  105. err = xml.Unmarshal(rawRet, &payRet)
  106. if err != nil {
  107. return "", errors.New(err.Error())
  108. }
  109. if payRet.ReturnCode == "SUCCESS" {
  110. //pay success
  111. if payRet.ResultCode == "SUCCESS" {
  112. return payRet.PrePayID, nil
  113. }
  114. return "", errors.New(payRet.ErrCode + payRet.ErrCodeDes)
  115. } else {
  116. return "", errors.New("xml unmarshal err : raw - " + string(rawRet))
  117. }
  118. }