transaction_deprecated.py 5.2 KB

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