Exemple #1
0
    def post(self):
        """
        获取当前可用的充值方式
        """
        form, error = AmountInputForm().request_validate()
        if error:
            return error.as_response()

        routers = ChannelListHelper.get_channel_payment_type_router(
            interface=InterfaceTypeEnum.CASHIER_H5,
            amount=form.amount.data,
            merchant=form.merchant.data,
            uid=g.user.uid,
        )

        channels = ChannelListHelper.get_available_channels(
            form.merchant.data,
            PayTypeEnum.DEPOSIT,
            client_ip=form.client_ip.data,
        )
        payment_type_list = ChannelListHelper.choice_one_channel_for_payment_type(
            channels,
            routers,
            form.merchant.data,
            form.amount.data,
        )

        return ResponsePaymentType(bs_data=dict(
            payment_type_list=payment_type_list)).as_response()
Exemple #2
0
    def post(self):
        """
        充值请求
        """
        if not EnvironEnum.is_local_evn(current_app.config['FLASK_ENV']):
            # 无论如何都记录一条log
            current_app.logger.info('path: %s, ip: %s, args: %s, data: %s',
                                    url_for("gateway_config_get"),
                                    IpKit.get_remote_ip(), request.args,
                                    request.json)

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

        merchant = form.merchant_id.data

        checker = GatewayFormChecker(merchant)

        # 1. IP白名单校验
        if not checker.verify_ip(form.client_ip.data):
            current_app.logger.error('msg: %s, ip: %s, white ips: %s',
                                     GatewayIPError.message,
                                     IpKit.get_remote_ip(),
                                     checker.get_white_ips())
            return GatewayIPError().as_response()

        # 2. 签名校验
        sign_fields = form.get_sign_fields()
        if not checker.verify_sign(form.sign.data, sign_fields):
            current_app.logger.error(
                'msg: %s, sign: %s, fields: %s, sign_str: %s',
                GatewaySignError.message, form.sign.data, sign_fields,
                checker.get_sign_str(sign_fields))
            return GatewaySignError().as_response()

        # 3. 返回可用的支付方式以及限额
        # 充值每种支付类型的限额
        payment_types = ChannelListHelper.get_channels_for_gateway(
            merchant,
            PayTypeEnum.DEPOSIT,
            client_ip=form.user_ip.data,
        )
        # 提现限额
        # limit_min, limit_max = ChannelLimitCacheCtl(PayTypeEnum.WITHDRAW).get_channel_limit()
        limit_min, limit_max = ChannelListHelper.get_channel_limit_range(
            merchant=merchant,
            payment_way=PayTypeEnum.WITHDRAW,
            client_ip=form.user_ip.data,
        )

        withdraw_config = dict(
            limit_min=limit_min,  # 最小限额列表的最小值
            limit_max=limit_max,  # 最大限额列表的最大值
        )

        return GatewayResponseConfig(bs_data=dict(
            payment_types=payment_types,
            withdraw_config=withdraw_config,
        )).as_response()
Exemple #3
0
    def get(self):
        """
        商户配置查询
        :return:
        """
        if not request.args:
            return ResponseSuccess(message="参数规则:?merchant=test").as_response()

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

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

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

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

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

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

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

        return ResponseSuccess(bs_data=bs_data).as_response()
Exemple #4
0
    def get(self):
        """
        查询可用的通道
        :return:
        """
        if not request.args:
            return ResponseSuccess(
                message="参数规则:?merchant=test&interface=&amount=&uid="
            ).as_response()

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

        try:
            interface = request.args.get('interface')
            if interface:
                interface = InterfaceTypeEnum.from_name(interface)
        except:
            return ResponseSuccess(
                message="请输入正确的 interface,有效的 interface 包括:%s" %
                InterfaceTypeEnum.get_names()).as_response()

        try:
            amount = request.args.get('amount') or 0
            if amount:
                amount = Decimal(amount)
        except:
            return ResponseSuccess(message="请输入正确的 amount")

        try:
            uid = request.args.get('uid')
            if uid:
                uid = int(uid)
        except:
            return ResponseSuccess(message="请输入正确的 uid")

        routers = ChannelListHelper.get_channel_payment_type_router(
            interface=interface,
            amount=amount,
            merchant=merchant,
            uid=uid,
        )
        channels = ChannelListHelper.get_available_channels(
            merchant, PayTypeEnum.DEPOSIT)
        payment_type_list = ChannelListHelper.choice_one_channel_for_payment_type(
            channels, routers, merchant, amount)

        for item in payment_type_list:
            item['limit_min'] = str(item['limit_min'])
            item['limit_max'] = str(item['limit_max'])

        return ResponseSuccess(bs_data=payment_type_list).as_response()
