Пример #1
0
    def order_alloc(cls, admin_account, order_id, merchant):
        """
        认领分配订单
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')

        order = OrderWithdraw.query_by_order_id(merchant, order_id)
        if not order:
            msg = '%s, params: %s' % (NoSuchWithdrawOrderError.message, params)
            current_app.logger.error(msg)
            return NoSuchWithdrawOrderError()

        if order.state != OrderStateEnum.INIT:
            msg = '%s, params: %s' % (DoNotAllowedOrderError.message, params)
            current_app.logger.error(msg)
            return DoNotAllowedOrderError()

        order, ref_id = OrderUpdateCtl.update_order_event(
            order.order_id,
            uid=order.uid,
            merchant=merchant,
            state=OrderStateEnum.ALLOC,
            op_account=admin_account,
            alloc_time=DateTimeKit.get_cur_datetime(),
        )

        if not order:
            msg = '%s, params: %s' % (AllowedOrderError.message, params)
            current_app.logger.error(msg)
            return AllowedOrderError()

        return ResponseSuccess()
Пример #2
0
    def manually_withdraw(cls, admin_user, merchant, order_id):
        """
        人工出款
        :return:
        """
        # 查询 提现订单表 获取提现金额, 提现用户指定行
        withdraw_entry = OrderWithdraw.query_by_order_id(merchant=merchant,
                                                         order_id=order_id)
        if not withdraw_entry:
            return OrderInfoMissingError()

        if withdraw_entry.state != OrderStateEnum.ALLOC:
            return BankOrderStateError()

        # 更新订单状态
        order, ref_id = OrderUpdateCtl.update_order_event(
            withdraw_entry.order_id,
            uid=int(withdraw_entry.uid),
            merchant=merchant,
            state=OrderStateEnum.DEALING,
            tx_amount=withdraw_entry.amount,
            deliver_type=DeliverTypeEnum.MANUALLY,
            deal_time=DateTimeKit.get_cur_datetime(),
            op_account=admin_user.account,
            mch_fee_id=withdraw_entry.mch_fee_id,
            commit=True)
        if not order:
            return WithdrawOrderStateChangeError()

        return ResponseSuccess()
Пример #3
0
    def failed_order_process(cls, order, tx_amount, channel_tx_id):
        """
        处理充值失败的订单
        :param order:
        :param tx_amount: 实际支付金额
        :param channel_tx_id: 通道订单号
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')
        params.pop('order')
        params['tx_id'] = order.sys_tx_id

        order, _ = OrderUpdateCtl.update_order_event(
            order.order_id,
            uid=order.uid,
            merchant=order.merchant,
            state=OrderStateEnum.FAIL,
            channel_tx_id=channel_tx_id,
            tx_amount=tx_amount,
        )
        if not order:
            msg = '充值订单状态更新失败, params: %s' % params
            current_app.config['SENTRY_DSN'] and current_app.logger.fatal(
                'msg: %s', msg)
            return False

        cls.do_notify(order)

        return True
Пример #4
0
    def manually_withdraw_success(cls, admin_user, merchant, order_id,
                                  channel_cost, comment):
        """
        完成人工出款
        :return:
        """

        withdraw_entry = OrderWithdraw.query_by_order_id(merchant=merchant,
                                                         order_id=order_id)
        if not withdraw_entry:
            return OrderInfoMissingError()

        if withdraw_entry.state != OrderStateEnum.DEALING:
            return BankOrderStateError()

        detail = OrderDetailWithdraw.query_by_order_id(
            order_id=withdraw_entry.order_id,
            merchant=merchant,
            create_time=withdraw_entry.create_time)
        if not detail:
            return NosuchOrderDetailDataError()

        if detail.fee == 0:
            return WithdrawFeeEmptyError()

        # 更新订单状态

        profit = FeeCalculator.calc_profit(detail.fee, channel_cost)

        order, ref_id = OrderUpdateCtl.update_order_event(
            withdraw_entry.order_id,
            uid=int(withdraw_entry.uid),
            merchant=merchant,
            tx_amount=withdraw_entry.amount,
            state=OrderStateEnum.SUCCESS,
            deliver_type=DeliverTypeEnum.MANUALLY,
            op_account=admin_user.account,
            comment=comment,
            cost=channel_cost,
            profit=profit,
            mch_fee_id=withdraw_entry.mch_fee_id,
            commit=True)
        if not order:
            return WithdrawOrderStateChangeError()

        cls.do_notify(
            order=order,
            op_account=admin_user.account,
            comment=comment,
        )

        return ResponseSuccess()
