wechat_client.go 11 KB

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