virtualpayment.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /*
  2. * Copyright silenceper/wechat Author(https://silenceper.com/wechat/). All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * You can obtain one at https://github.com/silenceper/wechat.
  17. *
  18. */
  19. package virtualpayment
  20. import (
  21. "context"
  22. "crypto/hmac"
  23. "crypto/sha256"
  24. "encoding/hex"
  25. "encoding/json"
  26. "errors"
  27. "strings"
  28. "github.com/silenceper/wechat/v2/util"
  29. )
  30. // SetSessionKey 设置 sessionKey
  31. func (s *VirtualPayment) SetSessionKey(sessionKey string) {
  32. s.sessionKey = sessionKey
  33. }
  34. // QueryUserBalance 查询虚拟支付余额
  35. func (s *VirtualPayment) QueryUserBalance(ctx context.Context, in *QueryUserBalanceRequest) (out *QueryUserBalanceResponse, err error) {
  36. var jsonByte []byte
  37. if jsonByte, err = json.Marshal(in); err != nil {
  38. return
  39. }
  40. var (
  41. params = URLParams{
  42. Path: queryUserBalance,
  43. Content: string(jsonByte),
  44. }
  45. address string
  46. )
  47. if address, err = s.requestAddress(params); err != nil {
  48. return
  49. }
  50. var response []byte
  51. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  52. return
  53. }
  54. // 使用通用方法返回错误
  55. if err = util.DecodeWithError(response, out, "QueryUserBalance"); err != nil {
  56. return
  57. }
  58. return
  59. }
  60. // CurrencyPay currency pay 扣减代币(一般用于代币支付)
  61. func (s *VirtualPayment) CurrencyPay(ctx context.Context, in *CurrencyPayRequest) (out *CurrencyPayResponse, err error) {
  62. var jsonByte []byte
  63. if jsonByte, err = json.Marshal(in); err != nil {
  64. return
  65. }
  66. var (
  67. params = URLParams{
  68. Path: currencyPay,
  69. Content: string(jsonByte),
  70. }
  71. address string
  72. )
  73. if address, err = s.requestAddress(params); err != nil {
  74. return
  75. }
  76. var response []byte
  77. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  78. return
  79. }
  80. // 使用通用方法返回错误
  81. if err = util.DecodeWithError(response, out, "CurrencyPay"); err != nil {
  82. return
  83. }
  84. return
  85. }
  86. // QueryOrder 查询创建的订单(现金单,非代币单)
  87. func (s *VirtualPayment) QueryOrder(ctx context.Context, in *QueryOrderRequest) (out *QueryOrderResponse, err error) {
  88. var jsonByte []byte
  89. if jsonByte, err = json.Marshal(in); err != nil {
  90. return
  91. }
  92. var (
  93. params = URLParams{
  94. Path: queryOrder,
  95. Signature: EmptyString,
  96. Content: string(jsonByte),
  97. }
  98. address string
  99. )
  100. if address, err = s.requestAddress(params); err != nil {
  101. return
  102. }
  103. var response []byte
  104. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  105. return
  106. }
  107. // 使用通用方法返回错误
  108. if err = util.DecodeWithError(response, out, "QueryOrder"); err != nil {
  109. return
  110. }
  111. return
  112. }
  113. // CancelCurrencyPay 取消订单 代币支付退款 (currency_pay 接口的逆操作)
  114. func (s *VirtualPayment) CancelCurrencyPay(ctx context.Context, in *CancelCurrencyPayRequest) (out *CancelCurrencyPayResponse, err error) {
  115. var jsonByte []byte
  116. if jsonByte, err = json.Marshal(in); err != nil {
  117. return
  118. }
  119. var (
  120. params = URLParams{
  121. Path: cancelCurrencyPay,
  122. Content: string(jsonByte),
  123. }
  124. address string
  125. )
  126. if address, err = s.requestAddress(params); err != nil {
  127. return
  128. }
  129. var response []byte
  130. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  131. return
  132. }
  133. // 使用通用方法返回错误
  134. if err = util.DecodeWithError(response, out, "CancelCurrencyPay"); err != nil {
  135. return
  136. }
  137. return
  138. }
  139. // NotifyProvideGoods 通知发货
  140. // 通知已经发货完成(只能通知现金单),正常通过 xpay_goods_deliver_notify 消息推送返回成功就不需要调用这个 api 接口。这个接口用于异常情况推送不成功时手动将单改成已发货状态
  141. func (s *VirtualPayment) NotifyProvideGoods(ctx context.Context, in *NotifyProvideGoodsRequest) (out *NotifyProvideGoodsResponse, err error) {
  142. var jsonByte []byte
  143. if jsonByte, err = json.Marshal(in); err != nil {
  144. return
  145. }
  146. var (
  147. params = URLParams{
  148. Path: notifyProvideGoods,
  149. Content: string(jsonByte),
  150. Signature: EmptyString,
  151. }
  152. address string
  153. )
  154. if address, err = s.requestAddress(params); err != nil {
  155. return
  156. }
  157. var response []byte
  158. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  159. return
  160. }
  161. // 使用通用方法返回错误
  162. if err = util.DecodeWithError(response, out, "NotifyProvideGoods"); err != nil {
  163. return
  164. }
  165. return
  166. }
  167. // PresentCurrency 代币赠送接口,由于目前不支付按单号查赠送单的功能,所以当需要赠送的时候可以一直重试到返回 0 或者返回 268490004(重复操作)为止
  168. func (s *VirtualPayment) PresentCurrency(ctx context.Context, in *PresentCurrencyRequest) (out *PresentCurrencyResponse, err error) {
  169. var jsonByte []byte
  170. if jsonByte, err = json.Marshal(in); err != nil {
  171. return
  172. }
  173. var (
  174. params = URLParams{
  175. Path: presentCurrency,
  176. Content: string(jsonByte),
  177. Signature: EmptyString,
  178. }
  179. address string
  180. )
  181. if address, err = s.requestAddress(params); err != nil {
  182. return
  183. }
  184. var response []byte
  185. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  186. return
  187. }
  188. // 使用通用方法返回错误
  189. if err = util.DecodeWithError(response, out, "PresentCurrency"); err != nil {
  190. return
  191. }
  192. return
  193. }
  194. // DownloadBill 下载订单交易账单
  195. func (s *VirtualPayment) DownloadBill(ctx context.Context, in *DownloadBillRequest) (out *DownloadBillResponse, err error) {
  196. var jsonByte []byte
  197. if jsonByte, err = json.Marshal(in); err != nil {
  198. return
  199. }
  200. var (
  201. params = URLParams{
  202. Path: downloadBill,
  203. Content: string(jsonByte),
  204. Signature: EmptyString,
  205. }
  206. address string
  207. )
  208. if address, err = s.requestAddress(params); err != nil {
  209. return
  210. }
  211. var response []byte
  212. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  213. return
  214. }
  215. // 使用通用方法返回错误
  216. if err = util.DecodeWithError(response, out, "DownloadBill"); err != nil {
  217. return
  218. }
  219. return
  220. }
  221. // RefundOrder 退款 对使用 jsapi 接口下的单进行退款
  222. func (s *VirtualPayment) RefundOrder(ctx context.Context, in *RefundOrderRequest) (out *RefundOrderResponse, err error) {
  223. var jsonByte []byte
  224. if jsonByte, err = json.Marshal(in); err != nil {
  225. return
  226. }
  227. var (
  228. params = URLParams{
  229. Path: refundOrder,
  230. Content: string(jsonByte),
  231. Signature: EmptyString,
  232. }
  233. address string
  234. )
  235. if address, err = s.requestAddress(params); err != nil {
  236. return
  237. }
  238. var response []byte
  239. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  240. return
  241. }
  242. // 使用通用方法返回错误
  243. if err = util.DecodeWithError(response, out, "RefundOrder"); err != nil {
  244. return
  245. }
  246. return
  247. }
  248. // CreateWithdrawOrder 创建提现单
  249. func (s *VirtualPayment) CreateWithdrawOrder(ctx context.Context, in *CreateWithdrawOrderRequest) (out *CreateWithdrawOrderResponse, err error) {
  250. var jsonByte []byte
  251. if jsonByte, err = json.Marshal(in); err != nil {
  252. return
  253. }
  254. var (
  255. params = URLParams{
  256. Path: createWithdrawOrder,
  257. Content: string(jsonByte),
  258. Signature: EmptyString,
  259. }
  260. address string
  261. )
  262. if address, err = s.requestAddress(params); err != nil {
  263. return
  264. }
  265. var response []byte
  266. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  267. return
  268. }
  269. // 使用通用方法返回错误
  270. if err = util.DecodeWithError(response, out, "CreateWithdrawOrder"); err != nil {
  271. return
  272. }
  273. return
  274. }
  275. // QueryWithdrawOrder 查询提现单
  276. func (s *VirtualPayment) QueryWithdrawOrder(ctx context.Context, in *QueryWithdrawOrderRequest) (out *QueryWithdrawOrderResponse, err error) {
  277. var jsonByte []byte
  278. if jsonByte, err = json.Marshal(in); err != nil {
  279. return
  280. }
  281. var (
  282. params = URLParams{
  283. Path: queryWithdrawOrder,
  284. Content: string(jsonByte),
  285. Signature: EmptyString,
  286. }
  287. address string
  288. )
  289. if address, err = s.requestAddress(params); err != nil {
  290. return
  291. }
  292. var response []byte
  293. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  294. return
  295. }
  296. // 使用通用方法返回错误
  297. if err = util.DecodeWithError(response, out, "QueryWithdrawOrder"); err != nil {
  298. return
  299. }
  300. return
  301. }
  302. // StartUploadGoods 开始上传商品
  303. func (s *VirtualPayment) StartUploadGoods(ctx context.Context, in *StartUploadGoodsRequest) (out *StartUploadGoodsResponse, err error) {
  304. var jsonByte []byte
  305. if jsonByte, err = json.Marshal(in); err != nil {
  306. return
  307. }
  308. var (
  309. params = URLParams{
  310. Path: startUploadGoods,
  311. Content: string(jsonByte),
  312. Signature: EmptyString,
  313. }
  314. address string
  315. )
  316. if address, err = s.requestAddress(params); err != nil {
  317. return
  318. }
  319. var response []byte
  320. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  321. return
  322. }
  323. // 使用通用方法返回错误
  324. if err = util.DecodeWithError(response, out, "StartUploadGoods"); err != nil {
  325. return
  326. }
  327. return
  328. }
  329. // QueryUploadGoods 查询上传商品
  330. func (s *VirtualPayment) QueryUploadGoods(ctx context.Context, in *QueryUploadGoodsRequest) (out *QueryUploadGoodsResponse, err error) {
  331. var jsonByte []byte
  332. if jsonByte, err = json.Marshal(in); err != nil {
  333. return
  334. }
  335. var (
  336. params = URLParams{
  337. Path: queryUploadGoods,
  338. Content: string(jsonByte),
  339. Signature: EmptyString,
  340. }
  341. address string
  342. )
  343. if address, err = s.requestAddress(params); err != nil {
  344. return
  345. }
  346. var response []byte
  347. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  348. return
  349. }
  350. // 使用通用方法返回错误
  351. if err = util.DecodeWithError(response, out, "QueryUploadGoods"); err != nil {
  352. return
  353. }
  354. return
  355. }
  356. // StartPublishGoods 开始发布商品
  357. func (s *VirtualPayment) StartPublishGoods(ctx context.Context, in *StartPublishGoodsRequest) (out *StartPublishGoodsResponse, err error) {
  358. var jsonByte []byte
  359. if jsonByte, err = json.Marshal(in); err != nil {
  360. return
  361. }
  362. var (
  363. params = URLParams{
  364. Path: startPublishGoods,
  365. Content: string(jsonByte),
  366. Signature: EmptyString,
  367. }
  368. address string
  369. )
  370. if address, err = s.requestAddress(params); err != nil {
  371. return
  372. }
  373. var response []byte
  374. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  375. return
  376. }
  377. // 使用通用方法返回错误
  378. if err = util.DecodeWithError(response, out, "StartPublishGoods"); err != nil {
  379. return
  380. }
  381. return
  382. }
  383. // QueryPublishGoods 查询发布商品
  384. func (s *VirtualPayment) QueryPublishGoods(ctx context.Context, in *QueryPublishGoodsRequest) (out *QueryPublishGoodsResponse, err error) {
  385. var jsonByte []byte
  386. if jsonByte, err = json.Marshal(in); err != nil {
  387. return
  388. }
  389. var (
  390. params = URLParams{
  391. Path: queryPublishGoods,
  392. Content: string(jsonByte),
  393. Signature: EmptyString,
  394. }
  395. address string
  396. )
  397. if address, err = s.requestAddress(params); err != nil {
  398. return
  399. }
  400. var response []byte
  401. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  402. return
  403. }
  404. // 使用通用方法返回错误
  405. if err = util.DecodeWithError(response, out, "QueryPublishGoods"); err != nil {
  406. return
  407. }
  408. return
  409. }
  410. // hmacSha256 hmac sha256
  411. func (s *VirtualPayment) hmacSha256(key, data string) string {
  412. h := hmac.New(sha256.New, []byte(key))
  413. h.Write([]byte(data))
  414. return hex.EncodeToString(h.Sum(nil))
  415. }
  416. // PaySign pay sign
  417. func (s *VirtualPayment) PaySign(url, data string) (string, error) {
  418. if strings.TrimSpace(s.ctx.Config.AppKey) == "" {
  419. return "", errors.New("appKey is empty")
  420. }
  421. return s.hmacSha256(s.ctx.Config.AppKey, url+"&"+data), nil
  422. }
  423. // Signature user signature
  424. func (s *VirtualPayment) Signature(data string) (string, error) {
  425. if strings.TrimSpace(s.sessionKey) == "" {
  426. return "", errors.New("sessionKey is empty")
  427. }
  428. return s.hmacSha256(s.sessionKey, data), nil
  429. }
  430. // PaySignature pay sign and signature
  431. func (s *VirtualPayment) PaySignature(url, data string) (paySign, signature string, err error) {
  432. if paySign, err = s.PaySign(url, data); err != nil {
  433. return
  434. }
  435. if signature, err = s.Signature(data); err != nil {
  436. return
  437. }
  438. return
  439. }
  440. // requestURL .组合 URL
  441. func (s *VirtualPayment) requestAddress(params URLParams) (url string, err error) {
  442. switch params.Path {
  443. case queryUserBalance:
  444. case currencyPay:
  445. case cancelCurrencyPay:
  446. if params.PaySign, params.Signature, err = s.PaySignature(params.Path, params.Content); err != nil {
  447. return
  448. }
  449. case queryOrder:
  450. case notifyProvideGoods:
  451. case presentCurrency:
  452. case downloadBill:
  453. case refundOrder:
  454. case createWithdrawOrder:
  455. case queryWithdrawOrder:
  456. case startUploadGoods:
  457. case queryUploadGoods:
  458. case startPublishGoods:
  459. case queryPublishGoods:
  460. if params.PaySign, err = s.PaySign(params.Path, params.Content); err != nil {
  461. return
  462. }
  463. default:
  464. err = errors.New("path is not exist")
  465. return
  466. }
  467. if params.AccessToken, err = s.ctx.GetAccessToken(); err != nil {
  468. return
  469. }
  470. url = baseSite + params.Path + "?" + accessToken + "=" + params.AccessToken
  471. if params.PaySign != EmptyString {
  472. url += "&" + paySignature + "=" + params.PaySign
  473. }
  474. if params.Signature != EmptyString {
  475. url += "&" + signature + "=" + params.Signature
  476. }
  477. return
  478. }