Пример #5
0
 def order_create_fail(cls, order):
     """
     订单创建失败处理
     :return:
     """
     order, ref_id = OrderUpdateCtl.update_order_event(
         order_id=order.order_id,
         uid=order.uid,
         merchant=order.merchant,
         state=OrderStateEnum.FAIL,
     )
     if not order:
         current_app.logger.error('update order failed, sys_tx_id: %s',
                                  order.sys_tx_id)
         return False
     return True
Пример #6
0
    def order_success(cls, order, tx_amount):
        """
        订单成功处理
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('order')
        params.pop('cls')
        params['tx_id'] = order.sys_tx_id

        rst = dict(
            code=0,
            msg='',
        )

        # 计算利润
        order_detail = OrderDetailWithdraw.query_by_order_id(
            order.merchant, order.order_id, order.create_time)
        profit = FeeCalculator.calc_profit(order_detail.fee, order_detail.cost)

        order, ref_id = OrderUpdateCtl.update_order_event(
            order.order_id,
            uid=order.uid,
            merchant=order.merchant,
            state=OrderStateEnum.SUCCESS,
            tx_amount=tx_amount,
            profit=profit,
        )
        if not order:
            current_app.logger.error('提现订单修改成功状态失败, ref_id: %s, sys_tx_id: %s',
                                     ref_id, params)
            return False

        # 累计当天通道充值额度
        channel_config = ProxyChannelConfig.query_by_channel_id(
            order.channel_id)
        ChannelLimitCacheCtl.add_day_amount(channel_config.channel_enum,
                                            order.amount)

        cls.do_notify(order=order)

        return True
Пример #7
0
    def order_update(cls, order, channel_tx_id):
        """
        更新订单
        :param order:
        :param channel_tx_id:
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('order')

        order, ref_id = OrderUpdateCtl.update_order_event(
            order_id=order.order_id,
            uid=order.uid,
            merchant=order.merchant,
            channel_tx_id=channel_tx_id,
        )
        if not order:
            current_app.logger.error(
                'update order failed, sys_tx_id: %s, params: %s',
                order.sys_tx_id, params)
            return None, PreOrderCreateError()
        return order, None
