예제 #1
0
 def __register_account(self, data):
     # 先往数据库生成一个账号
     AdminUser.register_account(account=data['account'],
                                login_pwd=data['password'])
     if not MerchantInfo.query_merchant(MerchantEnum.TEST):
         MerchantInfo.create_merchant(m_name=MerchantEnum.TEST,
                                      m_type=MerchantTypeEnum.TEST)
예제 #2
0
    def init_merchant(cls):
        # 先创建商户
        if not cls.get_merchant_info():
            MerchantInfo.create_merchant(m_name=cls.merchant,
                                         m_type=MerchantTypeEnum.TEST)

            # 给商户加钱
            rst, msg = MerchantBalanceEvent.update_balance(
                merchant=cls.merchant,
                ref_id=OrderUtils.gen_unique_ref_id(),
                source=OrderSourceEnum.MANUALLY,
                order_type=PayTypeEnum.MANUALLY,
                bl_type=BalanceTypeEnum.AVAILABLE,
                ad_type=BalanceAdjustTypeEnum.PLUS,
                tx_id=OrderUtils.gen_normal_tx_id(10),
                value=100000000,
                comment="手动脚本修改商户可用余额")
            # print(rst, msg)

        merchant_fee_list = list()
        if not cls.get_merchant_fee_config(PayTypeEnum.DEPOSIT):
            # 商户费率配置
            merchant_fee_list.append(
                dict(
                    merchant=cls.merchant,
                    payment_way=PayTypeEnum.DEPOSIT,
                    value="3",
                    fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
                    payment_method=cls.channel_enum.conf.payment_method,
                ))
            MerchantFeeConfig.update_fee_config(cls.merchant,
                                                merchant_fee_list)

            # 商户费率配置
            merchant_fee_list.append(
                dict(
                    merchant=cls.merchant,
                    payment_way=PayTypeEnum.DEPOSIT,
                    value="3",
                    fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
                    payment_method=cls.channel_enum2.conf.payment_method,
                ))
            MerchantFeeConfig.update_fee_config(cls.merchant,
                                                merchant_fee_list)

        if not cls.get_merchant_fee_config(PayTypeEnum.WITHDRAW):
            merchant_fee_list.append(
                dict(merchant=cls.merchant,
                     payment_way=PayTypeEnum.WITHDRAW,
                     value="3.2",
                     fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER))
            MerchantFeeConfig.update_fee_config(cls.merchant,
                                                merchant_fee_list)

        merchant_config = cls.get_merchant_fee_config(PayTypeEnum.DEPOSIT)
        assert merchant_config.fee_type == PaymentFeeTypeEnum.PERCENT_PER_ORDER

        merchant_config = cls.get_merchant_fee_config(PayTypeEnum.WITHDRAW)
        assert merchant_config.fee_type == PaymentFeeTypeEnum.PERCENT_PER_ORDER
