Exemplo n.º 1
0
    def query_by_tx_id(cls, tx_id):
        """
        根据交易ID查询订单
        :param tx_id:
        :return:
        """
        if cls.is_base_order():
            raise RuntimeError('can not query by base order')

        order_id = OrderUtils.parse_tx_id(tx_id)
        g_order_id = GlobalOrderId.query_global_id(order_id)
        if not g_order_id:
            return None

        return cls.query_by_order_id(g_order_id.merchant, order_id,
                                     g_order_id.create_time)
Exemplo n.º 2
0
    def query_by_order_id(cls, merchant, order_id, create_time=None):
        """
        更具订单号查询订单
        :param merchant:
        :param order_id:
        :param create_time:
        :return:
        """
        if cls.is_base_order():
            raise RuntimeError('can not query by base order')

        if not create_time:
            g_order_id = GlobalOrderId.query_global_id(order_id)
            if not g_order_id:
                return None
            create_time = g_order_id.create_time

        return cls.query_one(dict(id=order_id), date=create_time)
Exemplo n.º 3
0
    def get_order_by_order_id(cls, order_id):
        """
        根据order id查询订单
        :param order_id:
        :return:
        """
        g_order_id = GlobalOrderId.query_global_id(order_id)
        if not g_order_id:
            msg = '无法从 GlobalOrderId 查到订单,order_id: %s' % order_id
            current_app.config['SENTRY_DSN'] and current_app.logger.fatal(
                'msg: %s', msg)
            return None

        order = OrderWithdraw.query_by_order_id(merchant=g_order_id.merchant,
                                                order_id=order_id)
        if not order:
            msg = '无法从 OrderWithdraw 查到订单,order_id: %s' % order_id
            current_app.config['SENTRY_DSN'] and current_app.logger.fatal(
                'msg: %s', msg)
            return None

        return order
Exemplo n.º 4
0
    def post(self):
        """
        人工完成充值订单
        :return:
        """
        form, error = OrderIdCommentForm().request_validate()
        if error:
            return error.as_response()

        order_id = form.order_id.data
        g_order_id = GlobalOrderId.query_global_id(order_id)

        order = OrderDeposit.query_by_order_id(merchant=g_order_id.merchant,
                                               order_id=order_id)

        if DepositTransactionCtl.success_order_process(
                order,
                order.amount,
                comment=form.comment.data,
                op_account=g.user.account):
            return ResponseSuccess(message="处理成功")
        else:
            return ResponseSuccess(message="处理失败")
