__init__.py 6.2 KB

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