# -*- coding: utf-8 -*- # !/usr/bin/env python import logging from django.conf import settings from typing import TYPE_CHECKING from apilib.monetary import RMB from apilib.utils_sys import memcache_lock from apps.web.core import PayAppType from apps.web.core.exceptions import ServiceException from apps.web.core.payment import PaymentGateway from apps.web.dealer.define import REFUND_NOTIFY_URL from apps.web.dealer.models import RefundDealerRechargeRecord, DealerRechargeRecord from apps.web.exceptions import UserServerException from library.wechatbase.exceptions import WeChatPayException logger = logging.getLogger(__name__) if TYPE_CHECKING: pass def refund_cash_to_dealer(dealer_recharge_record, refundFee): if dealer_recharge_record.product == DealerRechargeRecord.ProductType.SimCard: return RefundSimRecharge(dealer_recharge_record, refundFee).execute() else: raise UserServerException(u'目前不支持该种类型订单退款') class RefundCash(object): # 最长的查询分账时间 MAX_LEDGER_CHECK_TIME = 15 def __init__(self, rechargeOrder, refundFee): # type:(DealerRechargeRecord, RMB) -> None self._payOrder = rechargeOrder self.refundFee = refundFee @property def outTradeNo(self): """ 交易单号 :return: """ return self._payOrder.orderNo @property def totalFee(self): return RMB(round(float(self._payOrder.totalFee) / 100, 2)) @property def payOrder(self): """ 交易订单 即支付订单 与第三方系统产生关联的订单 :return: """ return self._payOrder def pre_check(self): """ 退款的预检查 :return: """ # 首先检查退款的金额 原则上退款金额不能小于0 不能大于交易定安的金额 if self.refundFee <= RMB(0) or self.refundFee > self.totalFee: raise UserServerException(u"退费金额错误") # 检查退款订单是否已经存在 是否已经退款成功 refundOrder = RefundDealerRechargeRecord.objects.filter( rechargeObjId = self._payOrder.id).first() # type: RefundDealerRechargeRecord if refundOrder: if refundOrder.is_successful: raise UserServerException(u"该单已经退单") else: raise UserServerException(u"订单正在退款中") def execute(self): """ 执行退款的动作 :return: """ lockKey = "refund_dealer_recharge_cash_{}".format(self._payOrder.id) with memcache_lock(key = lockKey, value = self._payOrder.id, expire = 360) as acquired: if not acquired: raise UserServerException(u"退款订单正在处理,等订单结束后,您才能再次重试哦") self.pre_check() refundOrder = RefundDealerRechargeRecord.issue(self.payOrder, self.refundFee) logger.info( 'refund paras, order = %s out_refund_no=%s, out_trade_no=%s, refund_fee=%s, total_fee=%s' % ( self._payOrder.orderNo, self.payOrder.orderNo, refundOrder.orderNo, self.refundFee, str(float(self.payOrder.totalFee) / 100))) payGateway = PaymentGateway.clone_from_order(self.payOrder) # type: PaymentGateway refundOrder.processing() try: if payGateway.pay_app_type == PayAppType.WECHAT: # 微信的退款方式 try: result = payGateway.refund_to_user( out_trade_no = self.outTradeNo, out_refund_no = refundOrder.orderNo, refund_fee = self.refundFee, total_fee = self.totalFee, refund_reason = u'退费', notify_url = REFUND_NOTIFY_URL.WECHAT_REFUND_BACK) except WeChatPayException as e: logger.info('refund failed , refund orderNo = {} reason = {}'.format(refundOrder.orderNo, e)) refundOrder.fail(errorCode = e.errCode, errorDesc = e.errMsg) raise UserServerException('{}({})'.format(e.errMsg, e.errCode)) logger.info('WECHAT Refund request successfully! return = {}'.format(result)) else: refundOrder.fail(errorDesc = u"不支持的退款模式") raise UserServerException(u"不支持的退款模式") except ServiceException as se: raise se except Exception as ee: # 这一步就不再更改订单的状态 由于不知道是退款前出错还是退款后出错 使用poll拉取订单状态来更新 logger.exception(ee) raise UserServerException(u'未知异常') return refundOrder class RefundSimRecharge(RefundCash): def pre_check(self): super(RefundSimRecharge, self).pre_check() for partition in self.payOrder.settleInfo['partition']: if partition['id'] != settings.MY_PRIMARY_AGENT_ID and partition['earned'] > 0: raise UserServerException(u'目前仅支持不分账情况下退账。')