class UserBankDelete(Resource): method_decorators = admin_decorators @ns.expect(UserBankInfo) @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 用户银行信息编辑 """ form, error = UserBankDeleteForm().request_validate() if error: return error.as_response() print(form.card_id, "****************", form.card_id.data) card_id = form.card_id.data # 判断该用户银行卡 信息是否存在 card_entry = BankCard.query.filter_by( **dict(id=card_id, valid=1)).first() if not card_entry: return AdjustUserBalanceError(message="card_id 不存在").as_response() BankCard.delete_bankcard_by_card_id(card_id=card_id) return ResponseSuccess().as_response()
class OrderStateNotifyResource(Resource): method_decorators = merchant_decorators @ns.expect(OrderStateNotifyDoc) @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 给商户发通知 :return: """ form, error = OrderStateNotifyFrom().request_validate() if error: return error.as_response() order_id = str(form.order_id.data) if form.type.data == PayTypeEnum.WITHDRAW: order = WithdrawTransactionCtl.get_order(order_id) rst = WithdrawTransactionCtl.do_notify(order=order, op_account=g.user.account, comment="商户后台手动通知") elif form.type.data == PayTypeEnum.DEPOSIT: order = DepositTransactionCtl.get_order(order_id) rst = DepositTransactionCtl.do_notify( order=order, op_account=g.user.account, comment="商户后台手动通知", ) return ResponseSuccess(message=rst['msg'])
class OrderManuallyDone(Resource): method_decorators = admin_decorators @ns.expect(OrderIdCommentDoc) @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 人工完成充值订单 :return: """ form, error = OrderIdCommentForm().request_validate() if error: return error.as_response() order_id = form.order_id.data g_order_id = GlobalOrderId.query_global_id(order_id) order = OrderDeposit.query_by_order_id(merchant=g_order_id.merchant, order_id=order_id) if DepositTransactionCtl.success_order_process( order, order.amount, comment=form.comment.data, op_account=g.user.account): return ResponseSuccess(message="处理成功") else: return ResponseSuccess(message="处理失败")
class WithdrawPersonDone(Resource): method_decorators = admin_decorators @ns.expect(WithDrawPersonExecuteDoneParams) @ns.marshal_with(ResponseSuccess.gen_doc(api), as_list=True) def post(self): """ 运营确定出款成功 :return: """ form, error = WithDrawPersonExecutedDoneForm().request_validate() if error: return error.as_response() fee = form.fee.data if form.fee.data else "0" comment = form.comment.data rsp = WithdrawTransactionCtl.manually_withdraw_success( admin_user=g.user, merchant=form.merchant.data, order_id=form.order_id.data, channel_cost=fee, comment=comment, ) return rsp.as_response()
class ResetPassword(Resource): method_decorators = admin_decorators # 请求数据格式 @ns.expect(AdminResetPassword) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 修改登录密码 :return: """ # 格式验证 form, error = ResetWordForm().request_validate() if error: return error.as_response() # 获取用户ID uid = g.user.uid if not AdminUser.verify_password(uid=uid, password=form.ori_password.data): return PasswordError().as_response() if AdminUser.verify_password(uid=uid, password=form.new_password.data): return RePasswordError().as_response() flag = AdminUser.reset_password( uid=uid, login_pwd=form.new_password.data ) if not flag: return NoSourceError().as_response() return ResponseSuccess().as_response()
class ForgetPasswordVerifyAuth(Resource): method_decorators = [ limiter.limit("1/second"), ] # 请求数据格式 @ns.expect(MobileAuthCode) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 忘记密码 --验证验证码 :return: """ # 用户手机号格式验证 form, error = AuthCodeTrueForm().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() return ResponseSuccess().as_response()
class AuthUsername(Resource): method_decorators = [ limiter.limit("1/second"), ] @ns.expect(MobileNumber) @ns.marshal_with(ResponseSuccess.gen_doc(api)) 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()
class SMSCodeAuthentication(Resource): method_decorators = [limiter.limit("1/second"), ] # 期待客户端请求的数据模型,使用expect来装饰 @ns.expect(MobileAuthCode) # 给客户端返回的的响应数据模型,使用marshal_with来装饰 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 验证短信动态验证码 """ # 验证表单手机号及验证码格式验证 form, error = AuthCodeForm.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() return ResponseSuccess().as_response()
class PaymentPasswordForgetSet(Resource): method_decorators = cashier_decorators # 期待客户端请求数据模型, 用response 来装饰 @ns.expect(SetForgetPaymentPassword) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 忘记支付密码,重置支付密码 """ """ 检查验证码 判断是否存在支付密码 设置新的支付密码 """ form, error = SetForgetPaymentPasswordForm().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() # 从全局变量中取出用户ID,参考:verify_credential uid = g.user.uid # 判断是否存在支付密码 user = User.query_user(form.merchant.data, uid) if not user.trade_pwd: return PaymentPwdNotExistError().as_response() # 设置新的支付密码 flag = User.set_payment_password( form.merchant.data, uid=uid, trade_pwd=form.new_payment_password.data) # 设置失败的情况 if not flag: return NoSourceError().as_response() try: # 清理掉支付密码输入错误的次数记录 cache = UserPaymentPasswordLimitCache(uid=uid) # 密码设置成功 删除密码输入错误记录 cache.delete_cache() except: pass return ResponseSuccess().as_response()
class PaymentPasswordCheck(Resource): method_decorators = cashier_decorators # 期待客户端请求数据模型, 用response 来装饰 @ns.expect(PaymentPassword) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 校验支付密码 """ """ 判断是否设置了支付密码 校验支付密码 """ form, error = SetPaymentPassword().request_validate() if error: return error.as_response() # 从全局变量中取出用户ID,参考:verify_credential uid = g.user.uid # 判断是否存在支付密码 user = User.query_user(form.merchant.data, uid) if not user.trade_pwd: return PaymentPwdNotExistError().as_response() cache = UserPaymentPasswordLimitCache(uid=uid) # 获取支付密码输入错误次数是否达到上限 if cache.is_limited(): return PaymentPasswordLimitedError().as_response() flag = User.verify_payment_password( form.merchant.data, uid=uid, password=form.payment_password.data) # 交易密码校验失败 if not flag: cache.incr_times() times = cache.get_left_times() # 获取支付密码输入错误次数是否达到上限(多判断一次 防止出现剩余0次的情况) if cache.is_limited(): return PaymentPasswordLimitedError().as_response() return PaymentPasswordError(message=PaymentPasswordError.message. format(times)).as_response() # 密码校验成功 删除密码输入错误记录 cache.delete_cache() return ResponseSuccess().as_response()
class BankCardDelete(Resource): method_decorators = cashier_decorators # 期待客户端请求数据模型, 用response 来装饰 @ns.expect(BankCardDeleteParams) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 删除用户银行卡 """ """ 校验老的支付密码 删除银行卡 """ form, error = DeleteBankCardForm().request_validate() if error: return error.as_response() # 从全局变量中取出用户ID,参考:verify_credential uid = g.user.uid # 获取支付密码输入错误次数是否达到上限 if UserPaymentPasswordLimitCache(uid=uid).is_limited(): return PaymentPasswordLimitedError().as_response() # 校验支付密码 flag = User.verify_payment_password( form.merchant.data, uid=uid, password=form.payment_password.data) # 交易密码校验失败 if not flag: UserPaymentPasswordLimitCache(uid=uid).incr_times() return PaymentPasswordError().as_response() # 密码校验成功 删除密码输入错误记录 UserPaymentPasswordLimitCache(uid=uid).delete_cache() # 查询银行卡信息 bank_card = BankCard.query_bankcard_by_id(form.bank_card_id.data) if bank_card is None: return BankCardNotExistError().as_response() if uid != bank_card.uid: return BankCardNotMeError().as_response() BankCard.delete_bankcard_by_card_no(form.merchant.data, card_no=bank_card.card_no) return ResponseSuccess().as_response()
class ClientLogout(Resource): method_decorators = merchant_decorators @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 商户后台 登出 :return: """ # current_app.logger.debug('logout, path: %s, uid: %s', request.path, g.user.uid) # 记录登录状态 MerchantLoginToken.remove_token(g.user.mid) return ResponseSuccess().as_response()
class UserRegisterAccount(Resource): method_decorators = [ limiter.limit("1/second"), ] # 期待客户端请求数据模型, 用response 来装饰 @ns.expect(PassWordAuthCode) @ns.marshal_with(ResponseSuccess.gen_doc(api)) 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()
class ResetPasswordVerify(Resource): method_decorators = cashier_decorators # 请求数据格式 @ns.expect(ResetPasswordVerify) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 修改密码 验证原始密码是否正确 :return: """ # 用户手机号格式验证 form, error = ResetWordVerify().request_validate() if error: return error.as_response() # 获取用户信息 user = g.user # 判断当前用户状态是否为 INACTIVE 如果是则返回错误信息 if not user.is_active: return DisableUserError().as_response() # 验证原始密码是否正确 if not User.verify_password(merchant=form.merchant.data, uid=user.uid, password=form.ori_password.data): # 密码输入错误 则将密码输入错误次数 + 1 返回密码有误 信息 UserPasswordLimitCache(user.account).incr_times() # 判断用户当天密码错误次数是否达到上限 如果达到上限 更改账户状态 返回错误 if UserPasswordLimitCache(user.account).is_limited(): User.update_user_state(user.merchant, account=user.account, state=AccountStateEnum.INACTIVE) return OriPasswordError().as_response() return PasswordError().as_response() # 如果密码输入次数未达上限 且密码验证成功则删除缓存数据 UserPasswordLimitCache(user.account).delete_cache() return ResponseSuccess().as_response()
class WithdrawOrderCreate(Resource): method_decorators = cashier_decorators # 相应数据格式 前端传入用户 提现金额,用户所选的银行卡 标示号, 支付密码 @ns.expect(WithdrawRequestDoc) @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 提现接口: 检查支付密码是否正确,如果密码正确则创建用户提现订单 """ form, error = CreateWithdrawOrderForm().request_validate() if error: return error.as_response() uid = g.user.uid merchant = g.user.merchant if not g.user.has_permission(UserPermissionEnum.WITHDRAW): return UserPermissionDeniedError().as_response() amount = BalanceKit.round_4down_5up(Decimal(form.amount.data)) user_bank_id = form.user_bank.data client_ip = form.client_ip.data trade_password = form.trade_password.data # 判断 支付密码是否正确 if not User.verify_payment_password( merchant=merchant, uid=uid, password=trade_password): cache = UserPaymentPasswordLimitCache(uid=uid) cache.incr_times() times = cache.get_left_times() return PaymentPasswordError(message=PaymentPasswordError.message. format(times)).as_response() order, error = WithdrawTransactionCtl.order_create( user=g.user, amount=amount, client_ip=client_ip, user_bank_id=user_bank_id, ) if error: return error.as_response() return ResponseSuccess().as_response()
class ForgetPasswordGetAuth(Resource): method_decorators = [ limiter.limit("1/10"), ] # 请求数据格式 @ns.expect(MobileNumber) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 忘记密码 --验证手机号,获取验证码 :return: """ # 用户手机号格式验证 form, error = MobileRegisterTrueCheckForm().request_validate() if error: return error.as_response() # 首先获取当日是否已发送过验证码 if AuthCodeLimiter(form.number.data).is_limited(): return AuthCodeTimesLimitError().as_response() # 生成验证码 code = AuthCodeGenerator(form.number.data).generate_code() current_app.logger.info('code generated success, code: %s', code) # print('code: %s' % code) # 将验证码以短信方式发送到用户手机 try: if not current_app.config['DEBUG']: from app.services.celery.sms import async_send_auth_code async_send_auth_code.delay(phone=form.number.data, code=code) except: current_app.config['SENTRY_DSN'] and current_app.logger.fatal( traceback.format_exc()) # 验证码发送成功后,发送次数+1 AuthCodeLimiter(form.number.data).incr_times() return ResponseSuccess().as_response()
class WithdrawLaunch(Resource): method_decorators = admin_decorators @ns.expect(WithDrawChannelAllowed) @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 运营发起确认对订单的提现,向第三方发起提现请求 :return: """ form, error = WithdrawOrderPerformForm().request_validate() if error: return error.as_response() merchant = form.merchant.data order_id = form.order_id.data channel_id = form.channel_id.data rst = WithdrawTransactionCtl.order_deal(g.user.account, order_id, merchant, channel_id) return rst.as_response()
class AllowedOrderResource(Resource): method_decorators = admin_decorators @ns.expect(AllowedOrder) @ns.marshal_with(ResponseSuccess.gen_doc(api), as_list=True) def post(self): """ 提现订单: 运营人员认领订单 :return: """ form, error = WithDrawOrderAllowedForm().request_validate() if error: return error.as_response() order_id = form.order_id.data merchant = form.merchant_name.data rst = WithdrawTransactionCtl.order_alloc(g.user.account, order_id, merchant) return rst.as_response()
class PaymentPasswordSet(Resource): method_decorators = cashier_decorators # 期待客户端请求数据模型, 用response 来装饰 @ns.expect(PaymentPassword) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 设置支付密码 """ """ 判断密码是否是6位纯数字 判断密码是否是连续的 判断是否存在支付密码 写入支付密码到数据库 """ form, error = SetPaymentPassword().request_validate() if error: return error.as_response() # 从全局变量中取出用户ID,参考:verify_credential uid = g.user.uid # 判断是否存在支付密码 user = User.query_user(form.merchant.data, uid) if user.trade_pwd: return PaymentPwdNotExistError().as_response() # 设置支付密码 flag = User.set_payment_password(form.merchant.data, uid=uid, trade_pwd=form.payment_password.data) # 设置失败的情况 if not flag: return NoSourceError().as_response() return ResponseSuccess().as_response()
class WithdrawRefuseReimburse(Resource): method_decorators = admin_decorators @ns.expect(WithDrawPersonExecuteParams) @ns.marshal_with(ResponseSuccess.gen_doc(api), as_list=True) def post(self): """ 拒绝提现 或 提现失败 :return: """ form, error = WithDrawBankForm().request_validate() if error: return error.as_response() rsp = WithdrawTransactionCtl.manually_withdraw_failed( admin_user=g.user, merchant=form.merchant.data, order_id=form.order_id.data, ) return rsp.as_response()
class ClientRegister(Resource): method_decorators = [ check_ip_in_white_list(MERCHANT_ADMIN_IP_LIST), limiter.limit("1/second") ] @ns.expect(MerchantRegister) @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 商户后台 注册 """ form, error = MerchantLoginForm().request_validate() if error: return error.as_response() merchant_enum = form.account.data user = MerchantUser.register_account(mid=merchant_enum.value, account=merchant_enum.name, password=form.password.data) if user: return ResponseSuccess().as_response()
class GatewayDemoNotify(Resource): @ns.expect(DocOrderNotify) @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 发货通知,由商户实现 :return: """ 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_demo_notify"), IpKit.get_remote_ip(), request.args, request.json) form, error = DepositNotifyForm.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() # 商户自己的业务逻辑处理 current_app.logger.info('notify success: %s', form.json_data) return ResponseSuccess().as_response()
class SMSCodeGenerator(Resource): # 验证码的发送要限速 method_decorators = [limiter.limit("1/10")] # 期待客户端请求的数据模型,使用expect来装饰 @ns.expect(MobileNumber) # 给客户端返回的的响应数据模型,使用marshal_with来装饰 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 获取短信动态验证码 """ # 用户手机号格式验证 form, error = MobileRegisterCheckForm().request_validate() if error: return error.as_response() # 首先获取当日是否已发送过验证码 if AuthCodeLimiter(form.number.data).is_limited(): return AuthCodeTimesLimitError().as_response() # 生成验证码 code = AuthCodeGenerator(form.number.data).generate_code() # current_app.logger.info('code generated success, code: %s', code) # 将验证码以短信方式发送到用户手机 try: if not current_app.config['DEBUG'] or current_app.config['TESTING']: from app.services.celery.sms import async_send_auth_code async_send_auth_code.delay(phone=form.number.data, code=code) except: current_app.config['SENTRY_DSN'] and current_app.logger.fatal(traceback.format_exc()) # 验证码发送成功后,发送次数+1 AuthCodeLimiter(form.number.data).incr_times() return ResponseSuccess().as_response()
class ForgetPasswordReset(Resource): method_decorators = [ limiter.limit("1/second"), ] # 请求数据格式 @ns.expect(PassWordAuthCode) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 忘记密码 --设置新密码 :return: """ # 用户手机号格式验证 form, error = PasswordTrueForm().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() User.reset_password(form.merchant.data, account=form.number.data, login_pwd=form.password.data) UserPasswordLimitCache(form.number.data).delete_cache() User.update_user_state(merchant=form.merchant.data, account=form.number.data, state=AccountStateEnum.ACTIVE) return ResponseSuccess().as_response()
class ResetPassword(Resource): method_decorators = cashier_decorators # 请求数据格式 @ns.expect(ResetPassword) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 修改密码 :return: """ # 用户手机号格式验证 form, error = ResetWordForm().request_validate() if error: return error.as_response() # 从全局变量中取出用户ID,参考:verify_credential uid = g.user.uid if not User.verify_password(merchant=form.merchant.data, uid=uid, password=form.ori_password.data): return PasswordError().as_response() if User.verify_password(merchant=form.merchant.data, uid=uid, password=form.new_password.data): return RePasswordError().as_response() flag = User.reset_password(form.merchant.data, uid=uid, login_pwd=form.new_password.data) if not flag: return NoSourceError().as_response() return ResponseSuccess().as_response()
class PaymentPasswordReset(Resource): method_decorators = cashier_decorators # 期待客户端请求数据模型, 用response 来装饰 @ns.expect(ResetPaymentPassword) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 修改支付密码 """ """ 判断两次的密码是否相同 判断是否存在支付密码 校验老的支付密码 设置新的支付密码 """ form, error = ResetPaymentPasswordForm().request_validate() if error: return error.as_response() # 判断两次的密码是否相同 if form.ori_payment_password.data == form.new_payment_password.data: return PaymentPwdResetSameError().as_response() # 从全局变量中取出用户ID,参考:verify_credential uid = g.user.uid # 判断是否存在支付密码 user = User.query_user(form.merchant.data, uid) if not user.trade_pwd: return PaymentPwdNotExistError().as_response() cache = UserPaymentPasswordLimitCache(uid=uid) # 获取支付密码输入错误次数是否达到上限 if cache.is_limited(): return PaymentPasswordLimitedError().as_response() # 校验支付密码 flag = User.verify_payment_password( form.merchant.data, uid=uid, password=form.ori_payment_password.data) # 交易密码校验失败 if not flag: cache.incr_times() times = cache.get_left_times() return PaymentPasswordError(message=PaymentPasswordError.message. format(times)).as_response() # 密码校验成功 删除密码输入错误记录 cache.delete_cache() # 设置新的支付密码 flag = User.set_payment_password( form.merchant.data, uid=uid, trade_pwd=form.new_payment_password.data) # 设置失败的情况 if not flag: return NoSourceError().as_response() return ResponseSuccess().as_response()
class BankCardAdd(Resource): method_decorators = cashier_decorators # 期待客户端请求数据模型, 用response 来装饰 @ns.expect(BankCardParams) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 给用户添加银行卡 """ """ 判断是否达到了8张银行卡的限制 读取用户之前的银行卡信息 如果存在需要判断开户人跟之前的一致 判断卡号是否已经存在了 写入数据库 """ form, error = CreateBankCardForm().request_validate() if error: return error.as_response() # 从全局变量中取出用户ID,参考:verify_credential uid = g.user.uid merchant = g.user.merchant if not g.user.has_permission(UserPermissionEnum.BINDCARD): return UserPermissionDeniedError().as_response() # 获取支付密码输入错误次数是否达到上限 if UserPaymentPasswordLimitCache(uid=uid).is_limited(): return PaymentPasswordLimitedError().as_response() # 校验支付密码 flag = User.verify_payment_password( form.merchant.data, uid=uid, password=form.payment_password.data) # 交易密码校验失败 if not flag: UserPaymentPasswordLimitCache(uid=uid).incr_times() return PaymentPasswordError().as_response() # 密码校验成功 删除密码输入错误记录 UserPaymentPasswordLimitCache(uid=uid).delete_cache() # 判断是否达到了8张银行卡的限制 bank_card_list = BankCard.query_bankcards_by_uid(merchant, uid) bank_card_num = len(bank_card_list) if bank_card_num > USER_BANK_CARD_NUM_LIMIT: return BankCardNumLimitedError().as_response() account_name = form.account_name.data # 如果存在需要判断开户人跟之前的一致 if bank_card_num > 0: account_name = bank_card_list[0]['account_name'] if account_name != form.account_name.data: return BankCardAccountNameError().as_response() # 判断卡号是否已经存在了 bank_card_info = BankCard.query_bankcard_by_card_no( merchant, form.card_no.data) if bank_card_info is not None: return BankCardExistError().as_response() # 写入数据库 flag = BankCard.add_bank_card( merchant, uid=uid, bank_name=form.bank_name.data, bank_code=form.bank_code.data, card_no=form.card_no.data, account_name=form.account_name.data, branch=form.branch.data, province=form.province.data, city=form.city.data, ) # 设置失败的情况 if not flag: return NoSourceError().as_response() return ResponseSuccess().as_response()
class CreateDepositOrder(Resource): method_decorators = admin_decorators # 期望的参数 @ns.expect(CreateDepositOrderParams) @ns.marshal_with(ResponseSuccess.gen_doc(api), as_list=True) 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()
class Transfer(Resource): method_decorators = cashier_decorators # 期待客户端请求数据模型, 用response 来装饰 @ns.expect(TransferParam) # 相应数据格式 @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 转账 判断接受转账的用户是否存在 判断是否存在支付密码 校验老的支付密码 判断余额是否足够 执行转账操作 """ form, error = TransferForm().request_validate() if error: return error.as_response() # 从全局变量中取出用户ID,参考:verify_credential uid = g.user.uid if not g.user.has_permission(UserPermissionEnum.TRANSFER): return UserPermissionDeniedError().as_response() # 判断接受转账的用户是否存在 bind_user = UserBindInfo.query_bind(form.merchant.data, form.number.data) if not bind_user: # 未绑定账号,验证手机号码是否正确 account = form.join_phone_number() if not account: return AccountNotExistError(message="您输入的账号不存在").as_response() form.number.data = account else: # 使用绑定的手机号码 form.number.data = bind_user.account user_info = User.query_user(form.merchant.data, account=form.number.data) if not user_info: return AccountNotExistError(message="账号(%s)不存在" % form.number.data).as_response() no_transfer_limit = UserBindInfo.query_bind_by_uid(user_info.uid) if not no_transfer_limit and form.amount.data > TRANSFER_AMOUNT_LIMIT: # 非绑定用户转账限额检查 return UserPermissionDeniedError( message="单次转账额度不能超过%s" % TRANSFER_AMOUNT_LIMIT).as_response() # 判断是否是给自己转账 if uid == user_info.uid: return TransferToMeError().as_response() # 判断是否存在支付密码 user = User.query_user(form.merchant.data, uid) if not user.trade_pwd: return PaymentPwdNotExistError().as_response() cache = UserPaymentPasswordLimitCache(uid=uid) # 获取支付密码输入错误次数是否达到上限 if cache.is_limited(): return PaymentPasswordLimitedError().as_response() # 校验支付密码 flag = User.verify_payment_password( form.merchant.data, uid=uid, password=form.payment_password.data) # 交易密码校验失败 if not flag: cache.incr_times() times = cache.get_left_times() return PaymentPasswordError(message=PaymentPasswordError.message. format(times)).as_response() # 密码校验成功 删除密码输入错误记录 cache.delete_cache() # 判断余额是否足够 balance = UserBalance.query_balance(uid=uid, merchant=g.user.merchant).first() if BalanceKit.divide_unit(balance.balance) < form.amount.data: return AccountBalanceInsufficientError().as_response() # 执行转账动作 flag, msg = UserBalanceEvent.transfer(from_user=g.user, to_user=user_info, merchant=form.merchant.data, amount=form.amount.data, comment=form.comment.data) # 设置失败的情况 if not flag: return NoSourceError().as_response() return ResponseSuccess().as_response()
class UserBalanceEdit(Resource): method_decorators = admin_decorators @ns.expect(UserBalanceEditApi) @ns.marshal_with(ResponseSuccess.gen_doc(api)) def post(self): """ 用户余额调整 :return: """ form, error = UserBalanceEditForm.request_validate() if error: return error.as_response() uid = form.uid.data # 判断该用户id是否存在 user = User.query.filter_by(**dict(id=uid)).first() if not user: return AdjustUserBalanceError(message="系统找不到该用户").as_response() ad_type = BalanceAdjustTypeEnum.PLUS # 当调整状态为 减少时 需要判断 当前用户余额和商户余额值 amount = BalanceKit.round_4down_5up(Decimal(form.amount.data)) if form.adjust_type.data == BalanceAdjustTypeEnum.MINUS: ad_type = BalanceAdjustTypeEnum.MINUS # 获取用户余额 并判断 user_balance_info = UserBalance.query.filter( UserBalance.id == uid).first() if not user_balance_info: return UserBalanceNoFoundError().as_response() user_balance = user_balance_info.real_balance if Decimal(user_balance) - amount < 0: print("用户没有足够的金额", user_balance, amount) return AdjustUserBalanceError( message="用户没有足够的金额").as_response() # 获取商户余额 并判断 merchant_balance_info = MerchantInfo.query.filter_by(**dict( _merchant=user_balance_info.merchant.value)).first() if not merchant_balance_info: return AdjustUserBalanceError(message="该商户信息不存在").as_response() bl_ava = merchant_balance_info.balance_available if Decimal(bl_ava) - amount < 0: print("商户没有足够的余额", bl_ava, amount) return AdjustUserBalanceError( message="商户没有足够的余额").as_response() # 调整用户及商户余额 flag, error = AdjustTransactionCtl.adjust_create( user=user, source=OrderSourceEnum.MANUALLY, amount=amount, order_type=PayTypeEnum.MANUALLY, bl_type=BalanceTypeEnum.AVAILABLE, ad_type=ad_type, comment=form.comment.data) if not flag: return AdjustUserBalanceError(message="余额调整失败").as_response() return ResponseSuccess().as_response()