예제 #1
0
    def check_and_clean(self):

        delete_files = list()

        for name in os.listdir(self.log_path):
            # print(name)
            if name == self.filename:
                continue

            try:
                date = self.parse_suffix(name)
            except:
                continue

            delta_days = (DateTimeKit.get_cur_datetime() - date).days
            # print(delta_days, keep_days)
            if delta_days < self.keep_days:
                print('keep file: ', name)
                continue

            delete_files.append(name)

        for name in delete_files:
            file = os.path.join(self.log_path, name)
            print('to remove', file)
            if os.path.exists(file):
                print('removed ', file)
                os.remove(file)
예제 #2
0
    def is_in_trade_time(
        self, end_delta: datetime.timedelta = datetime.timedelta(minutes=5)):
        """
        判断当前通道是否处于交易时间内
        :param end_delta:
        :return:
        """
        if (self.trade_begin_hour == 0 and self.trade_begin_minute == 0) and (
            (self.trade_end_hour == 0 and self.trade_end_minute == 0) or
            (self.trade_end_hour == 23 and self.trade_end_minute == 59)):
            # 未设置交易时间,或者结束时间为23点59
            return True

        begin_time = self.begin_trade_time
        end_time = self.end_trade_time

        if end_delta:
            end_time -= end_delta

        cur_datetime = DateTimeKit.get_cur_datetime()

        if end_time < begin_time:
            # 如果设置的结束时间小于开始时间,比如交易周期是早上9点到凌晨的2点,那么交易时间是9点到0点以及0点到2点
            return cur_datetime <= end_time or cur_datetime >= begin_time

        return begin_time <= cur_datetime <= end_time
예제 #3
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()
예제 #4
0
    def create_order(cls):
        merchant = MerchantEnum.TEST
        order_id = OrderTstGid.generate_order_id()
        create_time = DateTimeKit.get_cur_datetime()

        fields = dict(
            uid=0,
            create_time=create_time,
            update_time=create_time,
            order_id=order_id,
            amount=Decimal("234142.33"),
            mch_tx_id=OrderUtils.generate_mch_tx_id(order_id),
            sys_tx_id=OrderUtils.generate_sys_tx_id(order_id),
            source=OrderSourceEnum.MANUALLY,
            state=OrderStateEnum.INIT,
            op_account='test',
            pay_method=PayMethodEnum.ZHIFUBAO_SAOMA,
            notify_url="https://google.com",
            result_url="https://google.com",
            extra=json.dumps(dict(x=1, y=2, z=3)),
        )

        rst = cls.add_model(fields,
                            merchant=merchant,
                            date=create_time,
                            commit=True)
        return rst
예제 #5
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()
예제 #6
0
    def check_and_clean(cls, keep_days=7):

        full_path = os.path.join(BASEDIR, log_path)

        delete_files = list()

        for name in os.listdir(full_path):
            # print(name)
            if log_prefix not in name:
                continue

            try:
                date_str = name.split('.')[-1]
                date = DateTimeKit.str_to_datetime(
                    date_str, DateTimeFormatEnum.DAY_FORMAT)
            except:
                continue

            delta_days = (DateTimeKit.get_cur_datetime() - date).days
            # print(delta_days, keep_days)
            if delta_days < keep_days:
                print('keep file: ', name)
                continue

            delete_files.append(name)

        for name in delete_files:
            file = os.path.join(full_path, name)
            print('removed ', file)
            os.remove(file)
예제 #7
0
    def drop_all_records(cls):
        create_time = DateTimeKit.get_cur_datetime()

        with db.auto_commit():
            model_cls = cls.get_model_cls(date=create_time)
            model_cls.query.delete()
            model_cls = cls.get_cold_model_cls(date=create_time)
            model_cls.query.delete()
예제 #8
0
 def count_all_records(cls):
     merchant = MerchantEnum.TEST
     create_time = DateTimeKit.get_cur_datetime()
     begin, end = DateTimeKit.get_month_begin_end(create_time.year,
                                                  create_time.month)
     return cls.query_by_create_time(begin,
                                     end,
                                     merchant=merchant,
                                     date=create_time).count()
예제 #9
0
    def __change_balance(self, data: dict, result: int, balance_check: dict, event_check: dict):
        """
        修改
        :param data:
        :param result:merchant_balance_event_test_91
        :param balance_check:
        :param event_check:
        :return:
        """
        # 修改余额
        data = copy.deepcopy(data)
        ref_id = OrderUtils.gen_unique_ref_id()
        data['ref_id'] = ref_id
        event_check['tx_id'] = data['tx_id'] = OrderUtils.gen_normal_tx_id(int(data['value']))

        rst, msg = MerchantBalanceEvent.update_balance(**data)
        self.assertEqual(result, rst)

        if rst == 0:
            event_check['ref_id'] = ref_id

        # 验证余额
        merchant = MerchantInfo.query_merchant(data['merchant'])
        self.assertIsNotNone(merchant)
        balance_total = sum(balance_check.values())
        self.assertEqual(balance_total, merchant.balance_total)
        self.assertEqual(balance_check['balance_available'], merchant.balance_available)
        self.assertEqual(balance_check['balance_frozen'], merchant.balance_frozen)
        self.assertEqual(balance_check['balance_income'], merchant.balance_income)

        if rst != 0:
            # 更新失败,就不用验证结果事件了
            return rst

        event = MerchantBalanceEvent.query_event(merchant=merchant.merchant,
                                                 create_time=DateTimeKit.get_cur_datetime(),
                                                 ref_id=ref_id).first()
        value = event_check['value']
        if event_check['ad_type'] == BalanceAdjustTypeEnum.MINUS:
            # 做减法
            value = -event_check['value']

        self.assertEqual(event_check['merchant'], event.merchant)
        self.assertEqual(event_check['source'], event.source)
        self.assertEqual(event_check['bl_type'], event.bl_type)
        self.assertEqual(event_check['ad_type'], event.ad_type)
        self.assertEqual(value, event.value_real)
        self.assertEqual(event_check['tx_id'], event.tx_id)
        self.assertEqual(event_check['comment'], event.comment)
        self.assertEqual(event_check['ref_id'], event.ref_id)

        return rst