Exemple #5
0
    def get(self):
        """
        通道配置查询
        :return:
        """
        deposit_channels = ChannelListHelper.get_config_channels(
            PayTypeEnum.DEPOSIT)
        withdraw_channels = ChannelListHelper.get_config_channels(
            PayTypeEnum.WITHDRAW)

        bs_data = dict(
            deposit_channels=[x.short_description for x in deposit_channels],
            withdraw_channels=[x.short_description for x in withdraw_channels],
        )
        return ResponseSuccess(bs_data=bs_data).as_response()
Exemple #6
0
    def post(self):
        """
        获取当前可用的充值方式
        """
        form, error = DomainForm().request_validate()
        if error:
            return error.as_response()

        channel_list = ChannelListHelper.get_available_channels(
            form.merchant.data, PayTypeEnum.WITHDRAW)
        withdraw_banks = []

        for channel in channel_list:
            banks = [
                dict(desc=bank.desc, value=bank.value)
                for bank in channel.banks
            ]
            withdraw_banks += banks

        value_list = []
        banks_lst = []
        for item in withdraw_banks:
            if item['value'] not in value_list:
                value_list.append(item['value'])
                banks_lst.append(item)
        banks_result = dict(banks=banks_lst)
        return ResponseBankWithdraw(bs_data=banks_result).as_response()
Exemple #7
0
    def post(self):
        """
        获取单笔交易最低最高限额
        """
        form, error = DomainForm().request_validate()
        if error:
            return error.as_response()

        # 获取用户余额
        uid = g.user.uid
        merchant = g.user.merchant

        user_balance = UserBalance.query_balance(uid=uid,
                                                 merchant=merchant).first()

        # limit_min, limit_max = ChannelLimitCacheCtl(PayTypeEnum.WITHDRAW).get_channel_limit()
        limit_min, limit_max = ChannelListHelper.get_channel_limit_range(
            merchant=merchant,
            payment_way=PayTypeEnum.WITHDRAW,
            client_ip=form.client_ip.data,
        )

        deposit_limit_config = dict(
            balance=user_balance.real_balance,
            limit_min=limit_min,
            limit_max=limit_max,
        )
        return ResponseWithdrawLimitConfig(
            bs_data=deposit_limit_config).as_response()
Exemple #8
0
    def post(self):
        """
        通道管理: 获取通道配置信息
        :return:
        """
        form, error = ChannelConfigQueryForm().request_validate()
        if error:
            return error.as_response()

        pay_type = form.pay_type.data

        config_channels_dict = ChannelListHelper.get_config_channels(
            pay_type, ret_dict=True)

        if pay_type == PayTypeEnum.DEPOSIT:
            configs = [
                c for c in ChannelConfigEnum if
                c.value not in config_channels_dict and c.conf['payment_type']
            ]
        else:
            configs = [
                c for c in ChannelConfigEnum
                if c.value not in config_channels_dict
                and not c.conf['payment_type']
            ]

        channel_config_list = [
            dict(
                channel_id=i.value,
                channel_desc=i.desc,
                id=i.conf["id"],
                provider=i.conf["provider"],
                name=i.conf["name"],
                payment_type=i.conf["payment_type"].desc
                if i.conf['payment_type'] else '',
                payment_method=i.conf["payment_method"].desc
                if i.conf['payment_method'] else '',
            ) for i in configs
        ]

        data = dict(
            channel_config=channel_config_list,
            payment_fee_type=PaymentFeeTypeEnum.get_desc_value_pairs(),
            settlement_type=SettleTypeEnum.get_name_value_pairs(),
            channel_state=ChannelStateEnum.get_desc_value_pairs(),
            banks=PaymentBankEnum.get_desc_value_pairs(),
            # banks=[item.value for item in PaymentBankEnum],
            interfaces=InterfaceTypeEnum.get_name_value_pairs(),
            payment_method=PayMethodEnum.get_desc_value_pairs(),
            merchant_name=MerchantEnum.get_name_value_pairs(),
            payment_types=PaymentTypeEnum.get_desc_name_pairs(),
        )
        return ChannelConfigResult(bs_data=data).as_response()