Пример #8
0
    def __add_event(self, uid, merchant, channel_enum, order_type):

        order_cls = OrderDeposit if order_type == PayTypeEnum.DEPOSIT else OrderWithdraw

        channel_config = ChannelConfig.query_latest_one(
            dict(channel_enum=channel_enum))
        merchant_fee_config = MerchantFeeConfig.query_latest_one(
            dict(
                merchant=merchant,
                payment_way=PayTypeEnum.DEPOSIT,
                payment_method=channel_enum.conf.payment_method,
            ))

        params = dict(
            uid=uid,
            merchant=merchant,
            channel_id=channel_config.channel_id,
            mch_fee_id=merchant_fee_config.config_id,
            source=OrderSourceEnum.TESTING,
            order_type=order_type,
            in_type=InterfaceTypeEnum.CASHIER_H5,
            amount=Decimal("500"),
            comment='谢谢',
            op_account='xxx',
            bank_id=123,
            fee=10 if order_type == PayTypeEnum.WITHDRAW else 0,
        )
        order, ref_id = OrderCreateCtl.create_order_event(**params)
        self.assertIsNotNone(order)

        event = OrderEvent.query_one(dict(ref_id=ref_id),
                                     merchant=merchant,
                                     date=order.create_time)
        self.assertIsNotNone(event)
        self.assertEqual(order.order_id, event.order_id)
        self.assertEqual(order.uid, event.uid)
        self.assertEqual(ref_id, event.ref_id)

        order = order_cls.query_by_order_id(order_id=event.order_id,
                                            merchant=merchant)
        self.assertIsNotNone(order)
        self.assertEqual(params['uid'], order.uid)
        self.assertEqual(params['merchant'], order.merchant)
        self.assertEqual(params['channel_id'], order.channel_id)
        self.assertEqual(params['source'], order.source)
        self.assertEqual(params['amount'], order.amount)
        self.assertTrue(len(order.mch_tx_id) > 0)
        self.assertTrue(len(order.sys_tx_id) > 0)

        # 更新订单
        order, ref_id = OrderUpdateCtl.update_order_event(
            order_id=order.order_id,
            uid=order.uid,
            merchant=merchant,
            state=OrderStateEnum.SUCCESS
            if order_type == PayTypeEnum.DEPOSIT else OrderStateEnum.ALLOC,
            tx_amount=Decimal("500.32"),
            channel_tx_id='1232283838229929292',
            settle=SettleStateEnum.DONE,
            deliver=DeliverStateEnum.DONE,
            channel_id=channel_config.channel_id,
            mch_fee_id=merchant_fee_config.config_id,
            op_account='xxxx',
            comment='改了改了',
            offer=Decimal('1.22'),
            fee=Decimal('1.22'),
            cost=Decimal('1.22'),
            profit=Decimal('1.22'),
            deliver_type=DeliverTypeEnum.PROXY,
            alloc_time=DateTimeKit.get_cur_datetime(),
            deal_time=DateTimeKit.get_cur_datetime(),
        )
        self.assertIsNotNone(order)

        event = OrderEvent.query_one(dict(ref_id=ref_id),
                                     merchant=merchant,
                                     date=order.create_time)
        self.assertEqual(order.order_id, event.order_id)
        self.assertEqual(order.uid, event.uid)
        self.assertEqual(ref_id, event.ref_id)

        order3 = order_cls.query_by_order_id(order_id=event.order_id,
                                             merchant=merchant)
        self.assertIsNotNone(order3)
        self.assertEqual(params['uid'], order3.uid)
        self.assertEqual(params['merchant'], order3.merchant)
        self.assertEqual(params['channel_id'], order3.channel_id)
        self.assertEqual(params['source'], order3.source)
        self.assertEqual(Decimal("500.32"), order3.tx_amount)
        self.assertEqual(order.order_id, order3.order_id)
        self.assertEqual(order.mch_tx_id, order3.mch_tx_id)
        self.assertEqual(order.sys_tx_id, order3.sys_tx_id)

        order3 = order_cls.query_by_tx_id(tx_id=order.sys_tx_id)
        self.assertIsNotNone(order3)
        self.assertEqual(params['uid'], order3.uid)
        self.assertEqual(params['merchant'], order3.merchant)
        self.assertEqual(params['channel_id'], order3.channel_id)
        self.assertEqual(params['source'], order3.source)
        self.assertEqual(Decimal("500.32"), order3.tx_amount)
        self.assertEqual(order.order_id, order3.order_id)
        self.assertEqual(order.mch_tx_id, order3.mch_tx_id)
        self.assertEqual(order.sys_tx_id, order3.sys_tx_id)

        order3 = order_cls.query_by_uid_someday(
            merchant=merchant, uid=uid, someday=order.create_time).all()[0]
        self.assertIsNotNone(order3)
        self.assertEqual(params['uid'], order3.uid)
        self.assertEqual(params['merchant'], order3.merchant)
        self.assertEqual(params['channel_id'], order3.channel_id)
        self.assertEqual(params['source'], order3.source)
        self.assertEqual(Decimal("500.32"), order3.tx_amount)
        self.assertEqual(order.order_id, order3.order_id)
        self.assertEqual(order.mch_tx_id, order3.mch_tx_id)
        self.assertEqual(order.sys_tx_id, order3.sys_tx_id)

        begin_time, end_time = DateTimeKit.get_day_begin_end(order.create_time)
        orders = order_cls.query_by_create_time(begin_time,
                                                end_time,
                                                merchant=merchant,
                                                uid=uid).all()
        order3 = orders[-1]
        self.assertIsNotNone(order3)
        self.assertFalse(order3.is_cold_table())
        self.assertEqual(params['uid'], order3.uid)
        self.assertEqual(params['merchant'], order3.merchant)
        self.assertEqual(params['channel_id'], order3.channel_id)
        self.assertEqual(params['source'], order3.source)
        self.assertEqual(Decimal("500.32"), order3.tx_amount)
        self.assertEqual(order.order_id, order3.order_id)
        self.assertEqual(order.mch_tx_id, order3.mch_tx_id)
        self.assertEqual(order.sys_tx_id, order3.sys_tx_id)

        # 冷表查询测试
        begin_time, end_time = DateTimeKit.get_day_begin_end(order.create_time)
        clean_date = order_cls.get_clean_date()
        if clean_date.month == begin_time.month:
            begin_time = clean_date
            orders = order_cls.query_by_create_time(begin_time,
                                                    end_time,
                                                    merchant=merchant,
                                                    uid=uid).all()
            order3 = orders[-1]
            self.assertIsNotNone(order3)
            self.assertTrue(order3.is_cold_table())
            self.assertEqual(params['uid'], order3.uid)
            self.assertEqual(params['merchant'], order3.merchant)
            self.assertEqual(params['channel_id'], order3.channel_id)
            self.assertEqual(params['source'], order3.source)
            self.assertEqual(Decimal("500.32"), order3.tx_amount)
            self.assertEqual(order.order_id, order3.order_id)
            self.assertEqual(order.mch_tx_id, order3.mch_tx_id)
            self.assertEqual(order.sys_tx_id, order3.sys_tx_id)