예제 #10
0
    def __change_balance(self, data: dict, result: int, balance_check: dict):
        """
        修改
        :param data:
        :param result:
        :param balance_check:
        :return:
        """
        # 修改余额
        data = copy.deepcopy(data)
        ref_id = OrderUtils.gen_unique_ref_id()
        data['ref_id'] = ref_id
        data['tx_id'] = OrderUtils.gen_normal_tx_id(data['uid'])

        rst, msg = UserBalanceEvent.update_user_balance(**data)
        self.assertEqual(result, rst)

        # 验证余额
        balance = UserBalance.query_balance(data['uid'],
                                            data['merchant']).first()
        self.assertIsNotNone(balance)
        self.assertEqual(balance_check['balance_available'],
                         balance.real_balance)

        if rst != 0:
            # 更新失败,就不用验证结果事件了
            return rst

        event = UserBalanceEvent.query_event(
            uid=data['uid'],
            merchant=balance.merchant,
            date=DateTimeKit.get_cur_datetime(),
            ref_id=ref_id).first()
        value = data['value']
        if data['ad_type'] == BalanceAdjustTypeEnum.MINUS:
            # 做减法
            value = -data['value']

        self.assertEqual(data['uid'], event.uid)
        self.assertEqual(data['merchant'], event.merchant)
        self.assertEqual(data['source'], event.source)
        self.assertEqual(data['bl_type'], event.bl_type)
        self.assertEqual(data['ad_type'], event.ad_type)
        self.assertEqual(value, event.value_real)
        self.assertEqual(ref_id, event.ref_id)

        if data['source'] == OrderSourceEnum.MANUALLY:
            self.assertEqual(data['comment'], event.comment)

        return rst
예제 #11
0
    def is_in_maintain_time(
        self, end_delta: datetime.timedelta = datetime.timedelta(minutes=5)):
        """
        判断当前通道是否处于维护时间内
        :param end_delta:
        :return:
        """
        begin_time = self.maintain_begin
        end_time = self.maintain_end

        if not begin_time or not end_time:
            # 未设置维护时间
            return False

        if end_delta:
            end_time -= end_delta

        return begin_time <= DateTimeKit.get_cur_datetime() <= end_time
예제 #12
0
    def test_datetime_kit(self):
        t = DateTimeKit.gen_midnight_timestamp()
        self.print("gen_midnight_timestamp, t: %s" % t)
        self.assertIsInstance(t, int)

        d = DateTimeKit.timestamp_to_datetime(t)
        self.print("timestamp_to_datetime, d: %s" % d)
        self.assertIsInstance(d, datetime.datetime)

        t = DateTimeKit.datetime_to_timestamp(d)
        self.print("datetime_to_timestamp, t: %s" % t)
        self.assertIsInstance(t, int)

        s = DateTimeKit.datetime_to_str(d)
        self.print("datetime_to_str, s: %s" % s)
        self.assertIsInstance(s, str)

        d = DateTimeKit.str_to_datetime(s)
        self.print("str_to_datetime, d: %s" % d)
        self.assertIsInstance(d, datetime.datetime)

        d = DateTimeKit.get_cur_date()
        self.print("get_cur_date, d: %s" % d)
        self.assertIsInstance(d, datetime.date)

        t = DateTimeKit.datetime_to_timestamp(d)
        self.print("datetime_to_timestamp, t: %s" % t)
        self.assertIsInstance(t, int)

        s = DateTimeKit.datetime_to_str(d)
        self.print("datetime_to_str, s: %s" % s)
        self.assertIsInstance(s, str)

        cur_time = DateTimeKit.get_cur_datetime()
        rst = DateTimeKit.is_month_begin_time(cur_time)
        self.assertFalse(rst)

        someday = DateTimeKit.to_date(year=2019, month=1, day=1)
        rst = DateTimeKit.is_month_begin_time(someday)
        self.assertTrue(rst)

        someday = DateTimeKit.to_date(year=2019, month=2, day=2)
        rst = DateTimeKit.is_month_begin_time(someday)
        self.assertFalse(rst)
예제 #13
0
    def export_deposit_list_csv(cls, order_list, order_detail_dict, all_channels):
        """
        导出充值订单列表
        :param order_list:
        :param order_detail_dict:
        :param all_channels:
        :return:
        """
        # 用户id、系统订单号、商户订单号、创建时间、完成时间、商户、通道商户号、通道、发起金额、实际支付金额、优惠金额、手续费、
        # 成本金额、收入金额、订单状态、充值类型
        data = list()

        for order in order_list:
            channel = all_channels[order.channel_id]
            order_detail = order_detail_dict[order.order_id]

            data.append({
                "用户ID": order.uid,
                "系统订单号": order.sys_tx_id,
                "商户订单号": order.mch_tx_id,
                "创建时间": order.str_create_time,
                "完成时间": order_detail.str_done_time,
                "商户": order.merchant.name,
                "通道商户号": channel.channel_enum.conf['mch_id'],
                "通道": channel.channel_enum.desc,
                "发起金额": str(order.amount),
                "实际支付金额": str(order.tx_amount),
                "优惠金额": str(order_detail.offer),
                "手续费": str(order_detail.fee),
                "成本金额": str(order_detail.cost),
                "收入金额": str(order_detail.profit),
                "订单状态": order.state.desc,
                "充值类型": order.source.desc,
            })

        filename = 'epay_deposit_record_%s.csv' % DateTimeKit.datetime_to_str(
            DateTimeKit.get_cur_datetime(),
            DateTimeFormatEnum.TIGHT_DAY_FORMAT
        )
        return CsvKit.send_csv(data, filename=filename, fields=data[0].keys())
