|  | @@ -6,28 +6,27 @@ import logging
 | 
	
		
			
				|  |  |  import time
 | 
	
		
			
				|  |  |  import uuid
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -from pymongo.errors import DuplicateKeyError
 | 
	
		
			
				|  |  | -from typing import TYPE_CHECKING, Dict, Any
 | 
	
		
			
				|  |  | +from bson import ObjectId
 | 
	
		
			
				|  |  | +from mongoengine import NotUniqueError
 | 
	
		
			
				|  |  | +from typing import TYPE_CHECKING, Dict
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  from apilib.monetary import VirtualCoin, RMB
 | 
	
		
			
				|  |  | -from apps.web.common.transaction.pay import RefundManager
 | 
	
		
			
				|  |  | -from apps.web.constant import USER_RECHARGE_TYPE, RechargeRecordVia
 | 
	
		
			
				|  |  | +from apilib.utils import flatten
 | 
	
		
			
				|  |  | +from apps.web.common.proxy import ClientDealerIncomeModelProxy
 | 
	
		
			
				|  |  | +from apps.web.common.transaction.refund import RefundCashMixin, RefundManager
 | 
	
		
			
				|  |  | +from apps.web.constant import USER_RECHARGE_TYPE, PARTITION_ROLE, RechargeRecordVia, AppPlatformType
 | 
	
		
			
				|  |  |  from apps.web.core import PayAppType, ROLE
 | 
	
		
			
				|  |  |  from apps.web.core.exceptions import ParameterError
 | 
	
		
			
				|  |  | -from apps.web.core.payment import PaymentGateway
 | 
	
		
			
				|  |  | -from apps.web.dealer.define import DEALER_INCOME_SOURCE
 | 
	
		
			
				|  |  | -from apps.web.dealer.proxy import DealerIncomeProxy
 | 
	
		
			
				|  |  | +from apps.web.core.payment import WithdrawGateway
 | 
	
		
			
				|  |  | +from apps.web.dealer.define import DEALER_INCOME_SOURCE, DEALER_INCOME_TYPE
 | 
	
		
			
				|  |  |  from apps.web.device.models import Group
 | 
	
		
			
				|  |  |  from apps.web.exceptions import UserServerException
 | 
	
		
			
				|  |  | -from apps.web.user.conf import REFUND_NOTIFY_URL
 | 
	
		
			
				|  |  | -from apps.web.user.models import MyUser, RechargeRecord, RefundMoneyRecord
 | 
	
		
			
				|  |  | -from library.alipay import AliException
 | 
	
		
			
				|  |  | -from library.wechatbase.exceptions import WeChatPayException
 | 
	
		
			
				|  |  | +from apps.web.user.models import MyUser, RechargeRecord, RefundMoneyRecord, Card, UserVirtualCard, MonthlyPackage
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  logger = logging.getLogger(__name__)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  if TYPE_CHECKING:
 | 
	
		
			
				|  |  | -    pass
 | 
	
		
			
				|  |  | +    from apps.web.dealer.proxy import DealerIncomeProxy
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def refund_money(device, money, openId):
 | 
	
	
		
			
				|  | @@ -63,61 +62,74 @@ def refund_money(device, money, openId):
 | 
	
		
			
				|  |  |          logger.exception('update record for feedback coins error=%s,orderNo=%s' % (e, orderNo))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -def refund_cash(recharge_record, refundFee, deductCoins, **kwargs):
 | 
	
		
			
				|  |  | -    # type:(RechargeRecord, RMB, VirtualCoin, Dict[str, Any])->RefundMoneyRecord
 | 
	
		
			
				|  |  | +def refund_cash(recharge_record, refundFee, **kwargs):
 | 
	
		
			
				|  |  | +    # type:(RechargeRecord, RMB, Dict)->RefundMoneyRecord
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      新的执行退款 为了保持导包顺序不变
 | 
	
		
			
				|  |  | -    :param deductCoins:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      :param recharge_record:
 | 
	
		
			
				|  |  |      :param refundFee:
 | 
	
		
			
				|  |  | -    :param kwargs 用户为资金实体的情况下, 传入user和minus_total_consume参数
 | 
	
		
			
				|  |  | +    :type kwargs: object
 | 
	
		
			
				|  |  |      :return:
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if recharge_record.via in [RechargeRecordVia.Balance, RechargeRecordVia.Cash, RechargeRecordVia.StartDevice]:
 | 
	
		
			
				|  |  | -        return RefundCash(recharge_record, refundFee, deductCoins).execute(
 | 
	
		
			
				|  |  | -            frozen_callable = frozen_refund_for_balance, **kwargs)
 | 
	
		
			
				|  |  | +    if recharge_record.via in [
 | 
	
		
			
				|  |  | +        RechargeRecordVia.Balance,
 | 
	
		
			
				|  |  | +        RechargeRecordVia.Cash,
 | 
	
		
			
				|  |  | +        RechargeRecordVia.Card,
 | 
	
		
			
				|  |  | +        RechargeRecordVia.VirtualCard,
 | 
	
		
			
				|  |  | +        RechargeRecordVia.MonthlyPackage,
 | 
	
		
			
				|  |  | +    ]:
 | 
	
		
			
				|  |  | +        return RefundCash(recharge_record, refundFee, **kwargs).execute(
 | 
	
		
			
				|  |  | +            frozen_callable = frozen_refund_func, refund_callable = refund_post_pay)
 | 
	
		
			
				|  |  |      else:
 | 
	
		
			
				|  |  |          raise UserServerException(u'不支持该类型订单退款')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class RefundCash(object):
 | 
	
		
			
				|  |  | -    # 最长的查询分账时间
 | 
	
		
			
				|  |  | -    MAX_LEDGER_CHECK_TIME = 15
 | 
	
		
			
				|  |  | +class RefundCash(RefundCashMixin):
 | 
	
		
			
				|  |  | +    MAX_LEDGER_CHECK_TIME = 15  # 最长的查询分账时间
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def __init__(self, rechargeOrder, refundFee, deductCoins):  # type:(RechargeRecord, RMB, VirtualCoin) -> None
 | 
	
		
			
				|  |  | -        self.paySubOrder = rechargeOrder
 | 
	
		
			
				|  |  | -        self.payOrder = self.paySubOrder.payOrder
 | 
	
		
			
				|  |  | +    def __init__(self, rechargeOrder, refundFee, **kwargs):
 | 
	
		
			
				|  |  | +        # type:(RechargeRecord, RMB, dict) -> None
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        self.refundFee = refundFee
 | 
	
		
			
				|  |  | -        self.deductCoins = deductCoins
 | 
	
		
			
				|  |  | +        super(RefundCash, self).__init__(rechargeOrder, refundFee)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        # self._nextSeq = 1
 | 
	
		
			
				|  |  | +        self.extraInfo = kwargs
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def outTradeNo(self):
 | 
	
		
			
				|  |  | -        """
 | 
	
		
			
				|  |  | -        交易单号
 | 
	
		
			
				|  |  | -        :return:
 | 
	
		
			
				|  |  | -        """
 | 
	
		
			
				|  |  | -        return self.payOrder.orderNo
 | 
	
		
			
				|  |  | +        # self._nextSeq = 1
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def totalFee(self):
 | 
	
		
			
				|  |  | -        return self.payOrder.money
 | 
	
		
			
				|  |  | +    def check_wallet(self, proxy, order):
 | 
	
		
			
				|  |  | +        partition_map = proxy.partition_map
 | 
	
		
			
				|  |  | +        for partition in list(flatten(partition_map.values())):
 | 
	
		
			
				|  |  | +            if partition['role'] == PARTITION_ROLE.OWNER:
 | 
	
		
			
				|  |  | +                leftBalance = order.owner.sub_balance(
 | 
	
		
			
				|  |  | +                    income_type = DEALER_INCOME_TYPE.DEVICE_INCOME,
 | 
	
		
			
				|  |  | +                    source_key = order.withdraw_source_key,
 | 
	
		
			
				|  |  | +                    only_ledger = True)
 | 
	
		
			
				|  |  | +                if abs(RMB(partition['money'])) > leftBalance:
 | 
	
		
			
				|  |  | +                    raise UserServerException(u"您的钱包余额不足,无法退款。")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            elif partition['role'] == PARTITION_ROLE.PARTNER:
 | 
	
		
			
				|  |  | +                from apps.web.dealer.models import Dealer
 | 
	
		
			
				|  |  | +                dealer = Dealer.objects(id = partition['id']).first()
 | 
	
		
			
				|  |  | +                leftBalance = dealer.sub_balance(
 | 
	
		
			
				|  |  | +                    income_type = DEALER_INCOME_TYPE.DEVICE_INCOME,
 | 
	
		
			
				|  |  | +                    source_key = order.withdraw_source_key)
 | 
	
		
			
				|  |  | +                if abs(RMB(partition['money'])) > leftBalance:
 | 
	
		
			
				|  |  | +                    raise UserServerException(u"您的分账合伙人钱包余额不足,无法退款(1001)。")
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  | +                from apps.web.agent.models import Agent
 | 
	
		
			
				|  |  | +                from apps.web.agent.define import AGENT_INCOME_TYPE
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def totalCoins(self):
 | 
	
		
			
				|  |  | -        return self.payOrder.coins
 | 
	
		
			
				|  |  | +                agent = Agent.objects(id = partition['id']).first()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def subTotalFee(self):
 | 
	
		
			
				|  |  | -        return self.paySubOrder.money
 | 
	
		
			
				|  |  | +                leftBalance = agent.sub_balance(
 | 
	
		
			
				|  |  | +                    income_type = AGENT_INCOME_TYPE.DEALER_DEVICE_FEE,
 | 
	
		
			
				|  |  | +                    source_key = order.withdraw_source_key)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def subTotalCoins(self):
 | 
	
		
			
				|  |  | -        return self.paySubOrder.coins
 | 
	
		
			
				|  |  | +                if abs(RMB(partition['money'])) > leftBalance:
 | 
	
		
			
				|  |  | +                    raise UserServerException(u"您的分账合伙人钱包余额不足,无法退款(1002)。")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def pre_check(self):
 | 
	
		
			
				|  |  |          """
 | 
	
	
		
			
				|  | @@ -125,38 +137,51 @@ class RefundCash(object):
 | 
	
		
			
				|  |  |          :return:
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if self.refundFee <= RMB(0) or self.refundFee > self.totalFee or self.refundFee > self.subTotalFee:
 | 
	
		
			
				|  |  | +        if self.refundFee <= RMB(0) or self.refundFee > self.subTotalFee:
 | 
	
		
			
				|  |  |              raise ParameterError(u"退费金额错误")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if self.deductCoins < VirtualCoin(
 | 
	
		
			
				|  |  | -                0) or self.deductCoins > self.totalCoins or self.deductCoins > self.subTotalCoins:
 | 
	
		
			
				|  |  | -            raise ParameterError(u"扣除用户金币数目错误")
 | 
	
		
			
				|  |  | +        if 'deductCoins' in self.extraInfo:
 | 
	
		
			
				|  |  | +            deductCoins = self.extraInfo['deductCoins']
 | 
	
		
			
				|  |  | +            if deductCoins < VirtualCoin(
 | 
	
		
			
				|  |  | +                    0) or deductCoins > self.subTotalCoins:
 | 
	
		
			
				|  |  | +                raise ParameterError(u"扣除用户金币数目错误")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          check_end_time = int(time.time()) + self.MAX_LEDGER_CHECK_TIME
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if not self.paySubOrder.is_success():
 | 
	
		
			
				|  |  | +        if not self.paySubOrder.is_success:
 | 
	
		
			
				|  |  |              raise UserServerException(u'非成功订单无法进行退款')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          while not self.paySubOrder.is_ledgered and int(time.time()) < check_end_time:
 | 
	
		
			
				|  |  |              logger.debug('{} is not allocated. wait to be allocated.'.format(repr(self.paySubOrder)))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            # TODO  考虑回调的方式进行
 | 
	
		
			
				|  |  |              time.sleep(5)
 | 
	
		
			
				|  |  |              self.paySubOrder.reload()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        proxy = DealerIncomeProxy.objects.filter(
 | 
	
		
			
				|  |  | -            ref_id = self.paySubOrder.id).first()  # type: DealerIncomeProxy
 | 
	
		
			
				|  |  | +        proxy = ClientDealerIncomeModelProxy.get_one(ref_id = self.paySubOrder.id)  # type: DealerIncomeProxy
 | 
	
		
			
				|  |  |          if not proxy:
 | 
	
		
			
				|  |  | -            raise UserServerException(u"订单尚未分账,无法退款")
 | 
	
		
			
				|  |  | +            raise UserServerException(u"订单尚未分账,无法退款(10002)")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if self.paySubOrder.gateway == AppPlatformType.ALIPAY:
 | 
	
		
			
				|  |  | +            over_time = 90 * 24 * 60 * 60
 | 
	
		
			
				|  |  | +        else:
 | 
	
		
			
				|  |  | +            over_time = 365 * 24 * 60 * 60
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (datetime.datetime.now() - self.paySubOrder.finishedTime).total_seconds() >= over_time:
 | 
	
		
			
				|  |  | +            raise UserServerException(u'超期订单不允许退款')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        checkWallet = self.extraInfo.get('checkWallet', False)
 | 
	
		
			
				|  |  | +        if checkWallet and WithdrawGateway.is_ledger(source_key = self.paySubOrder.withdraw_source_key):
 | 
	
		
			
				|  |  | +            self.check_wallet(proxy, self.paySubOrder)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          return proxy
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def create_refund_order(self):
 | 
	
		
			
				|  |  | -        refundOrder = RefundMoneyRecord.issue(self.paySubOrder, self.refundFee, self.deductCoins)
 | 
	
		
			
				|  |  | +    def create_refund_order(self, **extraInfo):
 | 
	
		
			
				|  |  | +        refundOrder = RefundMoneyRecord.issue(
 | 
	
		
			
				|  |  | +            self.paySubOrder, self.refundFee, **extraInfo)
 | 
	
		
			
				|  |  |          refundOrder.pay_sub_order = self.paySubOrder
 | 
	
		
			
				|  |  |          return refundOrder
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def execute(self, frozen_callable, **kwargs):
 | 
	
		
			
				|  |  | +    def execute(self, frozen_callable, refund_callable, notify_url = None):
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  |          执行退款的动作
 | 
	
		
			
				|  |  |          对于经销商商户的流程: 检查 >> 建单 >> 扣除用户金额 >> 退款 >> 收到退款成功通知后建立负收益单和扣除经销商的记录金额
 | 
	
	
		
			
				|  | @@ -166,114 +191,54 @@ class RefundCash(object):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          proxy = self.pre_check()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        payGateway = PaymentGateway.clone_from_order(self.payOrder)  # type: PaymentGateway
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            refundOrder = self.create_refund_order()  # type: RefundMoneyRecord
 | 
	
		
			
				|  |  | -        except DuplicateKeyError:
 | 
	
		
			
				|  |  | +            refundOrder = self.create_refund_order(**self.extraInfo)  # type: RefundMoneyRecord
 | 
	
		
			
				|  |  | +        except NotUniqueError:
 | 
	
		
			
				|  |  |              raise UserServerException(u'已经有退款订单正在进行')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if str(self.paySubOrder.id) == str(self.payOrder.id):
 | 
	
		
			
				|  |  | -            logger.info(
 | 
	
		
			
				|  |  | -                'refund paras, orderNo = {} refundOrderNo = {} refundFee = {} totalFee = {}'.format(
 | 
	
		
			
				|  |  | -                    self.paySubOrder.orderNo, refundOrder.orderNo, self.refundFee, self.subTotalFee)
 | 
	
		
			
				|  |  | -            )
 | 
	
		
			
				|  |  | -        else:
 | 
	
		
			
				|  |  | -            logger.info(
 | 
	
		
			
				|  |  | -                'refund paras, mix<orderNo = {}, totalFee={}>, sub<orderNo = {}, totalFee={}> '
 | 
	
		
			
				|  |  | -                'refundOrderNo = {} refundFee = {} '.format(
 | 
	
		
			
				|  |  | -                    self.payOrder.orderNo, self.totalFee, self.paySubOrder.orderNo, self.subTotalFee,
 | 
	
		
			
				|  |  | -                    refundOrder.orderNo, self.refundFee)
 | 
	
		
			
				|  |  | -            )
 | 
	
		
			
				|  |  | +        logger.info('refund paras: {} {}'.format(refundOrder.orderNo, self.refund_paras))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        split_map = proxy.partition_map
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        refund_recharge_order = self.paySubOrder.new_refund_cash_order(refundOrder)  # type: RechargeRecord
 | 
	
		
			
				|  |  | +        refund_income_order = self.paySubOrder.issue_refund_income_order(
 | 
	
		
			
				|  |  | +                refundOrder, split_map)  # type: RechargeRecord
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        frozen_callable(refundOrder, **kwargs)  # 对资金实体进行退款(用户余额,卡余额等)
 | 
	
		
			
				|  |  | +        frozen_callable(refundOrder)  # 对资金实体进行退款冻结(用户余额,卡余额等)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            if payGateway.pay_app_type == PayAppType.ALIPAY:
 | 
	
		
			
				|  |  | -                # 支付宝的退款方式
 | 
	
		
			
				|  |  | -                # 支付宝的退款很特殊,接口状态以及业务状态均在同步接口中返回 其中 code = 10000 表示接口成功 即申请成功了 fund_change= Y 表示退款成功
 | 
	
		
			
				|  |  | -                # 而当接口状态成功 code=10000 但是资金未发生变动 fund_change=N 的时候,则退款是不成功的(最好需要轮询一次),此时不改变退款单的状态
 | 
	
		
			
				|  |  | -                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'退费')
 | 
	
		
			
				|  |  | -                except AliException as e:
 | 
	
		
			
				|  |  | -                    logger.info('refund failed , refund orderNo = {} reason = {}'.format(refundOrder.orderNo, e))
 | 
	
		
			
				|  |  | -                    raise UserServerException('{}({})'.format(e.errMsg, e.errCode))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if result["code"] != "10000":
 | 
	
		
			
				|  |  | -                    refundOrder.fail(errorCode = "{}-{}".format(result["code"], result.get("sub_code")),
 | 
	
		
			
				|  |  | -                                     errorDesc = "{}-{}".format(result["msg"], result.get("sub_msg")))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                logger.info('ALIPAY Refund request successfully! return = {}'.format(result))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            elif payGateway.pay_app_type in [PayAppType.WECHAT, PayAppType.WECHAT_MINI]:
 | 
	
		
			
				|  |  | -                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))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        except UserServerException as se:
 | 
	
		
			
				|  |  | -            logger.error(se.message)
 | 
	
		
			
				|  |  | -            raise se
 | 
	
		
			
				|  |  | -        except Exception as ee:
 | 
	
		
			
				|  |  | -            # 这一步就不再更改订单的状态 由于不知道是退款前出错还是退款后出错 使用poll拉取订单状态来更新
 | 
	
		
			
				|  |  | -            logger.exception(ee)
 | 
	
		
			
				|  |  | -            raise UserServerException(ee.message)
 | 
	
		
			
				|  |  | +            self.submit_refund(
 | 
	
		
			
				|  |  | +                    refundOrder, refund_income_order.partition_map, u'现金退款',
 | 
	
		
			
				|  |  | +                    notify_url or refundOrder.notify_url, refund_callable)
 | 
	
		
			
				|  |  | +        except Exception:
 | 
	
		
			
				|  |  | +            import traceback
 | 
	
		
			
				|  |  | +            logger.warning(
 | 
	
		
			
				|  |  | +                'Refund request failure! orderNo = {}; e = {}'.format(refundOrder.orderNo, traceback.format_exc()))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          finally:
 | 
	
		
			
				|  |  | -            # 资金池方式下,直接记录负单.所有对账时间都以系统内时间为准
 | 
	
		
			
				|  |  | -            if payGateway.occupant.role == ROLE.agent:
 | 
	
		
			
				|  |  | +            refundOrder.reload()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if refundOrder.is_closed or refundOrder.is_success:
 | 
	
		
			
				|  |  | +                # 终态已经调用了post_pay, 所以不在做任何处理
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            elif refundOrder.my_payment_gateway.occupant.role == ROLE.agent:
 | 
	
		
			
				|  |  | +                # 资金池情况下认为成功, 冻结运营商金额
 | 
	
		
			
				|  |  | +                refund_income_order.result = RechargeRecord.PayResult.SUCCESS
 | 
	
		
			
				|  |  | +                refund_income_order.finishedTime = datetime.datetime.now()
 | 
	
		
			
				|  |  | +                refund_income_order.save()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |                  from apps.web.report.ledger import Ledger
 | 
	
		
			
				|  |  | -                ledger = Ledger(USER_RECHARGE_TYPE.REFUND_CASH, refund_recharge_order)
 | 
	
		
			
				|  |  | -                ledger.execute(journal = False, stats = True, check = False)
 | 
	
		
			
				|  |  | +                ledger = Ledger(refund_income_order.via, refund_income_order)
 | 
	
		
			
				|  |  | +                ledger.execute(stats = True)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          return refundOrder
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class RetryRefundCash(object):
 | 
	
		
			
				|  |  | +class RetryRefundCash(RefundCashMixin):
 | 
	
		
			
				|  |  |      def __init__(self, refundOrder):  # type:(RefundMoneyRecord) -> None
 | 
	
		
			
				|  |  | -        self.paySubOrder = refundOrder.pay_sub_order
 | 
	
		
			
				|  |  | -        self.payOrder = self.paySubOrder.payOrder
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        self.refundFee = refundOrder.money
 | 
	
		
			
				|  |  | -        self.deductCoins = refundOrder.coins
 | 
	
		
			
				|  |  | +        super(RetryRefundCash, self).__init__(refundOrder.pay_sub_order, refundOrder.money)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          self.refundOrder = refundOrder
 | 
	
		
			
				|  |  | -        # self._nextSeq = 1
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def outTradeNo(self):
 | 
	
		
			
				|  |  | -        """
 | 
	
		
			
				|  |  | -        交易单号
 | 
	
		
			
				|  |  | -        :return:
 | 
	
		
			
				|  |  | -        """
 | 
	
		
			
				|  |  | -        return self.payOrder.orderNo
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def totalFee(self):
 | 
	
		
			
				|  |  | -        return self.payOrder.money
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def totalCoins(self):
 | 
	
		
			
				|  |  | -        return self.payOrder.coins
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def subTotalFee(self):
 | 
	
		
			
				|  |  | -        return self.paySubOrder.money
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    @property
 | 
	
		
			
				|  |  | -    def subTotalCoins(self):
 | 
	
		
			
				|  |  | -        return self.paySubOrder.coins
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def pre_check(self):
 | 
	
		
			
				|  |  |          """
 | 
	
	
		
			
				|  | @@ -281,24 +246,26 @@ class RetryRefundCash(object):
 | 
	
		
			
				|  |  |          :return:
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if self.refundFee <= RMB(0) or self.refundFee > self.totalFee or self.refundFee > self.subTotalFee:
 | 
	
		
			
				|  |  | +        if self.refundFee <= RMB(0) or self.refundFee > self.subTotalFee:
 | 
	
		
			
				|  |  |              raise ParameterError(u"退费金额错误")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if self.deductCoins < VirtualCoin(
 | 
	
		
			
				|  |  | -                0) or self.deductCoins > self.totalCoins or self.deductCoins > self.subTotalCoins:
 | 
	
		
			
				|  |  | +        deductCoins = self.refundOrder.deductCoins
 | 
	
		
			
				|  |  | +        if deductCoins < VirtualCoin(0) or deductCoins > self.subTotalCoins:
 | 
	
		
			
				|  |  |              raise ParameterError(u"扣除用户金币数目错误")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if self.refundOrder.is_closed or self.refundOrder.is_success:
 | 
	
		
			
				|  |  | -            raise UserServerException(u'已经完结订单不能重试')
 | 
	
		
			
				|  |  | +        if not self.refundOrder.is_fail and not self.refundOrder.is_no_order:
 | 
	
		
			
				|  |  | +            raise UserServerException(u'状态非错误的订单不能重试')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        proxy = DealerIncomeProxy.objects.filter(
 | 
	
		
			
				|  |  | -            ref_id = self.paySubOrder.id).first()  # type: DealerIncomeProxy
 | 
	
		
			
				|  |  | +        proxy = ClientDealerIncomeModelProxy.get_one(ref_id = self.paySubOrder.id)
 | 
	
		
			
				|  |  |          if not proxy:
 | 
	
		
			
				|  |  | -            raise UserServerException(u"订单尚未分账,无法退款")
 | 
	
		
			
				|  |  | +            raise UserServerException(u"订单尚未分账,无法退款(10002)")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if self.refundOrder.checkWallet and WithdrawGateway.is_ledger(source_key = self.paySubOrder.withdraw_source_key):
 | 
	
		
			
				|  |  | +            self.check_wallet(proxy, self.paySubOrder)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          return proxy
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def execute(self, frozen_callable, **kwargs):
 | 
	
		
			
				|  |  | +    def execute(self, frozen_callable, refund_callable, notify_url = None):
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  |          执行退款的动作
 | 
	
		
			
				|  |  |          对于经销商商户的流程: 检查 >> 建单 >> 扣除用户金额 >> 退款 >> 收到退款成功通知后建立负收益单和扣除经销商的记录金额
 | 
	
	
		
			
				|  | @@ -308,126 +275,321 @@ class RetryRefundCash(object):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          proxy = self.pre_check()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        payGateway = PaymentGateway.clone_from_order(self.payOrder)  # type: PaymentGateway
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if str(self.paySubOrder.id) == str(self.payOrder.id):
 | 
	
		
			
				|  |  | -            logger.info(
 | 
	
		
			
				|  |  | -                'retry refund paras, orderNo = {} refundOrderNo = {} refundFee = {} totalFee = {}'.format(
 | 
	
		
			
				|  |  | -                    self.paySubOrder.orderNo, self.refundOrder.orderNo, self.refundFee, self.subTotalFee)
 | 
	
		
			
				|  |  | -            )
 | 
	
		
			
				|  |  | -        else:
 | 
	
		
			
				|  |  | -            logger.info(
 | 
	
		
			
				|  |  | -                'retry refund paras, mix<orderNo = {}, totalFee={}>, sub<orderNo = {}, totalFee={}> '
 | 
	
		
			
				|  |  | -                'refundOrderNo = {} refundFee = {} '.format(
 | 
	
		
			
				|  |  | -                    self.payOrder.orderNo, self.totalFee, self.paySubOrder.orderNo, self.subTotalFee,
 | 
	
		
			
				|  |  | -                    self.refundOrder.orderNo, self.refundFee)
 | 
	
		
			
				|  |  | -            )
 | 
	
		
			
				|  |  | +        logger.info('retry refund paras: {} {}'.format(self.refundOrder.orderNo, self.refund_paras))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        puller = RefundManager().get_poller(payGateway.pay_app_type)
 | 
	
		
			
				|  |  | -        puller(self.refundOrder).pull(payGateway, self.payOrder, refund_post_pay)
 | 
	
		
			
				|  |  | +        puller = RefundManager().get_poller(self.refundOrder.pay_app_type)
 | 
	
		
			
				|  |  | +        done = puller(self.refundOrder).pull(refund_post_pay)
 | 
	
		
			
				|  |  | +        if done:
 | 
	
		
			
				|  |  | +            return
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          self.refundOrder.reload()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if self.refundOrder.is_success or self.refundOrder.is_closed:
 | 
	
		
			
				|  |  | -            logger.debug('refund order {} has been finished.'.format(str(self.refundOrder)))
 | 
	
		
			
				|  |  | +        if not self.refundOrder.is_fail and not self.refundOrder.is_no_order:
 | 
	
		
			
				|  |  | +            logger.debug('refund order {} is not in fail status.'.format(str(self.refundOrder)))
 | 
	
		
			
				|  |  |              return
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        refund_order_record = self.refundOrder.refund_order_record
 | 
	
		
			
				|  |  | -        if not refund_order_record:
 | 
	
		
			
				|  |  | -            split_map = proxy.partition_map
 | 
	
		
			
				|  |  | -            refund_order_record = self.paySubOrder.new_refund_cash_order(
 | 
	
		
			
				|  |  | +        if self.refundOrder.retryCount > 10:
 | 
	
		
			
				|  |  | +            matched = self.refundOrder.closed(errorCode = 'TIMEOUT', errorDesc = u'重试次数超限,退款失败')
 | 
	
		
			
				|  |  | +            if matched:
 | 
	
		
			
				|  |  | +                return refund_post_pay(self.refundOrder, False)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        refund_income_order = self.refundOrder.refund_income_order
 | 
	
		
			
				|  |  | +        if not refund_income_order:
 | 
	
		
			
				|  |  | +            if 'billSplitOfOwner' in self.paySubOrder.attachParas:  # 老的商户分账模式
 | 
	
		
			
				|  |  | +                if "billSplitList" in self.paySubOrder.attachParas:
 | 
	
		
			
				|  |  | +                    owner_split = self.paySubOrder.attachParas['billSplitOfOwner']
 | 
	
		
			
				|  |  | +                    owner_split['merchantId'] = owner_split.pop('splitBillMerchantEmail')
 | 
	
		
			
				|  |  | +                    owner_split['money'] = owner_split.pop('splitBillAmount')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    split_map = {
 | 
	
		
			
				|  |  | +                        PARTITION_ROLE.OWNER: [owner_split],
 | 
	
		
			
				|  |  | +                        PARTITION_ROLE.AGENT: [],
 | 
	
		
			
				|  |  | +                        PARTITION_ROLE.PARTNER: []
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    for spliter in self.paySubOrder.attachParas['billSplitList']:
 | 
	
		
			
				|  |  | +                        spliter['merchantId'] = spliter.pop('splitBillMerchantEmail')
 | 
	
		
			
				|  |  | +                        spliter['money'] = spliter.pop('splitBillAmount')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        if spliter['role'] == PARTITION_ROLE.AGENT:
 | 
	
		
			
				|  |  | +                            split_map[PARTITION_ROLE.AGENT].append(spliter)
 | 
	
		
			
				|  |  | +                        elif spliter['role'] == PARTITION_ROLE.PARTNER:
 | 
	
		
			
				|  |  | +                            split_map[PARTITION_ROLE.PARTNER].append(spliter)
 | 
	
		
			
				|  |  | +                        else:
 | 
	
		
			
				|  |  | +                            raise UserServerException(u'错误的分账角色')
 | 
	
		
			
				|  |  | +                else:
 | 
	
		
			
				|  |  | +                    owner_split = self.paySubOrder.attachParas['billSplitOfOwner']
 | 
	
		
			
				|  |  | +                    owner_split['merchantId'] = owner_split.pop('splitBillMerchantEmail')
 | 
	
		
			
				|  |  | +                    owner_split['money'] = owner_split.pop('splitBillAmount')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    split_map = {
 | 
	
		
			
				|  |  | +                        PARTITION_ROLE.OWNER: [owner_split],
 | 
	
		
			
				|  |  | +                        PARTITION_ROLE.AGENT: [],
 | 
	
		
			
				|  |  | +                        PARTITION_ROLE.PARTNER: []
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  | +                split_map = proxy.partition_map
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            refund_income_order = self.paySubOrder.issue_refund_income_order(
 | 
	
		
			
				|  |  |                  self.refundOrder, split_map)  # type: RechargeRecord
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        # 将订单的状态切换为 正在处理中
 | 
	
		
			
				|  |  | -        self.refundOrder.processing()
 | 
	
		
			
				|  |  | +        succeed = self.refundOrder.retry_processing(
 | 
	
		
			
				|  |  | +            changeOrderNo = (not self.refundOrder.is_no_order) and self.refundOrder.pay_app_type in [PayAppType.JD_OPEN])
 | 
	
		
			
				|  |  | +        if not succeed:
 | 
	
		
			
				|  |  | +            logger.info(
 | 
	
		
			
				|  |  | +                'refund ignored. refund orderNo = {} reason = unique check failure.'.format(self.refundOrder.orderNo))
 | 
	
		
			
				|  |  | +            return
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        # 扣除实体的金额(用户或者实体卡)
 | 
	
		
			
				|  |  | -        frozen_callable(self.refundOrder, **kwargs)
 | 
	
		
			
				|  |  | +        self.refundOrder.reload()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        try:
 | 
	
		
			
				|  |  | -            if payGateway.pay_app_type == PayAppType.ALIPAY:
 | 
	
		
			
				|  |  | -                # 支付宝的退款方式
 | 
	
		
			
				|  |  | -                # 支付宝的退款很特殊,接口状态以及业务状态均在同步接口中返回 其中 code = 10000 表示接口成功 即申请成功了 fund_change= Y 表示退款成功
 | 
	
		
			
				|  |  | -                # 而当接口状态成功 code=10000 但是资金未发生变动 fund_change=N 的时候,则退款是不成功的(最好需要轮询一次),此时不改变退款单的状态
 | 
	
		
			
				|  |  | -                try:
 | 
	
		
			
				|  |  | -                    result = payGateway.refund_to_user(
 | 
	
		
			
				|  |  | -                        out_trade_no = self.outTradeNo, out_refund_no = self.refundOrder.orderNo,
 | 
	
		
			
				|  |  | -                        refund_fee = self.refundFee, total_fee = self.totalFee, refund_reason = u'退费')
 | 
	
		
			
				|  |  | -                except AliException as e:
 | 
	
		
			
				|  |  | -                    logger.info('refund failed , refund orderNo = {} reason = {}'.format(self.refundOrder.orderNo, e))
 | 
	
		
			
				|  |  | -                    raise UserServerException('{}({})'.format(e.errMsg, e.errCode))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if result["code"] != "10000":
 | 
	
		
			
				|  |  | -                    self.refundOrder.fail(errorCode = "{}-{}".format(result["code"], result.get("sub_code")),
 | 
	
		
			
				|  |  | -                                     errorDesc = "{}-{}".format(result["msg"], result.get("sub_msg")))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                logger.info('ALIPAY Refund request successfully! return = {}'.format(result))
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            elif payGateway.pay_app_type == PayAppType.WECHAT:
 | 
	
		
			
				|  |  | -                try:
 | 
	
		
			
				|  |  | -                    result = payGateway.refund_to_user(
 | 
	
		
			
				|  |  | -                        out_trade_no = self.outTradeNo, out_refund_no = self.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(self.refundOrder.orderNo, e))
 | 
	
		
			
				|  |  | -                    self.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:
 | 
	
		
			
				|  |  | -                self.refundOrder.fail(errorDesc = u"不支持的退款模式")
 | 
	
		
			
				|  |  | -                raise UserServerException(u"不支持的退款模式")
 | 
	
		
			
				|  |  | +        frozen_callable(self.refundOrder)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        except UserServerException as se:
 | 
	
		
			
				|  |  | -            logger.error(se.message)
 | 
	
		
			
				|  |  | -            raise se
 | 
	
		
			
				|  |  | -        except Exception as ee:
 | 
	
		
			
				|  |  | -            # 这一步就不再更改订单的状态 由于不知道是退款前出错还是退款后出错 使用poll拉取订单状态来更新
 | 
	
		
			
				|  |  | -            logger.exception(ee)
 | 
	
		
			
				|  |  | -            raise UserServerException(ee.message)
 | 
	
		
			
				|  |  | +        try:
 | 
	
		
			
				|  |  | +            self.submit_refund(
 | 
	
		
			
				|  |  | +                self.refundOrder, refund_income_order.partition_map, u'现金退款',
 | 
	
		
			
				|  |  | +                notify_url or self.refundOrder.notify_url, refund_callable)
 | 
	
		
			
				|  |  | +        except Exception:
 | 
	
		
			
				|  |  | +            import traceback
 | 
	
		
			
				|  |  | +            logger.warning(
 | 
	
		
			
				|  |  | +                'Refund request failure! orderNo = {}; e = {}'.format(self.refundOrder, traceback.format_exc()))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          finally:
 | 
	
		
			
				|  |  | -            # 资金池方式下,直接记录负单.所有对账时间都以系统内时间为准
 | 
	
		
			
				|  |  | -            if payGateway.occupant.role == ROLE.agent:
 | 
	
		
			
				|  |  | -                from apps.web.report.ledger import Ledger
 | 
	
		
			
				|  |  | -                ledger = Ledger(USER_RECHARGE_TYPE.REFUND_CASH, refund_order_record)
 | 
	
		
			
				|  |  | -                ledger.execute(journal = False, stats = True, check = False)
 | 
	
		
			
				|  |  | +            self.refundOrder.reload()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if self.refundOrder.is_closed or self.refundOrder.is_success:
 | 
	
		
			
				|  |  | +                # 终态已经调用了post_pay, 所以不在做任何处理
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            elif self.refundOrder.my_payment_gateway.occupant.role == ROLE.agent:
 | 
	
		
			
				|  |  | +                # 资金池情况下认为成功, 冻结运营商金额
 | 
	
		
			
				|  |  | +                if refund_income_order.result != RechargeRecord.PayResult.SUCCESS:
 | 
	
		
			
				|  |  | +                    refund_income_order.result = RechargeRecord.PayResult.SUCCESS
 | 
	
		
			
				|  |  | +                    refund_income_order.finishedTime = datetime.datetime.now()
 | 
	
		
			
				|  |  | +                    refund_income_order.save()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                if not refund_income_order.is_ledgered:
 | 
	
		
			
				|  |  | +                    from apps.web.report.ledger import Ledger
 | 
	
		
			
				|  |  | +                    ledger = Ledger(USER_RECHARGE_TYPE.REFUND_CASH, refund_income_order)
 | 
	
		
			
				|  |  | +                    ledger.execute(journal = False, stats = True, check = False)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          return self.refundOrder
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -def refund_post_pay(refundOrder, finishedTime):
 | 
	
		
			
				|  |  | -    # type: (RefundMoneyRecord, datetime)->None
 | 
	
		
			
				|  |  | +def refund_post_pay(refundOrder, success):
 | 
	
		
			
				|  |  | +    # type: (RefundMoneyRecord, bool)->None
 | 
	
		
			
				|  |  | +    try:
 | 
	
		
			
				|  |  | +        refund_income_order = refundOrder.refund_income_order # type: RechargeRecord
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    refundOrder.user.commit_refund_cash(refundOrder)
 | 
	
		
			
				|  |  | +        try:
 | 
	
		
			
				|  |  | +            refPay = refund_income_order.extraInfo['refPay']
 | 
	
		
			
				|  |  | +            if isinstance(refund_income_order.extraInfo['refPay'], dict):
 | 
	
		
			
				|  |  | +                refund_income_order.extraInfo['refPay'] = ObjectId(refPay.pop('objId'))
 | 
	
		
			
				|  |  | +                refund_income_order.save()
 | 
	
		
			
				|  |  | +        except:
 | 
	
		
			
				|  |  | +            pass
 | 
	
		
			
				|  |  | +        if success:
 | 
	
		
			
				|  |  | +            refund_success_callback(refundOrder, refundOrder.finishedTime, refund_income_order)
 | 
	
		
			
				|  |  | +        else:
 | 
	
		
			
				|  |  | +            refund_fail_callback(refundOrder, refundOrder.finishedTime, refund_income_order)
 | 
	
		
			
				|  |  | +    except Exception:
 | 
	
		
			
				|  |  | +        import traceback
 | 
	
		
			
				|  |  | +        logger.warning(
 | 
	
		
			
				|  |  | +            'Refund callback failure. orderNo = {}; e = {}'.format(refundOrder.orderNo, traceback.format_exc()))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    refund_order_record = refundOrder.refund_order_record  # type: RechargeRecord
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    refund_order_record.finishedTime = finishedTime
 | 
	
		
			
				|  |  | -    refund_order_record.result = RechargeRecord.PayResult.SUCCESS
 | 
	
		
			
				|  |  | -    refund_order_record.save()
 | 
	
		
			
				|  |  | +def refund_success_callback(refundOrder, finishedTime, refund_income_order):
 | 
	
		
			
				|  |  | +    # type: (RefundMoneyRecord, datetime, RechargeRecord)->None
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rechargeOrder = refundOrder.pay_sub_order
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if rechargeOrder.via in [USER_RECHARGE_TYPE.RECHARGE, USER_RECHARGE_TYPE.RECHARGE_CASH]:
 | 
	
		
			
				|  |  | +        refundOrder.user.commit_refund_cash(refundOrder)
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_CARD:
 | 
	
		
			
				|  |  | +        if rechargeOrder.attachParas.get('terminalRecharge', False):
 | 
	
		
			
				|  |  | +            pass
 | 
	
		
			
				|  |  | +        else:
 | 
	
		
			
				|  |  | +            card = Card.objects(id = rechargeOrder.attachParas['cardId']).first()  # type: Card
 | 
	
		
			
				|  |  | +            card.clear_frozen_balance(card.freeze_transaction_id('r'), VirtualCoin(0))
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_VIRTUAL_CARD:
 | 
	
		
			
				|  |  | +        userVirtualCard = UserVirtualCard.objects(id = rechargeOrder.attachParas['cardId']).first()
 | 
	
		
			
				|  |  | +        userVirtualCard.commit_refund(refundOrder)
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_MONTHLY_PACKAGE:
 | 
	
		
			
				|  |  | +        pass
 | 
	
		
			
				|  |  | +    else:
 | 
	
		
			
				|  |  | +        logger.debug('via({}) is not support.'.format(rechargeOrder.via))
 | 
	
		
			
				|  |  | +        return
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if not refund_income_order.is_ledgered:  # 记录收益
 | 
	
		
			
				|  |  | +        refund_income_order.finishedTime = finishedTime
 | 
	
		
			
				|  |  | +        refund_income_order.result = RechargeRecord.PayResult.SUCCESS
 | 
	
		
			
				|  |  | +        refund_income_order.save()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    # 记录资金池的变动
 | 
	
		
			
				|  |  | -    if not refund_order_record.is_ledgered:
 | 
	
		
			
				|  |  |          from apps.web.report.ledger import Ledger
 | 
	
		
			
				|  |  | -        ledger = Ledger(DEALER_INCOME_SOURCE.REFUND_CASH, refund_order_record)
 | 
	
		
			
				|  |  | +        ledger = Ledger(refund_income_order.via, refund_income_order)
 | 
	
		
			
				|  |  |          ledger.execute(stats=True)
 | 
	
		
			
				|  |  | +    else:
 | 
	
		
			
				|  |  | +        # FIX预扣单状态.不是SUCCESS先改成SUCCESS
 | 
	
		
			
				|  |  | +        if refund_income_order.result != RechargeRecord.PayResult.SUCCESS:
 | 
	
		
			
				|  |  | +            refund_income_order.finishedTime = refund_income_order.dateTimeAdded
 | 
	
		
			
				|  |  | +            refund_income_order.result = RechargeRecord.PayResult.SUCCESS
 | 
	
		
			
				|  |  | +            refund_income_order.save()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for item in rechargeOrder.extraInfo['refRefund']:
 | 
	
		
			
				|  |  | +        if 'deductId' in item:
 | 
	
		
			
				|  |  | +            if str(item['deductId']) == str(refund_income_order.id):
 | 
	
		
			
				|  |  | +                item['status'] = RefundMoneyRecord.Status.SUCCESS
 | 
	
		
			
				|  |  | +                item['finishedTime'] = refundOrder.finishedTime
 | 
	
		
			
				|  |  | +                rechargeOrder.save()
 | 
	
		
			
				|  |  | +                break
 | 
	
		
			
				|  |  | +        elif str(item['objId']) == str(refund_income_order.id):
 | 
	
		
			
				|  |  | +            item['status'] = RefundMoneyRecord.Status.SUCCESS
 | 
	
		
			
				|  |  | +            item['deductId'] = ObjectId(item.pop('objId'))
 | 
	
		
			
				|  |  | +            item['finishedTime'] = refundOrder.finishedTime
 | 
	
		
			
				|  |  | +            rechargeOrder.save()
 | 
	
		
			
				|  |  | +            break
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def refund_fail_callback(refundOrder, finishedTime, refund_income_order):
 | 
	
		
			
				|  |  | +    rechargeOrder = refundOrder.pay_sub_order
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if rechargeOrder.via in [USER_RECHARGE_TYPE.RECHARGE, USER_RECHARGE_TYPE.RECHARGE_CASH]:
 | 
	
		
			
				|  |  | +        user = refundOrder.pay_sub_order.myuser  # type: MyUser
 | 
	
		
			
				|  |  | +        user.revoke_refund_cash(refundOrder)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_CARD:
 | 
	
		
			
				|  |  | +        if rechargeOrder.attachParas.get('terminalRecharge', False):
 | 
	
		
			
				|  |  | +            pass
 | 
	
		
			
				|  |  | +        else:
 | 
	
		
			
				|  |  | +            card = Card.objects(id = rechargeOrder.attachParas['cardId']).first()
 | 
	
		
			
				|  |  | +            if not card:
 | 
	
		
			
				|  |  | +                raise UserServerException(u'充值卡不存在')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            card.recover_frozen_balance(transaction_id = card.freeze_transaction_id('r'), fee = refundOrder.deductCoins)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -def frozen_refund_for_balance(refundOrder, user = None, minus_total_consume = VirtualCoin(0)):
 | 
	
		
			
				|  |  | -    # type: (RefundMoneyRecord, MyUser, VirtualCoin)->bool
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_VIRTUAL_CARD:
 | 
	
		
			
				|  |  | +        userVirtualCard = UserVirtualCard.objects(id = rechargeOrder.attachParas['cardId']).first()
 | 
	
		
			
				|  |  | +        if not userVirtualCard:
 | 
	
		
			
				|  |  | +            raise UserServerException(u'虚拟卡不存在')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        userVirtualCard.revoke_refund(refundOrder)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_MONTHLY_PACKAGE:
 | 
	
		
			
				|  |  | +        monthlyPackage = MonthlyPackage.objects(id = rechargeOrder.attachParas['cardId']).first()
 | 
	
		
			
				|  |  | +        if not monthlyPackage:
 | 
	
		
			
				|  |  | +            raise UserServerException(u'包月套餐不存在')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        monthlyPackage.toggle_disable(isDisable = 0)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    else:
 | 
	
		
			
				|  |  | +        raise UserServerException(u'不支持的退款订单类型')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if refund_income_order.is_ledgered:
 | 
	
		
			
				|  |  | +        # 如果已经扣款分账则建立一个退单收益单
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        # FIX预扣单状态.不是SUCCESS先改成SUCCESS
 | 
	
		
			
				|  |  | +        if refund_income_order.result != RechargeRecord.PayResult.SUCCESS:
 | 
	
		
			
				|  |  | +            refund_income_order.finishedTime = refund_income_order.dateTimeAdded
 | 
	
		
			
				|  |  | +            refund_income_order.result = RechargeRecord.PayResult.SUCCESS
 | 
	
		
			
				|  |  | +            refund_income_order.save()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        revoke_income_order = refund_income_order.issue_refund_revoke_order()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        from apps.web.report.ledger import Ledger
 | 
	
		
			
				|  |  | +        ledger = Ledger(DEALER_INCOME_SOURCE.REVOKE_REFUND_CASH, revoke_income_order)
 | 
	
		
			
				|  |  | +        ledger.execute(journal = False, stats = True, check = False)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        for item in rechargeOrder.extraInfo['refRefund']:
 | 
	
		
			
				|  |  | +            if 'deductId' in item:
 | 
	
		
			
				|  |  | +                if str(item['deductId']) == str(refund_income_order.id):
 | 
	
		
			
				|  |  | +                    item['status'] = RefundMoneyRecord.Status.CLOSED
 | 
	
		
			
				|  |  | +                    item['revokeId'] = revoke_income_order.id
 | 
	
		
			
				|  |  | +                    item['finishedTime'] = refundOrder.finishedTime
 | 
	
		
			
				|  |  | +                    rechargeOrder.save()
 | 
	
		
			
				|  |  | +                    break
 | 
	
		
			
				|  |  | +            elif str(item['objId']) == str(refund_income_order.id):
 | 
	
		
			
				|  |  | +                item['status'] = RefundMoneyRecord.Status.CLOSED
 | 
	
		
			
				|  |  | +                item['deductId'] = ObjectId(item.pop('objId'))
 | 
	
		
			
				|  |  | +                item['revokeId'] = revoke_income_order.id
 | 
	
		
			
				|  |  | +                item['finishedTime'] = refundOrder.finishedTime
 | 
	
		
			
				|  |  | +                rechargeOrder.save()
 | 
	
		
			
				|  |  | +                break
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if user:
 | 
	
		
			
				|  |  | -        if user.openId != refundOrder.pay_sub_order.openId:
 | 
	
		
			
				|  |  | -            raise UserServerException(u"用户参数错误")
 | 
	
		
			
				|  |  | -        else:
 | 
	
		
			
				|  |  | -            if user.groupId != refundOrder.pay_sub_order.groupId:
 | 
	
		
			
				|  |  | -                user = refundOrder.pay_sub_order.user
 | 
	
		
			
				|  |  |      else:
 | 
	
		
			
				|  |  | -        user = refundOrder.pay_sub_order.user
 | 
	
		
			
				|  |  | +        refund_income_order.finishedTime = finishedTime
 | 
	
		
			
				|  |  | +        refund_income_order.result = RechargeRecord.PayResult.CANCEL
 | 
	
		
			
				|  |  | +        refund_income_order.save()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        for item in rechargeOrder.extraInfo['refRefund']:
 | 
	
		
			
				|  |  | +            if 'deductId' in item:
 | 
	
		
			
				|  |  | +                if str(item['deductId']) == str(refund_income_order.id):
 | 
	
		
			
				|  |  | +                    item['status'] = RefundMoneyRecord.Status.CLOSED
 | 
	
		
			
				|  |  | +                    item['finishedTime'] = refundOrder.finishedTime
 | 
	
		
			
				|  |  | +                    rechargeOrder.save()
 | 
	
		
			
				|  |  | +                    break
 | 
	
		
			
				|  |  | +            elif str(item['objId']) == str(refund_income_order.id):
 | 
	
		
			
				|  |  | +                item['status'] = RefundMoneyRecord.Status.CLOSED
 | 
	
		
			
				|  |  | +                item['deductId'] = item.pop('objId')
 | 
	
		
			
				|  |  | +                item['finishedTime'] = refundOrder.finishedTime
 | 
	
		
			
				|  |  | +                rechargeOrder.save()
 | 
	
		
			
				|  |  | +                break
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def frozen_refund_func(refundOrder):
 | 
	
		
			
				|  |  | +    # type: (RefundMoneyRecord)->None
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    """
 | 
	
		
			
				|  |  | +    :param refundOrder:
 | 
	
		
			
				|  |  | +    :return:
 | 
	
		
			
				|  |  | +    """
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rechargeOrder = refundOrder.pay_sub_order
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if rechargeOrder.via in [USER_RECHARGE_TYPE.RECHARGE, USER_RECHARGE_TYPE.RECHARGE_CASH]:
 | 
	
		
			
				|  |  | +        user = refundOrder.pay_sub_order.myuser
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if not user:
 | 
	
		
			
				|  |  | +            raise UserServerException(u'用户不存在')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        minus_total_consume = VirtualCoin(refundOrder.extraInfo.get('minus_total_consume', 0))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if not user:
 | 
	
		
			
				|  |  | -        raise UserServerException(u'用户不存在')
 | 
	
		
			
				|  |  | +        deduct_coins = refundOrder.deductCoins
 | 
	
		
			
				|  |  | +        frozen_coins = refundOrder.frozenCoins
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return user.prepare_refund_cash(refundOrder, minus_total_consume)
 | 
	
		
			
				|  |  | +        logger.debug('MyUser<id={}> prepare refund cash. money = {}, coins = {}, before = {}'.format(
 | 
	
		
			
				|  |  | +            str(user.id), refundOrder.money, deduct_coins, user.balance
 | 
	
		
			
				|  |  | +        ))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        user.prepare_refund_cash(refundOrder, deduct_coins, frozen_coins, minus_total_consume)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_CARD:
 | 
	
		
			
				|  |  | +        if rechargeOrder.attachParas.get('terminalRecharge', False):
 | 
	
		
			
				|  |  | +            logger.debug('Card<id={}> prepare refund cash. money = {}'.format(
 | 
	
		
			
				|  |  | +                'terminalRecharge', refundOrder.money
 | 
	
		
			
				|  |  | +            ))
 | 
	
		
			
				|  |  | +        else:
 | 
	
		
			
				|  |  | +            card = Card.objects(id = rechargeOrder.attachParas['cardId']).first()
 | 
	
		
			
				|  |  | +            if not card:
 | 
	
		
			
				|  |  | +                raise UserServerException(u'充值卡不存在')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            logger.debug('Card<id={}> prepare refund cash. money = {}, coins = {}, before = {}'.format(
 | 
	
		
			
				|  |  | +                str(card.id), refundOrder.money, refundOrder.deductCoins, card.balance
 | 
	
		
			
				|  |  | +            ))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            card.freeze_balance(transaction_id = card.freeze_transaction_id('r'), fee = refundOrder.deductCoins)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_VIRTUAL_CARD:
 | 
	
		
			
				|  |  | +        userVirtualCard = UserVirtualCard.objects(id=rechargeOrder.attachParas['cardId']).first()
 | 
	
		
			
				|  |  | +        if not userVirtualCard:
 | 
	
		
			
				|  |  | +            raise UserServerException(u'虚拟卡不存在')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        userVirtualCard.prepare_refund(refundOrder)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    elif rechargeOrder.via == USER_RECHARGE_TYPE.RECHARGE_MONTHLY_PACKAGE:
 | 
	
		
			
				|  |  | +        monthlyPackage = MonthlyPackage.objects(id = rechargeOrder.attachParas['cardId']).first()
 | 
	
		
			
				|  |  | +        if not monthlyPackage:
 | 
	
		
			
				|  |  | +            raise UserServerException(u'包月套餐不存在')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        monthlyPackage.toggle_disable(isDisable = 1)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    else:
 | 
	
		
			
				|  |  | +        logger.debug('via({}) is not support.'.format(rechargeOrder.via))
 |