__init__.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import string
  5. from mongoengine import NotUniqueError
  6. from typing import TYPE_CHECKING
  7. from apilib.systypes import IntEnum, IterConstant, StrEnum
  8. from apilib.utils_string import get_random_str
  9. from apps.web.core.exceptions import WithdrawError
  10. if TYPE_CHECKING:
  11. from apps.web.common.models import CapitalUser, WithdrawRecord
  12. class WithdrawStatus(IntEnum):
  13. SUBMITTED = 0
  14. SUCCEEDED = 1
  15. PROCESSING = 2
  16. FAILED = 3
  17. CLOSED = 4
  18. REFUND = 5
  19. BANK_PROCESSION = 6
  20. @classmethod
  21. def finished_status(cls):
  22. return [cls.SUCCEEDED, cls.CLOSED, cls.REFUND]
  23. class WITHDRAW_PAY_TYPE(IterConstant):
  24. BANK = 'bank'
  25. WECHAT = 'wechat'
  26. WITHDRAW_STATE_TRANSLATION = {
  27. WithdrawStatus.SUBMITTED: u'已提交申请',
  28. WithdrawStatus.SUCCEEDED: u'提现成功',
  29. WithdrawStatus.PROCESSING: u'提现申请已经受理',
  30. WithdrawStatus.FAILED: u'提现失败',
  31. WithdrawStatus.CLOSED: u'关闭',
  32. WithdrawStatus.REFUND: u'银行退单',
  33. WithdrawStatus.BANK_PROCESSION: u'银行正在处理中'
  34. }
  35. class WithdrawResult(object):
  36. def __init__(self, result, err_code, refund, message, show_message):
  37. self.result = result
  38. self.err_code = err_code
  39. self.refund = refund
  40. self.message = message
  41. self.show_message = show_message
  42. def __repr__(self):
  43. return 'WithdrawResult<errCode=%s, message=%s>' % (self.err_code, self.message)
  44. class WithdrawHandler(object):
  45. def __init__(self, payee, record):
  46. # type: (CapitalUser, WithdrawRecord) -> None
  47. self.payee = payee
  48. self.record = record
  49. def on_revoke(self):
  50. pass
  51. def revoke(self, remarks, description):
  52. # type:(basestring, basestring)->None
  53. """
  54. 提现失败返回金额到提现账户。在以下场景调用:
  55. 1、提现账户提现报错,并且明确一定是失败的情况下自动调用;
  56. 2、资金平台确认订单失败,调用该接口退款
  57. """
  58. from apps.web.common.models import WithdrawRefundRecord
  59. from apps.web.common.errors import DuplicatedRefundRecordFound
  60. try:
  61. WithdrawRefundRecord(withdraw_record_id = self.record.id).save()
  62. except NotUniqueError:
  63. raise DuplicatedRefundRecordFound('record = %r' % (self.record,))
  64. success = self.record.revoke(remarks, description)
  65. if success:
  66. self.payee.recover_frozen_balance(
  67. self.record.incomeType,
  68. self.record.amount,
  69. self.record.source_key,
  70. self.record.order)
  71. self.on_revoke()
  72. else:
  73. raise WithdrawError(u'更新提现状态失败(1001)')
  74. def on_approve(self):
  75. pass
  76. def approve(self, **kwargs):
  77. if 'remarks' not in kwargs:
  78. kwargs['remarks'] = u'提现成功'
  79. if 'description' not in kwargs:
  80. kwargs['description'] = u'提现成功'
  81. success = self.record.succeed(**kwargs)
  82. if success:
  83. self.payee.clear_frozen_balance(self.record.order)
  84. self.on_approve()
  85. else:
  86. raise WithdrawError(u'更新提现状态失败(1002)')
  87. def on_fail(self):
  88. pass
  89. def fail(self, remarks, description):
  90. success = self.record.fail(remarks, description)
  91. if success:
  92. self.on_fail()
  93. else:
  94. raise WithdrawError(u'更新提现状态失败(1003)')
  95. def on_processing(self):
  96. pass
  97. def processing(self, remarks, description):
  98. success = self.record.set_processing(remarks = remarks, description = description)
  99. if success:
  100. self.on_processing()
  101. else:
  102. raise WithdrawError(u'更新提现状态失败(1005)')
  103. def on_bank_processing(self):
  104. pass
  105. def bank_processing(self, **kwargs):
  106. success = self.record.set_bank_processing(**kwargs)
  107. if success:
  108. self.on_bank_processing()
  109. else:
  110. raise WithdrawError(u'更新提现状态失败(1006)')
  111. def translate_withdraw_state(state):
  112. return WITHDRAW_STATE_TRANSLATION.get(state)
  113. class OrderNoMaker(object):
  114. @classmethod
  115. def _order_prefix(cls, main_type, sub_type):
  116. return '{}{}'.format(main_type, sub_type)
  117. @classmethod
  118. def make_order_no_32(cls, identifier, main_type, sub_type):
  119. # type: (str, str, str)->str
  120. # time:14,prefix:2,identifier:13,resered:3
  121. return '{time}{prefix}{identifier}{reserved}'.format(
  122. time = datetime.datetime.now().strftime("%Y%m%d%H%M%S"),
  123. prefix = cls._order_prefix(main_type, sub_type),
  124. identifier = '{:0>13}'.format(identifier[-13:]).upper(),
  125. reserved = get_random_str(3, string.digits + string.uppercase))
  126. class OrderMainType(StrEnum):
  127. PAY = 'P' # 支付订单
  128. CONSUME = 'C' # 消费订单
  129. REFUND = 'R' # 退款单
  130. WITHDRAW = 'W' # 提现单
  131. '''
  132. UserPaySubType - 用户支付订单子类型
  133. DealerPaySubType - 经销商支付订单子类型
  134. 子类型在UserPaySubType和DealerPaySubType中是唯一的, 不能重复
  135. '''
  136. class RefundSubType(IterConstant):
  137. REFUND = 'R'
  138. class UserPaySubType(IterConstant):
  139. QUICK_PAY = 'Q'
  140. RECHARGE = 'R'
  141. CARD_RECHARGE = 'C'
  142. TERMINAL_RECHARGE = 'T' # 中创将模块作为终端充值机使用(实际是设备充值)
  143. VIRTUAL_CARD_RECHARGE_RECORD = 'V'
  144. CASH_PAY = 'P' # 后付费订单直接支付
  145. MONTHLY_PACKAGE = "M"
  146. INSURANCE = "I"
  147. REDPACK = 'F'
  148. MIX = "X"
  149. CARD_RECHARGE_RECORD = 'D' # 這個不是真正的訂單類型
  150. SWAP = 'W'
  151. class DealerPaySubType(IterConstant):
  152. SIM_CARD = 'S'
  153. AUTO_SIM_CARD = 'U'
  154. MANUAL_SIM_CARD = 'M'
  155. JOIN = 'J'
  156. API_COST = 'A'
  157. DISABLE_AD = 'D'
  158. SIM_ORDER_VERIFY = 'V'
  159. class UserConsumeSubType(StrEnum):
  160. """
  161. 消费订单子类型
  162. """
  163. NETPAY = 'N' # 自助设备消费
  164. CARD = 'C'
  165. COIN = 'I'
  166. VIRTUAL_CARD = 'V' # 虚拟卡消费