dlbpay.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. from __future__ import unicode_literals
  4. import hashlib
  5. import time
  6. from collections import OrderedDict
  7. import requests
  8. import simplejson as json
  9. import six
  10. from typing import Dict
  11. from library import to_binary, to_text
  12. from .exceptions import DlbPayException
  13. class DlbPay(object):
  14. PAY_HOST_DEV = 'https://openapi.duolabao.com'
  15. PAY_HOST = 'https://openapi.duolabao.com'
  16. def __str__(self):
  17. _repr = '{kclass}(customerNum: {customerNum}, shopNum: {shopNum})'.format(
  18. kclass = self.__class__.__name__,
  19. customerNum = self.customerNum,
  20. shopNum = self.shopNum)
  21. if six.PY2:
  22. return to_binary(_repr)
  23. else:
  24. return to_text(_repr)
  25. def __repr__(self):
  26. return str(self)
  27. def __init__(self, customerNum, shopNum, accessKey, secretKey, machineNum = None, debug = False):
  28. self.customerNum = customerNum
  29. self.shopNum = shopNum
  30. self.machineNum = machineNum
  31. self.secretKey = secretKey
  32. self.accessKey = accessKey
  33. self.debug = debug
  34. if self.debug:
  35. self.host_url = DlbPay.PAY_HOST_DEV
  36. else:
  37. self.host_url = DlbPay.PAY_HOST
  38. def token(self, data):
  39. # type: (OrderedDict)->str
  40. s = '&'.join([u'{}={}'.format(k, v) for k, v in data.iteritems()])
  41. s = s.encode('utf-8')
  42. return hashlib.sha1(s).hexdigest().upper()
  43. def get(self, endpoint, data = None, **kwargs):
  44. def make_token(data, uri, timestamp):
  45. sign_data = OrderedDict()
  46. sign_data['secretKey'] = self.secretKey
  47. sign_data['timestamp'] = timestamp
  48. sign_data['path'] = uri
  49. return self.token(sign_data)
  50. timestamp = str(int(time.time()))
  51. headers = {
  52. 'accessKey': self.accessKey,
  53. 'timestamp': timestamp,
  54. 'token': make_token(data, endpoint, timestamp)
  55. }
  56. return self._request(
  57. method = 'get',
  58. endpoint = endpoint,
  59. data = data,
  60. headers = headers,
  61. **kwargs)
  62. def post(self, endpoint, data = None, **kwargs):
  63. def make_token(data, uri, timestamp):
  64. sign_data = OrderedDict()
  65. sign_data['secretKey'] = self.secretKey
  66. sign_data['timestamp'] = timestamp
  67. sign_data['path'] = uri
  68. sign_data['body'] = json.dumps(data, ensure_ascii = False).encode('utf-8')
  69. return self.token(sign_data)
  70. timestamp = str(int(time.time()))
  71. headers = {
  72. 'accessKey': self.accessKey,
  73. 'timestamp': timestamp,
  74. 'token': make_token(data, endpoint, timestamp)
  75. }
  76. return self._request(
  77. method = 'post',
  78. endpoint = endpoint,
  79. data = data,
  80. headers = headers,
  81. **kwargs
  82. )
  83. def _decode_result(self, res):
  84. try:
  85. result = json.loads(res.content.decode('utf-8', 'ignore'), strict = False)
  86. except (TypeError, ValueError):
  87. return res
  88. return result
  89. def _handle_result(self, res, method = None, url = None,
  90. result_processor = None, **kwargs):
  91. if not isinstance(res, dict):
  92. result = self._decode_result(res)
  93. else:
  94. result = res
  95. if result['result'] == 'fail' or result['result'] == 'error':
  96. raise DlbPayException(
  97. errorCode = result['error']['errorCode'] if 'error' in result and 'errorCode' in result[
  98. 'error'] else '',
  99. errorMsg = result['error']['errorMsg'] if 'error' in result and 'errorMsg' in result[
  100. 'error'] else u'未知错误',
  101. client = self,
  102. request = None,
  103. response = None)
  104. return result if not result_processor else result_processor(result)
  105. def _request(self, method, endpoint, data = None, headers = None, **kwargs):
  106. url = '{base}{endpoint}'.format(
  107. base = self.host_url,
  108. endpoint = endpoint
  109. )
  110. if headers:
  111. headers.update({'Content-Type': 'application/json'})
  112. else:
  113. headers = {'Content-Type': 'application/json'}
  114. if data:
  115. body = json.dumps(data, ensure_ascii = False)
  116. body = body.encode('utf-8')
  117. kwargs['data'] = body
  118. kwargs['timeout'] = kwargs.get('timeout', 15)
  119. result_processor = kwargs.pop('result_processor', None)
  120. with requests.sessions.Session() as session:
  121. res = session.request(
  122. url = url,
  123. method = method,
  124. headers = headers,
  125. **kwargs)
  126. try:
  127. res.raise_for_status()
  128. except requests.RequestException as reqe:
  129. raise DlbPayException(
  130. errorCode = res.status_code,
  131. errorMsg = reqe.message,
  132. client = self,
  133. request = reqe.request,
  134. response = reqe.response
  135. )
  136. return self._handle_result(
  137. res, method, url, result_processor, **kwargs
  138. )
  139. def create_pay_url(self, requestNum, amount, notify_url, front_url = None, extraInfo = None):
  140. # type: (str, str, str, Dict)->str
  141. """
  142. 创建支付链接
  143. """
  144. data = {
  145. 'customerNum': self.customerNum,
  146. 'shopNum': self.shopNum,
  147. 'requestNum': requestNum,
  148. 'amount': amount,
  149. 'source': 'API',
  150. 'callbackUrl': notify_url
  151. }
  152. if self.machineNum:
  153. data.update({'machineNum': self.machineNum})
  154. if front_url:
  155. data.update({'completeUrl': front_url})
  156. if extraInfo:
  157. data.update({'extraInfo': extraInfo})
  158. result = self.post(endpoint = '/v1/customer/order/payurl/create',
  159. data = data)
  160. return result['data']['url']
  161. def query_trade_result(self, trade_no = None, out_trade_no = None):
  162. if out_trade_no:
  163. endpoint = '/v2/customer/order/payresult/{}/{}/{}'.format(self.customerNum, self.shopNum, out_trade_no)
  164. elif trade_no:
  165. endpoint = '/v2/customer/order/payresult/{}/{}/with/{}'.format(self.customerNum, self.shopNum, trade_no)
  166. else:
  167. assert False, u'trade_no和out_trade_no至少有一个不能为空'
  168. result = self.get(endpoint)
  169. return result['data']