transaction_deprecated.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. import uuid
  5. from django.conf import settings
  6. from pymongo.errors import DuplicateKeyError
  7. from typing import TYPE_CHECKING
  8. from apilib.monetary import RMB
  9. from apilib.utils_sys import memcache_lock
  10. from apps.web.agent.models import Agent
  11. from apps.web.constant import USER_RECHARGE_TYPE
  12. from apps.web.core import PayAppType
  13. from apps.web.core.exceptions import ServiceException
  14. from apps.web.core.payment import PaymentGateway
  15. from apps.web.dealer.define import REFUND_NOTIFY_URL
  16. from apps.web.dealer.models import RefundDealerRechargeRecord, DealerRechargeRecord, Dealer
  17. from apps.web.exceptions import UserServerException
  18. from library.jd.exceptions import JDPayException
  19. from library.wechatbase.exceptions import WeChatPayException
  20. logger = logging.getLogger(__name__)
  21. if TYPE_CHECKING:
  22. pass
  23. def refund_cash_to_dealer(dealer_recharge_record, refundFee):
  24. if dealer_recharge_record.product == DealerRechargeRecord.ProductType.SimCard:
  25. return RefundSimRecharge(dealer_recharge_record, refundFee).execute()
  26. else:
  27. raise UserServerException(u'目前不支持该种类型订单退款')
  28. class RefundCash(object):
  29. # 最长的查询分账时间
  30. MAX_LEDGER_CHECK_TIME = 15
  31. def __init__(self, rechargeOrder, refundFee): # type:(DealerRechargeRecord, RMB) -> None
  32. self._payOrder = rechargeOrder
  33. self.refundFee = refundFee
  34. @property
  35. def outTradeNo(self):
  36. """
  37. 交易单号
  38. :return:
  39. """
  40. return self._payOrder.orderNo
  41. @property
  42. def totalFee(self):
  43. return RMB(round(float(self._payOrder.totalFee) / 100, 2))
  44. @property
  45. def payOrder(self):
  46. """
  47. 交易订单 即支付订单 与第三方系统产生关联的订单
  48. :return:
  49. """
  50. return self._payOrder
  51. def pre_check(self):
  52. """
  53. 退款的预检查
  54. :return:
  55. """
  56. # 首先检查退款的金额 原则上退款金额不能小于0 不能大于交易定安的金额
  57. if self.refundFee <= RMB(0) or self.refundFee > self.totalFee:
  58. raise UserServerException(u"退费金额错误")
  59. # 检查退款订单是否已经存在 是否已经退款成功
  60. refundOrder = RefundDealerRechargeRecord.objects.filter(
  61. rechargeObjId = self._payOrder.id).first() # type: RefundDealerRechargeRecord
  62. if refundOrder:
  63. if refundOrder.is_successful:
  64. raise UserServerException(u"该单已经退单")
  65. else:
  66. raise UserServerException(u"订单正在退款中")
  67. def execute(self):
  68. """
  69. 执行退款的动作
  70. :return:
  71. """
  72. lockKey = "refund_dealer_recharge_cash_{}".format(self._payOrder.id)
  73. with memcache_lock(key = lockKey, value = self._payOrder.id, expire = 360) as acquired:
  74. if not acquired:
  75. raise UserServerException(u"退款订单正在处理,等订单结束后,您才能再次重试哦")
  76. self.pre_check()
  77. refundOrder = RefundDealerRechargeRecord.issue(self.payOrder, self.refundFee)
  78. logger.info(
  79. 'refund paras, order = %s out_refund_no=%s, out_trade_no=%s, refund_fee=%s, total_fee=%s' % (
  80. self._payOrder.orderNo, self.payOrder.orderNo, refundOrder.orderNo, self.refundFee,
  81. str(float(self.payOrder.totalFee) / 100)))
  82. payGateway = PaymentGateway.clone_from_order(self.payOrder) # type: PaymentGateway
  83. refundOrder.processing()
  84. try:
  85. if payGateway.pay_app_type == PayAppType.WECHAT:
  86. # 微信的退款方式
  87. try:
  88. result = payGateway.refund_to_user(
  89. out_trade_no = self.outTradeNo,
  90. out_refund_no = refundOrder.orderNo,
  91. refund_fee = self.refundFee,
  92. total_fee = self.totalFee,
  93. refund_reason = u'退费',
  94. notify_url = REFUND_NOTIFY_URL.WECHAT_REFUND_BACK)
  95. except WeChatPayException as e:
  96. logger.info('refund failed , refund orderNo = {} reason = {}'.format(refundOrder.orderNo, e))
  97. refundOrder.fail(errorCode = e.errCode, errorDesc = e.errMsg)
  98. raise UserServerException('{}({})'.format(e.errMsg, e.errCode))
  99. logger.info('WECHAT Refund request successfully! return = {}'.format(result))
  100. elif payGateway.pay_app_type == PayAppType.JD_AGGR:
  101. # 经销商支付是资金池模式, 不存在分账, 所以不需要分账信息
  102. try:
  103. result = payGateway.refund_to_user(
  104. out_trade_no = self.outTradeNo,
  105. out_refund_no = refundOrder.orderNo,
  106. refund_fee = self.refundFee,
  107. total_fee = self.totalFee,
  108. refund_reason = u'退费',
  109. notify_url = REFUND_NOTIFY_URL.JD_AGGRE_REFUND_BACK)
  110. except JDPayException as e:
  111. logger.info('refund failed , refund orderNo = {} reason = {}'.format(refundOrder.orderNo, e))
  112. refundOrder.fail(errorCode = e.errCode, errorDesc = e.errMsg)
  113. raise UserServerException('{}({})'.format(e.errMsg, e.errCode))
  114. logger.info('JDAGGRE Refund request successfully! return = {}'.format(result))
  115. else:
  116. refundOrder.fail(errorDesc = u"不支持的退款模式")
  117. raise UserServerException(u"不支持的退款模式")
  118. except ServiceException as se:
  119. raise se
  120. except Exception as ee:
  121. # 这一步就不再更改订单的状态 由于不知道是退款前出错还是退款后出错 使用poll拉取订单状态来更新
  122. logger.exception(ee)
  123. raise UserServerException(u'未知异常')
  124. return refundOrder
  125. class RefundSimRecharge(RefundCash):
  126. def pre_check(self):
  127. super(RefundSimRecharge, self).pre_check()
  128. for partition in self.payOrder.settleInfo['partition']:
  129. if partition['id'] != settings.MY_PRIMARY_AGENT_ID and partition['earned'] > 0:
  130. raise UserServerException(u'目前仅支持不分账情况下退账。')