Пример #9
0
    def do_notify(cls, order, op_account=None, comment=None):
        """
        网关回调通知
        :return:
        """
        rst = dict(msg=None)
        if not order.notify_url:
            # 不需要通知
            rst['msg'] = "没有通知URL,不需要发送通知"
            return rst

        if order.state not in OrderStateEnum.get_final_states():
            rst['msg'] = "订单不是成功/失败状态,还没有完成,不能通知商户"
            return rst

        data = dict(
            merchant_id=order.merchant.value,
            amount=str(order.amount),
            tx_amount=str(order.tx_amount),
            mch_tx_id=order.mch_tx_id,
            sys_tx_id=order.sys_tx_id,
            state=order.state.name,
        )

        data['sign'] = GatewaySign(order.merchant).generate_sign(data)
        data['extra'] = order.extra

        try:
            current_app.logger.info('do_notify: %s, data: %s',
                                    order.notify_url, data)
            rsp = requests.post(order.notify_url, json=data)
            current_app.logger.info('do_notify: %s, status code: %s, data: %s',
                                    order.notify_url, rsp.status_code,
                                    rsp.text)
        except:
            current_app.logger.fatal(traceback.format_exc())
            current_app.logger.error('do_notify, url: %s, request data: %s',
                                     order.notify_url, data)
            rst['msg'] = "通知失败,HTTP网络异常"
            return rst

        if rsp.status_code != 200:
            current_app.logger.error(
                'do_notify, url: %s, request data: %s, response data: %s',
                order.notify_url, data, rsp.text)
            rst['msg'] = "通知失败,HTTP status_code非200:%s" % rsp.status_code
            return rst

        try:
            json_data = rsp.json()
        except:
            current_app.logger.error(
                'do_notify, url: %s, request data: %s, response data: %s',
                order.notify_url, data, rsp.text)
            current_app.logger.fatal(traceback.format_exc())
            rst['msg'] = "通知失败,无法解析json数据"
            return rst

        if json_data['error_code'] != 200:
            rst['msg'] = "通知失败,error_code非200:%s" % json_data['error_code']
            current_app.logger.error(
                'do_notify, url: %s, request data: %s, response data: %s',
                order.notify_url, data, json_data)
            return rst

        # 更新订单通知完成
        if order.deliver != DeliverStateEnum.DONE:
            order, ref_id = OrderUpdateCtl.update_order_event(
                order_id=order.order_id,
                uid=order.uid,
                merchant=order.merchant,
                deliver=DeliverStateEnum.DONE,
                op_account=op_account,
                comment=comment,
            )
            if not order:
                rst['msg'] = '订单更新失败, 请研发检查错误日志, sys_tx_id: %s' % (
                    order.sys_tx_id, )
                current_app.logger.fatal(rst['msg'])
                return rst

        rst['msg'] = "通知成功"
        return rst
