Example #1
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
Example #2
0
    def post(self):
        """
        提现订单详情
        :return:
        """
        form, error = WithDrawBankForm().request_validate()
        if error:
            return error.as_response()

        order_id = form.order_id.data
        order_map = GlobalOrderId.query_global_id(order_id)
        merchant = order_map.merchant
        entry = OrderWithdraw.query_by_order_id(order_id=order_id,
                                                merchant=merchant)
        detail = OrderDetailWithdraw.query_by_order_id(merchant=merchant,
                                                       order_id=order_id)
        detail_head = dict(source=entry.source.desc,
                           op_account=detail.op_account,
                           deliver_type=detail.deliver_type.desc
                           if detail.deliver_type else None,
                           create_time=entry.str_create_time,
                           alloc_time=detail.str_alloc_time,
                           deal_time=detail.str_deal_time,
                           done_time=detail.str_done_time,
                           mch_tx_id=entry.mch_tx_id,
                           sys_tx_id=entry.sys_tx_id,
                           state=entry.state.get_back_desc(
                               PayTypeEnum.WITHDRAW),
                           settle=entry.settle.desc,
                           deliver=entry.deliver.desc,
                           amount=entry.amount)
        order_merchant_info = dict(
            merchant_name=merchant.name,
            fee=detail.fee,
            cost=detail.cost,
            profit=detail.profit,
            withdraw_type="测试" if merchant.is_test else "用户提现")
        deliver_info = None
        if entry.channel_id:
            proxy_entry = ProxyChannelConfig.query_by_channel_id(
                entry.channel_id)
            channel_enum = proxy_entry.channel_enum

            deliver_info = dict(channel_name=channel_enum.desc,
                                mch_id=channel_enum.conf['mch_id'],
                                channel_tx_id=entry.channel_tx_id)
        user_info = dict(
            user_id=entry.uid,
            ip=detail.ip,
            location=GeoIpKit(detail.ip).location,
            device="",
        )

        event_entries = OrderEvent.query_model(
            query_fields=dict(order_id=entry.order_id), date=entry.create_time)

        event_log_list = list()

        for event in event_entries:
            order_event = event.data_after[0]
            order_event.update(event.data_after[1])

            if 'state' in order_event:
                state = list(order_event['state'].keys())[0]
                event_log_list.append(
                    dict(
                        operate_type=OrderStateEnum.from_name(
                            state).get_back_desc(PayTypeEnum.WITHDRAW),
                        operator=order_event.get('op_account') or '',
                        result="成功",
                        operate_time=DateTimeKit.timestamp_to_datetime(
                            order_event['update_time']),
                        comment=order_event.get('comment') or '',
                    ))

            if 'deliver' in order_event:
                deliver = list(order_event['deliver'].keys())[0]
                event_log_list.append(
                    dict(
                        operate_type=DeliverStateEnum.from_name(deliver).desc,
                        operator=order_event.get('op_account') or '',
                        result="成功",
                        operate_time=DateTimeKit.timestamp_to_datetime(
                            order_event['update_time']),
                        comment=order_event.get('comment') or '',
                    ))

        return WithdrawOrderDetailResult(
            bs_data=dict(detail_head=detail_head,
                         order_merchant_info=order_merchant_info,
                         deliver_info=deliver_info,
                         user_info=user_info,
                         event_log_list=event_log_list)).as_response()
Example #3
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()
Example #4
0
    def post(self):
        """
        专一付代付回调
        :return:
        """
        if not EnvironEnum.is_local_evn(current_app.config['FLASK_ENV']):
            # 无论如何都记录一条log
            current_app.logger.info(
                'zhuanyifu withdraw callback, ip: %s, data: %s, headers: %s',
                IpKit.get_remote_ip(), request.json, request.headers)

        event = request.headers.get('ChinaRailway-Event')
        signature = request.headers.get('ChinaRailway-Signature')

        form, error = ZhuanYeFuWithdrawForm().request_validate()
        if error:
            current_app.logger.fatal('msg: %s, data: %s', error.message,
                                     request.args)
            return BaseResponse('FAIlURE')

        pp = signature.split('.')

        if event != "Pay.Succeeded":
            return ResponseSuccess(code=500).as_response()

        # 交易ID
        tx_id = form.order.data
        fee = Decimal(form.fee.data)
        order = WithdrawTransactionCtl.get_order(tx_id)
        if not order:
            return ResponseSuccess(
                code=500, message='curr order no found').as_response()

        curr_status = order.state
        if curr_status != OrderStateEnum.DEALING:
            return ResponseSuccess(
                code=500,
                message='curr order status must be DEALING').as_response()

        print(order.channel_id, form.order.data, order.merchant,
              order.create_time, order.order_id, order.uid)

        channel_config = ProxyChannelConfig.query_by_channel_id(
            order.channel_id)
        channel_cost = FeeCalculator.calc_cost(order.amount,
                                               channel_config.fee_type,
                                               channel_config.fee)
        if fee != channel_cost:
            current_app.logger.error(
                "ZYF withdraw fee info order_id:{}, channel_fee: {}, channel_cost:{}"
                .format(order.order_id, fee, channel_cost))

        try:
            flag = CryptoKit.rsa_verify(
                pp[1], pp[0],
                channel_config.channel_enum.conf['plat_public_key'])
            if flag != True:
                return ResponseSuccess(code=500, message='签名错误').as_response()
        except Exception as e:
            return ResponseSuccess(code=500).as_response()

        # 代付金额
        tx_amount = Decimal(form.amount.data)
        # 代付费率
        fee = Decimal(form.fee.data)
        # 通道订单号
        transaction = form.transaction.data
        client_ip = form.client_ip.data
        status = form.status.data

        if str(status) == "1":
            """
            修改订单状态, 记录代付费率
            """
            if not WithdrawTransactionCtl.order_success(order, tx_amount):
                return ResponseSuccess(code=500).as_response()

        elif str(status) == "2":
            """
            代付订单失败, 
            1.给用户退款,给商户退款+手续费
            2. 修改订单状态 
            """
            # order = WithdrawTransactionCtl.get_order(merchant, order_id)
            if not WithdrawTransactionCtl.order_fail(order):
                return ResponseSuccess(code=500).as_response()

        return ResponseSuccess(code=204).as_response()