virtualpayment.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  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. err = util.DecodeWithError(response, &out, "QueryUserBalance")
  56. return
  57. }
  58. // CurrencyPay currency pay 扣减代币(一般用于代币支付)
  59. func (s *VirtualPayment) CurrencyPay(ctx context.Context, in *CurrencyPayRequest) (out CurrencyPayResponse, err error) {
  60. var jsonByte []byte
  61. if jsonByte, err = json.Marshal(in); err != nil {
  62. return
  63. }
  64. var (
  65. params = URLParams{
  66. Path: currencyPay,
  67. Content: string(jsonByte),
  68. }
  69. address string
  70. )
  71. if address, err = s.requestAddress(params); err != nil {
  72. return
  73. }
  74. var response []byte
  75. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  76. return
  77. }
  78. // 使用通用方法返回错误
  79. err = util.DecodeWithError(response, &out, "CurrencyPay")
  80. return
  81. }
  82. // QueryOrder 查询创建的订单(现金单,非代币单)
  83. func (s *VirtualPayment) QueryOrder(ctx context.Context, in *QueryOrderRequest) (out QueryOrderResponse, err error) {
  84. var jsonByte []byte
  85. if jsonByte, err = json.Marshal(in); err != nil {
  86. return
  87. }
  88. var (
  89. params = URLParams{
  90. Path: queryOrder,
  91. Signature: EmptyString,
  92. Content: string(jsonByte),
  93. }
  94. address string
  95. )
  96. if address, err = s.requestAddress(params); err != nil {
  97. return
  98. }
  99. var response []byte
  100. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  101. return
  102. }
  103. // 使用通用方法返回错误
  104. err = util.DecodeWithError(response, &out, "QueryOrder")
  105. return
  106. }
  107. // CancelCurrencyPay 取消订单 代币支付退款 (currency_pay 接口的逆操作)
  108. func (s *VirtualPayment) CancelCurrencyPay(ctx context.Context, in *CancelCurrencyPayRequest) (out CancelCurrencyPayResponse, err error) {
  109. var jsonByte []byte
  110. if jsonByte, err = json.Marshal(in); err != nil {
  111. return
  112. }
  113. var (
  114. params = URLParams{
  115. Path: cancelCurrencyPay,
  116. Content: string(jsonByte),
  117. }
  118. address string
  119. )
  120. if address, err = s.requestAddress(params); err != nil {
  121. return
  122. }
  123. var response []byte
  124. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  125. return
  126. }
  127. // 使用通用方法返回错误
  128. err = util.DecodeWithError(response, &out, "CancelCurrencyPay")
  129. return
  130. }
  131. // NotifyProvideGoods 通知发货
  132. // 通知已经发货完成(只能通知现金单),正常通过 xpay_goods_deliver_notify 消息推送返回成功就不需要调用这个 api 接口。这个接口用于异常情况推送不成功时手动将单改成已发货状态
  133. func (s *VirtualPayment) NotifyProvideGoods(ctx context.Context, in *NotifyProvideGoodsRequest) (out NotifyProvideGoodsResponse, err error) {
  134. var jsonByte []byte
  135. if jsonByte, err = json.Marshal(in); err != nil {
  136. return
  137. }
  138. var (
  139. params = URLParams{
  140. Path: notifyProvideGoods,
  141. Content: string(jsonByte),
  142. Signature: EmptyString,
  143. }
  144. address string
  145. )
  146. if address, err = s.requestAddress(params); err != nil {
  147. return
  148. }
  149. var response []byte
  150. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  151. return
  152. }
  153. // 使用通用方法返回错误
  154. err = util.DecodeWithError(response, &out, "NotifyProvideGoods")
  155. return
  156. }
  157. // PresentCurrency 代币赠送接口,由于目前不支付按单号查赠送单的功能,所以当需要赠送的时候可以一直重试到返回 0 或者返回 268490004(重复操作)为止
  158. func (s *VirtualPayment) PresentCurrency(ctx context.Context, in *PresentCurrencyRequest) (out PresentCurrencyResponse, err error) {
  159. var jsonByte []byte
  160. if jsonByte, err = json.Marshal(in); err != nil {
  161. return
  162. }
  163. var (
  164. params = URLParams{
  165. Path: presentCurrency,
  166. Content: string(jsonByte),
  167. Signature: EmptyString,
  168. }
  169. address string
  170. )
  171. if address, err = s.requestAddress(params); err != nil {
  172. return
  173. }
  174. var response []byte
  175. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  176. return
  177. }
  178. // 使用通用方法返回错误
  179. err = util.DecodeWithError(response, &out, "PresentCurrency")
  180. return
  181. }
  182. // DownloadBill 下载订单交易账单
  183. func (s *VirtualPayment) DownloadBill(ctx context.Context, in *DownloadBillRequest) (out DownloadBillResponse, err error) {
  184. var jsonByte []byte
  185. if jsonByte, err = json.Marshal(in); err != nil {
  186. return
  187. }
  188. var (
  189. params = URLParams{
  190. Path: downloadBill,
  191. Content: string(jsonByte),
  192. Signature: EmptyString,
  193. }
  194. address string
  195. )
  196. if address, err = s.requestAddress(params); err != nil {
  197. return
  198. }
  199. var response []byte
  200. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  201. return
  202. }
  203. // 使用通用方法返回错误
  204. err = util.DecodeWithError(response, &out, "DownloadBill")
  205. return
  206. }
  207. // RefundOrder 退款 对使用 jsapi 接口下的单进行退款
  208. func (s *VirtualPayment) RefundOrder(ctx context.Context, in *RefundOrderRequest) (out RefundOrderResponse, err error) {
  209. var jsonByte []byte
  210. if jsonByte, err = json.Marshal(in); err != nil {
  211. return
  212. }
  213. var (
  214. params = URLParams{
  215. Path: refundOrder,
  216. Content: string(jsonByte),
  217. Signature: EmptyString,
  218. }
  219. address string
  220. )
  221. if address, err = s.requestAddress(params); err != nil {
  222. return
  223. }
  224. var response []byte
  225. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  226. return
  227. }
  228. // 使用通用方法返回错误
  229. err = util.DecodeWithError(response, &out, "RefundOrder")
  230. return
  231. }
  232. // CreateWithdrawOrder 创建提现单
  233. func (s *VirtualPayment) CreateWithdrawOrder(ctx context.Context, in *CreateWithdrawOrderRequest) (out CreateWithdrawOrderResponse, err error) {
  234. var jsonByte []byte
  235. if jsonByte, err = json.Marshal(in); err != nil {
  236. return
  237. }
  238. var (
  239. params = URLParams{
  240. Path: createWithdrawOrder,
  241. Content: string(jsonByte),
  242. Signature: EmptyString,
  243. }
  244. address string
  245. )
  246. if address, err = s.requestAddress(params); err != nil {
  247. return
  248. }
  249. var response []byte
  250. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  251. return
  252. }
  253. // 使用通用方法返回错误
  254. err = util.DecodeWithError(response, &out, "CreateWithdrawOrder")
  255. return
  256. }
  257. // QueryWithdrawOrder 查询提现单
  258. func (s *VirtualPayment) QueryWithdrawOrder(ctx context.Context, in *QueryWithdrawOrderRequest) (out QueryWithdrawOrderResponse, err error) {
  259. var jsonByte []byte
  260. if jsonByte, err = json.Marshal(in); err != nil {
  261. return
  262. }
  263. var (
  264. params = URLParams{
  265. Path: queryWithdrawOrder,
  266. Content: string(jsonByte),
  267. Signature: EmptyString,
  268. }
  269. address string
  270. )
  271. if address, err = s.requestAddress(params); err != nil {
  272. return
  273. }
  274. var response []byte
  275. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  276. return
  277. }
  278. // 使用通用方法返回错误
  279. err = util.DecodeWithError(response, &out, "QueryWithdrawOrder")
  280. return
  281. }
  282. // StartUploadGoods 开始上传商品
  283. func (s *VirtualPayment) StartUploadGoods(ctx context.Context, in *StartUploadGoodsRequest) (out StartUploadGoodsResponse, err error) {
  284. var jsonByte []byte
  285. if jsonByte, err = json.Marshal(in); err != nil {
  286. return
  287. }
  288. var (
  289. params = URLParams{
  290. Path: startUploadGoods,
  291. Content: string(jsonByte),
  292. Signature: EmptyString,
  293. }
  294. address string
  295. )
  296. if address, err = s.requestAddress(params); err != nil {
  297. return
  298. }
  299. var response []byte
  300. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  301. return
  302. }
  303. // 使用通用方法返回错误
  304. err = util.DecodeWithError(response, &out, "StartUploadGoods")
  305. return
  306. }
  307. // QueryUploadGoods 查询上传商品
  308. func (s *VirtualPayment) QueryUploadGoods(ctx context.Context, in *QueryUploadGoodsRequest) (out QueryUploadGoodsResponse, err error) {
  309. var jsonByte []byte
  310. if jsonByte, err = json.Marshal(in); err != nil {
  311. return
  312. }
  313. var (
  314. params = URLParams{
  315. Path: queryUploadGoods,
  316. Content: string(jsonByte),
  317. Signature: EmptyString,
  318. }
  319. address string
  320. )
  321. if address, err = s.requestAddress(params); err != nil {
  322. return
  323. }
  324. var response []byte
  325. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  326. return
  327. }
  328. // 使用通用方法返回错误
  329. err = util.DecodeWithError(response, &out, "QueryUploadGoods")
  330. return
  331. }
  332. // StartPublishGoods 开始发布商品
  333. func (s *VirtualPayment) StartPublishGoods(ctx context.Context, in *StartPublishGoodsRequest) (out StartPublishGoodsResponse, err error) {
  334. var jsonByte []byte
  335. if jsonByte, err = json.Marshal(in); err != nil {
  336. return
  337. }
  338. var (
  339. params = URLParams{
  340. Path: startPublishGoods,
  341. Content: string(jsonByte),
  342. Signature: EmptyString,
  343. }
  344. address string
  345. )
  346. if address, err = s.requestAddress(params); err != nil {
  347. return
  348. }
  349. var response []byte
  350. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  351. return
  352. }
  353. // 使用通用方法返回错误
  354. err = util.DecodeWithError(response, &out, "StartPublishGoods")
  355. return
  356. }
  357. // QueryPublishGoods 查询发布商品
  358. func (s *VirtualPayment) QueryPublishGoods(ctx context.Context, in *QueryPublishGoodsRequest) (out QueryPublishGoodsResponse, err error) {
  359. var jsonByte []byte
  360. if jsonByte, err = json.Marshal(in); err != nil {
  361. return
  362. }
  363. var (
  364. params = URLParams{
  365. Path: queryPublishGoods,
  366. Content: string(jsonByte),
  367. Signature: EmptyString,
  368. }
  369. address string
  370. )
  371. if address, err = s.requestAddress(params); err != nil {
  372. return
  373. }
  374. var response []byte
  375. if response, err = util.PostJSONContext(ctx, address, in); err != nil {
  376. return
  377. }
  378. // 使用通用方法返回错误
  379. err = util.DecodeWithError(response, &out, "QueryPublishGoods")
  380. return
  381. }
  382. // hmacSha256 hmac sha256
  383. func (s *VirtualPayment) hmacSha256(key, data string) string {
  384. h := hmac.New(sha256.New, []byte(key))
  385. h.Write([]byte(data))
  386. return hex.EncodeToString(h.Sum(nil))
  387. }
  388. // PaySign pay sign
  389. func (s *VirtualPayment) PaySign(url, data string) (string, error) {
  390. if strings.TrimSpace(s.ctx.Config.AppKey) == "" {
  391. return "", errors.New("appKey is empty")
  392. }
  393. return s.hmacSha256(s.ctx.Config.AppKey, url+"&"+data), nil
  394. }
  395. // Signature user signature
  396. func (s *VirtualPayment) Signature(data string) (string, error) {
  397. if strings.TrimSpace(s.sessionKey) == "" {
  398. return "", errors.New("sessionKey is empty")
  399. }
  400. return s.hmacSha256(s.sessionKey, data), nil
  401. }
  402. // PaySignature pay sign and signature
  403. func (s *VirtualPayment) PaySignature(url, data string) (paySign, signature string, err error) {
  404. if paySign, err = s.PaySign(url, data); err != nil {
  405. return
  406. }
  407. if signature, err = s.Signature(data); err != nil {
  408. return
  409. }
  410. return
  411. }
  412. // requestURL .组合 URL
  413. func (s *VirtualPayment) requestAddress(params URLParams) (url string, err error) {
  414. switch params.Path {
  415. case queryUserBalance:
  416. case currencyPay:
  417. case cancelCurrencyPay:
  418. if params.PaySign, params.Signature, err = s.PaySignature(params.Path, params.Content); err != nil {
  419. return
  420. }
  421. case queryOrder:
  422. case notifyProvideGoods:
  423. case presentCurrency:
  424. case downloadBill:
  425. case refundOrder:
  426. case createWithdrawOrder:
  427. case queryWithdrawOrder:
  428. case startUploadGoods:
  429. case queryUploadGoods:
  430. case startPublishGoods:
  431. case queryPublishGoods:
  432. if params.PaySign, err = s.PaySign(params.Path, params.Content); err != nil {
  433. return
  434. }
  435. default:
  436. err = errors.New("path is not exist")
  437. return
  438. }
  439. if params.AccessToken, err = s.ctx.GetAccessToken(); err != nil {
  440. return
  441. }
  442. url = baseSite + params.Path + "?" + accessToken + "=" + params.AccessToken
  443. if params.PaySign != EmptyString {
  444. url += "&" + paySignature + "=" + params.PaySign
  445. }
  446. if params.Signature != EmptyString {
  447. url += "&" + signature + "=" + params.Signature
  448. }
  449. return
  450. }