예제 #14
0
    def export_withdraw_list_csv(cls, order_list, order_detail_dict, all_channels):
        """
        导出提现订单列表
        :param order_list:
        :param order_detail_dict:
        :param all_channels:
        :return:
        """
        # 用户id、系统订单号、商户订单号、创建时间、完成时间、商户、通道商户号、通道、提现金额、手续费、成本金额、收入金额、订单状态、出款类型、备注
        data = list()

        for order in order_list:
            channel = all_channels.get(order.channel_id)
            order_detail = order_detail_dict[order.order_id]
            data.append({
                "用户ID": order.uid,
                "系统订单号": order.sys_tx_id,
                "商户订单号": order.mch_tx_id,
                "创建时间": order.str_create_time,
                "完成时间": order_detail.str_done_time,
                "商户": order.merchant.name,
                "通道商户号": channel.channel_enum.conf['mch_id'] if channel else '',
                "通道": channel.channel_enum.desc if channel else '',
                "提现金额": str(order.amount),
                "手续费": str(order_detail.fee),
                "成本金额": str(order_detail.cost),
                "收入金额": str(order_detail.profit),
                "订单状态": order.state.desc,
                "出款类型": order.source.desc,
                "备注": order_detail.comment or '',
            })

        filename = 'epay_withdraw_record_%s.csv' % DateTimeKit.datetime_to_str(
            DateTimeKit.get_cur_datetime(),
            DateTimeFormatEnum.TIGHT_DAY_FORMAT
        )
        return CsvKit.send_csv(data, filename=filename, fields=data[0].keys())
예제 #15
0
    def launch_pay(self, params_dict: dict):
        """
        发起代付
        :param params_dict:
        :return:
        """

        url = self.gen_url(params_dict['tx_id'])

        params_dict['batch_date'] = DateTimeKit.datetime_to_str(
            DateTimeKit.get_cur_datetime(),
            DateTimeFormatEnum.TIGHT_DAY_FORMAT)
        request_dict, sign_str = self.construct_request(params_dict)

        sign = CallbackEpayTong.generate_sign(sign_str)

        request_dict["sign"] = sign
        request_dict["signType"] = "SHA"

        try:
            headers = {"Content-Type": "application/x-www-form-urlencoded"}
            current_app.logger.info('EpayTong withdraw, url: %s, data: %s',
                                    url, request_dict)
            resp = requests.post(url=url, data=request_dict, headers=headers)
            current_app.logger.info(
                'EpayTong withdraw, status_code: %s, content: %s',
                resp.status_code, resp.text)
        except Exception as e:
            current_app.logger.fatal(traceback.format_exc())
            return dict(
                code=-100,
                msg="http请求失败",
                data=dict(),
            )

        print(resp.json(), resp.text, resp.content)
        return self.parse_response(resp, params_dict)
예제 #16
0
파일: base.py 프로젝트: LyanJin/check-pay
 def __init__(self):
     self.create_time = DateTimeKit.get_cur_datetime()
예제 #17
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
예제 #18
0
    def construct_request(self, params_dict: dict):
        tx_id = params_dict['tx_id']
        amount = Decimal(params_dict['amount'])
        bank_code = params_dict['bank_code']
        bank_account = params_dict['bank_account']
        bank_number = params_dict['bank_number']
        request_fields = ['merchantId', 'batchNo', 'batchRecord', 'currencyCode', 'totalAmount', 'payDate',
                          'isWithdrawNow',
                          'notifyUrl',
                          'signType', 'sign', 'detailList']
        detail_list = ['receiveType', 'accountType', 'serialNo', 'amount', 'bankName', 'bankNo', 'receiveName']
        fields = sorted(request_fields)
        request_dict = {}
        for field in fields:
            if field == 'merchantId':
                request_dict[field] = self.third_config['mch_id']
            elif field == 'batchNo':
                request_dict[field] = tx_id
            elif field == 'batchRecord':
                request_dict[field] = 1
            elif field == 'currencyCode':
                request_dict[field] = 'CNY'
            # 交易类型 固定值
            elif field == 'totalAmount':
                tx_amount = amount
                tx_amount = str(tx_amount)
                if tx_amount.find('.') < 0:
                    tx_amount += '.00'
                request_dict[field] = tx_amount
            # 交易金额 单位 分
            elif field == 'payDate':
                request_dict[field] = DateTimeKit.datetime_to_str(DateTimeKit.get_cur_datetime(),
                                                                  DateTimeFormatEnum.TIGHT_DAY_FORMAT)
            # 后台回调地址
            elif field == 'isWithdrawNow':
                request_dict[field] = '3'
            # 开户人名称
            elif field == 'notifyUrl':
                request_dict[field] = self.third_config['callback_url']
            elif field == 'signType':
                request_dict[field] = 'RSA'
            elif field == 'detailList':
                detail = request_dict.get(field, [])
                detail_dict = {}
                for item in detail_list:
                    if item == 'receiveType':
                        detail_dict[item] = '个人'
                    elif item == 'accountType':
                        detail_dict[item] = '储蓄卡'
                    elif item == 'serialNo':
                        detail_dict[item] = tx_id
                    elif item == 'amount':
                        tx_amount = str(amount)
                        if tx_amount.find('.') < 0:
                            tx_amount += '.00'
                        detail_dict[item] = tx_amount
                    elif item == 'bankName':
                        detail_dict[item] = bank_code
                    elif item == 'bankNo':
                        detail_dict[item] = bank_number
                    elif item == 'receiveName':
                        detail_dict[item] = bank_account
                detail.append(detail_dict)
                request_dict[field] = detail

        no_need_fields = ['detailList', 'signType', 'sign']
        request_body_str = "&".join(["{}={}".format(k, request_dict[k]) for k in fields if
                                     k not in no_need_fields and request_dict.get(k, False)])
        sign = CallbackOnePay.gen_sign(body_str=request_body_str, third_config=self.third_config)
        request_dict['sign'] = sign
        return request_dict