Exemplo n.º 5
0
    def create_order_event(
            cls,
            uid,
            amount,
            merchant: MerchantEnum,
            source: OrderSourceEnum,  # 订单来源
            order_type: PayTypeEnum,  # 订单类型
            in_type: InterfaceTypeEnum,  # 商户接入类型
            pay_method: PayMethodEnum = None,  # 支付方法
            create_time=None,
            bank_id=None,  # 用户提现银行卡ID
            ref_id=None,  # 票据ID
            op_account=None,  # 后台管理员账号,后台人工修改数据时必填
            comment=None,  # 管理后台修改备注,后台人工修改数据时必填
            mch_tx_id=None,  # 商户交易ID,当in_type为API时必填,其它时候选填
            channel_id: int = None,  # 通道费率ID,ABSChannelConfig 表中的主键ID,充值必填,提款不填
            mch_fee_id: int = None,  # 商户费率配置ID,MerchantFeeConfig 表的主键ID,充值必填,提款不填
            ip=None,
            fee=None,  # 提现订单,创建的时候就扣了手续费
            notify_url=None,
            result_url=None,
            extra=None,
            bank_info=None,  # 银行卡信息,用于API商户
            cost_type=None,
            commit=True,  # 是否立即提交事务
    ):
        if source == OrderSourceEnum.MANUALLY and not (op_account and comment):
            raise RuntimeError("人工修改订单必填管理员账号和备注")

        if in_type == InterfaceTypeEnum.API and not mch_tx_id:
            raise RuntimeError("开放API必须提供商户交易ID")

        if order_type == PayTypeEnum.DEPOSIT and not (channel_id and mch_fee_id):
            raise RuntimeError("充值必须填写channel_id/mch_fee_id/bank_id")

        if order_type == PayTypeEnum.WITHDRAW:
            if not (fee and mch_fee_id):
                raise RuntimeError("提现必须填写 fee/mch_fee_id")
            if not (bank_info or bank_id):
                raise RuntimeError("提现必须填写 bank_info 或 bank_id")

        create_time = create_time or DateTimeKit.get_cur_datetime()
        order_id = GlobalOrderId.generate_order_id(uid, order_type, create_time, merchant)
        sys_tx_id = OrderUtils.generate_sys_tx_id(order_id)

        if not mch_tx_id:
            mch_tx_id = OrderUtils.generate_mch_tx_id(order_id)

        params = copy.deepcopy(locals())
        params.pop('cls')

        if not ref_id:
            ref_id = OrderConstraint.apply_ref_id(order_id, order_type, OrderStateEnum.INIT)
            if not ref_id:
                return None, ''
            params['ref_id'] = ref_id

        try:
            # 订单模型
            rst, fields = cls.__create_order(**params)
            if rst['code'] != 0:
                OrderConstraint.revoke_order_state(order_id, OrderStateEnum.INIT)
                current_app.logger.error('failed to create order model, rst: %s, params: %s', rst, params)
                return None, ref_id

            models = list(rst['model'].values())
            order = rst['model']['hot']

            # 订单详情
            rst, fields = cls.__create_order_detail(**params)
            if rst['code'] != 0:
                OrderConstraint.revoke_order_state(order_id, OrderStateEnum.INIT)
                current_app.logger.error('failed to create order detail model, rst: %s, params: %s', rst, params)
                return None, ref_id

            models.extend(rst['model'].values())
            order_detail = rst['model']['hot']

            # 订单修改事件
            rst = cls.__create_event(**params)
            order_event = rst['model']
            order_event.data_after = [order.get_after_fields(), order_detail.get_after_fields()]
            models.append(order_event)

            # 提交事务
            assert len(models) == 5
            ModelOpMix.commit_models(models=models, commit=commit)

        except IntegrityError as e:
            OrderConstraint.revoke_order_state(order_id, OrderStateEnum.INIT)
            # sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1062, "Duplicate entry '4155' for key 'mch_tx_id'")
            msg = str(e)
            if 'Duplicate' in msg and 'mch_tx_id' in msg:
                # 订单号重复时不能重置订单状态
                msg = "商户订单号重复"
            current_app.logger.error(str(msg), exc_info=True)
            raise Exception(msg)

        except Exception as e:
            # 任何非期望的异常都要回滚状态
            OrderConstraint.revoke_order_state(order_id, OrderStateEnum.INIT)
            current_app.logger.error(str(e), exc_info=True)
            raise e

        return order, ref_id
