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)
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
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'])
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)
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("未创建的商户")
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()
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()
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
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()
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()
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
def test_merchant_model(self): self.__create_merchant_info() self.__check_add_event_params_error() self.__add_event() MerchantInfo.delete_all()
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()
def get_merchant_info(cls): return MerchantInfo.query_merchant(cls.merchant)
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"))
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))
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