Пример #10
0
    def manually_withdraw_failed(cls, admin_user, merchant, order_id):
        """
        手动更新提款状态为失败
        退款/审核拒绝
                流程:
            1. 获取创建订单时 扣除 用户及商户的费用
                获取订单 withdrawOrderDetail 数据, 获取 手续费 提现金额
            2. 给用户和商户新增费用
                更新 UserBalance 表
                更新 MerchantBalance表
            3. 修改订单状态
                更新 OrderUpdateCtl
        :param admin_user:
        :param merchant:
        :param order_id:
        :return:
        """
        # 查询该笔订单是否存在
        withdraw_entry = OrderWithdraw.query_by_order_id(merchant=merchant,
                                                         order_id=order_id)
        # 判断是否存在
        if not withdraw_entry:
            return OrderInfoMissingError()

        # 判断订单状态是否为 已认领 或 提现成功
        if withdraw_entry.state not in [
                OrderStateEnum.ALLOC, OrderStateEnum.SUCCESS
        ]:
            return BankOrderStateError()

        detail = OrderDetailWithdraw.query_by_order_id(
            order_id=withdraw_entry.order_id,
            merchant=merchant,
            create_time=withdraw_entry.create_time)
        if not detail:
            return NosuchOrderDetailDataError()

        # 提现订单 手续费 提现订单费用
        fee = detail.fee
        amount = detail.amount

        comment = "出款失败" if withdraw_entry.state == OrderStateEnum.SUCCESS else "系统拒绝"
        order_type = PayTypeEnum.REFUND if withdraw_entry.state == OrderStateEnum.SUCCESS else PayTypeEnum.MANUALLY

        merchant_config = MerchantFeeConfig.query_by_config_id(
            withdraw_entry.mch_fee_id)

        # 更新订单状态
        try:
            with db.auto_commit():

                order, ref_id = OrderUpdateCtl.update_order_event(
                    withdraw_entry.order_id,
                    uid=int(withdraw_entry.uid),
                    merchant=merchant,
                    state=OrderStateEnum.FAIL,
                    tx_amount=withdraw_entry.amount,
                    deliver_type=DeliverTypeEnum.MANUALLY,
                    op_account=admin_user.account,
                    commit=False)

                if not order:
                    msg = WithdrawOrderStateChangeError.message
                    current_app.logger.error(msg)
                    raise WithdrawOrderStateChangeError()

                # 加提现金额
                flag, msg = MerchantBalanceEvent.update_balance(
                    merchant=merchant,
                    ref_id=ref_id,
                    order_type=order_type,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    value=amount,
                    ad_type=BalanceAdjustTypeEnum.PLUS,
                    tx_id=order.sys_tx_id,
                    source=OrderSourceEnum.MANUALLY,
                    comment=comment,
                    commit=False,
                )
                if flag < 0:
                    msg = '%s' % ("提现退回增加商户余额失败, %s" % msg)
                    current_app.logger.error(msg)
                    raise DepositCallbackUserBalanceError()

                if merchant_config.cost_type == CostTypeEnum.MERCHANT:
                    # 给商户加手续费
                    flag, msg = MerchantBalanceEvent.update_balance(
                        merchant=merchant,
                        ref_id=OrderUtils.gen_unique_ref_id(),
                        order_type=PayTypeEnum.FEE,
                        bl_type=BalanceTypeEnum.AVAILABLE,
                        value=fee,
                        ad_type=BalanceAdjustTypeEnum.PLUS,
                        tx_id=order.sys_tx_id,
                        commit=False,
                        comment=comment,
                        source=OrderSourceEnum.MANUALLY)
                    if flag < 0:
                        msg = '%s' % ("提现退款增加商户手续费失败, %s" % msg)
                        current_app.logger.error(msg)
                        raise DepositCallbackUserBalanceError()

                refund_fee = amount
                if merchant_config.cost_type == CostTypeEnum.USER:
                    # 给用户退回手续费
                    refund_fee += fee

                # 增加用户余额
                flag, msg = UserBalanceEvent.update_user_balance(
                    uid=order.uid,
                    merchant=merchant,
                    ref_id=ref_id,
                    order_type=order_type,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    value=refund_fee,
                    ad_type=BalanceAdjustTypeEnum.PLUS,
                    tx_id=order.sys_tx_id,
                    commit=False,
                    comment=comment,
                    source=OrderSourceEnum.MANUALLY)
                if flag < 0:
                    msg = '%s' % ("提现退款增加用户余额失败, %s" % msg)
                    current_app.logger.error(msg)
                    raise DepositCallbackUserBalanceError()

        except APIException as e:
            current_app.logger.error(traceback.format_exc())
            return e

        cls.do_notify(
            order=order,
            op_account=admin_user.account,
            comment=comment,
        )

        return ResponseSuccess()
