wechat_client.go 11 KB

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