123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import traceback
- from django.conf import settings
- from typing import TYPE_CHECKING, Callable, Union, Optional, cast
- from apilib.monetary import RMB
- from apilib.utils_sys import memcache_lock
- from apps.web.agent.models import Agent
- from apps.web.common.models import Banks
- from apps.web.common.transaction import WithdrawStatus, WITHDRAW_PAY_TYPE, WithdrawResult
- from apps.web.core import ROLE
- from apps.web.core.exceptions import ServiceException, WithdrawError
- from apps.web.core.models import BankCard
- from apps.web.core.payment import WithdrawGateway
- from apps.web.core.payment.ali import AliPayWithdrawGateway
- from apps.web.core.payment.wechat import WechatWithdrawGateway
- from library.alipay import AliPayGatewayException
- from library.alipay import AliPayServiceException
- from library.wechatbase.exceptions import WechatNetworkException, WeChatPayException, WeChatException
- if TYPE_CHECKING:
- from contextlib import GeneratorContextManager
- from apps.web.common.models import CapitalUser
- from apps.web.common.models import WithdrawRecord
- from apps.web.core.payment.wechat import WechatWithdrawQueryResult
- logger = logging.getLogger(__name__)
- def withdraw_lock_key_fn(role_type, role_id):
- # type: (str, str)->str
- return '{role_type}-{role_id}-withdraw-lock'.format(role_type = role_type, role_id = role_id)
- def withdraw_lock(type_, id_, key_fn = withdraw_lock_key_fn, value = 1, timeout = 100):
- # type: (str, str, Callable, int, int)->GeneratorContextManager
- return memcache_lock(key_fn(type_, id_), value, timeout)
- def withdraw_via_bank_in_ali(gateway, record, bankcard):
- # type: (AliPayWithdrawGateway, WithdrawRecord, BankCard)->WithdrawResult
- def error_handler(err_code, err_code_des):
- # type:(str, str)->WithdrawResult
- remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
- if err_code == 'SYSTEM_ERROR' or err_code == 'PROCESS_FAIL`':
- return WithdrawResult(
- False, 0, False, remarks,
- u"商户平台系统错误,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1001)")
- elif err_code in ['PAYMENT_MONEY_NOT_ENOUGH', 'PAYER_BALANCE_NOT_ENOUGH', 'BALANCE_IS_NOT_ENOUGH']:
- from taskmanager.mediator import task_caller
- task_caller('withdraw_error_alert', err_type = 'NOTENOUGH')
- return WithdrawResult(False, 0, True, remarks,
- u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1001)")
- elif err_code in ['INVALID_PARAMETER', 'EXCEED_LIMIT_SM_AMOUNT', 'EXCEED_LIMIT_MM_AMOUNT',
- 'PAYCARD_UNABLE_PAYMENT', 'PAYER_STATUS_ERROR', 'PAYER_CERTIFY_CHECK_FAIL',
- 'PAYER_STATUS_ERROR', 'PAYER_USER_INFO_ERROR', 'PAYMENT_INFO_INCONSISTENCY',
- 'CARD_BIN_ERROR', 'PAYEE_CARD_INFO_ERROR', 'INST_PAY_UNABLE', 'REQUEST_PROCESSING',
- 'MEMO_REQUIRED_IN_TRANSFER_ERROR', 'PERMIT_CHECK_PERM_IDENTITY_THEFT',
- 'REMARK_HAS_SENSITIVE_WORD', 'EXCEED_LIMIT_DM_AMOUNT', 'NO_ACCOUNT_RECEIVE_PERMISSION',
- 'NO_ACCOUNT_PAYMENT_PERMISSION', 'INVALID_PARAMETER', 'PAYER_NOT_EXIST',
- 'PRODUCT_NOT_SIGN', 'PAYMENT_TIME_EXPIRE', 'PAYEE_NOT_EXIST', 'PAYEE_ACCOUNT_STATUS_ERROR',
- 'PERMIT_NON_BANK_LIMIT_PAYEE', 'PERMIT_NON_BANK_LIMIT_PAYEE',
- 'PAYEE_TRUSTEESHIP_ACC_OVER_LIMIT',
- 'NO_PERMISSION_ACCOUNT', 'TRUSTEESHIP_ACCOUNT_NOT_EXIST', 'PAYEE_ACCOUNT_NOT_EXSIT',
- 'ORDER_NOT_EXIST', 'PAYEE_USERINFO_STATUS_ERROR', 'TRUSTEESHIP_RECIEVE_QUOTA_LIMIT',
- 'SECURITY_CHECK_FAILED', 'NO_ORDER_PERMISSION', 'ORDER_STATUS_INVALID',
- 'PERM_AML_NOT_REALNAME_REV', 'PERM_AML_NOT_REALNAME_REV', 'USER_AGREEMENT_VERIFY_FAIL',
- 'PAYER_NOT_EQUAL_PAYEE_ERROR', 'EXCEED_LIMIT_DC_RECEIVED', 'PAYER_PERMLIMIT_CHECK_FAILURE',
- 'PAYEE_ACC_OCUPIED', 'PAYER_PAYEE_CANNOT_SAME', 'PERMIT_CHECK_PERM_LIMITED',
- 'PERMIT_CHECK_PERM_LIMITED', 'RESOURCE_LIMIT_EXCEED', 'INVALID_PAYER_ACCOUNT',
- 'EXCEED_LIMIT_DM_MAX_AMOUNT', 'EXCEED_LIMIT_PERSONAL_SM_AMOUNT',
- 'EXCEED_LIMIT_UNRN_DM_AMOUNT', 'INVALID_CARDNO', 'RELEASE_USER_FORBBIDEN_RECIEVE',
- 'PAYEE_USER_TYPE_ERROR', 'EXCEED_LIMIT_SM_MIN_AMOUNT', 'PERMIT_CHECK_RECEIVE_LIMIT',
- 'NOT_IN_WHITE_LIST', 'MONEY_PAY_CLOSED', 'NO_AVAILABLE_PAYMENT_TOOLS',
- 'PAYEE_NOT_RELNAME_CERTIFY', 'OVERSEA_TRANSFER_CLOSE', 'PAYMENT_FAIL',
- 'ALREADY_WITHDRAW_STD_RED_PACKET', 'BLOCK_USER_FORBBIDEN_RECIEVE', 'REQUEST_PROCESSING',
- 'USER_NOT_EXIST', 'PARAM_ILLEGAL', 'PROCESS_FAIL', 'CURRENCY_NOT_SUPPORT',
- 'PAYER_REQUESTER_RELATION_INVALID']:
- return WithdrawResult(False, 0, True, remarks, err_code_des)
- else:
- return WithdrawResult(False, 0, False, remarks,
- u"系统异常,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1001)")
- # 调用付款到银行接口
- try:
- result = gateway.withdraw_via_bank(total_amount = record.actualPay,
- order_no = record.order,
- bank_card = bankcard)
- if result['status'] == 'FAIL':
- return WithdrawResult(False, 0, True, u'支付失败', u'支付失败')
- elif result['status'] == 'REFUND':
- return WithdrawResult(False, 0, True, u'银行退单', u'银行退单')
- else:
- return WithdrawResult(True, 1, False, u'提现申请已经受理', u'提现申请已经受理')
- except AliPayGatewayException as e:
- logger.error(repr(e))
- return error_handler(e.errCode, e.errMsg)
- except AliPayServiceException as e:
- logger.error(repr(e))
- return error_handler(e.errCode, e.errMsg)
- except Exception as e:
- logger.exception(e)
- return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1001)')
- def withdraw_via_bank_in_wechat(gateway, record, bankcard):
- # type: (WechatWithdrawGateway, WithdrawRecord, BankCard)->WithdrawResult
- def error_handler(err_code, err_code_des):
- # type:(str, str)->WithdrawResult
- remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
- if err_code in ['SYSTEMERROR', 'SEND_FAILED']:
- return WithdrawResult(
- False, 0, False, remarks,
- u"商户平台系统错误,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1002)")
- elif err_code == 'NOTENOUGH':
- from taskmanager.mediator import task_caller
- task_caller('withdraw_error_alert', err_type = 'NOTENOUGH')
- return WithdrawResult(False, 0, True, remarks,
- u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1002)")
- elif err_code in ['AMOUNT_LIMIT', 'SENDNUM_LIMIT', 'INVALID_REQUEST', 'PARAM_ERROR', 'SIGNERROR', 'ORDERPAID',
- 'FATAL_ERROR', 'FREQUENCY_LIMITED', 'RECV_ACCOUNT_NOT_ALLOWED', 'PAY_CHANNEL_NOT_ALLOWED',
- 'NO_AUTH', 'FREQ_LIMIT', 'OPENID_ERROR', 'NOTENOUGH']:
- return WithdrawResult(False, 0, True, remarks, err_code_des)
- else:
- return WithdrawResult(
- False, 0, False, remarks,
- u"系统异常,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1002)")
- # 调用付款到银行接口
- try:
- gateway.withdraw_via_bank(order_no = record.order,
- total_amount = record.actualPay,
- bank_card = bankcard)
- return WithdrawResult(True, 1, False, u'提现申请微信已经受理', u'提现申请微信已经受理')
- except WechatNetworkException as e:
- logger.error(repr(e))
- if e.errMsg:
- return WithdrawResult(False, 0, False, e.errMsg, e.errMsg)
- else:
- return WithdrawResult(False, 0, False, u'微信通讯错误', u'微信通讯错误,请联系客服确认提现是否成功。')
- except WeChatPayException as e:
- logger.error(repr(e))
- return error_handler(e.errCode, e.errMsg)
- except Exception as e:
- logger.exception(e)
- return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1002)')
- def withdraw_via_bank(gateway, record, bankcard):
- # type: (WithdrawGateway, WithdrawRecord, BankCard)->(basestring, WithdrawResult)
- if isinstance(gateway, AliPayWithdrawGateway):
- return 'alipay', withdraw_via_bank_in_ali(gateway, record, bankcard)
- if isinstance(gateway, WechatWithdrawGateway):
- return 'wechat', withdraw_via_bank_in_wechat(gateway, record, bankcard)
- raise Exception(u'不支持的提现网关类型')
- def withdraw_via_wechat(gateway, record, payOpenId, real_user_name):
- # type:(cast[WithdrawGateway], WithdrawRecord, str, str)->WithdrawResult
- """
- 微信支付到个人微信
- :param gateway:
- :param record:
- :param payOpenId:
- :param real_user_name:
- :return:
- """
- def withdraw_via_v1(gateway, record, payOpenId, real_user_name):
- def error_handler(err_code, err_code_des):
- # type:(str, str)->(bool, bool, str, str)
- """
- SYSTEMERROR, SEND_FAILED: 不确定是否成功,这个必须必须设置为错误,联系微信确认订单是否成功
- 目前已经在微信中明确的错误码可以自动退款,没有明确的其他错误码必须联系微信确认
- """
- remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
- if err_code in ['SYSTEMERROR', 'SEND_FAILED']:
- # 该情况不能确认是否成功,需要平台侧查找订单确认
- return WithdrawResult(
- False, 0, False, remarks,
- u"商户平台系统错误,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1003)")
- elif err_code == 'NOTENOUGH':
- from taskmanager.mediator import task_caller
- task_caller('withdraw_error_alert', err_type = 'NOTENOUGH')
- return WithdrawResult(False, 0, True, remarks,
- u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1003)")
- elif err_code == 'NAME_MISMATCH':
- return WithdrawResult(False, 2, True, remarks, u"实名校验失败,请确保经销商名字与账户登录微信一致后重试。")
- elif err_code == 'V2_ACCOUNT_SIMPLE_BAN':
- return WithdrawResult(False, 4, True, remarks, u'您的微信尚未实名认证,请去微信绑定银行卡或身份证完成实名认证。')
- elif err_code in ['AMOUNT_LIMIT', 'MONEY_LIMIT', 'SENDNUM_LIMIT', 'NO_AUTH', 'PARAM_ERROR', 'OPENID_ERROR',
- 'SIGN_ERROR', 'XML_ERROR', 'FATAL_ERROR', 'FREQUENCY_LIMITED',
- 'FREQ_LIMIT', 'CA_ERROR', 'PARAM_IS_NOT_UTF8', 'RECV_ACCOUNT_NOT_ALLOWED',
- 'PAY_CHANNEL_NOT_ALLOWED']:
- return WithdrawResult(False, 0, True, remarks, err_code_des)
- else:
- return WithdrawResult(False, 0, False, remarks,
- u"系统异常,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1010)")
- try:
- gateway.withdraw_via_changes(amount = record.actualPay,
- payOpenId = payOpenId,
- order_no = record.order,
- real_user_name = real_user_name,
- subject = u'服务款项')
- return WithdrawResult(True, 1, False, u'提现成功', u'提现成功')
- except WechatNetworkException as e:
- logger.error(repr(e))
- if e.errMsg:
- return WithdrawResult(False, 0, False, e.errMsg, e.errMsg)
- else:
- return WithdrawResult(False, 0, False, u'微信通讯错误', u'微信通讯错误,请联系客服确认提现是否成功。')
- except WeChatPayException as e:
- logger.error(repr(e))
- return error_handler(e.errCode, e.errMsg)
- except Exception as e:
- logger.exception(e)
- return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1003)')
- def withdraw_via_v3(gateway, record, payOpenId, real_user_name):
- def error_handler(err_code, err_code_des):
- # type:(str, str)->(bool, bool, str, str)
- """
- SYSTEM_ERROR, SEND_FAILED: 不确定是否成功,这个必须必须设置为错误,联系微信确认订单是否成功
- 目前已经在微信中明确的错误码可以自动退款,没有明确的其他错误码必须联系微信确认
- """
- remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
- if err_code in ['SYSTEM_ERROR', 'SEND_FAILED']:
- # 该情况不能确认是否成功,需要平台侧查找订单确认
- return WithdrawResult(
- False, 0, False, remarks,
- u"商户平台系统错误,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1003)")
- elif err_code == 'NOT_ENOUGH':
- from taskmanager.mediator import task_caller
- task_caller('withdraw_error_alert', err_type = 'NOTENOUGH')
- return WithdrawResult(False, 0, True, remarks,
- u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1003)")
- elif err_code == 'NAME_MISMATCH':
- return WithdrawResult(False, 2, True, remarks, u"实名校验失败,请确保经销商名字与账户登录微信一致后重试。")
- elif err_code == 'V2_ACCOUNT_SIMPLE_BAN':
- return WithdrawResult(False, 4, True, remarks, u'您的微信尚未实名认证,请去微信绑定银行卡或身份证完成实名认证。')
- elif err_code in ['AMOUNT_LIMIT', 'MONEY_LIMIT', 'SENDNUM_LIMIT', 'NO_AUTH', 'PARAM_ERROR', 'OPENID_ERROR',
- 'SIGN_ERROR', 'XML_ERROR', 'FATAL_ERROR', 'FREQUENCY_LIMITED',
- 'FREQ_LIMIT', 'CA_ERROR', 'PARAM_IS_NOT_UTF8', 'RECV_ACCOUNT_NOT_ALLOWED',
- 'PAY_CHANNEL_NOT_ALLOWED']:
- return WithdrawResult(False, 0, True, remarks, err_code_des)
- else:
- return WithdrawResult(False, 0, False, remarks,
- u"系统异常,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1010)")
- try:
- gateway.withdraw_via_changes(amount = record.actualPay,
- payOpenId = payOpenId,
- order_no = record.order,
- real_user_name = real_user_name,
- subject = u'服务款项')
- return WithdrawResult(True, 1, False, u'提现成功', u'提现成功')
- except WechatNetworkException as e:
- logger.error(repr(e))
- if e.errMsg:
- return WithdrawResult(False, 0, False, e.errMsg, e.errMsg)
- else:
- return WithdrawResult(False, 0, False, u'微信通讯错误', u'微信通讯错误,请联系客服确认提现是否成功。')
- except WeChatPayException as e:
- logger.error(repr(e))
- return error_handler(e.errCode, e.errMsg)
- except Exception as e:
- logger.exception(e)
- return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1003)')
- if gateway.version == 'v3':
- return withdraw_via_v3(gateway, record, payOpenId, real_user_name)
- else:
- return withdraw_via_v1(gateway, record, payOpenId, real_user_name)
- class WithdrawService(object):
- def __init__(self, payee, income_type, amount, pay_type, bank_card_no = None):
- # type: (CapitalUser, str, RMB, str, Union[str, Optional[str]]) -> None
- self.payee = payee
- self.income_type = income_type
- self.amount = amount
- self.pay_type = pay_type
- self.bank_card_no = bank_card_no
- self.record = None
- def execute(self, source_key, recurrent = False):
- try:
- logger.debug('now to withdraw for {}'.format(source_key))
- if not settings.CAN_WITHDRAW_FUND:
- logger.info('test environment do not support withdraw.')
- raise ServiceException({'result': 0, 'description': u'测试环境不允许提现', 'payload': {}})
- if self.amount < RMB(settings.WITHDRAW_MINIMUM):
- raise ServiceException(
- {'result': 0, 'description': u"提现金额不能少于%s元" % (settings.WITHDRAW_MINIMUM,), 'payload': {}})
- if self.amount > RMB(settings.WITHDRAW_MAXIMUM):
- raise ServiceException(
- {'result': 0, 'description': u"单次提现金额不得大于%s元" % (settings.WITHDRAW_MAXIMUM,), 'payload': {}})
- #: 大额提现单预警
- if self.amount >= RMB(settings.WHALE_WITHDRAWAL_ORDER_AMOUNT):
- from taskmanager.mediator import task_caller
- task_caller('whale_withdraw_order_alert')
- if self.payee.no_withdraw:
- raise ServiceException(
- {'result': 0, 'description': u"您的账号权限不足或者异常,暂时不能提现。", 'payload': {}})
- if not self.payee.can_withdraw_today:
- raise ServiceException(
- {'result': 0, 'description': u"超过每日最大提现次数,请明天再试。", 'payload': {}})
- with withdraw_lock(self.payee.role, str(self.payee.id)) as acquired:
- if acquired:
- if self.payee.sub_balance(self.income_type, source_key) < self.amount:
- raise ServiceException({'result': 0, 'description': u'余额不足', 'payload': {}})
- withdraw_gateway_list = Agent.withdraw_gateway_list(source_key)
- default_withdraw_gateway = withdraw_gateway_list['wechat'] # type: WechatWithdrawGateway
- if not default_withdraw_gateway.ledger:
- raise ServiceException({'result': 0, 'description': u'暂时不支持提现(1001)', 'payload': {}})
- if self.pay_type == WITHDRAW_PAY_TYPE.WECHAT:
- if default_withdraw_gateway.manual_withdraw:
- logger.debug('gateway<{}> is manual withdraw.'.format(repr(default_withdraw_gateway)))
- raise ServiceException({'result': 0, 'description': u'暂时不支持提现(1002)', 'payload': {}})
- pay_entity = BankCard(
- cardNo = self.payee.withdraw_open_id,
- holderName = self.payee.nickname,
- bankName = u'微信',
- branchName = u'微信企业付款'
- )
- self.record = self.payee.new_withdraw_record(
- withdraw_gateway = default_withdraw_gateway,
- pay_entity = pay_entity,
- source_key = source_key,
- income_type = self.income_type,
- amount = self.amount,
- pay_type = self.pay_type,
- manual = default_withdraw_gateway.manual_withdraw,
- recurrent = recurrent)
- if not self.record:
- raise ServiceException({'result': 0, 'description': u'系统繁忙,请稍后再试', 'payload': {}})
- if self.payee.abnormal:
- raise ServiceException({'result': 0, 'description': u'该帐号资金异常,请联系客服处理', 'payload': {}})
- handler = self.payee.new_withdraw_handler(self.record)
- updated = self.payee.freeze_balance(self.record.incomeType,
- self.record.amount,
- self.record.source_key,
- self.record.order)
- if not updated:
- handler.revoke(remarks = u'扣款失败', description = u'扣款失败')
- raise ServiceException({'result': 0, 'description': u'扣款失败', 'payload': {}})
- withdraw_result = withdraw_via_wechat(
- gateway = default_withdraw_gateway,
- record = self.record,
- payOpenId = self.payee.withdraw_open_id,
- real_user_name = self.payee.nickname) # type: WithdrawResult
- logger.debug(
- 'withdraw via wechat. record = %s; result = %s' % (
- self.record.order, repr(withdraw_result)))
- if withdraw_result.result is True:
- handler.approve(finishedTime=datetime.datetime.now())
- return {'result': 1, 'description': withdraw_result.show_message,
- 'payload': {'paymentId': str(self.record.id)}}
- else:
- if withdraw_result.refund:
- handler.revoke(remarks = withdraw_result.message,
- description = withdraw_result.show_message)
- else:
- handler.fail(remarks = withdraw_result.message,
- description = withdraw_result.show_message)
- raise ServiceException(
- {'result': withdraw_result.err_code, 'description': withdraw_result.show_message,
- 'payload': {}})
- if self.pay_type == WITHDRAW_PAY_TYPE.BANK:
- bank_card = self.payee.withdraw_bank_card(self.bank_card_no)
- if not bank_card:
- raise ServiceException({'result': 0, 'description': u'银行卡不存在', 'payload': {}})
- manual = False
- withdraw_gateway = default_withdraw_gateway
- if bank_card.manual:
- manual = True
- elif bank_card.accountType == BankCard.AccountType.PUBLIC:
- if withdraw_gateway_list['alipay']:
- withdraw_gateway = withdraw_gateway_list['alipay'] # type: AliPayWithdrawGateway
- if not Banks.support_public(bank_card.bankName):
- raise ServiceException({
- 'result': 0,
- 'description': u'不支持提现到此银行卡或者银行名称错误, 请联系平台客服(1001)',
- 'payload': {}})
- else:
- manual = True
- else:
- if default_withdraw_gateway.manual_withdraw:
- manual = True
- else:
- wechat_bank_code = Banks.get_wechat_bank_code(bank_card.bankName)
- if not wechat_bank_code:
- raise ServiceException({
- 'result': 0,
- 'description': u'不支持提现到此银行卡或者银行名称错误, 请联系平台客服(1002)',
- 'payload': {}})
- self.record = self.payee.new_withdraw_record(
- withdraw_gateway = withdraw_gateway,
- pay_entity = bank_card,
- source_key = source_key,
- income_type = self.income_type,
- amount = self.amount,
- pay_type = self.pay_type,
- manual = manual,
- recurrent = recurrent) # type: WithdrawRecord
- if not self.record:
- raise ServiceException({'result': 0, 'description': u'系统繁忙,请稍后再试(1002)', 'payload': {}})
- handler = self.payee.new_withdraw_handler(self.record)
- updated = self.payee.freeze_balance(self.record.incomeType,
- self.record.amount,
- self.record.source_key,
- self.record.order)
- if not updated:
- handler.revoke(remarks = u'扣款失败', description = u'扣款失败')
- raise ServiceException({'result': 0, 'description': u'扣款失败', 'payload': {}})
- # 对公的提现, 或者不支持的银行卡(使用manual标记的BankCard 或者 Merchant)
- if manual:
- withdraw_result = WithdrawResult(True, 1, False, u'提现申请已经受理', u'提现申请已经受理')
- logger.debug(
- 'withdraw via bank(manual). record = %s; result = %s' % (
- self.record.order, repr(withdraw_result)))
- handler.processing(remarks = u'提现申请已经受理', description = u'提现申请已经受理')
- return {
- 'result': withdraw_result.err_code,
- 'description': withdraw_result.show_message,
- 'payload': {'paymentId': str(self.record.id)}}
- else:
- gateway_type, withdraw_result = withdraw_via_bank(withdraw_gateway, self.record, bank_card)
- logger.debug(
- 'withdraw via bank({}). record = {}; result = {}'.format(
- gateway_type, self.record.order, repr(withdraw_result)))
- if withdraw_result.result is True:
- handler.processing(remarks = u'提现申请已经受理', description = u'提现申请已经受理')
- return {
- 'result': 1,
- 'description': u'提现申请已经受理',
- 'payload': {
- 'paymentId': str(self.record.id)
- }
- }
- else:
- if withdraw_result.refund:
- handler.revoke(remarks = withdraw_result.message,
- description = withdraw_result.show_message)
- else:
- handler.fail(remarks = withdraw_result.message,
- description = withdraw_result.show_message)
- raise ServiceException(
- {'result': withdraw_result.err_code, 'description': withdraw_result.show_message,
- 'payload': {}})
- raise ServiceException({'result': 0, 'description': u'系统错误,请稍后再试', 'payload': {}})
- else:
- raise ServiceException({'result': 0, 'description': u'操作频繁,请稍后再试', 'payload': {}})
- except ServiceException as e:
- logger.exception(e)
- if self.record:
- if 'payload' in e.result:
- e.result['payload'].update({'paymentId': str(self.record.id)})
- else:
- e.result['payload'] = {'paymentId': str(self.record.id)}
- return e.result
- except WithdrawError as e:
- logger.exception(e)
- return {
- 'result': 0,
- 'description': e.message,
- 'payload': {'paymentId': str(self.record.id)} if str(self.record.id) else {}
- }
- class WithdrawRetryService(object):
- def __init__(self, record, only_fail = True):
- # type: (WithdrawRecord, bool)->None
- self.record = record # type : WithdrawRecord
- self.only_fail = only_fail
- def get_payee(self):
- return ROLE.from_role_id(self.record.role, self.record.ownerId)
- def check_retry_over(self, handler):
- end = self.record.postTime + datetime.timedelta(days = 3) # type: datetime
- now = datetime.datetime.now()
- if end.year > now.year or end.month > now.month or end.day > now.day:
- handler.revoke(remarks = u'平台退单', description = u'提现失败')
- raise ServiceException(
- {
- 'result': 0,
- 'description': u'重试次数超限,平台退单',
- 'payload': {}
- })
- def execute(self):
- try:
- if self.record.refunded:
- raise ServiceException(
- {
- 'result': 0,
- 'description': 'record<id={}> has refunded.'.format(str(self.record.id)),
- 'payload': {}
- })
- if self.record.manual:
- raise ServiceException(
- {
- 'result': 0,
- 'description': 'record<id={}> can not be manual.'.format(str(self.record.id)),
- 'payload': {}
- })
- if self.only_fail:
- if self.record.status != WithdrawStatus.FAILED:
- raise ServiceException(
- {
- 'result': 0,
- 'description': 'record<id={}> must be fail.'.format(str(self.record.id)),
- 'payload': {}
- })
- else:
- if self.record.status not in [WithdrawStatus.SUBMITTED, WithdrawStatus.FAILED]:
- raise ServiceException(
- {
- 'result': 0,
- 'description': 'record<id={}> must be fail.'.format(str(self.record.id)),
- 'payload': {}
- })
- payee = self.get_payee() # type: CapitalUser
- if payee.role != self.record.role:
- raise ServiceException(
- {
- 'result': 0,
- 'description': 'role is not same',
- 'payload': {}
- })
- withdraw_gateway = WithdrawGateway.from_withdraw_gateway_key(
- self.record.withdrawGatewayKey,
- self.record.extras.get('gateway_version', 'v1')) # type: WithdrawGateway
- if self.record.payType == WITHDRAW_PAY_TYPE.WECHAT:
- handler = payee.new_withdraw_handler(self.record)
- try:
- query_result = withdraw_gateway.get_transfer_result_via_changes(
- self.record.order) # type: WechatWithdrawQueryResult
- errcode, errmsg = query_result.error_desc
- if query_result.is_failed:
- handler.revoke(remarks = errcode,
- description = errmsg)
- return {
- 'result': 1,
- 'description': errmsg,
- 'payload': {
- 'paymentId': str(self.record.id)
- }
- }
- if query_result.is_successful:
- handler.approve()
- return {
- 'result': 1,
- 'description': 'SUCCESS',
- 'payload': {
- 'paymentId': str(self.record.id)
- }
- }
- if query_result.is_processing:
- pass
- else:
- raise ServiceException(
- {
- 'result': 0,
- 'description': u'未知订单状态{}'.format(query_result.order_status),
- 'payload': {}
- })
- except WeChatPayException as e:
- # 如果业务状态是查不到订单, 需要继续处理, 其他抛出异常
- if e.errCode not in ['ORDERNOTEXIST', 'NOT_FOUND']:
- raise e
- self.check_retry_over(handler)
- payee.freeze_balance(self.record.incomeType,
- self.record.amount,
- self.record.source_key,
- self.record.order)
- withdraw_result = withdraw_via_wechat(withdraw_gateway,
- self.record,
- self.record.accountCode,
- self.record.cardUserName) # type: WithdrawResult
- logger.debug(
- 'withdraw via wechat. record = %s; result = %s' % (
- self.record.order, repr(withdraw_result)))
- if withdraw_result.result is True:
- handler.approve()
- return {
- 'result': 1,
- 'description': withdraw_result.show_message,
- 'payload': {
- 'paymentId': str(self.record.id)
- }
- }
- else:
- raise ServiceException(
- {
- 'result': withdraw_result.err_code,
- 'description': withdraw_result.show_message,
- 'payload': {}
- })
- if self.record.payType == WITHDRAW_PAY_TYPE.BANK:
- handler = payee.new_withdraw_handler(self.record)
- try:
- query_result = withdraw_gateway.get_transfer_result_via_bank(
- self.record.order) # type: WechatWithdrawQueryResult
- errcode, errmsg = query_result.error_desc
- if query_result.is_failed:
- handler.revoke(remarks = errcode,
- description = errmsg)
- return {
- 'result': 1,
- 'description': errmsg,
- 'payload': {
- 'paymentId': str(self.record.id)
- }
- }
- if query_result.is_successful:
- handler.approve()
- return {
- 'result': 1,
- 'description': 'SUCCESS',
- 'payload': {
- 'paymentId': str(self.record.id)
- }
- }
- if query_result.is_processing:
- pass
- else:
- raise ServiceException(
- {
- 'result': 0,
- 'description': u'未知订单状态{}'.format(query_result.order_status),
- 'payload': {}
- })
- except (WeChatPayException, AliPayServiceException) as e:
- # 如果业务状态是查不到订单, 需要继续处理, 否则继续抛出异常
- if e.errCode not in ['ORDERNOTEXIST', 'NOT_FOUND', 'ORDER_NOT_EXIST']:
- raise e
- self.check_retry_over(handler)
- bank_card = payee.withdraw_bank_card(self.record.accountCode)
- if not bank_card:
- raise ServiceException(
- {
- 'result': 0,
- 'description': '银行卡不存在',
- 'payload': {}
- })
- payee.freeze_balance(self.record.incomeType,
- self.record.amount,
- self.record.source_key,
- self.record.order)
- gateway_type, withdraw_result = withdraw_via_bank(withdraw_gateway, self.record, bank_card)
- logger.debug(
- 'withdraw via bank({}). record = {}; result = {}'.format(
- gateway_type, self.record.order, repr(withdraw_result)))
- if withdraw_result.result is True:
- handler.processing(remarks=withdraw_result.message, description=withdraw_result.show_message)
- return {
- 'result': withdraw_result.err_code,
- 'description': withdraw_result.show_message,
- 'payload': {
- 'paymentId': str(self.record.id)
- }
- }
- else:
- raise ServiceException(
- {
- 'result': withdraw_result.err_code,
- 'description': withdraw_result.show_message,
- 'payload': {}
- })
- except WechatNetworkException as e:
- logger.exception(e)
- return {'result': 0, 'description': e.errMsg, 'payload': {}}
- except WeChatPayException as e:
- logger.exception(e)
- return {'result': 0, 'description': e.errMsg, 'payload': {}}
- except ServiceException as e:
- return e.result
- except WithdrawError as e:
- logger.exception(e)
- return {'result': 0, 'description': e.message, 'payload': {}}
- except Exception as e:
- logger.exception(e)
- return {'result': 0, 'description': e.message, 'payload': {}}
|