Пример #11
0
    def order_fail(cls, order):
        """
        订单失败处理
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')
        params.pop('order')
        params['tx_id'] = order.sys_tx_id

        rst = dict(
            code=0,
            msg='',
        )

        # 手续费存在订单详情里面
        order_detail = OrderDetailWithdraw.query_by_order_id(
            order.merchant, order.order_id, order.create_time)
        merchant_config = MerchantFeeConfig.query_by_config_id(
            order.mch_fee_id)

        try:
            # 创建提现订单/扣商户余额/扣用户余额,在同一个事务里面
            with db.auto_commit():
                order, ref_id = OrderUpdateCtl.update_order_event(
                    order.order_id,
                    uid=order.uid,
                    merchant=order.merchant,
                    state=OrderStateEnum.FAIL,
                    commit=False,
                )
                if not order:
                    raise RuntimeError('提现订单修改失败状态失败, params: %s' % params)

                # 给商户退回提现订单的发起金额
                flag, msg = MerchantBalanceEvent.update_balance(
                    merchant=order.merchant,
                    ref_id=ref_id,
                    source=order.source,
                    order_type=PayTypeEnum.REFUND,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    # 订单发起金额
                    value=order.amount,
                    ad_type=BalanceAdjustTypeEnum.PLUS,
                    tx_id=order.sys_tx_id,
                    commit=False,
                )
                # print('update_balance', flag, msg)
                if flag < 0:
                    raise RuntimeError(msg + ", params: %s" % params)

                if merchant_config.cost_type == CostTypeEnum.MERCHANT:
                    # 给商户退回提手续费
                    flag, msg = MerchantBalanceEvent.update_balance(
                        merchant=order.merchant,
                        ref_id=OrderUtils.gen_unique_ref_id(),
                        tx_id=order.sys_tx_id,
                        source=order.source,
                        order_type=PayTypeEnum.FEE,
                        bl_type=BalanceTypeEnum.AVAILABLE,
                        # 收取商户的手续费
                        value=order_detail.fee,
                        ad_type=BalanceAdjustTypeEnum.PLUS,
                        commit=False,
                    )
                    # print('update_balance', flag, msg)
                    if flag < 0:
                        raise RuntimeError(msg + ", params: %s" % params)

                refund_fee = order.amount
                if merchant_config.cost_type == CostTypeEnum.USER:
                    # 给用户退回手续费
                    refund_fee += order_detail.fee

                # 给用户退回发起金额
                flag, msg = UserBalanceEvent.update_user_balance(
                    uid=order.uid,
                    merchant=order.merchant,
                    ref_id=ref_id,
                    source=order.source,
                    order_type=PayTypeEnum.REFUND,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    # 订单发起金额
                    value=refund_fee,
                    ad_type=BalanceAdjustTypeEnum.PLUS,
                    tx_id=order.sys_tx_id,
                    commit=False,
                )
                # print('update_user_balance', flag, msg)
                if flag < 0:
                    raise RuntimeError(msg + ", params: %s" % params)

        except APIException as e:
            current_app.logger.error(traceback.format_exc())
            return False

        cls.do_notify(order=order)

        return True
Пример #12
0
    def order_deal(cls,
                   admin_account,
                   order_id,
                   merchant,
                   channel_id,
                   test=False):
        """

        :param admin_account:
        :param order_id:
        :param merchant:
        :param channel_id:
        :param test: 单元测试填写
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')

        if EnvironEnum.is_production(current_app.config['FLASK_ENV']) and test:
            # 非调试环境不该填写 channel_tx_id,应该发送请求到第三方去申请
            raise RuntimeError('invalid test param')

        order = OrderWithdraw.query_by_order_id(merchant, order_id)
        if not order:
            msg = '%s, params: %s' % (NoSuchWithdrawOrderError.message, params)
            current_app.logger.error(msg)
            return NoSuchWithdrawOrderError()

        if order.state != OrderStateEnum.ALLOC:
            msg = '%s, params: %s, state: %s, tx_id: %s' % (
                NotAllocOrderError.message, params, order.state.desc,
                order.sys_tx_id)
            current_app.logger.error(msg)
            return NotAllocOrderError(message=NotAllocOrderError.message +
                                      ", 订单状态:" + order.state.desc)

        channel_config = ProxyChannelConfig.query_by_channel_id(channel_id)
        if not channel_config:
            msg = '%s, params: %s' % (InvalidChannelError.message, params)
            current_app.logger.error(msg)
            return InvalidChannelError()

        bank_card = order.get_bank_card()
        if not bank_card:
            msg = '%s, params: %s' % (WithdrawBankNoExistError.message, params)
            current_app.logger.error(msg)
            return WithdrawBankNoExistError()

        # 开始更新订单,根据通道计算费率和成本
        # 通道收取的手续费
        channel_cost = FeeCalculator.calc_cost(order.amount,
                                               channel_config.fee_type,
                                               channel_config.fee)
        tx_amount = order.amount

        channel_enum = channel_config.channel_enum
        if channel_enum.plus_fee_for_withdraw():
            # 特殊通道,要发起金额要加上手续费,通道测扣除手续费才是实际到账金额
            # 实际提款金额=发起金额+通道手续费
            tx_amount += channel_cost

        # 发起支付
        launch_pay = channel_enum.get_launch_pay_func(PayTypeEnum.WITHDRAW)

        if not test:
            # 第三方返回由第三方生成交易ID:channel_tx_id
            rst = launch_pay(
                dict(order_id=order.order_id,
                     tx_id=order.sys_tx_id,
                     amount=tx_amount,
                     channel_cost=channel_cost,
                     bank_code=bank_card.bank_code,
                     bank_name=bank_card.bank_name,
                     bank_account=bank_card.account_name,
                     bank_number=bank_card.card_no,
                     bank_address=bank_card.bank_address,
                     bank_branch=bank_card.branch,
                     province=bank_card.province,
                     city=bank_card.city))

            current_app.logger.info('withdraw launch_pay, params: %s, rst: %s',
                                    params, rst)

            if rst['code'] != 0:
                # 不要改变订单状态,让客服去选择其它通道重试
                # cls.order_fail(order, client_ip)
                current_app.logger.error(
                    '%s, %s, params: %s' %
                    (FailedLaunchWithdrawError.message, rst['msg'], params))
                return FailedLaunchWithdrawError(message=rst['msg'])

        # 发起成功后更新状态为处理中
        order, _ = OrderUpdateCtl.update_order_event(
            order.order_id,
            uid=order.uid,
            merchant=merchant,
            state=OrderStateEnum.DEALING,
            channel_id=channel_id,
            cost=channel_cost,
            deal_time=DateTimeKit.get_cur_datetime(),
            op_account=admin_account,
        )
        if not order:
            msg = '%s, params: %s' % (WithdrawUpdateDealingError.message,
                                      params)
            current_app.logger.error(msg)
            return WithdrawUpdateDealingError()

        return ResponseSuccess()
