base.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import random
  5. import uuid
  6. from django.utils.module_loading import import_string
  7. from typing import TYPE_CHECKING, Optional, cast
  8. from apilib.systypes import IterConstant
  9. from apps.web.core import PAY_APP_MAP, APP_KEY_DELIMITER, PayAppType, BaseAppProxy
  10. if TYPE_CHECKING:
  11. from apps.web.core.models import PayAppBase
  12. from apps.web.core.payment.type_checking import PaymentGatewayT
  13. from apps.web.common.transaction.pay import RechargeRecordT
  14. class _PaymentGateway(BaseAppProxy):
  15. def __init__(self, app):
  16. super(_PaymentGateway, self).__init__(app)
  17. @property
  18. def pay_app_type(self):
  19. if hasattr(self.app, 'pay_app_type'):
  20. return getattr(self.app, 'pay_app_type')
  21. else:
  22. raise AttributeError('no __pay_app_type__ attribute')
  23. @property
  24. def gateway_type(self):
  25. if hasattr(self, '__gateway_type__'):
  26. return getattr(self, '__gateway_type__')
  27. else:
  28. raise AttributeError('no __gateway_type__ attribute')
  29. @property
  30. def gateway_key(self):
  31. if hasattr(self.app, '__gateway_key__'):
  32. key = getattr(self.app, '__gateway_key__')
  33. return APP_KEY_DELIMITER.join([self.app.pay_app_type, key])
  34. else:
  35. raise AttributeError('no __gateway_key__ attribute')
  36. @classmethod
  37. def parse_gateway_key(cls, gateway_key, default_pay_app_type = PayAppType.WECHAT):
  38. # type: (str, str)->tuple
  39. tokens = gateway_key.split(APP_KEY_DELIMITER)
  40. if tokens[0] in PayAppType.choices():
  41. return tokens[0], tokens[1], tokens[2:]
  42. else:
  43. return default_pay_app_type, tokens[0], tokens[1:]
  44. @classmethod
  45. def get_app_from_gateway_key(cls, gateway_key, default_pay_app_type = PayAppType.WECHAT):
  46. pay_app_type, occupant_id, tokens = cls.parse_gateway_key(gateway_key, default_pay_app_type)
  47. pay_app_name = PAY_APP_MAP[pay_app_type]
  48. app_cls = import_string(pay_app_name)
  49. factory_fun = getattr(app_cls, '__from_gateway_key__')
  50. app = factory_fun(occupant_id, tokens) # type: Optional[cast(PayAppBase)]
  51. return app
  52. class PaymentGateway(_PaymentGateway):
  53. """
  54. 支付网关基类
  55. """
  56. GATEWAY_PREFIX = 'gk{}'.format(APP_KEY_DELIMITER)
  57. class TradeStatus(IterConstant):
  58. Init = 'init'
  59. Unknown = 'unknown'
  60. Success = 'success'
  61. Paying = 'paying'
  62. Closed = 'closed'
  63. Finished = 'finished'
  64. Cancel = 'cancel'
  65. Refund = 'fefund'
  66. Refunding = 'refunding'
  67. RefundFail = 'refundFail'
  68. Error = 'error'
  69. @classmethod
  70. def from_gateway_key(cls, gateway_type, gateway_key, default_pay_app_type):
  71. pay_app = cls.get_app_from_gateway_key(gateway_key = gateway_key,
  72. default_pay_app_type = default_pay_app_type)
  73. return pay_app.new_gateway(gateway_type)
  74. @classmethod
  75. def clone_from_order(cls, order): # type:(RechargeRecordT) -> PaymentGatewayT
  76. gateway_type = order.my_gateway
  77. gateway_key = order.pay_gateway_key
  78. pay_app_type = order.pay_app_type
  79. pay_app = cls.get_app_from_gateway_key(
  80. gateway_key = gateway_key,
  81. default_pay_app_type = pay_app_type
  82. )
  83. if not pay_app.occupant:
  84. source_cls = import_string('apps.web.{}.models.{}'.format(pay_app.role, pay_app.role.capitalize()))
  85. pay_app.occupant = source_cls.objects.get(id = pay_app.occupantId)
  86. return pay_app.new_gateway(gateway_type)
  87. def api_trade_query(self, out_trade_no = None, trade_no = None):
  88. raise NotImplementedError('sub class must implement api_trade_query')
  89. def refund_to_user(self, out_trade_no, out_refund_no, refund_fee, total_fee, refund_reason, **kwargs):
  90. raise NotImplementedError('sub class must implement refund_to_user')
  91. # service 2位 18, 28 ,38
  92. # product 4位 0001, 0002, 0003
  93. # WF180001201712281209393393822345
  94. @staticmethod
  95. def generate_order_no(service, product):
  96. return '%s%s%s%s%s' % (
  97. service, product, datetime.datetime.now().strftime("%Y%m%d%H%M%S"),
  98. (str(abs(hash(uuid.uuid1())))[0:6]).rjust(6, '0'),
  99. random.randint(1000, 9999))
  100. def withdraw_source_key(self):
  101. return self.occupant.withdraw_source_key(self.app)
  102. def api_refund_query(self, out_refund_no, out_trade_no = None):
  103. raise NotImplementedError('must implement api_refund_query.')
  104. class WithdrawGateway(_PaymentGateway):
  105. """
  106. 提现网关基类
  107. """
  108. LEDGER_PREFIX = 'ledger'
  109. NO_LEDGER_PREFIX = 'noledger'
  110. def __init__(self, app):
  111. # type: (cast(PayAppBase))->None
  112. super(WithdrawGateway, self).__init__(app)
  113. @classmethod
  114. def is_ledger(cls, source_key):
  115. # type: (str)->bool
  116. return source_key.startswith(cls.LEDGER_PREFIX)
  117. @property
  118. def support_withdraw(self):
  119. return self.app.supportWithdraw
  120. @property
  121. def support_withdraw_bank(self):
  122. return self.app.supportWithdrawBank
  123. @classmethod
  124. def from_withdraw_gateway_key(cls, withdraw_gateway_key, gateway_version):
  125. pay_app = cls.get_app_from_gateway_key(gateway_key = withdraw_gateway_key,
  126. default_pay_app_type = PayAppType.WECHAT) # type: cast(PayAppBase)
  127. return pay_app.new_withdraw_gateway(gateway_version = gateway_version)
  128. @property
  129. def manual_withdraw(self):
  130. # type:()->bool
  131. return self.app.manual_withdraw
  132. def get_transfer_result_via_bank(self, order_no):
  133. """
  134. 查询银行卡提现的返回结果
  135. :return:
  136. """
  137. raise NotImplementedError()
  138. def get_transfer_result_via_changes(self, order_no):
  139. """
  140. 查询现金提现的返回结果
  141. :return:
  142. """
  143. raise NotImplementedError()
  144. def withdraw_via_bank(self, order_no, total_amount, bank_card, order_title = u'服务款项'):
  145. """
  146. 提现到银行卡
  147. :return:
  148. """
  149. raise NotImplementedError()
  150. def withdraw_via_changes(self, amount, payOpenId, order_no, real_user_name, subject):
  151. """
  152. 提现到现金
  153. :return:
  154. """
  155. raise NotImplementedError()