예제 #3
0
    def __test_api_merchant_fee(self):
        # 先删除所有商户
        MerchantInfo.delete_all()

        r1 = dict(name=PayMethodEnum.WEIXIN_TO_BANK.value,
                  value="3.2",
                  fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER.value)
        r2 = dict(name=PayMethodEnum.BANK_TO_BANK.value,
                  value="4.45",
                  fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER.value)
        w1 = dict(
            value='3.3',
            fee_type=PaymentFeeTypeEnum.YUAN_PER_ORDER.value,
            cost_type=CostTypeEnum.MERCHANT.name,
        )
        post_data = dict(name=MerchantEnum.TEST.name,
                         type=MerchantTypeEnum.TEST.value,
                         deposit_info=[r1, r2],
                         withdraw_info=w1)

        # 新建商户费率
        self.path = '/merchant/fee/add'

        # 新建商户费率成功
        response = self.do_request(post_data)
        self.assertEqual(response.status_code, ResponseSuccess.code)
        self.assertEqual(response.json['error_code'],
                         ResponseSuccess.error_code, response.json['message'])

        # 新建商户费率 商户名重复
        response = self.do_request(post_data)
        self.assertEqual(response.status_code, SqlIntegrityError.code)
        self.assertEqual(response.json['error_code'],
                         SqlIntegrityError.error_code,
                         response.json['message'])

        # 费率编辑
        self.path = '/merchant/fee/edit'
        r1 = dict(name=PayMethodEnum.WEIXIN_SAOMA.value,
                  value="0.2",
                  fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER.value)
        edit_post_data = dict(name='Test',
                              deposit_info=[r1, r2],
                              withdraw_info=w1)

        response = self.do_request(edit_post_data)
        self.assertEqual(ResponseSuccess.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'], response.json['message'])
예제 #4
0
    def __create_merchant_info(self):
        # 创建商户TEST
        merchant = MerchantInfo.create_merchant(MerchantEnum.TEST, MerchantTypeEnum.TEST)
        self.assertEqual(merchant.id, 1)
        self.assertEqual(merchant.merchant, MerchantEnum.TEST)
        self.assertEqual(merchant.m_type, MerchantTypeEnum.TEST)
        self.assertEqual(merchant.state, AccountStateEnum.ACTIVE)

        # 创建商户QF2
        merchant = MerchantInfo.create_merchant(MerchantEnum.QF2, MerchantTypeEnum.NORMAL)
        self.assertEqual(merchant.id, 2)
        self.assertEqual(merchant.merchant, MerchantEnum.QF2)
        self.assertEqual(merchant.m_type, MerchantTypeEnum.NORMAL)
        self.assertEqual(merchant.state, AccountStateEnum.ACTIVE)

        # 创建商户QF3
        merchant = MerchantInfo.create_merchant(MerchantEnum.QF3, MerchantTypeEnum.NORMAL)
        self.assertEqual(merchant.id, 3)
        self.assertEqual(merchant.merchant, MerchantEnum.QF3)
        self.assertEqual(merchant.m_type, MerchantTypeEnum.NORMAL)
        self.assertEqual(merchant.state, AccountStateEnum.ACTIVE)

        # 应该有3个商户
        all_merchant = list(MerchantInfo.query_all())
        self.assertEqual(len(all_merchant), 3)

        # 删除TEST商户
        MerchantInfo.delete_merchant(MerchantEnum.TEST)

        # 还有2个商户
        all_merchant = list(MerchantInfo.query_all())
        self.assertEqual(len(all_merchant), 2)

        # 查不到TEST商户
        merchant = MerchantInfo.query_merchant(MerchantEnum.TEST)
        self.assertIsNone(merchant)

        # 再次添加TEST商户
        merchant = MerchantInfo.create_merchant(MerchantEnum.TEST, MerchantTypeEnum.TEST)
        self.assertEqual(merchant.id, 4)
        self.assertEqual(merchant.merchant, MerchantEnum.TEST)
        self.assertEqual(merchant.m_type, MerchantTypeEnum.TEST)
        self.assertEqual(merchant.state, AccountStateEnum.ACTIVE)

        # 又有3个商户
        all_merchant = list(MerchantInfo.query_all())
        self.assertEqual(len(all_merchant), 3)
예제 #5
0
    def validate_name(self, value):
        try:
            self.name.data = MerchantEnum.from_name(value.data)
        except Exception:
            raise StopValidation("无效的商户名称")

        if not MerchantInfo.query_merchant(self.name.data):
            raise StopValidation("未创建的商户")
예제 #6
0
    def get(self):
        """
        商户配置查询
        :return:
        """
        if not request.args:
            return ResponseSuccess(message="参数规则:?merchant=test").as_response()

        try:
            merchant = MerchantEnum.from_name(request.args['merchant'])
        except:
            return ResponseSuccess(message="请输入正确的商户名称,有效的商户名称包括:%s" %
                                   MerchantEnum.get_names()).as_response()

        merchant_info = MerchantInfo.query_merchant(merchant)
        if not merchant_info:
            return ResponseSuccess(message="未创建商户").as_response()

        bs_data = dict(
            balance=dict(
                balance_total=str(merchant_info.balance_total),
                balance_available=str(merchant_info.balance_available),
                balance_income=str(merchant_info.balance_income),
                balance_frozen=str(merchant_info.balance_frozen),
            ),
            merchant=merchant.name,
            domains=MerchantDomainConfig.get_domains(merchant),
            # db=DBEnum(merchant.name).get_db_name(),
        )

        deposit_fees = MerchantFeeConfig.query_active_configs(
            query_fields=dict(
                merchant=merchant,
                payment_way=PayTypeEnum.DEPOSIT,
            ))
        deposit_fees = MerchantFeeConfig.filter_latest_items(deposit_fees)
        if not deposit_fees:
            return MerchantConfigDepositError(bs_data=bs_data).as_response()
        bs_data['deposit_fees'] = [x.short_description for x in deposit_fees]

        withdraw_fees = MerchantFeeConfig.query_latest_one(query_fields=dict(
            merchant=merchant,
            payment_way=PayTypeEnum.WITHDRAW,
        ))
        if not withdraw_fees:
            return MerchantConfigWithdrawError(bs_data=bs_data).as_response()
        bs_data['withdraw_fees'] = withdraw_fees.short_description

        channels = ChannelListHelper.get_available_channels(
            merchant, PayTypeEnum.DEPOSIT)
        bs_data['deposit_channels'] = [x.short_description for x in channels]

        channels = ChannelListHelper.get_available_channels(
            merchant, PayTypeEnum.WITHDRAW)
        bs_data['withdraw_channels'] = [x.short_description for x in channels]

        return ResponseSuccess(bs_data=bs_data).as_response()
예제 #7
0
    def post(self):
        """
        商户列表
        :return:
        """
        merchant_list = list()

        merchants = MerchantInfo.query_all()

        all_configs = MerchantFeeConfig.query_all()
        all_configs = MerchantFeeConfig.filter_latest_items(all_configs)

        merchant_all_configs = defaultdict(list)
        for fee in all_configs:
            merchant_all_configs[fee.merchant].append(fee)

        for merchant in merchants:
            fee_configs = merchant_all_configs.get(merchant.merchant)
            if not fee_configs:
                continue

            withdraw_fees = [x for x in fee_configs if x.payment_way == PayTypeEnum.WITHDRAW]
            recharge_fees = [x for x in fee_configs if x.payment_way == PayTypeEnum.DEPOSIT]

            withdraw_desc = ''
            cost_type = None
            if withdraw_fees:
                withdraw_desc = withdraw_fees[0].value_description
                cost_type = withdraw_fees[0].cost_type.name

            merchant_list.append(dict(
                id=merchant.id,
                name=merchant.merchant.name,
                balance_total=merchant.balance_total,
                balance_available=merchant.balance_available,
                balance_income=merchant.balance_income,
                balance_frozen=merchant.balance_frozen,
                type=merchant.m_type.name,
                domains='\n'.join(MerchantDomainConfig.get_domains(merchant.merchant)),
                state=merchant.state.name,
                channel_fees=dict(
                    withdraw=withdraw_desc,
                    cost_type=cost_type,
                    deposit=[
                        dict(
                            desc=x.payment_method.desc,
                            value=x.payment_method.value,
                            rate=x.value_description,
                        )
                        for x in recharge_fees
                    ]
                )
            ))

        data = dict(counts=len(merchant_list), merchants=merchant_list)

        return MerchantListResult(bs_data=data).as_response()
예제 #8
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
예제 #9
0
    def post(self):
        """
        商户基本信息
        :return:
        """
        user = g.user
        info = MerchantInfo.query_merchant(MerchantEnum(user.mid))
        if not info:
            return MerchantInfoNoExistError().as_response()

        merchant_info = dict(account=user.account,
                             balance_total=info.balance_total,
                             available_balance=info.balance_available,
                             incoming_balance=info.balance_income,
                             frozen_balance=info.balance_frozen)
        return MerchantBaseInfoResponse(bs_data=merchant_info).as_response()
예제 #10
0
    def post(self):
        """
        新建商户 新增费率
        """
        form, error = MerchantFeeAddForm().request_validate()
        if error:
            return error.as_response()

        # 充值费率设置
        for item in form.deposit_info:
            if not isinstance(item.data['value'], str):
                return ParameterException(message="充值费率必须传入字符串类型").as_response()

        # 提现费率
        if not isinstance(form.withdraw_info.data['value'], str):
            return ParameterException(message="提现费率必须传入字符串类型").as_response()

        merchant = form.data['name']

        # 第一步向数据库中插入商户数据
        models = MerchantInfo.create_merchant_models(merchant, form.data['type'])

        # 充值费率设置
        merchant_fee_dict = []
        for item in form.deposit_info:
            merchant_fee_dict.append(dict(
                merchant=merchant,
                payment_way=PayTypeEnum.DEPOSIT,
                value=item.data['value'],
                fee_type=item.data['fee_type'],
                payment_method=item.data['name'],
            ))

        # 提现费率
        merchant_fee_dict.append(dict(
            merchant=merchant,
            payment_way=PayTypeEnum.WITHDRAW,
            value=form.withdraw_info.data['value'],
            fee_type=form.withdraw_info.data['fee_type'],
            cost_type=form.withdraw_info.data['cost_type'],
        ))

        rst, error = MerchantFeeConfig.update_fee_config(merchant, merchant_fee_dict, models)
        if error:
            return error.as_response()

        return ResponseSuccess().as_response()
예제 #11
0
    def __test_callback_ponypay_deposit(self):
        self.path = "/callback/ponypay/deposit"

        # 初始化数据
        kwargs = dict(
            fee="2.5",
            fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
            limit_per_min="200",
            limit_per_max="10000",
            trade_begin_hour="00",
            trade_begin_minute="00",
            trade_end_hour="23",
            trade_end_minute="59",
            maintain_begin=DateTimeKit.str_to_datetime("2019-09-07 09:00:00"),
            maintain_end=DateTimeKit.str_to_datetime("2019-09-07 09:00:00"),
            settlement_type=SettleTypeEnum.D0,
            state=ChannelStateEnum.TESTING,
            priority="101")
        rst, error = ChannelConfig.update_channel(
            ChannelConfigEnum.CHANNEL_1001, **kwargs)
        self.assertEqual(rst, True)
        self.assertEqual(error, None)
        # ChannelLimitCacheCtl(PayTypeEnum.DEPOSIT).sync_db_channels_to_cache()

        merchant = MerchantEnum.TEST
        merchant_fee_list = [
            dict(merchant=merchant,
                 payment_way=PayTypeEnum.DEPOSIT,
                 value="3",
                 fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
                 payment_method=PayMethodEnum.ZHIFUBAO_SAOMA),
            dict(merchant=merchant,
                 payment_way=PayTypeEnum.WITHDRAW,
                 value="3.2",
                 fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER)
        ]

        ret, error = MerchantFeeConfig.update_fee_config(
            merchant, merchant_fee_list)
        self.assertEqual(rst, True)
        self.assertEqual(error, None)

        info = dict(
            account="+8618977772222",
            merchant=MerchantEnum.TEST,
            ac_type=AccountTypeEnum.MOBILE,
            login_pwd="123456789",
        )
        MerchantInfo.create_merchant(MerchantEnum.TEST, MerchantTypeEnum.TEST)
        user = User.register_account(merchant=info['merchant'],
                                     account=info['account'],
                                     ac_type=info['ac_type'],
                                     login_pwd=info['login_pwd'])

        uid = user.id
        channel_config = ChannelConfig.query_latest_one(query_fields=dict(
            channel_enum=ChannelConfigEnum.CHANNEL_1001))
        channel_conf = ChannelConfigEnum.CHANNEL_1001.conf
        channel_conf['white_ip'].append("127.0.0.1")
        merchant_fee = MerchantFeeConfig.query_latest_one(
            query_fields=dict(merchant=MerchantEnum.TEST,
                              payment_way=PayTypeEnum.DEPOSIT,
                              payment_method=channel_conf.payment_method))

        self.__test_callback_order_success(uid, channel_config, merchant_fee,
                                           '1')
        self.__test_callback_order_success(uid, channel_config, merchant_fee,
                                           '-1')

        stop = 1
예제 #12
0
 def test_merchant_model(self):
     self.__create_merchant_info()
     self.__check_add_event_params_error()
     self.__add_event()
     MerchantInfo.delete_all()
예제 #13
0
    def get(self):
        """
        查询商户余额变更流水
        :return:
        """
        if not request.args:
            return ResponseSuccess(
                message="参数规则:?merchant=test&date=20190901&export=1"
            ).as_response()

        try:
            merchant = MerchantEnum.from_name(request.args['merchant'])
            merchant_info = MerchantInfo.query_merchant(merchant)
            if not merchant_info:
                raise
        except:
            return ResponseSuccess(message="请输入正确的商户名称,有效的商户名称包括:%s" %
                                   MerchantEnum.get_names()).as_response()

        try:
            date = request.args.get('date')
            if date:
                date = DateTimeKit.str_to_datetime(
                    date, DateTimeFormatEnum.TIGHT_DAY_FORMAT, to_date=True)
            else:
                date = DateTimeKit.get_cur_date()
        except:
            return ResponseSuccess(
                message="请输入有效的查询日期,格式为:20190901").as_response()

        balance = dict(
            balance_total=str(merchant_info.balance_total),
            balance_available=str(merchant_info.balance_available),
            balance_income=str(merchant_info.balance_income),
            balance_frozen=str(merchant_info.balance_frozen),
        )

        events = MerchantBalanceEvent.query_by_date(date,
                                                    merchant=merchant,
                                                    date=date)

        rst = dict(
            data=list(),
            sum_value=0,
            balance=balance,
        )

        rst['sql'] = str(events)

        for event in events:
            rst['sum_value'] += event.value_real
            rst['data'].append(
                dict(
                    create_time=event.create_time,
                    ref_id=event.ref_id,
                    order_type=event.order_type.desc,
                    source=event.source.desc,
                    bl_type=event.bl_type.desc,
                    value=str(event.value_real),
                    ad_type=event.ad_type.desc,
                    tx_id=event.tx_id,
                    comment=event.comment,
                ))

        rst['data'] = sorted(rst['data'],
                             key=lambda x: x['create_time'],
                             reverse=True)
        for x in rst['data']:
            x['create_time'] = DateTimeKit.datetime_to_str(x['create_time'])

        if rst['data'] and request.args.get('export'):
            filename = 'merchant_balance_events_%s.csv' % DateTimeKit.datetime_to_str(
                date, DateTimeFormatEnum.TIGHT_DAY_FORMAT)
            return CsvKit.send_csv(rst['data'],
                                   filename=filename,
                                   fields=rst['data'][0].keys())

        rst['sum_value'] = str(rst['sum_value'])

        return ResponseSuccess(bs_data=rst).as_response()
예제 #14
0
 def get_merchant_info(cls):
     return MerchantInfo.query_merchant(cls.merchant)
예제 #15
0
    def __test_api_merchant_balance(self):
        self.path = '/merchant/balance/edit'

        post_data = dict(
            name='XXXXX',
            adjustment_type=ManualAdjustmentType.MINUS.name,
            amount="500.34",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(ParameterException.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ParameterException.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue("无效的商户名称" in response.json['message'])

        post_data = dict(
            name=MerchantEnum.QF3.name,
            adjustment_type=ManualAdjustmentType.MINUS.name,
            amount="500.34",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(ParameterException.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ParameterException.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue("未创建的商户" in response.json['message'])

        # # 创建TEST商户
        # merchant = MerchantInfo.create_merchant(MerchantEnum.TEST, MerchantTypeEnum.TEST)
        # self.assertEqual(merchant.id, 1)
        # self.assertEqual(merchant.mch_name, MerchantEnum.TEST)
        # self.assertEqual(merchant.m_type, MerchantTypeEnum.TEST)
        # self.assertEqual(merchant.state, AccountStateEnum.ACTIVE)
        # self.assertEqual(merchant.balance_total, 0)

        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type='XXXXX',
            amount="500.34",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(ParameterException.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ParameterException.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue("无效的调整类型" in response.json['message'])

        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.MINUS.name,
            amount="500.x4",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(ParameterException.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ParameterException.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue("无效的金额" in response.json['message'])

        # 余额不足,减失败
        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.MINUS.name,
            amount="500.34",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(MerchantUpdateError.code, response.status_code,
                         response.json['message'])
        self.assertEqual(MerchantUpdateError.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue("可用余额不足" in response.json['message'])

        # 增加余额
        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.PLUS.name,
            amount="500.34",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(ResponseSuccess.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue(ResponseSuccess.message == response.json['message'])

        merchant = MerchantInfo.query_merchant(MerchantEnum.TEST)
        self.assertEqual(merchant.balance_available, Decimal("500.34"))
        self.assertEqual(merchant.balance_total, Decimal("500.34"))

        # 可用余额够了,减成功
        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.MINUS.name,
            amount="100.01",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(ResponseSuccess.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue(ResponseSuccess.message == response.json['message'])

        merchant = MerchantInfo.query_merchant(MerchantEnum.TEST)
        self.assertEqual(merchant.balance_available, Decimal("400.33"))
        self.assertEqual(merchant.balance_total, Decimal("400.33"))

        # 可用余额不足,冻结失败
        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.FROZEN.name,
            amount="500.01",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(MerchantUpdateError.code, response.status_code,
                         response.json['message'])
        self.assertEqual(MerchantUpdateError.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue("可用余额不足" in response.json['message'])

        merchant = MerchantInfo.query_merchant(MerchantEnum.TEST)
        self.assertEqual(merchant.balance_available, Decimal("400.33"))
        self.assertEqual(merchant.balance_total, Decimal("400.33"))

        # 可用余额够了,冻结成功
        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.FROZEN.name,
            amount="100.01",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(ResponseSuccess.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue(ResponseSuccess.message == response.json['message'])

        merchant = MerchantInfo.query_merchant(MerchantEnum.TEST)
        self.assertEqual(merchant.balance_available, Decimal("300.32"))
        self.assertEqual(merchant.balance_frozen, Decimal("100.01"))
        self.assertEqual(merchant.balance_total, Decimal("400.33"))

        # 冻结余额不足,解冻失败
        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.UNFROZEN.name,
            amount="200.01",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(MerchantUpdateError.code, response.status_code,
                         response.json['message'])
        self.assertEqual(MerchantUpdateError.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue("冻结余额不足" in response.json['message'])

        merchant = MerchantInfo.query_merchant(MerchantEnum.TEST)
        self.assertEqual(merchant.balance_available, Decimal("300.32"))
        self.assertEqual(merchant.balance_frozen, Decimal("100.01"))
        self.assertEqual(merchant.balance_total, Decimal("400.33"))

        # 冻结余额不足,解冻失败
        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.UNFROZEN.name,
            amount="50.01",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(ResponseSuccess.code, response.status_code,
                         response.json['message'])
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue(ResponseSuccess.message == response.json['message'])

        merchant = MerchantInfo.query_merchant(MerchantEnum.TEST)
        self.assertEqual(merchant.balance_available, Decimal("350.33"))
        self.assertEqual(merchant.balance_frozen, Decimal("50.00"))
        self.assertEqual(merchant.balance_total, Decimal("400.33"))

        # 在途余额不足,扣减失败
        post_data = dict(
            name=MerchantEnum.TEST.name,
            adjustment_type=ManualAdjustmentType.MINUS_INCOME.name,
            amount="200.01",
            reason="因为要改,所有就改了",
        )
        response = self.do_request(post_data)
        self.assertEqual(MerchantUpdateError.code, response.status_code,
                         response.json['message'])
        self.assertEqual(MerchantUpdateError.error_code,
                         response.json['error_code'], response.json['message'])
        self.assertTrue("在途余额不足" in response.json['message'])

        merchant = MerchantInfo.query_merchant(MerchantEnum.TEST)
        self.assertEqual(merchant.balance_available, Decimal("350.33"))
        self.assertEqual(merchant.balance_frozen, Decimal("50.00"))
        self.assertEqual(merchant.balance_income, 0)
        self.assertEqual(merchant.balance_total, Decimal("400.33"))
예제 #16
0
    def __test_api_withdraw(self):
        """
        后台准备数据:
            充值通道数据
            代付通道数据
            商户费率配置数据

        钱包端:
            1. 创建充值订单
            2. 充值
            3. 用户设置支付密码
            4. 用户绑定银行卡
            5. 获取充值配置信息(用户余额,充值最低最高限制)

        发起提现请求:

        :return:
        """
        merchant = MerchantEnum.from_name("TEST")
        info = dict(merchant=merchant,
                    account="+8618988888888",
                    auth_code="8888",
                    password="******",
                    trade_pwd="b943a52cc24dcdd12bf2ba3afda92351",
                    ac_type=AccountTypeEnum.MOBILE)
        user = User.register_account(info['merchant'], info['account'],
                                     info['ac_type'], info['password'])

        self.path = '/auth/account/login'

        login_data = dict(number=info['account'], password=info['password'])
        response = self.do_request(login_data)
        self.assertEqual(ResponseSuccessLogin.code, response.status_code)
        self.assertEqual(ResponseSuccessLogin.error_code,
                         response.json['error_code'])
        self.token = response.json['data']['token']

        self.path = "/withdraw/banks/list"

        # 1. 向数据库添加代付通道信息
        withdraw_item = dict(fee="2.5",
                             fee_type=PaymentFeeTypeEnum(1),
                             limit_per_min="200",
                             limit_per_max="5000",
                             limit_day_max="50000",
                             trade_begin_hour="00",
                             trade_begin_minute="00",
                             trade_end_hour="23",
                             trade_end_minute="59",
                             maintain_begin=DateTimeKit.str_to_datetime(
                                 "2019-09-27 00:00:00",
                                 DateTimeFormatEnum.SECONDS_FORMAT),
                             maintain_end=DateTimeKit.str_to_datetime(
                                 "2019-10-20 23:59:00",
                                 DateTimeFormatEnum.SECONDS_FORMAT),
                             state=ChannelStateEnum(10),
                             banks=[
                                 PaymentBankEnum(1),
                                 PaymentBankEnum(2),
                                 PaymentBankEnum(4),
                                 PaymentBankEnum(3),
                                 PaymentBankEnum(15)
                             ])

        ProxyChannelConfig.update_channel(ChannelConfigEnum.CHANNEL_1001,
                                          **withdraw_item)

        # 2. 向数据库插入 商户费率配置信息
        # 充值费率设置
        merchant_fee_dict = []
        merchant_fee_dict.append(
            dict(
                merchant=MerchantEnum.from_name('TEST'),
                payment_way=PayTypeEnum.DEPOSIT,
                value="3.5",
                fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
                payment_method=PayMethodEnum.ZHIFUBAO_SAOMA,
            ))

        # 提现费率
        merchant_fee_dict.append(
            dict(
                merchant=MerchantEnum.from_name('TEST'),
                payment_way=PayTypeEnum.WITHDRAW,
                value="3.5",
                fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
            ))

        rst, error = MerchantFeeConfig.update_fee_config(
            merchant, merchant_fee_dict)
        self.assertEqual(True, rst)

        # 3. 给用户和商户充值
        uid = user.uid

        ref_id = hashlib.md5('lakjdflasjfadl;kfja'.encode('utf8')).hexdigest()

        data = dict(
            uid=uid,
            merchant=merchant,
            ref_id=ref_id,
            source=OrderSourceEnum.TESTING,
            order_type=PayTypeEnum.DEPOSIT,
            bl_type=BalanceTypeEnum.AVAILABLE,
            ad_type=BalanceAdjustTypeEnum.PLUS,
            tx_id=OrderUtils.gen_normal_tx_id(uid),
            value=Decimal("10000.00"),
            comment="xxx",
        )
        rst, msg = UserBalanceEvent.update_user_balance(**data)
        self.assertEqual(0, rst)

        balance = UserBalance.query_balance(data['uid'],
                                            data['merchant']).first()

        # 添加商户余额
        data = dict(
            merchant=MerchantEnum.TEST,
            source=OrderSourceEnum.TESTING,
            order_type=PayTypeEnum.DEPOSIT,
            bl_type=BalanceTypeEnum.AVAILABLE,
            ad_type=BalanceAdjustTypeEnum.PLUS,
            tx_id=OrderUtils.gen_normal_tx_id(100),
            value=Decimal("10000.00"),
            comment=msg,
        )

        ref_id = hashlib.md5(
            RandomString.gen_random_str(
                length=128).encode('utf8')).hexdigest()
        data['ref_id'] = ref_id
        event_check = dict(total=1)
        event_check.update(data)
        rst, msg = MerchantBalanceEvent.update_balance(**data)

        self.assertEqual(0, rst)

        # 设置支付密码
        flag = User.set_payment_password(merchant,
                                         uid=uid,
                                         trade_pwd=info["trade_pwd"])

        self.assertEqual(True, flag)

        # 绑定银行卡
        bank_info = {
            "payment_password": info['trade_pwd'],
            "bank_name": "中国工商银行",
            "bank_code": "ICBC",
            "card_no": "6212260405014627955",
            "account_name": "张三",
            "branch": "广东东莞东莞市长安镇支行",
            "province": "广东省",
            "city": "东莞市"
        }

        flag = BankCard.add_bank_card(merchant,
                                      uid=uid,
                                      bank_name=bank_info['bank_name'],
                                      bank_code=bank_info['bank_code'],
                                      card_no=bank_info['card_no'],
                                      account_name=bank_info['account_name'],
                                      branch=bank_info['branch'],
                                      province=bank_info['province'],
                                      city=bank_info['city'])
        self.assertEqual(bank_info['card_no'], flag.card_no)

        self.path = "/withdraw/limit/config/get"
        response = self.do_request()
        self.assertEqual(ResponseBankWithdraw.code, response.status_code)
        self.assertEqual(ResponseBankWithdraw.error_code,
                         response.json['error_code'])
        self.assertEqual("10000", response.json['data']['balance'])
        self.assertEqual("200", response.json['data']['limit_min'])
        self.assertEqual("5000", response.json['data']['limit_max'])

        self.path = "/withdraw/order/create"
        create_data = dict(amount=1000.001,
                           user_bank=1,
                           trade_password=info['trade_pwd'])

        # 测试小于 最低限额

        create_data['amount'] = 100

        response = self.do_request(json_data=create_data)
        self.assertEqual(WithdrawOrderAmountInvalidError.code,
                         response.status_code)
        self.assertEqual(WithdrawOrderAmountInvalidError.error_code,
                         response.json['error_code'])

        create_data['amount'] = 6000

        response = self.do_request(json_data=create_data)
        self.assertEqual(WithdrawOrderAmountInvalidError.code,
                         response.status_code)
        self.assertEqual(WithdrawOrderAmountInvalidError.error_code,
                         response.json['error_code'])

        create_data['amount'] = str(500.56)
        create_data['user_bank'] = 100

        response = self.do_request(json_data=create_data)
        self.assertEqual(WithdrawBankNoExistError.code, response.status_code)
        self.assertEqual(WithdrawBankNoExistError.error_code,
                         response.json['error_code'], response.json['message'])

        use_balance = UserBalance.query_balance(user.uid, merchant).first()
        ori_merchant = MerchantInfo.query_merchant(merchant)

        balance = ori_merchant.bl_ava - BalanceKit.round_4down_5up(
            Decimal(create_data['amount'])) * 100 - BalanceKit.round_4down_5up(
                Decimal(create_data['amount']) * Decimal(3.5))
        merchant_balance = BalanceKit.round_4down_5up(
            balance / Decimal(100)) * 100

        u_balance = BalanceKit.round_4down_5up(
            Decimal(use_balance.balance) / Decimal(100) -
            Decimal(create_data['amount'])) * Decimal(100)
        create_data['user_bank'] = 1

        response = self.do_request(json_data=create_data)
        self.assertEqual(ResponseSuccess.code, response.status_code)
        self.assertEqual(ResponseSuccess.error_code,
                         response.json['error_code'])

        cur_balance = UserBalance.query_balance(user.uid, merchant).first()
        cur_merchant = MerchantInfo.query_merchant(merchant)

        self.assertEqual(int(merchant_balance), int(cur_merchant.bl_ava))
        self.assertEqual(int(u_balance), int(cur_balance.balance))
예제 #17
0
    def order_create(cls,
                     user,
                     amount,
                     client_ip,
                     user_bank_id=None,
                     bank_info=None,
                     notify_url=None,
                     mch_tx_id=None,
                     extra=None):
        """
        申请创建订单
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')

        order = None

        if not bank_info:
            card_entry = BankCard.query_bankcard_by_id(card_id=user_bank_id)
            if not card_entry:
                msg = '%s, params: %s' % (WithdrawBankNoExistError.message,
                                          params)
                current_app.logger.error(msg)
                return order, WithdrawBankNoExistError()

        # 判断 金额是否在有效范围 获取代付系统当前最高最低交易金额
        # limit_min, limit_max = ChannelLimitCacheCtl(PayTypeEnum.WITHDRAW).get_channel_limit()
        # if amount < limit_min or amount > limit_max:
        if ChannelListHelper.is_amount_out_of_range(
                amount=amount,
                merchant=user.merchant,
                payment_way=PayTypeEnum.WITHDRAW,
                client_ip=client_ip):
            msg = '%s, params: %s' % (WithdrawOrderAmountInvalidError.message,
                                      params)
            current_app.logger.error(msg)
            return order, WithdrawOrderAmountInvalidError()

        # 商户配置信息
        merchant_config = MerchantFeeConfig.query_latest_one(query_fields=dict(
            merchant=user.merchant,
            payment_way=PayTypeEnum.WITHDRAW,
        ))

        # 手续费计算
        fee_value = FeeCalculator.calc_fee(amount, merchant_config.fee_type,
                                           merchant_config.value)
        user_fee = merchant_fee = 0
        if merchant_config.cost_type == CostTypeEnum.MERCHANT:
            merchant_fee = fee_value
        elif merchant_config.cost_type == CostTypeEnum.USER:
            user_fee = fee_value
            # 用户实际到账金额要减去手续费
            amount -= user_fee

        # 用户余额判断
        if user.uid:
            user_balance = UserBalance.query_balance(
                uid=user.uid, merchant=user.merchant).first()
            if user_balance.real_balance < amount + user_fee:
                msg = '%s, params: %s' % ("用户余额不足", params)
                current_app.logger.error(msg)
                return order, AccountBalanceInsufficientError(message="用户余额不足")

        # 判断商户余额是否充足
        merchant_balance = MerchantInfo.query_merchant(m_name=user.merchant)
        if merchant_balance.balance_available <= amount + merchant_fee:
            msg = '%s, params: %s' % ("商户余额不足", params)
            current_app.logger.error(msg)
            return order, AccountBalanceInsufficientError(message="商户余额不足")

        # 订单来源
        source = OrderSourceEnum.TESTING if user.is_test_user else OrderSourceEnum.ONLINE

        try:
            # 创建提现订单/扣商户余额/扣用户余额,在同一个事务里面
            with db.auto_commit():
                order, ref_id = OrderCreateCtl.create_order_event(
                    uid=user.uid,
                    amount=amount,
                    merchant=user.merchant,
                    source=source,
                    order_type=PayTypeEnum.WITHDRAW,
                    in_type=InterfaceTypeEnum.CASHIER_H5,
                    bank_id=user_bank_id,  # 提现时需要填入 银行卡信息
                    mch_fee_id=merchant_config.config_id,
                    mch_tx_id=mch_tx_id,
                    ip=client_ip,
                    fee=fee_value,
                    cost_type=merchant_config.cost_type,
                    notify_url=notify_url,
                    bank_info=bank_info,
                    extra=extra,
                    commit=False,
                )

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

                # 扣提现金额
                flag, msg = MerchantBalanceEvent.update_balance(
                    merchant=user.merchant,
                    ref_id=ref_id,
                    source=source,
                    order_type=PayTypeEnum.WITHDRAW,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    value=amount,
                    ad_type=BalanceAdjustTypeEnum.MINUS,
                    tx_id=order.sys_tx_id,
                    commit=False,
                )
                if flag < 0:
                    msg = '%s, params: %s' % ("扣商户余额失败, %s" % msg, params)
                    current_app.logger.error(msg)
                    raise DepositCallbackUserBalanceError(message="扣商户余额失败")

                if merchant_fee:
                    # 扣商户手续费
                    flag, msg = MerchantBalanceEvent.update_balance(
                        merchant=user.merchant,
                        ref_id=OrderUtils.gen_unique_ref_id(),
                        source=source,
                        order_type=PayTypeEnum.FEE,
                        bl_type=BalanceTypeEnum.AVAILABLE,
                        value=merchant_fee,
                        ad_type=BalanceAdjustTypeEnum.MINUS,
                        tx_id=order.sys_tx_id,
                        commit=False,
                    )
                    if flag < 0:
                        msg = '%s, params: %s' % ("扣商户手续费失败, %s" % msg, params)
                        current_app.logger.error(msg)
                        raise DepositCallbackUserBalanceError(
                            message="扣商户手续费失败")

                if user_fee:
                    # 扣除用户手续费
                    flag, msg = UserBalanceEvent.update_user_balance(
                        uid=user.uid,
                        merchant=user.merchant,
                        ref_id=OrderUtils.gen_unique_ref_id(),
                        source=source,
                        order_type=PayTypeEnum.FEE,
                        bl_type=BalanceTypeEnum.AVAILABLE,
                        value=user_fee,
                        ad_type=BalanceAdjustTypeEnum.MINUS,
                        tx_id=order.sys_tx_id,
                        commit=False,
                    )
                    if flag < 0:
                        msg = '%s, params: %s' % ("扣用户手续费失败, %s" % msg, params)
                        current_app.logger.error(msg)
                        raise DepositCallbackUserBalanceError(
                            message="扣用户手续费失败")

                # 扣除用户余额
                flag, msg = UserBalanceEvent.update_user_balance(
                    uid=user.uid,
                    merchant=user.merchant,
                    ref_id=ref_id,
                    source=source,
                    order_type=PayTypeEnum.WITHDRAW,
                    bl_type=BalanceTypeEnum.AVAILABLE,
                    value=amount,
                    ad_type=BalanceAdjustTypeEnum.MINUS,
                    tx_id=order.sys_tx_id,
                    commit=False,
                )
                if flag < 0:
                    msg = '%s, params: %s' % ("扣用户余额失败, %s" % msg, params)
                    current_app.logger.error(msg)
                    raise DepositCallbackUserBalanceError(message="扣用户余额失败")

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

        return order, None