wechat_client.go 11 KB

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