예제 #19
0
    def update_balance(cls,
                       merchant: MerchantEnum,
                       ref_id,
                       source: OrderSourceEnum,
                       bl_type: BalanceTypeEnum,
                       value: float,
                       order_type: PayTypeEnum,
                       tx_id,
                       ad_type: BalanceAdjustTypeEnum = None,
                       comment: str = '',
                       create_time=None,
                       commit=True):
        """
        修改余额
        :param merchant:
        :param ref_id: 32位的md5ID
        :param source: 操作来源
        :param order_type: 订单类型
        :param bl_type: 余额类型
        :param ad_type: 当人工操作时,必填调整类型;订单类型的调整不需要填写ad_type
        :param value: 始终传入正整数
        :param comment: 当人工调整时,必填备注信息
        :param create_time:
        :param tx_id: 交易流水号
        :param commit: 是否立即提交事务
        :return: (result, msg)
        """
        # 记录一个参数日志, locals自动收集这行代码之前出现过的局部变量
        params = copy.deepcopy(locals())
        params.pop('cls')

        # current_app.config['SENTRY_DSN'] and current_app.logger.info('update_balance, params: %s', params)

        rst, msg = cls.__check_event_params(params)
        if rst != 0:
            return rst, msg

        if ad_type is None:
            if source == PayTypeEnum.DEPOSIT:
                # 存款+
                ad_type = BalanceAdjustTypeEnum.PLUS
            else:
                # 提款-
                ad_type = BalanceAdjustTypeEnum.MINUS

        # 金额入库乘100转换为分钱
        value = BalanceKit.multiple_hundred(value)

        if ad_type == BalanceAdjustTypeEnum.MINUS:
            # 做减法
            value = -value

        try:
            with db.auto_commit(commit):
                # 生成修改事件
                create_time = create_time or DateTimeKit.get_cur_datetime()
                fields = dict(
                    source=source,
                    order_type=order_type,
                    bl_type=bl_type,
                    value=value,
                    tx_id=tx_id,
                    ad_type=ad_type,
                    comment=comment,
                    ref_id=ref_id,
                    create_time=create_time,
                    extra=dict(),
                )
                rst = cls.add_model(fields,
                                    merchant=merchant,
                                    date=create_time)
                db.session.add(rst['model'])

                # 更新商户余额
                before_merchant, after_merchant = cls.__update_balance(
                    merchant, source, order_type, bl_type, ad_type, value)
                # 添加admin日志
                if source == OrderSourceEnum.MANUALLY:
                    log_model = cls.add_admin_log(
                        data_before=before_merchant.get_before_fields(),
                        data_after=after_merchant.get_after_fields(),
                    )
                    log_model and db.session.add(log_model)

        except RuntimeError as e:
            # 捕获异常,返回失败
            msg = "%s, params: %s" % (str(e), params)
            current_app.config['SENTRY_DSN'] and current_app.logger.fatal(msg)
            return -100, str(e)

        except InsufficientBalanceException as e:
            msg = str(e)
            if bl_type == BalanceTypeEnum.AVAILABLE:
                msg = "可用余额不足"
            if bl_type == BalanceTypeEnum.INCOME:
                msg = "在途余额不足"
            if bl_type == BalanceTypeEnum.FROZEN:
                if ad_type == BalanceAdjustTypeEnum.PLUS:
                    msg = "可用余额不足"
                else:
                    msg = "冻结余额不足"
            return -101, msg

        return 0, ''