Exemple #9
0
    def post(self):
        """
        获取单笔交易最低最高限额
        """
        form, error = DomainForm().request_validate()
        if error:
            return error.as_response()

        merchant = form.merchant.data

        # limit_min, limit_max = ChannelLimitCacheCtl(PayTypeEnum.DEPOSIT).get_channel_limit()
        limit_min, limit_max = ChannelListHelper.get_channel_limit_range(
            merchant=merchant,
            payment_way=PayTypeEnum.DEPOSIT,
            client_ip=form.client_ip.data,
        )

        return ResponseDepositLimitConfig(bs_data=dict(
            limit_min=limit_min, limit_max=limit_max)).as_response()
Exemple #10
0
    def test_channel_guid_rule(self):
        data = dict(
            config_list=[
                dict(payment_type=PaymentTypeEnum.ZHIFUBAO, priority=100),
                dict(payment_type=PaymentTypeEnum.WEIXIN, priority=200),
                dict(payment_type=PaymentTypeEnum.YINLIAN, priority=10),
                dict(payment_type=PaymentTypeEnum.YUNSHANFU, priority=50),
            ],
            amount_min=Decimal('100.33'),
            amount_max=Decimal('500.22'),
            interface=InterfaceTypeEnum.CASHIER_H5,
            merchants=[MerchantEnum.TEST, MerchantEnum.QF2],
            uid_list=[1, 2, 3, 4],
        )
        rule = ChannelRouter.create_rule(**data)

        def check_item_and_data(_data, _item):
            self.assertEqual(set([x.name for x in _data['merchants']]), set([x.name for x in _item.merchants]))
            self.assertEqual(set(_data['uid_list']), set(_item.uid_list))
            self.assertEqual(_data['interface'], _item.interface)
            self.assertEqual(len(_data['config_list']), len(_item.config_list))

        check_item_and_data(data, rule)

        count = 1
        all_configs = list(ChannelRouter.query_all())
        self.assertEqual(count, len(all_configs))
        q_rule = all_configs[count - 1]
        self.assertEqual(rule.router_id, q_rule.router_id)

        data = dict(
            router_id=q_rule.router_id,
            config_list=[
                dict(payment_type=PaymentTypeEnum.ZHIFUBAO, priority=100),
                dict(payment_type=PaymentTypeEnum.WEIXIN, priority=200),
                dict(payment_type=PaymentTypeEnum.YUNSHANFU, priority=50),
            ],
            amount_min=Decimal('800.33'),
            amount_max=Decimal('3000.22'),
            interface=InterfaceTypeEnum.CASHIER_H5,
            merchants=[MerchantEnum.TEST, MerchantEnum.QF3],
            uid_list=[1, 3, 4],
        )
        rule, error = ChannelRouter.update_rule(**data)
        check_item_and_data(data, rule)

        data['uid_list'] = [10, 20, 30]
        data.pop('router_id')
        data['config_list'] = [
            dict(payment_type=PaymentTypeEnum.ZHIFUBAO, priority=100),
            dict(payment_type=PaymentTypeEnum.WEIXIN, priority=200),
            dict(payment_type=PaymentTypeEnum.YINLIAN, priority=10),
            dict(payment_type=PaymentTypeEnum.YUNSHANFU, priority=50),
        ]
        rule = ChannelRouter.create_rule(**data)
        check_item_and_data(data, rule)

        data['amount_min'] = Decimal('400')
        data['amount_max'] = Decimal('500')
        data['interface'] = InterfaceTypeEnum.API
        data['merchants'] = []
        data['uid_list'] = []
        data['config_list'] = [
            dict(payment_type=PaymentTypeEnum.YINLIAN, priority=10),
            dict(payment_type=PaymentTypeEnum.YUNSHANFU, priority=50),
        ]
        rule = ChannelRouter.create_rule(**data)
        check_item_and_data(data, rule)

        data['amount_min'] = 0
        data['amount_max'] = 0
        data['interface'] = InterfaceTypeEnum.CASHIER_PC
        data['merchants'] = []
        data['uid_list'] = []
        data['config_list'] = [
            dict(payment_type=PaymentTypeEnum.YINLIAN, priority=4),
            dict(payment_type=PaymentTypeEnum.YUNSHANFU, priority=8),
            dict(payment_type=PaymentTypeEnum.ZHIFUBAO, priority=3),
            dict(payment_type=PaymentTypeEnum.WEIXIN, priority=5),
            dict(payment_type=PaymentTypeEnum.BANKCARD, priority=10),
            dict(payment_type=PaymentTypeEnum.JDQIANBAO, priority=7),
        ]
        rule = ChannelRouter.create_rule(**data)
        check_item_and_data(data, rule)

        data['interface'] = None
        data['config_list'] = [
            dict(payment_type=PaymentTypeEnum.YINLIAN, priority=4),
            dict(payment_type=PaymentTypeEnum.ZHIFUBAO, priority=3),
            dict(payment_type=PaymentTypeEnum.WEIXIN, priority=5),
            dict(payment_type=PaymentTypeEnum.BANKCARD, priority=10),
            dict(payment_type=PaymentTypeEnum.JDQIANBAO, priority=7),
        ]
        rule = ChannelRouter.create_rule(**data)
        check_item_and_data(data, rule)

        count = 5
        all_configs = list(ChannelRouter.query_all())
        self.assertEqual(count, len(all_configs))
        q_rule = all_configs[count - 1]
        self.assertEqual(rule.router_id, q_rule.router_id)

        config_list = ChannelListHelper.get_channel_payment_type_router(uid=3, amount=Decimal("1000"),
                                                                        merchant=MerchantEnum.QF3,
                                                                        interface=InterfaceTypeEnum.CASHIER_H5)
        print('config_list', config_list)
        self.assertEqual(3, len(config_list))

        config_list = ChannelListHelper.get_channel_payment_type_router(uid=20, amount=Decimal("1000"),
                                                                        merchant=MerchantEnum.QF3,
                                                                        interface=InterfaceTypeEnum.CASHIER_H5)
        print('config_list', config_list)
        self.assertEqual(4, len(config_list))

        config_list = ChannelListHelper.get_channel_payment_type_router(amount=Decimal("450"),
                                                                        interface=InterfaceTypeEnum.API)
        print('config_list', config_list)
        self.assertEqual(2, len(config_list))

        config_list = ChannelListHelper.get_channel_payment_type_router(interface=InterfaceTypeEnum.CASHIER_PC)
        print('config_list', config_list)
        self.assertEqual(6, len(config_list))

        config_list = ChannelListHelper.get_channel_payment_type_router()
        print('config_list', config_list)
        self.assertEqual(5, len(config_list))

        config_list = ChannelListHelper.get_channel_payment_type_router(interface=InterfaceTypeEnum.API)
        print('config_list', config_list)
        self.assertEqual(5, len(config_list))
