pay.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. from __future__ import unicode_literals
  4. import logging
  5. from library import random_string
  6. from apilib.systypes import IterConstant
  7. from library.jdopen import JDOpenErrorCode
  8. from library.jdbase.exceptions import JDException, JDRequestException
  9. from library.jdopen.client import JdOpenBaseClient
  10. logger = logging.getLogger(__name__)
  11. __all__ = ('JDOpenPay')
  12. class SubOrderType(IterConstant):
  13. NORMAL = 'NORMAL'
  14. LEDGER = 'LEDGER'
  15. class JDOpenPay(JdOpenBaseClient):
  16. API_BASE_URL = 'https://openapi.duolabao.com'
  17. def __init__(self, agentNum, accessKey, secretKey, customerNum, shopNum, timeout = None, auto_retry = True):
  18. super(JDOpenPay, self).__init__(agentNum, accessKey, secretKey, timeout, auto_retry)
  19. self.customerNum = customerNum
  20. self.shopNum = shopNum
  21. def create_pay_url(self, out_trade_no, total_fee, notify_url, extraInfo = None, **kwargs):
  22. """
  23. 创建主扫链接
  24. :param out_trade_no:
  25. :param total_fee:
  26. :param notify_url:
  27. :param extraInfo:
  28. :param kwargs: 接受tableNum, machineNum参数
  29. :return:
  30. """
  31. url = '/v3/order/payurl/create'
  32. data = {
  33. 'agentNum': self.agentNum,
  34. 'customerNum': self.customerNum,
  35. 'shopNum': self.shopNum,
  36. 'requestNum': out_trade_no,
  37. 'amount': total_fee,
  38. 'callbackUrl': notify_url,
  39. 'source': 'API',
  40. 'payModel': 'ONCE'
  41. }
  42. if extraInfo:
  43. data.update({
  44. 'extraInfo': extraInfo
  45. })
  46. data.update(kwargs)
  47. result = self.post(url = url, data = data)
  48. return result['data']['url']
  49. def unified_order(self, authId, bankType, requestNum, amount, callbackUrl, subject, ledgerRule = None,
  50. extraInfo = None, **kwargs):
  51. """
  52. :param authId:
  53. :param bankType:
  54. :param requestNum:
  55. :param amount:
  56. :param callbackUrl:
  57. :param subject:
  58. :param ledgerRule:
  59. :param extraInfo:
  60. :param kwargs: 接受ledgerRule
  61. {
  62. "ledgerType":"FIXED", -- 分账类型 固定金额:FIXED 比例:RATE 目前只支持固定金额分账
  63. "ledgerFeeAssume":"RECEIVER", 收款方承担:RECEIVER 分账方承担:LEDGER 默认为RECEIVER,目前仅支持收款方承担手续费
  64. "list":[ -- 分账list必须全部包括收款方和分账方,分账金额保留两位小数,且分账金额相加需等于订单金额
  65. {
  66. "customerNum":"10001115199597283447316",
  67. "amount":"0.01"
  68. },
  69. {
  70. "customerNum":"10001114504252653551690",
  71. "amount":"0.01"
  72. }
  73. ]
  74. }
  75. :return:
  76. """
  77. def processor(self, result):
  78. if 'success' not in result or not result['success'] or result['code'] != 'success':
  79. raise JDException(
  80. errCode = result.get('code'),
  81. errMsg = result.get("msg"),
  82. client = self)
  83. else:
  84. return result
  85. url = '/api/createPayWithCheck'
  86. data = {
  87. 'version': 'V4.0',
  88. 'agentNum': str(self.agentNum),
  89. 'customerNum': str(self.customerNum),
  90. # 'shopNum': str(self.shopNum),
  91. 'authCode': str(authId),
  92. 'bankType': str(bankType),
  93. 'requestNum': str(requestNum),
  94. 'orderAmount': str(amount),
  95. 'callbackUrl': str(callbackUrl),
  96. 'payModel': 'ONCE',
  97. 'orderType': 'SALES',
  98. 'payType': 'ACTIVE',
  99. 'bussinessType': 'QRCODE_TRAD',
  100. 'source': 'API',
  101. 'paySource': str(bankType)
  102. }
  103. if self.shopNum:
  104. data.update({
  105. 'shopNum': str(self.shopNum)
  106. })
  107. if ledgerRule:
  108. data.update({
  109. 'subOrderType': str(SubOrderType.LEDGER),
  110. 'LedgerRequest': ledgerRule
  111. })
  112. else:
  113. data.update({
  114. 'subOrderType': str(SubOrderType.NORMAL)
  115. })
  116. data.update(kwargs)
  117. if extraInfo:
  118. data.update({
  119. 'extraInfo': extraInfo
  120. })
  121. return self.post(url = url, data = data, processor = processor)
  122. def api_trade_query(self, out_trade_no = None):
  123. def processor(self, result):
  124. if 'success' not in result or not result['success']:
  125. raise JDException(
  126. errCode = result.get('code'),
  127. errMsg = result.get("msg"),
  128. client = self)
  129. if 'queryOrderInfoRes' not in result:
  130. raise JDException(
  131. errCode = 'QUERY_ORDER_FAILURE',
  132. errMsg = u'查询订单信息失败',
  133. client = self)
  134. if not result['queryOrderInfoRes']['success']:
  135. raise JDException(
  136. errCode = result['queryOrderInfoRes']['code'],
  137. errMsg = result['queryOrderInfoRes']['msg'],
  138. client = self)
  139. return result
  140. url = '/api/queryOrderPayDetail'
  141. data = {
  142. 'customerNum': self.customerNum,
  143. }
  144. if out_trade_no:
  145. data.update({'requestNum': out_trade_no})
  146. else:
  147. raise JDException(
  148. errCode = JDOpenErrorCode.MY_INVALID_PARAMETER, errMsg = u'缺少requestNum参数')
  149. return self.post(url = url, data = data, processor = processor)
  150. def api_trade_refund(self, outTradeNo, outRefundNo, amount, **kwargs):
  151. """
  152. 退款
  153. :param outTradeNo:
  154. :param outRefundNo:
  155. :param amount:
  156. :param kwargs:
  157. :return:
  158. """
  159. def processor(self, result):
  160. if 'result' not in result or not result['result'] or result['resultCode'] != 'success':
  161. raise JDRequestException(
  162. errCode = result.get('resultCode'),
  163. errMsg = result.get("message"),
  164. client = self)
  165. else:
  166. return result
  167. url = '/api/refundByRequestNum'
  168. data = {
  169. 'requestVersion': 'V4.0',
  170. 'agentNum': self.agentNum,
  171. 'customerNum': self.customerNum,
  172. # 'shopNum': self.shopNum,
  173. 'requestNum': outTradeNo,
  174. 'refundRequestNum': outRefundNo,
  175. 'refundPartAmount': amount
  176. }
  177. if self.shopNum:
  178. data.update({
  179. 'shopNum': self.shopNum
  180. })
  181. ledgerInfoList = kwargs.pop('ledgerInfoList', None)
  182. if ledgerInfoList:
  183. data.update({'list': ledgerInfoList})
  184. data.update(kwargs)
  185. return self.post(url = url, data = data, processor = processor)
  186. def api_refund_query(self, out_refund_no):
  187. """
  188. 查询退款订单
  189. :param out_refund_no: 退款单号
  190. :return:
  191. """
  192. def processor(self, result):
  193. if 'success' not in result or not result['success'] or result['code'] != 'success':
  194. raise JDRequestException(
  195. errCode = result.get('code'),
  196. errMsg = result.get("msg"),
  197. client = self)
  198. else:
  199. return result
  200. url = '/api/queryRefundOrderByRequestNum'
  201. data = {
  202. 'agentNum': self.agentNum,
  203. 'customerNum': self.customerNum,
  204. # 'shopNum': self.shopNum
  205. }
  206. if self.shopNum:
  207. data.update({
  208. 'shopNum': self.shopNum
  209. })
  210. if out_refund_no:
  211. data.update({
  212. 'requestNum': out_refund_no
  213. })
  214. else:
  215. raise JDException(
  216. errCode = JDOpenErrorCode.MY_INVALID_PARAMETER, errMsg = u'缺少退款订单号参数')
  217. nonce_str = random_string(32)
  218. if self.shopNum:
  219. _str = 'agentnum={}&customernum={}&nonce_str={}&requestnum={}&shopnum={}&key=xrtbvjJk213W'.format(
  220. self.agentNum, self.customerNum, nonce_str, out_refund_no, self.shopNum)
  221. else:
  222. _str = 'agentnum={}&customernum={}&nonce_str={}&requestnum={}&key=xrtbvjJk213W'.format(
  223. self.agentNum, self.customerNum, nonce_str, out_refund_no)
  224. import hashlib
  225. sign = hashlib.md5(_str.encode('utf-8')).hexdigest().upper()
  226. data.update({
  227. 'nonce_str': nonce_str,
  228. 'sign': sign
  229. })
  230. return self.post(url = url, data = data, processor = processor)
  231. def add_wechat_auth_pay_dir(self, auth_pay_dir):
  232. """
  233. 追加商户微信支付目录
  234. :param auth_pay_dir:
  235. :return:
  236. """
  237. def processor(self, result):
  238. if 'success' not in result or not result['success'] or result['code'] != 'success':
  239. raise JDException(
  240. errCode = result.get('code'),
  241. errMsg = result.get("message"),
  242. client = self)
  243. else:
  244. return result
  245. url = '/api/addAuthPayDirsDevConfig'
  246. data = {
  247. 'customerNum': self.customerNum,
  248. 'authPayDir': auth_pay_dir
  249. }
  250. return self.post(url = url, data = data, processor = processor)
  251. def query_wechat_auth_pay_dir(self, batch_num):
  252. """
  253. 查询商户微信支付目录
  254. :param batch_num:
  255. :return:
  256. """
  257. def processor(self, result):
  258. if 'success' not in result or not result['success'] or result['code'] != 'success':
  259. raise JDException(
  260. errCode = result.get('code'),
  261. errMsg = result.get("message"),
  262. client = self)
  263. else:
  264. return result
  265. url = '/api/queryAddAuthPayDirsDevConfigByBatchNum'
  266. data = {
  267. 'customerNum': self.customerNum,
  268. 'batchNum': batch_num
  269. }
  270. return self.post(url = url, data = data, processor = processor)
  271. def api_close_order(self, requestNum):
  272. def processor(self, result):
  273. if 'success' not in result or not result['success'] or result['code'] != 'success':
  274. raise JDException(
  275. errCode = result.get('code'),
  276. errMsg = result.get("msg"),
  277. client = self)
  278. else:
  279. return result
  280. url = '/api/close'
  281. data = {
  282. 'agentNum': self.agentNum,
  283. 'customerNum': self.customerNum,
  284. 'requestNum': requestNum
  285. }
  286. return self.post(url = url, data = data, processor = processor)
  287. def api_cancel_order(self, requestNum):
  288. def processor(self, result):
  289. if 'success' not in result or not result['success'] or result['code'] != 'success':
  290. raise JDException(
  291. errCode = result.get('code'),
  292. errMsg = result.get("msg"),
  293. client = self)
  294. else:
  295. return result
  296. url = '/api/cancel'
  297. data = {
  298. 'agentNum': self.agentNum,
  299. 'customerNum': self.customerNum,
  300. 'requestNum': requestNum
  301. }
  302. return self.post(url = url, data = data, processor = processor)
  303. def download_bill(self, bill_date):
  304. url = '/v1/agent/checkaccountfile/download'
  305. data = {
  306. 'agentNum': self.agentNum,
  307. 'customerNum': self.customerNum,
  308. 'billType': 'QRBILL',
  309. 'date': bill_date
  310. }
  311. result = self.post(url = url, data = data)
  312. return result['data']['downloadUrl']