wechat_client.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. package gopay
  2. import (
  3. "crypto/tls"
  4. "crypto/x509"
  5. "encoding/xml"
  6. "github.com/parnurzeal/gorequest"
  7. "io/ioutil"
  8. )
  9. type weChatClient struct {
  10. AppId string
  11. MchId string
  12. apiKey string
  13. isProd bool
  14. }
  15. //初始化微信客户端 ok
  16. // appId:应用ID
  17. // MchID:商户ID
  18. // apiKey:API秘钥值
  19. // isProd:是否是正式环境
  20. func NewWeChatClient(appId, mchId, apiKey string, isProd bool) (client *weChatClient) {
  21. client = new(weChatClient)
  22. client.AppId = appId
  23. client.MchId = mchId
  24. client.apiKey = apiKey
  25. client.isProd = isProd
  26. return client
  27. }
  28. //提交付款码支付 ok
  29. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
  30. func (this *weChatClient) Micropay(body BodyMap) (wxRsp *WeChatMicropayResponse, err error) {
  31. var bytes []byte
  32. if this.isProd {
  33. //正式环境
  34. tlsConfig := new(tls.Config)
  35. tlsConfig.InsecureSkipVerify = true
  36. bytes, err = this.doWeChat(body, wxURL_Micropay, tlsConfig)
  37. if err != nil {
  38. return nil, err
  39. }
  40. } else {
  41. bytes, err = this.doWeChat(body, wxURL_SanBox_Micropay)
  42. if err != nil {
  43. return nil, err
  44. }
  45. }
  46. wxRsp = new(WeChatMicropayResponse)
  47. err = xml.Unmarshal(bytes, wxRsp)
  48. if err != nil {
  49. return nil, err
  50. }
  51. return wxRsp, nil
  52. }
  53. //统一下单 ok
  54. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
  55. func (this *weChatClient) UnifiedOrder(body BodyMap) (wxRsp *WeChatUnifiedOrderResponse, err error) {
  56. var bytes []byte
  57. if this.isProd {
  58. //正式环境
  59. tlsConfig := new(tls.Config)
  60. tlsConfig.InsecureSkipVerify = true
  61. bytes, err = this.doWeChat(body, wxURL_UnifiedOrder, tlsConfig)
  62. if err != nil {
  63. return nil, err
  64. }
  65. } else {
  66. body.Set("total_fee", 101)
  67. bytes, err = this.doWeChat(body, wxURL_SanBox_UnifiedOrder)
  68. if err != nil {
  69. return nil, err
  70. }
  71. }
  72. wxRsp = new(WeChatUnifiedOrderResponse)
  73. err = xml.Unmarshal(bytes, wxRsp)
  74. if err != nil {
  75. return nil, err
  76. }
  77. return wxRsp, nil
  78. }
  79. //查询订单 ok
  80. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
  81. func (this *weChatClient) QueryOrder(body BodyMap) (wxRsp *WeChatQueryOrderResponse, err error) {
  82. var bytes []byte
  83. if this.isProd {
  84. //正式环境
  85. tlsConfig := new(tls.Config)
  86. tlsConfig.InsecureSkipVerify = true
  87. bytes, err = this.doWeChat(body, wxURL_OrderQuery, tlsConfig)
  88. if err != nil {
  89. return nil, err
  90. }
  91. } else {
  92. bytes, err = this.doWeChat(body, wxURL_SanBox_OrderQuery)
  93. if err != nil {
  94. return nil, err
  95. }
  96. }
  97. wxRsp = new(WeChatQueryOrderResponse)
  98. err = xml.Unmarshal(bytes, wxRsp)
  99. if err != nil {
  100. return nil, err
  101. }
  102. return wxRsp, nil
  103. }
  104. //关闭订单 ok
  105. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
  106. func (this *weChatClient) CloseOrder(body BodyMap) (wxRsp *WeChatCloseOrderResponse, err error) {
  107. var bytes []byte
  108. if this.isProd {
  109. //正式环境
  110. tlsConfig := new(tls.Config)
  111. tlsConfig.InsecureSkipVerify = true
  112. bytes, err = this.doWeChat(body, wxURL_CloseOrder, tlsConfig)
  113. if err != nil {
  114. return nil, err
  115. }
  116. } else {
  117. bytes, err = this.doWeChat(body, wxURL_SanBox_CloseOrder)
  118. if err != nil {
  119. return nil, err
  120. }
  121. }
  122. wxRsp = new(WeChatCloseOrderResponse)
  123. err = xml.Unmarshal(bytes, wxRsp)
  124. if err != nil {
  125. return nil, err
  126. }
  127. return wxRsp, nil
  128. }
  129. //撤销订单 ok
  130. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3
  131. func (this *weChatClient) Reverse(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp *WeChatReverseResponse, err error) {
  132. var bytes []byte
  133. if this.isProd {
  134. //正式环境
  135. pkcsPool := x509.NewCertPool()
  136. pkcs, err := ioutil.ReadFile(pkcs12FilePath)
  137. if err != nil {
  138. return nil, err
  139. }
  140. pkcsPool.AppendCertsFromPEM(pkcs)
  141. certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
  142. if err != nil {
  143. return nil, err
  144. }
  145. tlsConfig := new(tls.Config)
  146. tlsConfig.Certificates = []tls.Certificate{certificate}
  147. tlsConfig.RootCAs = pkcsPool
  148. tlsConfig.InsecureSkipVerify = true
  149. bytes, err = this.doWeChat(body, wxURL_Reverse, tlsConfig)
  150. if err != nil {
  151. return nil, err
  152. }
  153. } else {
  154. bytes, err = this.doWeChat(body, wxURL_SanBox_Reverse)
  155. if err != nil {
  156. return nil, err
  157. }
  158. }
  159. wxRsp = new(WeChatReverseResponse)
  160. err = xml.Unmarshal(bytes, wxRsp)
  161. if err != nil {
  162. return nil, err
  163. }
  164. return wxRsp, nil
  165. }
  166. //申请退款 ok
  167. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
  168. func (this *weChatClient) Refund(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp *WeChatRefundResponse, err error) {
  169. var bytes []byte
  170. if this.isProd {
  171. //正式环境
  172. pkcsPool := x509.NewCertPool()
  173. pkcs, err := ioutil.ReadFile(pkcs12FilePath)
  174. if err != nil {
  175. return nil, err
  176. }
  177. pkcsPool.AppendCertsFromPEM(pkcs)
  178. certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
  179. if err != nil {
  180. return nil, err
  181. }
  182. tlsConfig := new(tls.Config)
  183. tlsConfig.Certificates = []tls.Certificate{certificate}
  184. tlsConfig.RootCAs = pkcsPool
  185. tlsConfig.InsecureSkipVerify = true
  186. bytes, err = this.doWeChat(body, wxURL_Refund, tlsConfig)
  187. if err != nil {
  188. return nil, err
  189. }
  190. } else {
  191. bytes, err = this.doWeChat(body, wxURL_SanBox_Refund)
  192. if err != nil {
  193. return nil, err
  194. }
  195. }
  196. wxRsp = new(WeChatRefundResponse)
  197. err = xml.Unmarshal(bytes, wxRsp)
  198. if err != nil {
  199. return nil, err
  200. }
  201. return wxRsp, nil
  202. }
  203. //查询退款 ok
  204. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
  205. func (this *weChatClient) QueryRefund(body BodyMap) (wxRsp *WeChatQueryRefundResponse, err error) {
  206. var bytes []byte
  207. if this.isProd {
  208. //正式环境
  209. tlsConfig := new(tls.Config)
  210. tlsConfig.InsecureSkipVerify = true
  211. bytes, err = this.doWeChat(body, wxURL_RefundQuery, tlsConfig)
  212. if err != nil {
  213. return nil, err
  214. }
  215. } else {
  216. bytes, err = this.doWeChat(body, wxURL_SanBox_RefundQuery)
  217. if err != nil {
  218. return nil, err
  219. }
  220. }
  221. wxRsp = new(WeChatQueryRefundResponse)
  222. err = xml.Unmarshal(bytes, wxRsp)
  223. if err != nil {
  224. return nil, err
  225. }
  226. return wxRsp, nil
  227. }
  228. //下载对账单 ok
  229. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6
  230. func (this *weChatClient) DownloadBill(body BodyMap) (wxRsp string, err error) {
  231. var bytes []byte
  232. if this.isProd {
  233. //正式环境
  234. tlsConfig := new(tls.Config)
  235. tlsConfig.InsecureSkipVerify = true
  236. bytes, err = this.doWeChat(body, wxURL_DownloadBill, tlsConfig)
  237. } else {
  238. bytes, err = this.doWeChat(body, wxURL_SanBox_DownloadBill)
  239. }
  240. wxRsp = string(bytes)
  241. if err != nil {
  242. return wxRsp, err
  243. }
  244. return wxRsp, nil
  245. }
  246. //下载资金账单 ok
  247. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_18&index=7
  248. // 好像不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名
  249. func (this *weChatClient) DownloadFundFlow(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp string, err error) {
  250. var bytes []byte
  251. if this.isProd {
  252. //正式环境
  253. pkcsPool := x509.NewCertPool()
  254. pkcs, err := ioutil.ReadFile(pkcs12FilePath)
  255. if err != nil {
  256. return null, err
  257. }
  258. pkcsPool.AppendCertsFromPEM(pkcs)
  259. certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
  260. if err != nil {
  261. return null, err
  262. }
  263. tlsConfig := new(tls.Config)
  264. tlsConfig.Certificates = []tls.Certificate{certificate}
  265. tlsConfig.RootCAs = pkcsPool
  266. tlsConfig.InsecureSkipVerify = true
  267. bytes, err = this.doWeChat(body, wxURL_DownloadFundFlow, tlsConfig)
  268. } else {
  269. bytes, err = this.doWeChat(body, wxURL_SanBox_DownloadFundFlow)
  270. }
  271. if err != nil {
  272. return null, err
  273. }
  274. wxRsp = string(bytes)
  275. return wxRsp, nil
  276. }
  277. //拉取订单评价数据
  278. // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=11
  279. // 好像不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名
  280. func (this *weChatClient) BatchQueryComment(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp string, err error) {
  281. var bytes []byte
  282. if this.isProd {
  283. //正式环境
  284. body.Set("sign_type", SignType_HMAC_SHA256)
  285. pkcsPool := x509.NewCertPool()
  286. pkcs, err := ioutil.ReadFile(pkcs12FilePath)
  287. if err != nil {
  288. return null, err
  289. }
  290. pkcsPool.AppendCertsFromPEM(pkcs)
  291. certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
  292. if err != nil {
  293. return null, err
  294. }
  295. tlsConfig := new(tls.Config)
  296. tlsConfig.Certificates = []tls.Certificate{certificate}
  297. tlsConfig.RootCAs = pkcsPool
  298. tlsConfig.InsecureSkipVerify = true
  299. bytes, err = this.doWeChat(body, wxURL_BatchQueryComment, tlsConfig)
  300. } else {
  301. bytes, err = this.doWeChat(body, wxURL_SanBox_BatchQueryComment)
  302. }
  303. if err != nil {
  304. return null, err
  305. }
  306. wxRsp = string(bytes)
  307. return wxRsp, nil
  308. }
  309. //向微信发送请求 ok
  310. func (this *weChatClient) doWeChat(body BodyMap, url string, tlsConfig ...*tls.Config) (bytes []byte, err error) {
  311. var sign string
  312. body.Set("appid", this.AppId)
  313. body.Set("mch_id", this.MchId)
  314. //===============生成参数===================
  315. if !this.isProd {
  316. //沙箱环境
  317. body.Set("sign_type", SignType_MD5)
  318. //从微信接口获取SanBoxSignKey
  319. key, err := getSanBoxSign(this.MchId, body.Get("nonce_str"), this.apiKey, body.Get("sign_type"))
  320. if err != nil {
  321. return nil, err
  322. }
  323. sign = getLocalSign(key, body.Get("sign_type"), body)
  324. } else {
  325. //正式环境
  326. //本地计算Sign
  327. sign = getLocalSign(this.apiKey, body.Get("sign_type"), body)
  328. }
  329. body.Set("sign", sign)
  330. reqXML := generateXml(body)
  331. //===============发起请求===================
  332. agent := gorequest.New()
  333. if this.isProd && tlsConfig != nil {
  334. agent.TLSClientConfig(tlsConfig[0])
  335. }
  336. agent.Post(url)
  337. agent.Type("xml")
  338. agent.SendString(reqXML)
  339. _, bytes, errs := agent.EndBytes()
  340. if len(errs) > 0 {
  341. return nil, errs[0]
  342. }
  343. return bytes, nil
  344. }