Exemple #11
0
    def post(self):
        """
        充值通道列表
        :return:
        """

        router2_dict = ChannelListHelper.get_router2_dict()

        channel_list = []
        channels = ChannelConfig.query_all()
        channels = ChannelConfig.filter_latest_items(channels)
        for channel in channels:
            channel_enum = channel.channel_enum
            channel_conf = channel_enum.conf

            merchants = list()
            router = router2_dict.get(channel_enum)
            if router:
                merchants = router.merchants

            channel_list.append(
                dict(
                    channel_id=channel_enum.value,
                    channel_desc=channel_enum.desc,
                    id=channel_conf['mch_id'],
                    provider=channel_conf['provider'],
                    payment_type=dict(desc=PaymentTypeEnum(
                        channel_conf['payment_type']).desc,
                                      value=PaymentTypeEnum(
                                          channel_conf['payment_type']).value),
                    payment_method=dict(
                        desc=PayMethodEnum(
                            channel_conf['payment_method']).desc,
                        value=PayMethodEnum(
                            channel_conf['payment_method']).value),
                    fee=channel.fee,
                    fee_type=dict(
                        desc=PaymentFeeTypeEnum(channel.fee_type).desc,
                        value=PaymentFeeTypeEnum(channel.fee_type).value),
                    limit_per_min=channel.limit_per_min,
                    limit_per_max=channel.limit_per_max,
                    limit_day_max=channel.limit_day_max,
                    settlement_type=dict(
                        key=SettleTypeEnum(channel.settlement_type).value,
                        value=SettleTypeEnum(channel.settlement_type).name),
                    trade_start_time=":".join([
                        str(channel.trade_begin_hour),
                        str(channel.trade_begin_minute)
                    ]),
                    # trade_start_time=dict(trade_begin_hour=channel.trade_begin_hour,
                    #                       trade_begin_minute=channel.trade_begin_minute),
                    trade_end_time=":".join([
                        str(channel.trade_end_hour),
                        str(channel.trade_end_minute)
                    ]),
                    # trade_end_time=dict(trade_end_hour=channel.trade_end_hour,
                    #                     trade_end_minute=channel.trade_end_minute),
                    main_time=dict(maintain_begin=channel.maintain_begin
                                   if channel.maintain_begin else None,
                                   maintain_end=channel.maintain_end
                                   if channel.maintain_end else None),
                    state=dict(desc=channel.state.desc,
                               value=channel.state.value),
                    reason=channel.get_reason_desc(),
                    priority=channel.priority,
                    merchants=[x.name for x in merchants],
                ))
        channel_list = sorted(channel_list,
                              key=lambda item: item['state']['value'])
        data = dict(counts=len(channel_list), channels=channel_list)

        return ChannelListResult(bs_data=data).as_response()