예제 #20
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()
예제 #21
0
    def __test_admin_log(self):
        begin_time = DateTimeKit.get_cur_datetime()

        data = dict(
            account='lucy',
            ip='127.0.0.1',
            module=AdminModuleEnum.SYSTEM,
            data_before=dict(x=1, y=2, c="你好"),
            data_after=dict(x=3, y=4, c="哈哈"),
        )
        AdminLog.add_log(**data)
        # time.sleep(1)

        end_time = DateTimeKit.get_cur_datetime()

        logs = list(AdminLog.query_log(begin_time, end_time))
        log = logs[0]
        self.assertEqual(len(logs), 1)
        self.assertEqual(log.account, data['account'])
        self.assertEqual(log.ip, data['ip'])
        self.assertEqual(log.module, data['module'])
        self.assertEqual(log.data_before, data['data_before'])
        self.assertEqual(log.data_after, data['data_after'])

        data = dict(
            account='clark',
            ip='192.168.12.233',
            module=AdminModuleEnum.TRADE,
            data_before=dict(x=1, y=2, c="你好"),
            data_after=dict(x=3, y=4, c="哈哈"),
        )
        AdminLog.add_log(**data)
        # time.sleep(1)
        end_time = DateTimeKit.get_cur_datetime()

        logs = list(AdminLog.query_log(begin_time, end_time))
        log = logs[1]
        self.assertEqual(len(logs), 2)
        self.assertEqual(log.account, data['account'])
        self.assertEqual(log.ip, data['ip'])
        self.assertEqual(log.module, data['module'])
        self.assertEqual(log.data_before, data['data_before'])
        self.assertEqual(log.data_after, data['data_after'])

        # 以日期查询,当天0点到第二天0点
        logs = list(
            AdminLog.query_log(
                begin_time.date(),
                end_time.date() + DateTimeKit.time_delta(days=1)))
        log = logs[1]
        self.assertEqual(len(logs), 2)
        self.assertEqual(log.account, data['account'])
        self.assertEqual(log.ip, data['ip'])
        self.assertEqual(log.module, data['module'])
        self.assertEqual(log.data_before, data['data_before'])
        self.assertEqual(log.data_after, data['data_after'])

        logs = list(
            AdminLog.query_by_account(begin_time, end_time, account='clark'))
        log = logs[0]
        self.assertEqual(len(logs), 1)
        self.assertEqual(log.account, data['account'])
        self.assertEqual(log.ip, data['ip'])
        self.assertEqual(log.module, data['module'])
        self.assertEqual(log.data_before, data['data_before'])
        self.assertEqual(log.data_after, data['data_after'])

        logs = list(AdminLog.query_all())
        self.assertEqual(len(logs), 2)
        AdminLog.delete_all()
        logs = list(AdminLog.query_all())
        self.assertEqual(len(logs), 0)
예제 #22
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
예제 #23
0
 def get_suffix(self, cur_datetime=None):
     cur_datetime = cur_datetime or DateTimeKit.get_cur_datetime()
     return DateTimeKit.datetime_to_str(cur_datetime, self.suffix_format)
예제 #24
0
    def post(self):
        """
        商户提现订单查询
        :return:
        """
        form, error = WithdrawOrderSelectForm().request_validate()
        if error:
            return error.as_response()

        user = g.user

        try:
            order_list_query = OrderWithdraw.query_by_create_time(
                begin_time=form.start_datetime.data,
                end_time=form.end_datetime.data,
                merchant=MerchantEnum(user.mid))

        except MultiMonthQueryException as e:
            return MultiMonthQueryError().as_response()

        kwargs = {}
        if form.order_id.data:
            kwargs["mch_tx_id"] = form.order_id.data

        if form.state.data != "0":
            kwargs["_state"] = form.state.data.value

        entries = order_list_query.filter_by(**kwargs).all()

        bank_lst = list(set([o.bank_id for o in entries]))
        kwargs = {}

        if form.order_id.data and entries:
            kwargs['id'] = entries[0].order_id
        if not MerchantEnum(user.mid).is_api_merchant:
            bank_items = {
                bank.id: bank
                for bank in BankCard.query.filter(BankCard.id.in_(
                    bank_lst)).all()
            }
        order_items = {
            item.id: item
            for item in OrderDetailWithdraw.query_by_create_time(
                begin_time=form.start_datetime.data,
                end_time=form.end_datetime.data,
                merchant=MerchantEnum(user.mid)).filter_by(**kwargs).all()
        }

        items = []
        for order in entries:
            if not MerchantEnum(user.mid).is_api_merchant:
                bank = bank_items.get(order.bank_id, {})
            else:
                bank = order.get_bank_card()
            detail = order_items.get(order.order_id, {})
            if not bank or not detail:
                continue
            items.append(
                dict(mch_tx_id=order.mch_tx_id,
                     sys_tx_id=order.sys_tx_id,
                     amount=detail.amount,
                     fee=detail.fee,
                     account_name=bank.account_name,
                     bank_name=bank.bank_name,
                     branch="{}{}{}".format(bank.province, bank.city,
                                            bank.branch),
                     card_no=bank.card_no,
                     create_time=order.str_create_time,
                     done_time=order.update_time,
                     state=order.state.desc,
                     deliver=order.deliver.desc))
        items = sorted(items, key=lambda item: item['create_time'])

        if items:
            data = list()
            for item in items:
                data.append({
                    "商户订单号": item['mch_tx_id'],
                    "提现金额": str(item['amount']),
                    "手续费": str(item['fee']),
                    "开户名": item['account_name'],
                    "开户银行": item['bank_name'],
                    "开户地址": item['branch'],
                    "银行卡号": item['card_no'],
                    "创建时间": item['create_time'],
                    "完成时间": item['done_time'],
                    "订单状态": item['state'],
                    "通知状态": item['deliver']
                })

            filename = 'epay_withdraw_record_%s.csv' % (
                DateTimeKit.datetime_to_str(
                    DateTimeKit.get_cur_datetime(),
                    DateTimeFormatEnum.TIGHT_DAY_FORMAT))

            return CsvKit.send_csv(data,
                                   filename=filename,
                                   fields=data[0].keys())