Exemplo n.º 6
0
    def update_order_event(
            cls,
            order_id,
            uid,
            merchant: MerchantEnum,
            state: OrderStateEnum = None,  # 订单状态
            tx_amount=0,  # 实际支付金额
            pay_method: PayMethodEnum = None,  # 支付方法
            ref_id=None,  # 票据ID
            channel_tx_id=None,  # 通道交易ID,支付成功后填写
            settle: SettleStateEnum = None,  # 结算状态
            deliver: DeliverStateEnum = None,  # 给下游商户的发货(通知)状态
            channel_id: int = None,  # 通道费率ID,ABSChannelConfig 表中的主键ID,确认出款时必填
            mch_fee_id: int = None,  # 商户费率配置ID,MerchantFeeConfig 表的主键ID,确认出款时必填
            op_account=None,  # 后台管理员账号
            comment=None,  # 管理后台修改备注
            offer=None,  # 优惠金额
            fee=None,  # 手续费
            cost=None,  # 成本金额
            profit=None,  # 利润(收入)金额
            deliver_type: DeliverTypeEnum = None,  # 出款类型
            alloc_time=None,  # 提款订单分配时间
            deal_time=None,  # 提款订单处理时间
            done_time=None,  # 订单完成时间
            commit=True,  # 是否立即提交事务
    ):
        update_time = DateTimeKit.get_cur_datetime()
        g_order_id = GlobalOrderId.query_global_id(order_id)
        create_time = g_order_id.create_time
        order_type = g_order_id.order_type

        if order_type == PayTypeEnum.WITHDRAW:
            if state == OrderStateEnum.DEALING:
                if deliver_type == DeliverTypeEnum.MANUALLY:
                    if not (mch_fee_id and deal_time):
                        raise RuntimeError("人工出款时必填 mch_fee_id/deal_time")
                else:
                    if not deal_time:
                        raise RuntimeError("出款时必填 deal_time")
            if state == OrderStateEnum.ALLOC and not alloc_time:
                raise RuntimeError("认领订单必填认领时间")

        elif order_type == PayTypeEnum.DEPOSIT:
            pass

        if state in [OrderStateEnum.SUCCESS, OrderStateEnum.FAIL
                     ] and not done_time:
            # 订单交易完成
            done_time = update_time

        params = copy.deepcopy(locals())
        params.pop('cls')

        if not ref_id:
            ref_id = OrderConstraint.apply_ref_id(order_id, order_type, state)
            if not ref_id:
                # 票据申请失败
                return None, ref_id
            params['ref_id'] = ref_id

        try:
            # 订单事件
            rst = cls.__create_event(**params)
            order_event = rst['model']

            rst, fields = cls.__update_order(**params)
            if rst['code'] != 0:
                OrderConstraint.revoke_order_state(order_id, state)
                current_app.logger.error('rst: %s, params: %s', rst, params)
                return None, ''

            models = list(rst['model'].values())
            order = rst['model']['hot']

            # 订单详情
            rst, fields = cls.__update_order_detail(**params)
            if rst['code'] != 0:
                OrderConstraint.revoke_order_state(order_id, state)
                current_app.logger.error('rst: %s, params: %s', rst, params)
                return None, ''

            models.extend(rst['model'].values())
            order_detail = rst['model']['hot']

            # 订单修改事件
            rst = cls.__create_event(**params)
            order_event = rst['model']
            order_event.data_before = [
                order.get_before_fields(),
                order_detail.get_before_fields()
            ]
            order_event.data_after = [
                order.get_after_fields(),
                order_detail.get_after_fields()
            ]
            models.append(order_event)

            # 提交事务
            assert len(models) == 5
            ModelOpMix.commit_models(models=models, commit=commit)

        except Exception as e:
            # 任何非期望的异常都要回滚状态
            OrderConstraint.revoke_order_state(order_id, state)
            current_app.logger.error(traceback.format_exc())
            return None, ref_id

        return order, ref_id
Exemplo n.º 7
0
 def get_user_latest_order(cls, uid, order_type: PayTypeEnum):
     g_order_id = GlobalOrderId.query_latest_one(uid, order_type)
     return OrderWithdraw.query_by_order_id(merchant=g_order_id.merchant,
                                            order_id=g_order_id.order_id)
Exemplo n.º 8
0
    def post(self):
        """
        充值订单详情
        :return:
        """
        # form表单验证用提款的
        form, error = WithDrawBankForm().request_validate()
        if error:
            return error.as_response()

        # 取得订单id
        order_id = form.order_id.data
        # 根据订单id获取对应的商户 用户id
        order_map = GlobalOrderId.query_global_id(order_id)
        # 获取商户id
        merchant = order_map.merchant

        # 根据商户id 订单id查询具体的订单信息
        entry = OrderDeposit.query_by_order_id(order_id=order_id,
                                               merchant=merchant)

        # 查询对应的订单详情表
        detail = OrderDetailDeposit.query_by_order_id(merchant=merchant,
                                                      order_id=order_id)

        # 拼接返回数据
        detail_head = dict(
            source=entry.source.desc,
            create_time=entry.str_create_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.DEPOSIT),
            settle=entry.settle.desc,
            deliver=entry.deliver.desc,
            amount=entry.amount,
        )
        order_merchant_info = dict(merchant_name=merchant.name,
                                   offer=detail.offer,
                                   fee=detail.fee,
                                   cost=detail.cost,
                                   profit=detail.profit)
        deliver_info = None
        if entry.channel_id:
            proxy_entry = ChannelConfig.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.DEPOSIT),
                        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 DepositOrderDetailResult(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()
Exemplo n.º 9
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()