Exemple #12
0
    def order_create(cls,
                     user,
                     amount,
                     client_ip,
                     user_bank_id=None,
                     bank_info=None,
                     notify_url=None,
                     mch_tx_id=None,
                     extra=None):
        """
        申请创建订单
        :return:
        """
        params = copy.deepcopy(locals())
        params.pop('cls')

        order = None

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return order, None
Exemple #13
0
    def init_channel2(cls):
        if not cls.get_deposit_channel2():
            # 充值通道配置
            kwargs = dict(fee="2.5",
                          fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
                          limit_per_min="500",
                          limit_per_max="20000",
                          trade_begin_hour="0",
                          trade_begin_minute="0",
                          trade_end_hour="0",
                          trade_end_minute="0",
                          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
                          if cls.merchant.is_test else ChannelStateEnum.ONLINE,
                          priority="101")

            ChannelConfig.update_channel(cls.channel_enum2, **kwargs)
            channel_config = ChannelConfig.query_latest_one(query_fields=dict(
                channel_enum=cls.channel_enum2))
            # print(channel_config)

            # limit_min, limit_max = ChannelLimitCacheCtl(PayTypeEnum.DEPOSIT).get_channel_limit()
            limit_min, limit_max = ChannelListHelper.get_channel_limit_range(
                merchant=cls.merchant,
                payment_way=PayTypeEnum.DEPOSIT,
            )
            # print('limit_min: %s, limit_max: %s' % (limit_min, limit_max))
            assert 0 != limit_min
            assert 0 != limit_max

        if not cls.get_withdraw_channel2():
            # 提款代付通道配置
            withdraw_item = dict(
                fee="1.3",
                fee_type=PaymentFeeTypeEnum.PERCENT_PER_ORDER,
                limit_per_min="300",
                limit_per_max="10000",
                limit_day_max="500000",
                trade_begin_hour="0",
                trade_begin_minute="0",
                trade_end_hour="0",
                trade_end_minute="0",
                maintain_begin=DateTimeKit.str_to_datetime(
                    "2019-09-07 09:00:00"),
                maintain_end=DateTimeKit.str_to_datetime(
                    "2019-09-07 09:00:00"),
                state=ChannelStateEnum.TESTING
                if cls.merchant.is_test else ChannelStateEnum.ONLINE,
                banks=[
                    PaymentBankEnum.ZHONGGUO,
                    PaymentBankEnum.GONGSHANG,
                    PaymentBankEnum.JIANSHE,
                ])
            ProxyChannelConfig.update_channel(cls.channel_enum2,
                                              **withdraw_item)

            channel_config = ProxyChannelConfig.query_latest_one(
                query_fields=dict(channel_enum=cls.channel_enum2))
            # print(channel_config)

            # limit_min, limit_max = ChannelLimitCacheCtl(PayTypeEnum.WITHDRAW).get_channel_limit()
            limit_min, limit_max = ChannelListHelper.get_channel_limit_range(
                merchant=cls.merchant,
                payment_way=PayTypeEnum.WITHDRAW,
            )
            # print('limit_min: %s, limit_max: %s' % (limit_min, limit_max))
            assert 0 != limit_min
            assert 0 != limit_max