예제 #25
0
    def post(self):
        """
        商户充值订单查询数据导出
        :return:
        """
        form, error = DepositOrderSelectForm().request_validate()
        if error:
            return error.as_response()

        user = g.user

        try:
            order_list_query = OrderDeposit.query_by_create_time(
                begin_time=form.start_datetime.data,
                end_time=form.end_datetime.data,
                merchant=MerchantEnum(user.mid))
        except MultiMonthQueryException as e:
            return MultiMonthQueryError().as_response()

        kwargs = {}
        if form.order_id.data:
            kwargs["sys_tx_id"] = form.order_id.data

        if form.state.data != "0":
            kwargs["_state"] = form.state.data.value

        entries = order_list_query.filter_by(**kwargs).all()

        items = []
        channel_lst = list(set([o.channel_id for o in entries]))
        channel_items = {
            Channel.id: Channel
            for Channel in ChannelConfig.query.filter(
                ChannelConfig.id.in_(channel_lst)).all()
        }
        kwargs = {}
        if form.order_id.data and entries:
            kwargs['id'] = entries[0].order_id
        order_items = {
            item.id: item
            for item in OrderDetailDeposit.query_by_create_time(
                begin_time=form.start_datetime.data,
                end_time=form.end_datetime.data,
                merchant=MerchantEnum(user.mid)).filter_by(**kwargs).all()
        }
        for order in entries:
            item_channel = channel_items.get(order.channel_id, {})
            detail = order_items.get(order.order_id, {})
            if not item_channel or not detail:
                continue

            items.append(
                dict(mch_tx_id=order.mch_tx_id,
                     sys_tx_id=order.sys_tx_id,
                     payment_type=item_channel.channel_enum.
                     conf['payment_type'].desc,
                     amount=detail.amount,
                     tx_amount=detail.tx_amount,
                     fee=detail.fee,
                     create_time=order.str_create_time,
                     done_time=order.update_time,
                     state=order.state.desc,
                     deliver=order.deliver.desc))
        items = sorted(items, key=lambda item: item['create_time'])

        if items:
            data = list()
            for item in items:
                data.append({
                    "商户订单号": item['mch_tx_id'],
                    "支付方式": item['payment_type'],
                    "发起金额": str(item['amount']),
                    "实际支付金额": str(item['tx_amount']),
                    "手续费": str(item['fee']),
                    "创建时间": item['create_time'],
                    "完成时间": item['done_time'],
                    "订单状态": item['state'],
                    "通知状态": item['deliver']
                })

            filename = 'epay_deposit_record_%s.csv' % (
                DateTimeKit.datetime_to_str(
                    DateTimeKit.get_cur_datetime(),
                    DateTimeFormatEnum.TIGHT_DAY_FORMAT))

            return CsvKit.send_csv(data,
                                   filename=filename,
                                   fields=data[0].keys())
예제 #26
0
    def __test_api_withdraw(self):
        """
        1. 新建用户提现订单
        :return:
        """
        order_cls = OrderWithdraw
        uid = 1000

        channel_enum = ChannelConfigEnum.CHANNEL_1001

        banks = [
            PaymentBankEnum(int(bank))
            for bank in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
        ]
        proxy_channel = dict(fee=Decimal("2.5"),
                             fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
                             limit_per_min=300,
                             limit_per_max=1000,
                             limit_day_max=0,
                             trade_begin_hour=0,
                             trade_begin_minute=0,
                             trade_end_hour=23,
                             trade_end_minute=59,
                             maintain_begin=DateTimeKit.str_to_datetime(
                                 "2019-12-11 09:00:00",
                                 DateTimeFormatEnum.SECONDS_FORMAT),
                             maintain_end=DateTimeKit.str_to_datetime(
                                 "2025-12-20 23:00:00",
                                 DateTimeFormatEnum.SECONDS_FORMAT),
                             state=ChannelStateEnum.TESTING,
                             banks=banks)
        ProxyChannelConfig.update_channel(channel_enum, **proxy_channel)

        merchant = MerchantEnum.TEST

        # 准备配置数据
        bank = BankCard.add_bank_card(
            merchant,
            uid=uid,
            bank_name="中国工商银行",
            bank_code="ICBC",
            card_no="6212260405014627955",
            account_name="张三",
            branch="广东东莞东莞市长安镇支行",
            province="广东省",
            city="东莞市",
        )

        OrderMixes.add_one_channel_config(channel_enum)
        OrderMixes.add_one_merchant_config(merchant,
                                           channel_enum,
                                           payment_way=PayTypeEnum.WITHDRAW)

        channel_config = ChannelConfig.query_latest_one(
            dict(channel_enum=channel_enum))

        merchant_fee_config = MerchantFeeConfig.query_latest_one(
            dict(
                merchant=merchant,
                payment_way=PayTypeEnum.WITHDRAW,
                payment_method=channel_enum.conf.payment_method,
            ))

        amount = Decimal("500")
        fee = BalanceKit.round_4down_5up(
            Decimal(merchant_fee_config.value) * amount / Decimal(100))
        # 创建提现订单
        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=PayTypeEnum.WITHDRAW,
            in_type=InterfaceTypeEnum.CASHIER_H5,
            amount=amount,
            bank_id=bank.id,
            fee=fee,
        )
        order, ref_id = OrderCreateCtl.create_order_event(**params)
        event = OrderEvent.query_one(dict(ref_id=ref_id),
                                     merchant=merchant,
                                     date=order.create_time)

        data = order_cls.query_by_order_id(order_id=event.order_id,
                                           merchant=merchant)

        begin_time, end_time = DateTimeKit.get_month_begin_end(
            year=int(DateTimeKit.get_cur_datetime().year),
            month=int(DateTimeKit.get_cur_datetime().month))
        withdraw_params = dict(merchant_name="TEST",
                               page_size=10,
                               page_index=1,
                               begin_time=DateTimeKit.datetime_to_str(
                                   begin_time,
                                   DateTimeFormatEnum.SECONDS_FORMAT),
                               end_time=DateTimeKit.datetime_to_str(
                                   end_time,
                                   DateTimeFormatEnum.SECONDS_FORMAT),
                               state="0")

        self.path = "/trade_manage/withdraw/list"
        # 通过接口 查询提现订单
        response = self.do_request(json_data=withdraw_params)
        print(response.json, "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")
        self.assertEqual("1", response.json['data']['total'])
        self.assertEqual("待认领", response.json['data']['entries'][0]['state'])

        self.path = "/trade_manage/order/allowed"
        # 通过接口, 认领订单
        allowed_params = dict(order_id=order.id, merchant_name="TEST")
        response = self.do_request(allowed_params)
        self.assertEqual(ResponseSuccess.code, response.status_code)
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'])
        # 查询当前订单状态是否已修改为已认领
        data = order_cls.query_by_order_id(order_id=event.order_id,
                                           merchant=merchant)
        self.assertEqual(OrderStateEnum.ALLOC, data.state)

        # 通过接口查询 审核列表 已有的认领订单为1
        self.path = '/trade_manage/withdraw/review/list'

        request_review_params = dict(
            year=str(DateTimeKit.get_cur_datetime().year),
            mouth=str(DateTimeKit.get_cur_datetime().month))
        response = self.do_request(json_data=request_review_params)
        self.assertEqual(1, len(response.json['data']['entries']))
        self.assertEqual("已认领", response.json['data']['entries'][0]['state'])

        # 通过接口查询 当前可用的 代付通道
        proxy_channel_suppor = dict(bank_type=bank.bank_enum.name,
                                    merchant_name="TEST",
                                    amount=str(amount))

        self.path = "/trade_manage/withdraw/available/channel"

        response = self.do_request(json_data=proxy_channel_suppor)
        self.assertEqual(WithdrawBankEntryResult.code, response.status_code)
        self.assertEqual(WithdrawBankEntryResult.error_code,
                         response.json['error_code'])
        self.assertEqual(
            channel_enum.conf['provider'] + channel_enum.conf['mch_id'],
            response.json['data']['entries'][0]['key'])

        # 测试人工出款 处理订单
        self.path = '/trade_manage/withdraw/person/execute'

        execute_params = dict(order_id=order.order_id, merchant="Test")

        response = self.do_request(json_data=execute_params)
        self.assertEqual(ResponseSuccess.code, response.status_code)
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'])
        data = order_cls.query_by_order_id(order_id=event.order_id,
                                           merchant=merchant)
        self.assertEqual(OrderStateEnum.DEALING, data.state)

        # 测试人工出款  出款

        self.path = "/trade_manage/withdraw/person/done"
        done_params = dict(order_id=order.order_id,
                           merchant='TEST',
                           comment='测试',
                           fee='5')

        response = self.do_request(json_data=done_params)
        self.assertEqual(ResponseSuccess.code, response.status_code)
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'])
        data = order_cls.query_by_order_id(order_id=event.order_id,
                                           merchant=merchant)
        self.assertEqual(OrderStateEnum.SUCCESS, data.state)
