jssdk.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package pay
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "time"
  6. "unicode/utf8"
  7. "github.com/yaotian/gowechat/mch/base"
  8. "github.com/yaotian/gowechat/util"
  9. )
  10. //OrderInput 下单
  11. //官网文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
  12. type OrderInput struct {
  13. OpenID string //trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
  14. Body string //String(128)
  15. OutTradeNum string //String(32) 20150806125346 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
  16. TotalFee int //分为单位
  17. IP string
  18. NotifyURL string //异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
  19. ProductID string //trade_type=NATIVE时(即扫码支付),此参数必传
  20. tradeType string //JSAPI,NATIVE,APP
  21. }
  22. //SetTradeType 设置TradeType
  23. func (c *OrderInput) setTradeType(tradeType string) {
  24. c.tradeType = tradeType
  25. }
  26. //WxPayInfo 统一下单后,返回的信息,这些信息是前端jssdk支付时需要的配置
  27. type WxPayInfo struct {
  28. AppID string `json:"appId"`
  29. TimeStamp string `json:"timeStamp"`
  30. NonceStr string `json:"nonceStr"`
  31. Package string `json:"package"`
  32. SignType string `json:"signType"`
  33. PaySign string `json:"paySign"`
  34. resultMap map[string]string
  35. }
  36. //ToString wx.chooseWXPay content
  37. func (c *WxPayInfo) ToString() (str string) {
  38. return fmt.Sprintf(`
  39. timestamp: %s,
  40. nonceStr: '%s',
  41. package: '%s',
  42. signType: '%s',
  43. paySign: '%s',
  44. `, c.TimeStamp, c.NonceStr, c.Package, c.SignType, c.PaySign)
  45. }
  46. //ToJSON WeixinJSBridge json content
  47. func (c *WxPayInfo) ToJSON() (str string) {
  48. js, err := json.Marshal(c)
  49. if err == nil {
  50. return string(js)
  51. }
  52. return
  53. }
  54. //ToMap result map[string]string
  55. func (c *WxPayInfo) ToMap() (m map[string]string) {
  56. return c.resultMap
  57. }
  58. /*GetJsAPIConfig 前端JsAPI支付时,需要提交的信息
  59. */
  60. func (c *Pay) GetJsAPIConfig(order OrderInput) (config *WxPayInfo, err error) {
  61. order.setTradeType("JSAPI")
  62. err = c.checkOrder(order)
  63. if err != nil {
  64. return
  65. }
  66. var prepayID string
  67. prepayID, err = c.getPrepayID(order)
  68. if err != nil {
  69. return
  70. }
  71. nocestr := util.RandomStr(8)
  72. timestamp := fmt.Sprint(time.Now().Unix())
  73. result := make(map[string]string)
  74. result["appId"] = c.AppID
  75. result["timeStamp"] = timestamp
  76. result["nonceStr"] = nocestr
  77. result["package"] = "prepay_id=" + prepayID
  78. result["signType"] = "MD5"
  79. sign := base.Sign(result, c.MchAPIKey, nil)
  80. result["paySign"] = sign
  81. config = new(WxPayInfo)
  82. config.NonceStr = util.RandomStr(8)
  83. config.TimeStamp = fmt.Sprint(time.Now().Unix())
  84. config.AppID = c.AppID
  85. config.Package = "prepay_id=" + prepayID
  86. config.SignType = "MD5"
  87. config.PaySign = sign
  88. config.resultMap = result
  89. return
  90. }
  91. //GetNativePayQrcodePicURL native支付时二维码图片的url
  92. func (c *Pay) GetNativePayQrcodePicURL(order OrderInput) (qrcodeURL string, err error) {
  93. order.setTradeType("NATIVE")
  94. input := c.createUnifiedOrderMap(order)
  95. var result map[string]string
  96. if result, err = c.UnifiedOrder(input); err == nil { //有prepay_id
  97. qrcodeURL = result["code_url"]
  98. if len(qrcodeURL) == 0 {
  99. err = fmt.Errorf("native pay Qrcode url is empty")
  100. }
  101. }
  102. return
  103. }
  104. // 调用 UnifiedOrder 获得 prepayID
  105. func (c *Pay) getPrepayID(order OrderInput) (prepayID string, err error) {
  106. input := c.createUnifiedOrderMap(order)
  107. var result map[string]string
  108. if result, err = c.UnifiedOrder(input); err == nil { //有prepay_id
  109. prepayID := result["prepay_id"]
  110. if prepayID != "" {
  111. return prepayID, nil
  112. }
  113. err = fmt.Errorf("prepayID is empty")
  114. }
  115. return
  116. }
  117. func (c *Pay) createUnifiedOrderMap(order OrderInput) (input map[string]string) {
  118. input = make(map[string]string)
  119. input["appid"] = c.AppID //设置微信分配的公众账号ID
  120. input["mch_id"] = c.MchID //设置微信支付分配的商户号
  121. input["nonce_str"] = util.RandomStr(5) //设置随机字符串,不长于32位。推荐随机数生成算法
  122. input["body"] = order.Body //获取商品或支付单简要描述的值
  123. input["out_trade_no"] = order.OutTradeNum //设置商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
  124. input["total_fee"] = util.ToStr(order.TotalFee) //设置订单总金额,只能为整数,详见支付金额
  125. input["spbill_create_ip"] = order.IP //设置APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
  126. input["notify_url"] = order.NotifyURL //设置接收微信支付异步通知回调地址
  127. input["trade_type"] = order.tradeType
  128. //设置取值如下:JSAPI,NATIVE,APP,详细说明见参数规定
  129. if order.ProductID != "" {
  130. input["product_id"] = order.ProductID //这个
  131. }
  132. input["openid"] = order.OpenID //设置trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid
  133. //sign
  134. sign := base.Sign(input, c.MchAPIKey, nil)
  135. input["sign"] = sign
  136. return
  137. }
  138. func (c *Pay) checkOrder(order OrderInput) (err error) {
  139. tradeType := order.tradeType
  140. if tradeType != "JSAPI" && tradeType != "APP" && tradeType != "NATIVE" {
  141. return fmt.Errorf("tradeType is invalid")
  142. }
  143. if tradeType == "NATIVE" {
  144. if order.ProductID == "" {
  145. err = fmt.Errorf("Native TradeType need ProductID")
  146. return
  147. }
  148. }
  149. if tradeType == "JSAPI" {
  150. if order.OpenID == "" {
  151. err = fmt.Errorf("OpenID can not be empty when pay mode is JSAPI")
  152. return
  153. }
  154. }
  155. if utf8.RuneCountInString(order.Body) > 128 || order.Body == "" {
  156. err = fmt.Errorf("Body is invalid. Size can not exceed 128.")
  157. return
  158. }
  159. if utf8.RuneCountInString(order.OutTradeNum) > 32 || order.OutTradeNum == "" {
  160. err = fmt.Errorf("OutTradeNum is invalid. Size can not exceed 128.")
  161. return
  162. }
  163. if order.TotalFee <= 0 {
  164. err = fmt.Errorf("Order TotalFee is invalid.")
  165. return
  166. }
  167. if order.IP == "" {
  168. err = fmt.Errorf("Order IP is invalid.")
  169. return
  170. }
  171. if order.NotifyURL == "" {
  172. err = fmt.Errorf("Notify URL is invalid.")
  173. return
  174. }
  175. return
  176. }