Exemple #14
0
    def post(self):
        """
        充值请求
        """
        if not EnvironEnum.is_local_evn(current_app.config['FLASK_ENV']):
            # 无论如何都记录一条log
            current_app.logger.info('path: %s, ip: %s, args: %s, data: %s',
                                     url_for("gateway_deposit_request"), IpKit.get_remote_ip(), request.args,
                                     request.json)

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

        checker = GatewayFormChecker(form.merchant_id.data)

        # 1. IP白名单校验
        if not checker.verify_ip(form.client_ip.data):
            current_app.logger.error('msg: %s, ip: %s, white ips: %s', GatewayIPError.message, IpKit.get_remote_ip(),
                                     checker.get_white_ips())
            return GatewayIPError().as_response()

        # 2. 签名校验
        sign_fields = form.get_sign_fields()
        if not checker.verify_sign(form.sign.data, sign_fields):
            current_app.logger.error('msg: %s, sign: %s, fields: %s, sign_str: %s',
                                     GatewaySignError.message, form.sign.data, sign_fields,
                                     checker.get_sign_str(sign_fields))
            return GatewaySignError().as_response()

        # 3. 获取对应支付方式下的充值通道
        channel = ChannelListHelper.get_one_channel_by_payment_type(
            merchant=form.merchant_id.data,
            payment_type=form.payment_type.data,
            amount=form.amount.data,
            client_ip=form.user_ip.data,
        )
        if not channel:
            current_app.logger.error("no channel found, request data: %s", form.get_data())
            return GatewayChannelError().as_response()

        # # 境外IP检查
        # if channel.is_ip_forbidden(form.user_ip.data):
        #     return GatewayDepositError(message="此通道不支持境外IP,请使用VPN后重试").as_response()

        # 4. 获取用户对象
        user = checker.get_fake_user(form.user_id.data)

        # 5. 发起支付
        rst = DepositHelper.do_deposit_request(
            client_ip=form.user_ip.data,
            user_agent=form.user_agent.data,
            user=user,
            amount=form.amount.data,
            channel_enum=channel.channel_enum,
            notify_url=form.notify_url.data,
            result_url=form.result_url.data,
            mch_tx_id=form.mch_tx_id.data,
            extra=form.extra.data,
            source=OrderSourceEnum.TESTING if form.merchant_id.data.is_test else OrderSourceEnum.ONLINE,
            in_type=InterfaceTypeEnum.API,
        )
        if rst['error']:
            return GatewayDepositError(message=rst['error'])

        # 6. 解析URL
        ps_rst = DepositHelper.parse_result(
            data=rst['data'],
            order_id=rst['order'].order_id,
            endpoint='gateway_deposit_redirect',
            channel_enum=channel.channel_enum,
        )

        return GatewayResponseDeposit(bs_data=dict(
            redirect_url=ps_rst['redirect_url'],
            valid_time=ps_rst['valid_time'],
            sys_tx_id=rst['order'].sys_tx_id,
            mch_tx_id=rst['order'].mch_tx_id,
        )).as_response()
Exemple #15
0
    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()