예제 #27
0
    def post(self):
        """
        代付通道管理: 编辑代付通道
        :return:
        """
        form, error = WithdrawAddForm().request_validate()

        if error:
            return error.as_response()

        if form.start_time.data >= form.end_time.data:
            return DateStartMoreThanError().as_response()

        if form.maintain_begin.data:
            if form.maintain_begin.data >= form.maintain_end.data or form.maintain_begin.data < DateTimeKit.get_cur_datetime(
            ):
                return DateStartMoreThanError().as_response()

        if Decimal(form.limit_per_min.data) >= Decimal(
                form.limit_per_max.data):
            return DataStartMoreThanError().as_response()

        if form.limit_day_max.data and Decimal(
                form.limit_per_max.data) > Decimal(form.limit_day_max.data):
            return PerLimitMustLittleDayLimitError().as_response()

        banks = [PaymentBankEnum(int(bank)) for bank in form.banks.data]

        kwargs = dict(
            fee=form.fee.data,
            fee_type=form.fee_type.data,
            limit_per_min=form.limit_per_min.data,
            limit_per_max=form.limit_per_max.data,
            limit_day_max=form.limit_day_max.data
            if form.limit_day_max.data != "" else 0,
            trade_begin_hour=form.start_time.data.hour,
            trade_begin_minute=form.start_time.data.minute,
            trade_end_hour=form.end_time.data.hour,
            trade_end_minute=form.end_time.data.minute,
            maintain_begin=form.maintain_begin.data
            if form.maintain_begin else "",
            maintain_end=form.maintain_end.data if form.maintain_end else "",
            state=form.state.data,
            banks=banks)

        rst, error = ProxyChannelConfig.update_channel(form.channel_id.data,
                                                       **kwargs)
        if error:
            return error.as_response()

        # 同步缓存
        # ChannelLimitCacheCtl(PayTypeEnum.WITHDRAW).sync_db_channels_to_cache()

        return ResponseSuccess().as_response()
