__init__.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. import dicttoxml
  5. import six
  6. from django.utils.module_loading import import_string
  7. from typing import TYPE_CHECKING, Optional, cast, Union
  8. from apilib.systypes import IterConstant
  9. from apps.web.constant import AppPlatformType
  10. from library.jdopen import JDOpenErrorCode, BankType, JdOpenException
  11. from library.jd.pay import PiType
  12. from library.ys.base import PayOpt
  13. if TYPE_CHECKING:
  14. from apps.web.core.models import WechatPayApp, PayAppBase, EmbeddedApp, WechatMiniApp
  15. from apps.web.common.models import CapitalUser
  16. logger = logging.getLogger(__name__)
  17. class PayAppType(IterConstant):
  18. # 资金池模式只使用ALIPAY,WECHAT,JD_AGGR三种支付方式
  19. ALIPAY = 'alipay'
  20. WECHAT = 'wechat'
  21. # 蓝牙特殊处理, 使用小程序绑定的支付方式, 后续可以迁移到京东聚合支付
  22. WECHAT_MINI = 'wechat_mini'
  23. # 各经销商可使用的支付方式(包括JD_AGGR以及ALIPAY+WECHAT)
  24. PLATFORM_PROMOTION = "platform_promotion"
  25. PLATFORM_WALLET = 'platform_wallet'
  26. PLATFORM_RECONCILE ='platform_reconcile'
  27. MANUAL = 'manual'
  28. SWAP = 'swap'
  29. LEDGER_CONSUME = "ledgerConsume"
  30. PAY_APP_TYPE_TRANSLATION = {
  31. AppPlatformType.ALIPAY: u'支付宝',
  32. AppPlatformType.WECHAT: u'微信',
  33. PayAppType.WECHAT_MINI: u'微信小程序',
  34. }
  35. PAY_APP_MAP = {
  36. PayAppType.ALIPAY: 'apps.web.core.models.AliApp',
  37. PayAppType.WECHAT: 'apps.web.core.models.WechatPayApp',
  38. PayAppType.WECHAT_MINI: 'apps.web.core.models.WechatMiniApp'
  39. PayAppType.PLATFORM_PROMOTION: 'apps.web.core.models.PlatformPromotionApp',
  40. PayAppType.PLATFORM_WALLET: 'app.web.core.models.PlatformWalletApp',
  41. PayAppType.PLATFORM_RECONCILE: 'app.web.core.models.PlatformReconcileApp',
  42. PayAppType.MANUAL: 'apps.web.core.models.ManualPayApp'
  43. }
  44. APP_KEY_DELIMITER = '-'
  45. wechat_bound_openid_key = lambda appid: APP_KEY_DELIMITER.join([AppPlatformType.WECHAT, appid])
  46. class BaseAppProxy(object):
  47. def __init__(self, app):
  48. # type: (Optional[EmbeddedApp, WechatPayApp])->None
  49. self._app = app # type: Optional[EmbeddedApp, WechatPayApp]
  50. @property
  51. def app(self):
  52. return self._app
  53. @property
  54. def client(self):
  55. if hasattr(self, '__client__'):
  56. return getattr(self, '__client__')
  57. else:
  58. raise AttributeError('no __client__ attribute')
  59. @property
  60. def occupantId(self):
  61. return self._app.occupantId
  62. @property
  63. def occupant(self):
  64. return self._app.occupant
  65. @property
  66. def bound_openid_key(self):
  67. if not hasattr(self, '__bound_openid_key__'):
  68. raise AttributeError('no __bound_openid_key__ attribute')
  69. return getattr(self, '__bound_openid_key__')
  70. @property
  71. def gateway_type(self):
  72. if not hasattr(self, '__gateway_type__'):
  73. raise AttributeError('no __gateway_type__ attribute')
  74. return getattr(self, '__gateway_type__')
  75. @property
  76. def debug(self):
  77. return self._app.debug
  78. @property
  79. def enable(self):
  80. return self._app.enable
  81. @property
  82. def valid(self):
  83. return self._app.valid
  84. class _BaseMixin(object):
  85. @property
  86. def __app_for_inner__(self):
  87. # type: ()->Union[cast(PayAppBase), EmbeddedApp]
  88. if not hasattr(self, 'app'):
  89. raise AttributeError('no app attribute')
  90. return getattr(self, 'app')
  91. @property
  92. def __client_for_inner__(self):
  93. if not hasattr(self, 'client'):
  94. raise AttributeError('no client attribute')
  95. return getattr(self, 'client')
  96. @property
  97. def __bound_openid_key__(self):
  98. raise AttributeError('no __bound_openid_key__ attribute')
  99. class WechatMixin(_BaseMixin):
  100. @property
  101. def appid(self):
  102. return self.__app_for_inner__.appid
  103. @property
  104. def secret(self):
  105. return self.__app_for_inner__.secret
  106. @classmethod
  107. def reply(cls, msg, ok):
  108. # type: (basestring, bool)->str
  109. response = {
  110. "return_code": "SUCCESS" if ok else "FAIL",
  111. "return_msg": msg
  112. }
  113. return cls.serialize_wechat_xml(response)
  114. def check(self, data, order = True, token = True):
  115. return self.__client_for_inner__.check_signature(data)
  116. @classmethod
  117. def serialize_wechat_xml(cls, payload):
  118. # type:(dict)->str
  119. return dicttoxml.dicttoxml(payload, attr_type = False, custom_root = 'xml', cdata = True)
  120. @property
  121. def __bound_openid_key__(self):
  122. return wechat_bound_openid_key(self.appid)
  123. # @property
  124. # def __gateway_type__(self):
  125. # return AppPlatformType.WECHAT
  126. class DlbMixin(_BaseMixin):
  127. @property
  128. def merchant_no(self):
  129. return self.__app_for_inner__.merchant_no
  130. @property
  131. def shop_no(self):
  132. return self.__app_for_inner__.shop_no
  133. @property
  134. def machine_no(self):
  135. return self.__app_for_inner__.machine_no
  136. @property
  137. def access_key(self):
  138. return self.__app_for_inner__.access_key
  139. @property
  140. def secret_key(self):
  141. return self.__app_for_inner__.secret_key
  142. @property
  143. def __bound_openid_key__(self):
  144. # 哆啦宝不需要获取openid
  145. raise AttributeError("no __bound_openid_key__ attribute")
  146. class YsMixin(_BaseMixin):
  147. GATEWAY_TYPE_TO_OPT = {
  148. AppPlatformType.WECHAT: PayOpt.wxPreOrder,
  149. AppPlatformType.ALIPAY: PayOpt.apPreOrder
  150. }
  151. @property
  152. def channel_id(self):
  153. return self.__app_for_inner__.channel_id
  154. @property
  155. def mer_id(self):
  156. return self.__app_for_inner__.mer_id
  157. @property
  158. def term_id(self):
  159. return self.__app_for_inner__.term_id
  160. @property
  161. def work_key(self):
  162. return self.__app_for_inner__.work_key
  163. @property
  164. def __bound_openid_key__(self):
  165. # 接口调用的时候获取openid
  166. raise AttributeError("no __bound_openid_key__ attribute")
  167. class AlipayMixin(_BaseMixin):
  168. """
  169. Alipay 支付网关,扩展原库没有的接口 ``alipay.trade.create``
  170. """
  171. @property
  172. def appid(self):
  173. return self.__app_for_inner__.appid
  174. @property
  175. def app_private_key_string(self):
  176. return self.__app_for_inner__.app_private_key_string
  177. @property
  178. def public_key_string(self):
  179. return self.__app_for_inner__.public_key_string
  180. @property
  181. def public_key_cert_string(self):
  182. return self.__app_for_inner__.public_key_cert_string
  183. @property
  184. def root_cert_string(self):
  185. return self.__app_for_inner__.root_cert_string
  186. @property
  187. def app_public_key_cert_string(self):
  188. return self.__app_for_inner__.app_public_key_cert_string
  189. @property
  190. def aes_encrypt_key(self):
  191. return self.__app_for_inner__.aes_encrypt_key
  192. @property
  193. def signKeyType(self):
  194. return self.__app_for_inner__.signKeyType
  195. @property
  196. def __gateway_type__(self):
  197. return AppPlatformType.ALIPAY
  198. @property
  199. def __bound_openid_key__(self):
  200. # 支付宝用户ID是唯一的,直接以平台类型区分
  201. return AppPlatformType.ALIPAY
  202. @property
  203. def __client__(self):
  204. return self.__app_for_inner__.client
  205. def check(self, data, pop_sign_type = True):
  206. # type:(dict, bool)->bool
  207. sign = data.pop('sign')
  208. if not sign:
  209. logger.error('has no sign')
  210. return False
  211. return self.__client__.verify(data, sign, pop_sign_type)
  212. class ROLEMetaClass(type):
  213. def __setattr__(self, name, value):
  214. raise NotImplementedError()
  215. def __getattr__(self, item):
  216. if item in self.choices():
  217. return item
  218. else:
  219. raise KeyError()
  220. class ROLE(six.with_metaclass(ROLEMetaClass)):
  221. __role_map = {
  222. 'myuser': {
  223. 'module': 'apps.web.user.models.MyUser',
  224. 'sub_type': 'U'
  225. },
  226. 'dealer': {
  227. 'module': 'apps.web.dealer.models.Dealer',
  228. 'sub_type': 'D'
  229. },
  230. 'subaccount': {
  231. 'module': 'apps.web.dealer.models.SubAccount',
  232. 'sub_type': 'B'
  233. },
  234. 'agent': {
  235. 'module': 'apps.web.agent.models.Agent',
  236. 'sub_type': 'A'
  237. },
  238. 'manager': {
  239. 'module': 'apps.web.management.models.Manager',
  240. 'sub_type': 'M'
  241. },
  242. 'supermanager': {
  243. 'module': 'apps.web.superamdin.models.SuperManager',
  244. 'sub_type': 'S'
  245. },
  246. 'advertiser': {
  247. 'module': 'apps.web.ad.models.Advertiser',
  248. 'sub_type': 'V'
  249. },
  250. 'tester': {
  251. 'module': 'apps.web.test.models.Tester',
  252. 'sub_type': 'T'
  253. },
  254. 'advertisement': {
  255. 'module': 'apps.web.ad.models.Advertisement',
  256. 'sub_type': 'T'
  257. },
  258. 'anonymoususer': {
  259. 'module': 'django.contrib.auth.models.AnonymousUser',
  260. 'sub_type': 'N'
  261. },
  262. 'manufacturer': {
  263. 'module': 'apps.web.management.models.Manager',
  264. 'sub_type': 'M'
  265. }
  266. }
  267. def __init__(self):
  268. raise NotImplementedError()
  269. @classmethod
  270. def choices(cls):
  271. return cls.__role_map.keys()
  272. @classmethod
  273. def from_role_id(cls, role, id):
  274. if role == cls.anonymoususer:
  275. return import_string(cls.__role_map[role]['module'])()
  276. else:
  277. return import_string(cls.__role_map[role]['module']).objects(id = id).get()
  278. @classmethod
  279. def sub_type(cls, role):
  280. return cls.__role_map[role]['sub_type']
  281. @classmethod
  282. def sub_type_list(cls):
  283. return [item['sub_type'] for item in cls.__role_map.values()]