base.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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 WechatMiniApp, PayAppBase
  12. from apps.web.core.payment 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), WechatMiniApp]
  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 bill_split_rule(self, partition_map):
  103. return self.app.bill_split_rule(partition_map)
  104. def refund_bill_split_list(self, partition_map):
  105. return self.app.refund_bill_split_list(partition_map)
  106. class WithdrawGateway(_PaymentGateway):
  107. """
  108. 提现网关基类
  109. """
  110. LEDGER_PREFIX = 'ledger'
  111. NO_LEDGER_PREFIX = 'noledger'
  112. _WITHDRAW_MAP = {
  113. # 只支持微信提现
  114. PayAppType.WECHAT: 'apps.web.core.payment.wechat.WechatWithdrawGateway'
  115. }
  116. def __init__(self, app, ledger = False):
  117. # type: (cast(PayAppBase), bool)->None
  118. super(WithdrawGateway, self).__init__(app)
  119. self._ledger = ledger
  120. @property
  121. def ledger(self):
  122. return self._ledger
  123. @classmethod
  124. def is_ledger(cls, source_key):
  125. # type: (str)->bool
  126. return source_key.startswith(cls.LEDGER_PREFIX)
  127. @classmethod
  128. def from_withdraw_gateway_key(cls, withdraw_gateway_key, gateway_version):
  129. pay_app = cls.get_app_from_gateway_key(gateway_key = withdraw_gateway_key,
  130. default_pay_app_type = PayAppType.WECHAT) # type: cast(PayAppBase)
  131. return pay_app.new_withdraw_gateway(is_ledger = True, gateway_version = gateway_version)
  132. @property
  133. def manual_withdraw(self):
  134. # type:()->bool
  135. return self.app.manual_withdraw
  136. def get_transfer_result_via_bank(self, order_no):
  137. """
  138. 查询银行卡提现的返回结果
  139. :return:
  140. """
  141. raise NotImplementedError()
  142. def get_transfer_result_via_changes(self, order_no):
  143. """
  144. 查询现金提现的返回结果
  145. :return:
  146. """
  147. raise NotImplementedError()
  148. def withdraw_via_bank(self, order_no, total_amount, bank_card, order_title = u'服务款项'):
  149. """
  150. 提现到银行卡
  151. :return:
  152. """
  153. raise NotImplementedError()
  154. def withdraw_via_changes(self, amount, payOpenId, order_no, real_user_name, subject):
  155. """
  156. 提现到现金
  157. :return:
  158. """
  159. raise NotImplementedError()