Пример #13
0
    def success_order_process(cls,
                              order,
                              tx_amount,
                              channel_tx_id=None,
                              comment: str = '',
                              op_account=None,
                              commit=True):
        """
        处理充值成功的订单
        :param order:
        :param tx_amount: 实际支付金额
        :param channel_tx_id: 通道订单号
        :param comment: 备注
        :param op_account: 备注
        :param commit: 是否立即提交事务
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')
        params.pop('order')
        params['tx_id'] = order.sys_tx_id

        rst = dict(
            code=0,
            msg='',
        )

        # 计算一笔订单的各种费用
        channel_config = ChannelConfig.query_by_channel_id(order.channel_id)
        merchant_config = MerchantFeeConfig.query_by_config_id(
            order.mch_fee_id)
        order_fee = OrderFeeHelper.calc_order_fee(order, tx_amount,
                                                  channel_config,
                                                  merchant_config)

        try:
            with db.auto_commit(commit):
                order, ref_id = OrderUpdateCtl.update_order_event(
                    order.order_id,
                    uid=order.uid,
                    merchant=order.merchant,
                    state=OrderStateEnum.SUCCESS,
                    channel_tx_id=channel_tx_id,
                    tx_amount=tx_amount,
                    offer=order_fee['offer'],  # 优惠金额
                    fee=order_fee['merchant_fee'],  # 手续费
                    cost=order_fee['channel_cost'],  # 成本金额
                    profit=order_fee['profit'],  # 利润(收入)金额
                    commit=False,
                    pay_method=channel_config.channel_enum.
                    conf['payment_method'],
                    comment=comment,
                    op_account=op_account,
                )
                if not order:
                    msg = '订单更新失败, params: %s' % params
                    raise RuntimeError(msg)

                # 给用户充值
                code, msg = UserBalanceEvent.update_user_balance(
                    uid=order.uid,
                    merchant=order.merchant,
                    ref_id=ref_id,
                    source=order.source,
                    order_type=order.order_type,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    value=order.amount,
                    ad_type=BalanceAdjustTypeEnum.PLUS,
                    comment=comment,
                    tx_id=order.sys_tx_id,
                    commit=False,
                )
                if code != 0:
                    raise RuntimeError(msg)

                # 根据结算类型获取商户余额变更类型
                balance_type = SettleHelper.get_balance_type_by_settle(
                    channel_config.settlement_type)

                # 更新商户余额,加用户充值金额
                code, msg = MerchantBalanceEvent.update_balance(
                    merchant=order.merchant,
                    ref_id=ref_id,
                    source=order.source,
                    order_type=order.order_type,
                    bl_type=balance_type,
                    value=order.amount,
                    ad_type=BalanceAdjustTypeEnum.PLUS,
                    tx_id=order.sys_tx_id,
                    comment=comment,
                    commit=False,
                )
                if code != 0:
                    raise RuntimeError(msg)

                # 更新商户余额,扣手续费
                ref_id = OrderUtils.gen_unique_ref_id()
                code, msg = MerchantBalanceEvent.update_balance(
                    merchant=order.merchant,
                    ref_id=ref_id,
                    source=order.source,
                    order_type=PayTypeEnum.FEE,
                    bl_type=balance_type,
                    value=order_fee['merchant_fee'],
                    ad_type=BalanceAdjustTypeEnum.MINUS,
                    tx_id=order.sys_tx_id,
                    comment=comment,
                    commit=False,
                )
                if code != 0:
                    raise RuntimeError(msg)

        except RuntimeError as e:
            current_app.logger.error('An error occurred.', exc_info=True)
            return False

        # 累计当天通道充值额度
        ChannelLimitCacheCtl.add_day_amount(channel_config.channel_enum,
                                            order.amount)

        cls.do_notify(order)

        return True