def post(self): """ 商户编辑 编辑费率 """ # 验证费率表单 form, error = EditMerchantRateForm().request_validate() if error: return error.as_response() merchant = form.data['name'] 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'], )) if form.withdraw_info.data['value']: 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'], )) MerchantFeeConfig.update_fee_config(merchant, merchant_fee_dict) return ResponseSuccess().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 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): """ 检查手机号是否已经注册 """ form, error = MobileRegisterCheckForm().request_validate() if error: return error.as_response() if not MerchantFeeConfig.query_latest_one(query_fields=dict( merchant=form.merchant.data, payment_way=PayTypeEnum.DEPOSIT, )): return MerchantConfigDepositError().as_response() if not MerchantFeeConfig.query_latest_one(query_fields=dict( merchant=form.merchant.data, payment_way=PayTypeEnum.WITHDRAW, )): return MerchantConfigWithdrawError().as_response() return ResponseSuccess().as_response()
def check_result(self, conf_dict): payment_method = conf_dict.get('payment_method') conf = MerchantFeeConfig.query_latest_one(query_fields=dict( merchant=conf_dict['merchant'], payment_way=conf_dict['payment_way'], payment_method=payment_method )) self.assertIsNotNone(conf) self.assertEqual(conf_dict['merchant'], conf['merchant']) if payment_method: self.assertEqual(conf_dict['payment_method'], conf['payment_method']) self.assertEqual(conf_dict['fee_type'], conf['fee_type']) self.assertEqual(conf_dict['value'], conf['value'])
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 post(self): """ 手机号码注册 :return: """ # 验证 手机号 验证码 密码格式 form, error = PasswordForm().request_validate() if error: return error.as_response() # 判断验证码是否过期 if AuthCodeGenerator(form.number.data).is_expired(form.auth_code.data): return AuthCodeExpiredError().as_response() # 判断验证码是否正确 if not AuthCodeGenerator(form.number.data).verify_code( form.auth_code.data): return AuthCodeError().as_response() if not MerchantFeeConfig.query_latest_one(query_fields=dict( merchant=form.merchant.data, payment_way=PayTypeEnum.DEPOSIT, )): return MerchantConfigDepositError().as_response() if not MerchantFeeConfig.query_latest_one(query_fields=dict( merchant=form.merchant.data, payment_way=PayTypeEnum.WITHDRAW, )): return MerchantConfigWithdrawError().as_response() User.register_account( merchant=form.merchant.data, account=form.number.data, ac_type=AccountTypeEnum.MOBILE, login_pwd=form.password.data, ) return ResponseSuccess().as_response()
def get(self): """ 查询可用的通道 :return: """ if not request.args: return ResponseSuccess( message="参数规则:?tx_id=xxxx,交易ID,必填").as_response() try: tx_id = request.args['tx_id'] except: return ResponseSuccess(message="必填交易ID").as_response() order = OrderDeposit.query_by_tx_id(tx_id) if not order: return ResponseSuccess(message="订单不存在,请确认交易号是否正确: %s" % tx_id).as_response() order_detail = OrderDetailDeposit.query_by_tx_id(tx_id) channel = ChannelConfig.query_by_channel_id(order.channel_id) fee_config = MerchantFeeConfig.query_by_config_id(order.mch_fee_id) order_info = dict( order_id=order.order_id, create_time=order.str_create_time, uid=order.uid, sys_tx_id=order.sys_tx_id, mch_tx_id=order.mch_tx_id, channel_tx_id=order.channel_tx_id, amount=str(order.amount), tx_amount=str(order.tx_amount), source=order.source.desc, pay_method=order.pay_method.desc, state=order.state.desc, settle=order.settle.desc, deliver=order.deliver.desc, channel_id=order.channel_id, channel=channel.short_description, mch_fee_id=order.mch_fee_id, mch_fee=fee_config.short_description, in_type=order_detail.in_type.desc, offer=str(order_detail.offer), fee=str(order_detail.fee), cost=str(order_detail.cost), profit=str(order_detail.profit), ip=order_detail.ip, ) return ResponseSuccess(bs_data=order_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 get_available_channels(cls, merchant, payment_way: PayTypeEnum, amount=0, client_ip=None, payment_type=None): """ 获取可用的通道 :param merchant: :param payment_way: :param amount: :param client_ip: :param payment_type: :return: """ channels = list() # 找出最新版本的商户费率配置 merchant_fees = MerchantFeeConfig.get_latest_active_configs(merchant=merchant, payment_way=payment_way) if not merchant_fees: return channels # 充值需要判断到通道支付方式是否已经在商户配置费率 # 提现配置没有payment_method,列表置为空,不用判断 payment_methods = [m.payment_method for m in merchant_fees if m.payment_method] routers_dict = cls.get_router2_dict() channel_configs = cls.get_config_channels(payment_way) for channel in channel_configs: channel_enum = channel.channel_enum router = routers_dict.get(channel_enum) if router and not router.is_router_match(merchant=merchant, amount=amount): continue if payment_type and channel_enum.conf['payment_type'] != payment_type: # 支付类型不匹配 continue if payment_methods and channel_enum.conf['payment_method'] not in payment_methods: # 过滤掉未设置费率的支付方式 continue if not channel.is_channel_valid(merchant.is_test, amount=amount, client_ip=client_ip): continue channels.append(channel) return channels
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
def success_order_process(cls, order, tx_amount, channel_tx_id=None, comment: str = '', op_account=None, commit=True): """ 处理充值成功的订单 :param order: :param tx_amount: 实际支付金额 :param channel_tx_id: 通道订单号 :param comment: 备注 :param op_account: 备注 :param commit: 是否立即提交事务 :return: """ params = copy.deepcopy(locals()) params.pop('cls') params.pop('order') params['tx_id'] = order.sys_tx_id rst = dict( code=0, msg='', ) # 计算一笔订单的各种费用 channel_config = ChannelConfig.query_by_channel_id(order.channel_id) merchant_config = MerchantFeeConfig.query_by_config_id( order.mch_fee_id) order_fee = OrderFeeHelper.calc_order_fee(order, tx_amount, channel_config, merchant_config) try: with db.auto_commit(commit): order, ref_id = OrderUpdateCtl.update_order_event( order.order_id, uid=order.uid, merchant=order.merchant, state=OrderStateEnum.SUCCESS, channel_tx_id=channel_tx_id, tx_amount=tx_amount, offer=order_fee['offer'], # 优惠金额 fee=order_fee['merchant_fee'], # 手续费 cost=order_fee['channel_cost'], # 成本金额 profit=order_fee['profit'], # 利润(收入)金额 commit=False, pay_method=channel_config.channel_enum. conf['payment_method'], comment=comment, op_account=op_account, ) if not order: msg = '订单更新失败, params: %s' % params raise RuntimeError(msg) # 给用户充值 code, msg = UserBalanceEvent.update_user_balance( uid=order.uid, merchant=order.merchant, ref_id=ref_id, source=order.source, order_type=order.order_type, bl_type=BalanceTypeEnum.AVAILABLE, value=order.amount, ad_type=BalanceAdjustTypeEnum.PLUS, comment=comment, tx_id=order.sys_tx_id, commit=False, ) if code != 0: raise RuntimeError(msg) # 根据结算类型获取商户余额变更类型 balance_type = SettleHelper.get_balance_type_by_settle( channel_config.settlement_type) # 更新商户余额,加用户充值金额 code, msg = MerchantBalanceEvent.update_balance( merchant=order.merchant, ref_id=ref_id, source=order.source, order_type=order.order_type, bl_type=balance_type, value=order.amount, ad_type=BalanceAdjustTypeEnum.PLUS, tx_id=order.sys_tx_id, comment=comment, commit=False, ) if code != 0: raise RuntimeError(msg) # 更新商户余额,扣手续费 ref_id = OrderUtils.gen_unique_ref_id() code, msg = MerchantBalanceEvent.update_balance( merchant=order.merchant, ref_id=ref_id, source=order.source, order_type=PayTypeEnum.FEE, bl_type=balance_type, value=order_fee['merchant_fee'], ad_type=BalanceAdjustTypeEnum.MINUS, tx_id=order.sys_tx_id, comment=comment, commit=False, ) if code != 0: raise RuntimeError(msg) except RuntimeError as e: current_app.logger.error('An error occurred.', exc_info=True) return False # 累计当天通道充值额度 ChannelLimitCacheCtl.add_day_amount(channel_config.channel_enum, order.amount) cls.do_notify(order) return True
def get_merchant_fee_config(cls, payment_way: PayTypeEnum): return MerchantFeeConfig.query_latest_one(query_fields=dict( merchant=cls.merchant, payment_way=payment_way, ))
def render_page(cls): """ 渲染页面 :return: """ print(request.args) try: order_id = request.args['order_id'] except: return TemplateKit.render_template('deposit_simple.html', body="参数错误") order = DepositTransactionCtl.get_order_by_order_id(order_id) if not order: return TemplateKit.render_template('deposit_simple.html', body="订单不存在") cache = DepositPageRenderCache(order.order_id) cache_data = cache.get_content() print("&&&&&&&&&&&&&&", cache_data) if not cache_data: return TemplateKit.render_template('deposit_simple.html', body="订单已经过期") is_h5 = False prompt_msgs = [] channel_enum = cache_data.get('channel_enum') if channel_enum: channel_enum = ChannelConfigEnum(int(channel_enum)) prompt_msgs = channel_enum.get_prompt_info_detail() is_h5 = channel_enum.conf.payment_method.is_h5 if cache_data['render_type'] == SdkRenderType.QR_CODE: merchant_config = MerchantFeeConfig.query_by_config_id(order.mch_fee_id) b64_img = QRCodeKit.gen_base64_qr_code_png(cache_data['render_content']) return TemplateKit.render_template( 'deposit_qrcode.html', b64_img=b64_img, payment_type=merchant_config.payment_method.desc, sys_tx_id=order.sys_tx_id, amount=str(order.amount), valid_time=cache.EXPIRATION, payment_url=cache_data['render_content'] if is_h5 else None, prompt_msgs=prompt_msgs, ) if cache_data['render_type'] == SdkRenderType.FORM: return TemplateKit.render_template('deposit_base.html', body=cache_data['render_content']) if cache_data['render_type'] == SdkRenderType.HTML: return TemplateKit.render_template_string(cache_data['render_content']) if cache_data['render_type'] == SdkRenderType.TRANSFER: data = json.loads(cache_data['render_content'], encoding='utf8') return TemplateKit.render_template( 'bank.html', CardName=data['CardName'], CardNumber=data['CardNumber'], BankName=data['BankName'], amount=data['amount'], tx_id=data['tx_id'], start_time=data['start_time'] ) current_app.logger.error('failed to render page, order_id: %s, cache_data: %s', order.order_id, cache_data) return TemplateKit.render_template('deposit_simple.html', body="渲染失败")
def order_create(cls, user, amount, channel_enum, client_ip, source: OrderSourceEnum, in_type: InterfaceTypeEnum, notify_url=None, result_url=None, mch_tx_id=None, extra=None): """ 申请创建订单 :return: """ merchant = user.merchant # 找出最新版本的商户费率配置 channel_config = ChannelConfig.query_latest_one(query_fields=dict( channel_enum=channel_enum)) if not channel_config: current_app.logger.error( 'no channel config found, channel_enum: %s', channel_enum.desc) return None, InvalidDepositChannelError() if channel_config.is_in_maintain_time(): current_app.logger.error('channel in maintain, channel_enum: %s', channel_enum.desc) return None, ChannelNoValidityPeriodError(message="通道(%s)维护中" % channel_enum.desc) if not channel_config.is_in_trade_time(): current_app.logger.error( 'channel not in trade time, channel_enum: %s', channel_enum.desc) return None, ChannelNoValidityPeriodError( message="当前时间不在通道(%s)交易时间内" % channel_enum.desc) if channel_config.is_amount_per_limit(amount): current_app.logger.error( 'per amount limit, channel_enum: %s, amount: %s', channel_enum.desc, amount) return None, DepositOrderAmountInvalidError( message="%s,通道(%s)" % (DepositOrderAmountInvalidError.message, channel_enum.desc)) if channel_enum.is_fixed_amount_channel( ) and not channel_enum.is_amount_in_fixed_list(amount): current_app.logger.error( 'invalid amount, channel: %s, input amount: %s, fixed amounts: %s', channel_enum.desc, amount, channel_enum.get_fixed_amounts()) return DepositOrderAmountInvalidError(message="固额支付类型额度匹配失败") if not channel_config.state.is_available(user.is_test_user): current_app.logger.error( 'channel state not available, channel_enum: %s, uid: %s, merchant: %s', channel_enum.desc, user.uid, merchant.name) return None, ChannelNoValidityPeriodError( message="通道(%s)状态:%s" % (channel_enum.desc, channel_config.state.desc)) # 找出最新版本的商户费率配置 merchant_fee = MerchantFeeConfig.query_latest_one( query_fields=dict(merchant=merchant, payment_way=PayTypeEnum.DEPOSIT, payment_method=channel_enum.conf.payment_method)) if not merchant_fee: current_app.logger.error( 'no merchant fee config for channel, channel_enum: %s, uid: %s, merchant: %s', channel_enum.desc, user.uid, merchant.name) return None, MerchantConfigDepositError() kwargs = dict( uid=user.uid, merchant=merchant, amount=amount, channel_id=channel_config.channel_id, mch_fee_id=merchant_fee.config_id, order_type=PayTypeEnum.DEPOSIT, source=source, in_type=in_type, ip=client_ip, pay_method=channel_enum.conf.payment_method, notify_url=notify_url, result_url=result_url, mch_tx_id=mch_tx_id, extra=extra, ) try: order, _ = OrderCreateCtl.create_order_event(**kwargs) except Exception as e: return None, PreOrderCreateError(message=str(e)) if not order: return None, PreOrderCreateError() return order, None
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)
def post(self): """ 用户登陆获取token :return: """ # 验证登陆表单是否正确 form, error = LoginForm().request_validate() if error: return error.as_response() if not MerchantFeeConfig.query_latest_one(query_fields=dict( merchant=form.merchant.data, payment_way=PayTypeEnum.DEPOSIT, )): return MerchantConfigDepositError().as_response() if not MerchantFeeConfig.query_latest_one(query_fields=dict( merchant=form.merchant.data, payment_way=PayTypeEnum.WITHDRAW, )): return MerchantConfigWithdrawError().as_response() # 验证手机号是否已注册 user_info = User.query_user(form.merchant.data, account=form.number.data) if not user_info: return AccountNotExistError().as_response() if user_info.state == AccountStateEnum.INACTIVE: return DisableUserError().as_response() # 验证用户名 密码是否正确 if not User.verify_login(merchant=form.merchant.data, account=form.number.data, password=form.password.data): UserPasswordLimitCache(mobile_number=form.number.data).incr_times() # 获取密码输入错误次数是否达到上限 if UserPasswordLimitCache( mobile_number=form.number.data).is_limited(): User.update_user_state(form.merchant.data, account=form.number.data, state=AccountStateEnum.INACTIVE) return OriPasswordError().as_response() return LoginPasswordError().as_response() UserPasswordLimitCache(mobile_number=form.number.data).delete_cache() # 生成token 返回给客户端 token = UserLoginToken.generate_token( uid=user_info.uid, merchant=form.merchant.data.value) # 显示用户名 bind_info = UserBindInfo.query_bind_by_uid(user_info.uid) if bind_info: bind_name = bind_info.name else: bind_name = PhoneNumberParser.hide_number(user_info.account) return ResponseSuccessLogin(bs_data=dict( token=token, service_url=SERVICE_URL, permissions=user_info.permission_names, bind_name=bind_name, user_flag=user_info.flag.name, )).as_response()
def test_merchant_fee_config_model(self): # 添加测试 count = 0 latest_count = 0 for merchant in MerchantEnum: for payment_way in PayTypeEnum: if payment_way == PayTypeEnum.DEPOSIT: for payment_method in PayMethodEnum: for fee_type in PaymentFeeTypeEnum: params = [ dict( merchant=merchant, payment_way=payment_way, payment_method=payment_method, fee_type=fee_type, value=Decimal("1.22"), ), ] self.add_one_config(merchant, params) count += 1 latest_count += 1 else: for fee_type in PaymentFeeTypeEnum: params = [ dict( merchant=merchant, payment_way=payment_way, fee_type=fee_type, value=Decimal("3.22"), ), ] self.add_one_config(merchant, params) count += 1 latest_count += 1 all_configs = MerchantFeeConfig.query_all() add_num = len(list(all_configs)) self.assertEqual(count, add_num) latest_configs = MerchantFeeConfig.filter_latest_items(all_configs) x_latest_count = len(list(latest_configs)) self.assertEqual(len(MerchantEnum.get_names()), x_latest_count) # 修改测试 count = 0 for merchant in MerchantEnum: for payment_way in PayTypeEnum: if payment_way == PayTypeEnum.DEPOSIT: for payment_method in PayMethodEnum: for fee_type in PaymentFeeTypeEnum: params = [ dict( merchant=merchant, payment_way=payment_way, payment_method=payment_method, fee_type=fee_type, value=Decimal("1.33"), ), ] self.add_one_config(merchant, params) count += 1 else: for fee_type in PaymentFeeTypeEnum: params = [ dict( merchant=merchant, payment_way=payment_way, fee_type=fee_type, value=Decimal("2.33"), ), ] self.add_one_config(merchant, params) count += 1 add_num += count # 更新不会增加条目 all_configs = MerchantFeeConfig.query_all() num = len(list(all_configs)) self.assertEqual(add_num, num) latest_configs = MerchantFeeConfig.filter_latest_items(all_configs) x_latest_count = len(list(latest_configs)) self.assertEqual(len(MerchantEnum.get_names()), x_latest_count) # 批量修改测试 count = 0 valid_count = 0 for merchant in MerchantEnum: params = [] for payment_way in PayTypeEnum: if payment_way == PayTypeEnum.DEPOSIT: for payment_method in PayMethodEnum: params.append(dict( merchant=merchant, payment_way=payment_way, payment_method=payment_method, fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER, value=Decimal("1.22"), )) count += 1 else: params.append(dict( merchant=merchant, payment_way=payment_way, fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER, value=Decimal("3.22"), )) count += 1 valid_count += len(params) self.add_one_config(merchant, params) add_num += count all_configs = MerchantFeeConfig.query_all() num = len(list(all_configs)) self.assertEqual(add_num, num) latest_configs = MerchantFeeConfig.filter_latest_items(all_configs) x_latest_count = len(list(latest_configs)) self.assertEqual(valid_count, x_latest_count) MerchantFeeConfig.delete_all() num = len(list(MerchantFeeConfig.query_all())) self.assertEqual(0, num)
def order_fail(cls, order): """ 订单失败处理 :return: """ params = copy.deepcopy(locals()) params.pop('cls') params.pop('order') params['tx_id'] = order.sys_tx_id rst = dict( code=0, msg='', ) # 手续费存在订单详情里面 order_detail = OrderDetailWithdraw.query_by_order_id( order.merchant, order.order_id, order.create_time) merchant_config = MerchantFeeConfig.query_by_config_id( order.mch_fee_id) try: # 创建提现订单/扣商户余额/扣用户余额,在同一个事务里面 with db.auto_commit(): order, ref_id = OrderUpdateCtl.update_order_event( order.order_id, uid=order.uid, merchant=order.merchant, state=OrderStateEnum.FAIL, commit=False, ) if not order: raise RuntimeError('提现订单修改失败状态失败, params: %s' % params) # 给商户退回提现订单的发起金额 flag, msg = MerchantBalanceEvent.update_balance( merchant=order.merchant, ref_id=ref_id, source=order.source, order_type=PayTypeEnum.REFUND, bl_type=BalanceTypeEnum.AVAILABLE, # 订单发起金额 value=order.amount, ad_type=BalanceAdjustTypeEnum.PLUS, tx_id=order.sys_tx_id, commit=False, ) # print('update_balance', flag, msg) if flag < 0: raise RuntimeError(msg + ", params: %s" % params) if merchant_config.cost_type == CostTypeEnum.MERCHANT: # 给商户退回提手续费 flag, msg = MerchantBalanceEvent.update_balance( merchant=order.merchant, ref_id=OrderUtils.gen_unique_ref_id(), tx_id=order.sys_tx_id, source=order.source, order_type=PayTypeEnum.FEE, bl_type=BalanceTypeEnum.AVAILABLE, # 收取商户的手续费 value=order_detail.fee, ad_type=BalanceAdjustTypeEnum.PLUS, commit=False, ) # print('update_balance', flag, msg) if flag < 0: raise RuntimeError(msg + ", params: %s" % params) refund_fee = order.amount if merchant_config.cost_type == CostTypeEnum.USER: # 给用户退回手续费 refund_fee += order_detail.fee # 给用户退回发起金额 flag, msg = UserBalanceEvent.update_user_balance( uid=order.uid, merchant=order.merchant, ref_id=ref_id, source=order.source, order_type=PayTypeEnum.REFUND, bl_type=BalanceTypeEnum.AVAILABLE, # 订单发起金额 value=refund_fee, ad_type=BalanceAdjustTypeEnum.PLUS, tx_id=order.sys_tx_id, commit=False, ) # print('update_user_balance', flag, msg) if flag < 0: raise RuntimeError(msg + ", params: %s" % params) except APIException as e: current_app.logger.error(traceback.format_exc()) return False cls.do_notify(order=order) return True
def manually_withdraw_failed(cls, admin_user, merchant, order_id): """ 手动更新提款状态为失败 退款/审核拒绝 流程: 1. 获取创建订单时 扣除 用户及商户的费用 获取订单 withdrawOrderDetail 数据, 获取 手续费 提现金额 2. 给用户和商户新增费用 更新 UserBalance 表 更新 MerchantBalance表 3. 修改订单状态 更新 OrderUpdateCtl :param admin_user: :param merchant: :param 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 not in [ OrderStateEnum.ALLOC, OrderStateEnum.SUCCESS ]: return BankOrderStateError() detail = OrderDetailWithdraw.query_by_order_id( order_id=withdraw_entry.order_id, merchant=merchant, create_time=withdraw_entry.create_time) if not detail: return NosuchOrderDetailDataError() # 提现订单 手续费 提现订单费用 fee = detail.fee amount = detail.amount comment = "出款失败" if withdraw_entry.state == OrderStateEnum.SUCCESS else "系统拒绝" order_type = PayTypeEnum.REFUND if withdraw_entry.state == OrderStateEnum.SUCCESS else PayTypeEnum.MANUALLY merchant_config = MerchantFeeConfig.query_by_config_id( withdraw_entry.mch_fee_id) # 更新订单状态 try: with db.auto_commit(): order, ref_id = OrderUpdateCtl.update_order_event( withdraw_entry.order_id, uid=int(withdraw_entry.uid), merchant=merchant, state=OrderStateEnum.FAIL, tx_amount=withdraw_entry.amount, deliver_type=DeliverTypeEnum.MANUALLY, op_account=admin_user.account, commit=False) if not order: msg = WithdrawOrderStateChangeError.message current_app.logger.error(msg) raise WithdrawOrderStateChangeError() # 加提现金额 flag, msg = MerchantBalanceEvent.update_balance( merchant=merchant, ref_id=ref_id, order_type=order_type, bl_type=BalanceTypeEnum.AVAILABLE, value=amount, ad_type=BalanceAdjustTypeEnum.PLUS, tx_id=order.sys_tx_id, source=OrderSourceEnum.MANUALLY, comment=comment, commit=False, ) if flag < 0: msg = '%s' % ("提现退回增加商户余额失败, %s" % msg) current_app.logger.error(msg) raise DepositCallbackUserBalanceError() if merchant_config.cost_type == CostTypeEnum.MERCHANT: # 给商户加手续费 flag, msg = MerchantBalanceEvent.update_balance( merchant=merchant, ref_id=OrderUtils.gen_unique_ref_id(), order_type=PayTypeEnum.FEE, bl_type=BalanceTypeEnum.AVAILABLE, value=fee, ad_type=BalanceAdjustTypeEnum.PLUS, tx_id=order.sys_tx_id, commit=False, comment=comment, source=OrderSourceEnum.MANUALLY) if flag < 0: msg = '%s' % ("提现退款增加商户手续费失败, %s" % msg) current_app.logger.error(msg) raise DepositCallbackUserBalanceError() refund_fee = amount if merchant_config.cost_type == CostTypeEnum.USER: # 给用户退回手续费 refund_fee += fee # 增加用户余额 flag, msg = UserBalanceEvent.update_user_balance( uid=order.uid, merchant=merchant, ref_id=ref_id, order_type=order_type, bl_type=BalanceTypeEnum.AVAILABLE, value=refund_fee, ad_type=BalanceAdjustTypeEnum.PLUS, tx_id=order.sys_tx_id, commit=False, comment=comment, source=OrderSourceEnum.MANUALLY) if flag < 0: msg = '%s' % ("提现退款增加用户余额失败, %s" % msg) current_app.logger.error(msg) raise DepositCallbackUserBalanceError() except APIException as e: current_app.logger.error(traceback.format_exc()) return e cls.do_notify( order=order, op_account=admin_user.account, comment=comment, ) return ResponseSuccess()
def __test_api_deposit(self): self.path = "/auth/account/register" register_data = dict(number="+8618912341234", auth_code="8888", password="******") response = self.do_request(register_data) self.assertEqual(ResponseSuccess.code, response.status_code) self.assertEqual(ResponseSuccess.error_code, response.json['error_code']) self.path = '/auth/account/login' login_data = dict(number="+8618912341234", password="******") response = self.do_request(login_data) print(response.json) self.assertEqual(ResponseSuccessLogin.code, response.status_code) self.assertEqual(ResponseSuccessLogin.error_code, response.json['error_code']) self.token = response.json['data']['token'] 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") # print(channel1['channel_id']) ChannelConfig.update_channel(ChannelConfigEnum.CHANNEL_1001, **kwargs) 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.path = "/deposit/limit/config/get" response = self.do_request() self.assertEqual(ResponseDepositLimitConfig.code, response.status_code) self.assertEqual(ResponseDepositLimitConfig.error_code, response.json['error_code']) self.path = "/deposit/payment/type/list" response = self.do_request(dict(amount=500)) self.assertEqual(ResponsePaymentType.code, response.status_code) self.assertEqual(ResponsePaymentType.error_code, response.json['error_code'], response.json['message']) self.path = "/deposit/order/create" create_order_data = dict( payment_type="20", amount="400.03", channel_id=ChannelConfigEnum.CHANNEL_1001.value, ) response = self.do_request(create_order_data) self.assertEqual(InvalidDepositPaymentTypeError.code, response.status_code) self.assertEqual(InvalidDepositPaymentTypeError.error_code, response.json['error_code'], response.json['message']) # create_order_data['payment_type'] = '10' # response = self.do_request(create_order_data) # self.assertEqual(ResponseSuccess.code, response.status_code) # self.assertEqual(ResponseSuccess.error_code, response.json['error_code']) # create_order_data['channel_id'] = '105' # response = self.do_request(create_order_data) # self.assertEqual(InvalidDepositChannelError.code, response.status_code) # self.assertEqual(InvalidDepositChannelError.error_code, response.json['error_code']) # create_order_data['channel_id'] = '101' # create_order_data['payment_type'] = "20" # response = self.do_request(create_order_data) # self.assertEqual(ChannelNoValidityPeriodError.code, response.status_code) # self.assertEqual(ChannelNoValidityPeriodError.error_code, response.json['error_code']) # create_order_data['payment_type'] = "30" # create_order_data['channel_id'] = '107' # response = self.do_request(create_order_data) # self.assertEqual(ResponseSuccess.code, response.status_code) # self.assertEqual(ResponseSuccess.error_code, response.json['error_code']) self.path = "/user/balance/get" response = self.do_request() print(response.json) self.assertEqual(ResponseUserBalance.code, response.status_code) self.assertEqual(ResponseUserBalance.error_code, response.json['error_code'])
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 add_one_config(self, merchant, params): MerchantFeeConfig.update_fee_config(merchant, params) for conf_dict in params: self.check_result(conf_dict)
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)
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 post(self): """ 手动补单 :return: """ form, error = CreateDepositOrderForm().request_validate() if error: return error.as_response() ''' 根据商户查询用户是否存在 构造数据 创建充值订单 更改订单状态为完成 ''' # 根据商户查询用户是否存在 user = User.query_user(form.merchant.data, uid=form.uid.data) if not user: return AccountNotExistError().as_response() # 构造数据 client_ip = form.client_ip.data channel_enum = form.channel_id.data payment_type = form.payment_type.data amount = Decimal(form.amount.data) # 判断当前传入的支付类型是否该渠道支持 if payment_type != channel_enum.conf['payment_type']: return InvalidDepositPaymentTypeError() merchant = form.merchant.data # 找出最新版本的商户费率配置 channel_config = ChannelConfig.query_latest_one(query_fields=dict( channel_enum=channel_enum)) if not channel_config: return InvalidDepositChannelError().as_response() if not channel_config.is_channel_valid(merchant.is_test): return ChannelNoValidityPeriodError().as_response() if ChannelListHelper.is_amount_out_of_range( amount=amount, merchant=merchant, payment_way=PayTypeEnum.DEPOSIT, client_ip=client_ip): return DepositOrderAmountInvalidError().as_response() # limit_min, limit_max = ChannelLimitCacheCtl( # PayTypeEnum.DEPOSIT).get_channel_limit() # try: # if limit_min > amount or limit_max < amount: # return DepositOrderAmountInvalidError().as_response() # except Exception as e: # return MustRequestDepositLimitError().as_response() # 找出最新版本的商户费率配置 merchant_fee = MerchantFeeConfig.query_latest_one( query_fields=dict(merchant=merchant, payment_way=PayTypeEnum.DEPOSIT, payment_method=channel_enum.conf.payment_method)) if not merchant_fee: return MerchantConfigDepositError().as_response() try: with db.auto_commit(): # 创建待支付订单 order, _ = OrderCreateCtl.create_order_event( uid=user.uid, merchant=merchant, amount=amount, channel_id=channel_config.channel_id, mch_fee_id=merchant_fee.config_id, order_type=PayTypeEnum.DEPOSIT, source=OrderSourceEnum.MANUALLY, in_type=InterfaceTypeEnum.CASHIER_H5, ip=client_ip, pay_method=channel_enum.conf.payment_method, op_account=g.user.account, # 后台管理员账号,后台人工修改数据时必填 comment=form.remark.data, # 管理后台修改备注,后台人工修改数据时必填 mch_tx_id=form.mch_tx_id.data, commit=False, ) if not order: raise Exception('order create failed') # 支付成功 if not DepositTransactionCtl.success_order_process( order=order, tx_amount=amount, channel_tx_id=form.mch_tx_id.data, comment="手动补单订单", commit=False, ): raise Exception('order process failed') except Exception as e: if str(e).find("Duplicate entry") >= 0: return PreOrderCreateError(message="商户订单号重复: {}".format( form.mch_tx_id.data)).as_response() return PreOrderCreateError(message=str(e)).as_response() return ResponseSuccess().as_response()