예제 #28
0
    def post(self):
        """
        通道管理: 删除通道
        :return:
        """
        form, error = ChannelAddForm().request_validate()
        if error:
            return error.as_response()

        # if form.start_time.data >= form.end_time.data:
        #     return DateStartMoreThanError().as_response()

        if form.maintain_begin.data:
            if form.maintain_begin.data >= form.maintain_end.data or form.maintain_begin.data < DateTimeKit.get_cur_datetime(
            ):
                return DateStartMoreThanError().as_response()

        if int(form.limit_per_min.data) >= int(form.limit_per_max.data):
            return DataStartMoreThanError().as_response()

        if form.limit_day_max.data and Decimal(
                form.limit_per_max.data) > Decimal(form.limit_day_max.data):
            return PerLimitMustLittleDayLimitError().as_response()

        kwargs = dict(fee=form.fee.data,
                      fee_type=form.fee_type.data,
                      limit_per_min=form.limit_per_min.data,
                      limit_per_max=form.limit_per_max.data,
                      limit_day_max=form.limit_day_max.data
                      if form.limit_day_max.data != "" else 0,
                      trade_begin_hour=form.start_time.data.hour,
                      trade_begin_minute=form.start_time.data.minute,
                      trade_end_hour=form.end_time.data.hour,
                      trade_end_minute=form.end_time.data.minute,
                      maintain_begin=form.maintain_begin.data,
                      maintain_end=form.maintain_end.data,
                      settlement_type=form.settlement_type.data,
                      state=form.state.data,
                      priority=form.priority.data,
                      valid=ModelBase.INVALID)

        rst, error = ChannelConfig.update_channel(form.channel_id.data,
                                                  **kwargs)
        if error:
            return error.as_response()

        # 同步缓存
        # ChannelLimitCacheCtl(PayTypeEnum.DEPOSIT).sync_db_channels_to_cache()

        return ResponseSuccess().as_response()
예제 #29
0
    def post(self):
        """
        用户最近一周充值提现交易记录
        :return:
        """

        form, error = UserTransactionForm().request_validate()
        if error:
            return error.as_response()

        uid = form.uid.data
        pay_type = form.pay_type.data
        page_size = form.page_size.data
        page_index = form.page_index.data

        entry = User.query.filter_by(**dict(id=uid)).first()
        if not entry:
            return AdjustUserBalanceError(message="系统找不到该用户").as_response()

        # 获取用户最近一周的充值提现记录
        query_datetime_lst = []
        c_date = DateTimeKit.get_cur_datetime()
        s_date = c_date + DateTimeKit.time_delta(days=-7)
        if not DateTimeKit.is_same_month(s_date,
                                         DateTimeKit.get_cur_datetime()):
            _, e_m_date = DateTimeKit.get_month_begin_end(year=s_date.year,
                                                          month=s_date.month)
            s_m_date, _ = DateTimeKit.get_month_begin_end(year=c_date.year,
                                                          month=c_date.month)
            s_date, _ = DateTimeKit.get_day_begin_end(s_date)
            query_datetime_lst.append([s_date, e_m_date])
            query_datetime_lst.append([s_m_date, c_date])
        else:
            s_date, _ = DateTimeKit.get_day_begin_end(s_date)
            query_datetime_lst.append([s_date, c_date])

        order_list = []
        for s_e_date in query_datetime_lst:

            if pay_type == PayTypeEnum.DEPOSIT:
                base_order = OrderDeposit.query_by_create_time(
                    begin_time=s_e_date[0],
                    end_time=s_e_date[1],
                    merchant=entry.merchant).filter_by(**dict(
                        uid=entry.uid)).all()

                # order_detail = OrderDetailDeposit.query_by_create_time(begin_time=s_e_date[0],
                #                                                        end_time=s_e_date[1],
                #                                                        merchant=entry.merchant).filter_by(
                #     uid=entry.uid).all()
                # detail_dict = [{detail.order_id: detail} for detail in order_detail]
                [
                    order_list.append(
                        dict(mch_tx_id=item.mch_tx_id,
                             sys_tx_id=item.sys_tx_id,
                             pay_method=item.pay_method.desc,
                             amount=item.amount,
                             tx_amount=item.tx_amount,
                             create_time=item.str_create_time,
                             state=item.state.get_back_desc(
                                 PayTypeEnum.DEPOSIT))) for item in base_order
                ]
            elif pay_type == PayTypeEnum.WITHDRAW:

                base_order = OrderWithdraw.query_by_create_time(
                    begin_time=s_e_date[0],
                    end_time=s_e_date[1],
                    merchant=entry.merchant).filter_by(**dict(
                        uid=entry.uid)).all()
                detail_order = OrderDetailWithdraw.query_by_create_time(
                    begin_time=s_e_date[0],
                    end_time=s_e_date[1],
                    merchant=entry.merchant).filter_by(**dict(
                        uid=entry.uid)).all()
                detail_dict = {
                    detail.order_id: detail
                    for detail in detail_order
                }

                bank_lst = [order.bank_id for order in base_order]

                bank_info = BankCard.query.filter(
                    BankCard.id.in_(bank_lst)).all()

                bank_dct = {bank.id: bank for bank in bank_info}

                [
                    order_list.append(
                        dict(mch_tx_id=item.mch_tx_id,
                             sys_tx_id=item.sys_tx_id,
                             amount=item.amount,
                             fee=detail_dict[item.order_id].fee,
                             bank_name=bank_dct[item.bank_id].bank_name,
                             card_no=bank_dct[
                                 item.bank_id].card_no_short_description,
                             state=item.state.get_back_desc(
                                 PayTypeEnum.WITHDRAW),
                             done_time=item.update_time,
                             create_time=item.str_create_time))
                    for item in base_order
                ]

        items, total = Pagination.paginate_list(items=order_list,
                                                page_index=page_index,
                                                page_size=page_size,
                                                sort_key="create_time")

        if pay_type == PayTypeEnum.WITHDRAW:
            return UserDepositResult(bs_data=dict(
                depositInfo=dict(entries=[], total=0),
                withdrawInfo=dict(entries=items, total=total))).as_response()
        elif pay_type == PayTypeEnum.DEPOSIT:
            return UserDepositResult(bs_data=dict(
                depositInfo=dict(entries=items, total=total),
                withdrawInfo=dict(entries=[], total=0))).as_response()
예제 #30
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)