transaction_deprecated.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. from django.conf import settings
  6. from typing import TYPE_CHECKING
  7. from apilib.monetary import RMB, Percent
  8. from apilib.utils_sys import memcache_lock
  9. from apps.web.common.transaction.refund import RefundCashMixin
  10. from apps.web.dealer.define import DealerConst
  11. from apps.web.dealer.models import RefundDealerRechargeRecord, DealerRechargeRecord, Dealer
  12. from apps.web.dealer.proxy import record_income_proxy
  13. from apps.web.exceptions import UserServerException
  14. from apps.web.user.models import RechargeRecord
  15. logger = logging.getLogger(__name__)
  16. if TYPE_CHECKING:
  17. pass
  18. def refund_post_pay(refundOrder, success):
  19. pass
  20. def refund_cash_to_dealer(dealer_recharge_record, refundFee, isInsure = False):
  21. if dealer_recharge_record.product == DealerRechargeRecord.ProductType.SimCard:
  22. return RefundSimRecharge(dealer_recharge_record, refundFee, u'流量卡退款').execute(
  23. frozen_callable = None, refund_callable = refund_post_pay)
  24. elif dealer_recharge_record.product == DealerRechargeRecord.ProductType.AutoSimCard:
  25. # return RefundSimWallet(dealer_recharge_record, refundFee).execute()
  26. # else:
  27. raise UserServerException(u'目前不支持该种类型订单退款')
  28. else:
  29. raise UserServerException(u'目前不支持该种类型订单退款')
  30. class RefundCash(RefundCashMixin):
  31. MAX_LEDGER_CHECK_TIME = 15 # 最长的查询分账时间
  32. def __init__(self, rechargeOrder, refundFee, reason): # type:(DealerRechargeRecord, RMB, basestring) -> None
  33. super(RefundCash, self).__init__(rechargeOrder, refundFee)
  34. self.reason = reason
  35. def pre_check(self):
  36. """
  37. 退款的预检查
  38. :return:
  39. """
  40. # 首先检查退款的金额 原则上退款金额不能小于0 不能大于交易定安的金额
  41. if self.refundFee <= RMB(0) or self.refundFee > self.totalFee:
  42. raise UserServerException(u"退费金额错误")
  43. refundOrder = RefundDealerRechargeRecord.objects.filter(
  44. rechargeObjId = self.paySubOrder.id).first() # type: RefundDealerRechargeRecord
  45. if refundOrder:
  46. if refundOrder.is_successful:
  47. raise UserServerException(u"该单已经退单")
  48. else:
  49. raise UserServerException(u"订单正在退款中")
  50. def execute(self, frozen_callable, refund_callable, notify_url = None):
  51. """
  52. 执行退款的动作
  53. :return:
  54. """
  55. lockKey = "refund_dealer_recharge_cash_{}".format(self.paySubOrder.id)
  56. with memcache_lock(key = lockKey, value = self.paySubOrder.id, expire = 360) as acquired:
  57. if not acquired:
  58. raise UserServerException(u"退款订单正在处理,等订单结束后,您才能再次重试哦")
  59. self.pre_check()
  60. refundOrder = RefundDealerRechargeRecord.issue(self.payOrder, self.refundFee)
  61. logger.info('refund paras: {} {}'.format(refundOrder.orderNo, self.refund_paras))
  62. refundOrder.processing()
  63. try:
  64. self.submit_refund(
  65. refundOrder, None, self.reason, notify_url or refundOrder.notify_url, refund_callable)
  66. except Exception:
  67. import traceback
  68. logger.warning(
  69. 'Refund request failure! orderNo = {}; e = {}'.format(refundOrder.orderNo, traceback.format_exc()))
  70. finally:
  71. pass
  72. return refundOrder
  73. class RefundSimRecharge(RefundCash):
  74. def pre_check(self):
  75. super(RefundSimRecharge, self).pre_check()
  76. for partition in self.payOrder.settleInfo['partition']:
  77. if partition['id'] != settings.MY_PRIMARY_AGENT_ID and partition['earned'] > 0:
  78. raise UserServerException(u'目前仅支持不分账情况下退账。')
  79. class RefundInsureRecharge(RefundCash):
  80. def pre_check(self):
  81. if self.refundFee <= RMB(0) or self.refundFee > self.totalFee: # DealerChargeRecord中的退费总额是负数
  82. raise UserServerException(u"退费金额错误")
  83. # 目前只支持一个单,退一次,不允许退多次
  84. refundOrder = RefundDealerRechargeRecord.objects.filter(
  85. rechargeObjId = self.paySubOrder.id).first() # type: RefundDealerRechargeRecord
  86. if refundOrder:
  87. if refundOrder.is_successful:
  88. raise UserServerException(u"该单已经退单")
  89. else:
  90. raise UserServerException(u"订单正在退款中")
  91. class RefundToWallet(object):
  92. def __init__(self, rechargeOrder, refundFee, reason, subType, via, source):
  93. self._payOrder = rechargeOrder
  94. self.refundFee = refundFee
  95. self.reason = reason
  96. self.subType = subType
  97. self.via = via
  98. self.source = source
  99. @property
  100. def outTradeNo(self):
  101. """
  102. 交易单号
  103. :return:
  104. """
  105. return self._payOrder.orderNo
  106. @property
  107. def totalFee(self):
  108. return RMB(round(float(self._payOrder.totalFee) / 100, 2))
  109. @property
  110. def payOrder(self):
  111. """
  112. 交易订单 即支付订单 与第三方系统产生关联的订单
  113. :return:
  114. """
  115. return self._payOrder
  116. def pre_check(self):
  117. """
  118. 退款的预检查
  119. :return:
  120. """
  121. # 首先检查退款的金额 原则上退款金额不能小于0 不能大于交易定安的金额
  122. if self.refundFee <= RMB(0) or self.refundFee > self.totalFee:
  123. raise UserServerException(u"退费金额错误")
  124. # 检查退款订单是否已经存在 是否已经退款成功
  125. refundOrder = RefundDealerRechargeRecord.objects.filter(
  126. rechargeObjId = self._payOrder.id).first() # type: RefundDealerRechargeRecord
  127. if refundOrder:
  128. if refundOrder.is_successful:
  129. raise UserServerException(u"该单已经退单")
  130. else:
  131. raise UserServerException(u"订单正在退款中")
  132. def refund_dealer_balance(self, refundFee):
  133. source_key = self._payOrder.withdraw_source_key
  134. income_type = DealerConst.MAP_SOURCE_TO_TYPE[self.source]
  135. dealer = Dealer.objects(id = self._payOrder.dealerId).first()
  136. if not dealer:
  137. return False
  138. fundKey = dealer.fund_key(income_type = income_type, source_key = source_key)
  139. queryFilter = {
  140. '_id': dealer.id
  141. }
  142. update = {
  143. '$inc': {'{fundKey}.balance'.format(fundKey = fundKey): (refundFee).mongo_amount}
  144. }
  145. result = Dealer.get_collection().update_one(queryFilter, update, upsert = False)
  146. if result.matched_count == 1 and result.modified_count == 1:
  147. return True
  148. else:
  149. return False
  150. def execute(self, refund_callable = None):
  151. """
  152. 执行退款的动作
  153. :return:
  154. """
  155. lockKey = "refund_dealer_recharge_wallet_{}".format(self._payOrder.id)
  156. with memcache_lock(key = lockKey, value = self._payOrder.id, expire = 360) as acquired:
  157. if not acquired:
  158. raise UserServerException(u"退款订单正在处理,等订单结束后,您才能再次重试哦")
  159. self.pre_check()
  160. refundOrder = RefundDealerRechargeRecord.issue(self.payOrder, self.refundFee)
  161. logger.info(
  162. 'refund[wallet], order = %s out_refund_no=%s, out_trade_no=%s, refund_fee=%s, total_fee=%s' % (
  163. self._payOrder.orderNo, self.payOrder.orderNo, refundOrder.orderNo, self.refundFee,
  164. str(float(self.payOrder.totalFee) / 100)))
  165. refundOrder.processing()
  166. try:
  167. self.refund_dealer_balance(self.refundFee) # 先把钱包的余额返回
  168. income_record = RechargeRecord.issue_refund_order(self.payOrder, self.refundFee, self.subType,
  169. self.via) # 补充一条充值记录
  170. except Exception as e:
  171. logger.exception(e)
  172. refundOrder.fail(errorCode = 'EXCEPTION', errorDesc = u'退款过程发生异常')
  173. raise e
  174. record_income_proxy(self.source, income_record, {
  175. "owner": [
  176. {
  177. "money": RMB(income_record.money).mongo_amount,
  178. "role": "owner",
  179. "share": Percent("100.0").mongo_amount,
  180. "id": str(income_record.ownerId)
  181. }
  182. ],
  183. 'partner': []
  184. })
  185. refundOrder.succeed(finishedTime = datetime.datetime.now())
  186. if refund_callable: # 用于退款到钱包后的善后工作,比如扣除代理商的分成,补充退款记录
  187. refund_callable(refundOrder, True)
  188. return refundOrder