withdraw.py 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import traceback
  6. import arrow
  7. from django.conf import settings
  8. from typing import TYPE_CHECKING, Callable, Union, Optional, cast
  9. from apilib.monetary import RMB
  10. from apilib.utils_sys import memcache_lock
  11. from apps.web.agent.models import Agent
  12. from apps.web.common.models import WithdrawBanks, WithdrawBankCard
  13. from apps.web.common.transaction import WithdrawStatus, WITHDRAW_PAY_TYPE, WithdrawResult
  14. from apps.web.core import ROLE
  15. from apps.web.core.exceptions import ServiceException, WithdrawError
  16. from apps.web.core.payment import WithdrawGateway
  17. from apps.web.core.payment.ali import AliPayWithdrawGateway, AlipayWithdrawQueryResult
  18. from apps.web.core.payment.wechat import WechatWithdrawGateway
  19. from apps.web.exceptions import WithdrawOrderNotExist
  20. from library.alipay import AliPayGatewayException
  21. from library.alipay import AliPayServiceException
  22. from library.wechatbase.exceptions import WechatNetworkException, WeChatPayException
  23. if TYPE_CHECKING:
  24. from contextlib import GeneratorContextManager
  25. from apps.web.common.models import CapitalUser
  26. from apps.web.common.models import WithdrawRecord
  27. from apps.web.core.payment.wechat import WechatWithdrawQueryResult
  28. from apps.web.core.payment import WithdrawGatewayT
  29. logger = logging.getLogger(__name__)
  30. def withdraw_lock_key_fn(role_type, role_id):
  31. # type: (str, str)->str
  32. return '{role_type}-{role_id}-withdraw-lock'.format(role_type = role_type, role_id = role_id)
  33. def withdraw_lock(type_, id_, key_fn = withdraw_lock_key_fn, value = 1, timeout = 100):
  34. # type: (str, str, Callable, int, int)->GeneratorContextManager
  35. return memcache_lock(key_fn(type_, id_), value, timeout)
  36. class AlipayWithdraw(object):
  37. @classmethod
  38. def service_error_handler(cls, err_code, err_code_des):
  39. # type:(str, str)->WithdrawResult
  40. remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
  41. if err_code == 'SYSTEM_ERROR' or err_code == 'PROCESS_FAIL':
  42. return WithdrawResult(
  43. False, 0, False, remarks,
  44. u"商户平台系统错误,无法获取转账状态。平台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1021)")
  45. elif err_code in ['PAYER_BALANCE_NOT_ENOUGH', 'BALANCE_IS_NOT_ENOUGH']:
  46. from taskmanager.mediator import task_caller
  47. task_caller('withdraw_error_alert', err_type = 'NOTENOUGH')
  48. return WithdrawResult(False, 0, True, remarks,
  49. u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1001)")
  50. elif err_code in [
  51. 'INVALID_PARAMETER', 'EXCEED_LIMIT_SM_AMOUNT', 'EXCEED_LIMIT_MM_AMOUNT', 'PAYCARD_UNABLE_PAYMENT',
  52. 'PAYER_STATUS_ERROR', 'PAYER_CERTIFY_CHECK_FAIL', 'PAYER_USER_INFO_ERROR', 'PAYMENT_INFO_INCONSISTENCY',
  53. 'CARD_BIN_ERROR', 'PAYEE_CARD_INFO_ERROR', 'INST_PAY_UNABLE', 'MEMO_REQUIRED_IN_TRANSFER_ERROR',
  54. 'PERMIT_CHECK_PERM_IDENTITY_THEFT', 'REMARK_HAS_SENSITIVE_WORD', 'EXCEED_LIMIT_DM_AMOUNT',
  55. 'NO_ACCOUNT_RECEIVE_PERMISSION', 'NO_ACCOUNT_PAYMENT_PERMISSION', 'INVALID_PARAMETER',
  56. 'PAYER_NOT_EXIST', 'PRODUCT_NOT_SIGN', 'PAYMENT_TIME_EXPIRE', 'PAYEE_NOT_EXIST',
  57. 'PAYEE_ACCOUNT_STATUS_ERROR', 'PERMIT_NON_BANK_LIMIT_PAYEE', 'PAYEE_TRUSTEESHIP_ACC_OVER_LIMIT',
  58. 'NO_PERMISSION_ACCOUNT', 'TRUSTEESHIP_ACCOUNT_NOT_EXIST', 'PAYEE_ACCOUNT_NOT_EXSIT',
  59. 'ORDER_NOT_EXIST', 'PAYEE_USERINFO_STATUS_ERROR', 'TRUSTEESHIP_RECIEVE_QUOTA_LIMIT',
  60. 'SECURITY_CHECK_FAILED', 'NO_ORDER_PERMISSION', 'ORDER_STATUS_INVALID', 'PERM_AML_NOT_REALNAME_REV',
  61. 'USER_AGREEMENT_VERIFY_FAIL', 'PAYER_NOT_EQUAL_PAYEE_ERROR', 'EXCEED_LIMIT_DC_RECEIVED',
  62. 'PAYER_PERMLIMIT_CHECK_FAILURE', 'PAYEE_ACC_OCUPIED', 'PAYER_PAYEE_CANNOT_SAME',
  63. 'PERMIT_CHECK_PERM_LIMITED', 'RESOURCE_LIMIT_EXCEED', 'INVALID_PAYER_ACCOUNT',
  64. 'EXCEED_LIMIT_DM_MAX_AMOUNT', 'EXCEED_LIMIT_PERSONAL_SM_AMOUNT', 'EXCEED_LIMIT_UNRN_DM_AMOUNT',
  65. 'INVALID_CARDNO', 'RELEASE_USER_FORBBIDEN_RECIEVE', 'PAYEE_USER_TYPE_ERROR',
  66. 'EXCEED_LIMIT_SM_MIN_AMOUNT', 'PERMIT_CHECK_RECEIVE_LIMIT', 'NOT_IN_WHITE_LIST',
  67. 'MONEY_PAY_CLOSED', 'NO_AVAILABLE_PAYMENT_TOOLS', 'PAYEE_NOT_RELNAME_CERTIFY',
  68. 'OVERSEA_TRANSFER_CLOSE', 'PAYMENT_FAIL', 'BLOCK_USER_FORBBIDEN_RECIEVE',
  69. 'REQUEST_PROCESSING', 'USER_NOT_EXIST', 'PARAM_ILLEGAL', 'CURRENCY_NOT_SUPPORT',
  70. 'PAYER_REQUESTER_RELATION_INVALID', 'AUTHOREE_IS_NOT_MATCH', 'NO_ACCOUNT_USER_FORBBIDEN_RECIEVE',
  71. 'SIGN_INVALID', 'SIGN_INVOKE_PID_INCONSISTENT', 'SIGN_QUERY_APP_INFO_ERROR',
  72. 'SIGN_QUERY_AGGREMENT_ERROR', 'SIGN_AGREEMENT_NO_INCONSISTENT', 'SIGN_PARAM_INVALID',
  73. 'SIGN_NOT_ALLOW_SKIP', 'EXCEED_LIMIT_ENT_SM_AMOUNT', 'ISV_AUTH_ERROR', 'PAYER_USERINFO_NOT_EXSIT',
  74. 'BLOCK_USER_FORBBIDEN_SEND', 'BIZ_UNIQUE_EXCEPTION', 'NO_ACCOUNTBOOK_PERMISSION',
  75. 'PERMIT_CHECK_PERM_AML_CERT_EXPIRED', 'MRCHPROD_QUERY_ERROR', 'PERMIT_PAYER_FORBIDDEN',
  76. 'IDENTITY_FUND_RELATION_NOT_FOUND', 'PERMIT_LIMIT_PAYEE', 'PERM_PAY_USER_DAILY_QUOTA_ORG_BALANCE_LIMIT',
  77. 'PERM_PAY_USER_MONTH_QUOTA_ORG_BALANCE_LIMIT', 'PERM_PAY_CUSTOMER_DAILY_QUOTA_ORG_BALANCE_LIMIT',
  78. 'PERM_PAY_CUSTOMER_MONTH_QUOTA_ORG_BALANCE_LIMIT', 'NOT_SUPPORT_PAYER_ACCOUNT_TYPE',
  79. 'EXCEED_LIMIT_MM_MAX_AMOUNT', 'ILLEGAL_OPERATION',
  80. ]:
  81. return WithdrawResult(False, 0, True, remarks, err_code_des)
  82. else:
  83. return WithdrawResult(False, 0, False, remarks,
  84. u"系统异常,无法获取转账状态。平台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1001)")
  85. @classmethod
  86. def common_error_handler(cls, err_code, err_code_des):
  87. # type:(str, str)->WithdrawResult
  88. remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
  89. if err_code in ['isp.unknow-error', 'aop.unknow-error', 'isv.app-call-limited', 'isv.method-call-limited']:
  90. return WithdrawResult(False, 0, True, remarks, u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1005)")
  91. elif 'aop.' in err_code or 'isv.' in err_code or err_code in ['app-cert-expired', 'invalid-auth-relations']:
  92. return WithdrawResult(False, 0, True, remarks, err_code_des)
  93. else:
  94. return WithdrawResult(False, 0, False, remarks,
  95. u"系统异常,无法获取转账状态。平台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1002)")
  96. @classmethod
  97. def withdraw_via_bank_in_ali(cls, gateway, record, bankcard):
  98. # type: (AliPayWithdrawGateway, WithdrawRecord, WithdrawBankCard)->WithdrawResult
  99. try:
  100. result = gateway.withdraw_via_bank(total_amount = record.actualPay,
  101. order_no = record.order,
  102. bank_card = bankcard)
  103. if result['status'] == 'FAIL':
  104. return WithdrawResult(False, 0, True, u'支付失败', u'支付失败')
  105. elif result['status'] == 'REFUND':
  106. return WithdrawResult(False, 0, True, u'银行退单', u'银行退单')
  107. else:
  108. return WithdrawResult(True, 1, False, u'提现申请已经受理', u'提现申请已经受理')
  109. except AliPayGatewayException as e:
  110. logger.error(repr(e))
  111. return cls.common_error_handler(e.errCode, e.errMsg)
  112. except AliPayServiceException as e:
  113. logger.error(repr(e))
  114. return cls.service_error_handler(e.errCode, e.errMsg)
  115. except Exception as e:
  116. logger.exception(e)
  117. return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1006)')
  118. @classmethod
  119. def withdraw_via_changes_in_ali(cls, gateway, record):
  120. # type: (AliPayWithdrawGateway, WithdrawRecord)->WithdrawResult
  121. try:
  122. result = gateway.withdraw_via_changes(
  123. amount = record.actualPay,
  124. payOpenId = record.accountCode,
  125. order_no = record.order,
  126. real_user_name = record.cardUserName)
  127. if result['status'] == 'SUCCESS':
  128. if result['out_biz_no'] != record.order:
  129. logger.warning(
  130. 'alipay withdraw order not match. {} != {}'.format(result['out_biz_no'], record.order))
  131. return WithdrawResult(False, 0, False, u'提现失败(订单号错误)', u'提现失败,请联系平台客服解决')
  132. else:
  133. return WithdrawResult(True, 1, False, u'提现成功', u'提现成功', result)
  134. elif result['status'] == 'FAIL':
  135. return WithdrawResult(False, 0, True, u'支付失败', u'支付失败')
  136. else:
  137. logger.warning('invalid status = {}'.format(result['status']))
  138. return WithdrawResult(False, 0, False, u'支付失败(无效状态)', u'支付失败(无效状态)')
  139. except AliPayGatewayException as e:
  140. logger.error(repr(e))
  141. return cls.common_error_handler(e.errCode, e.errMsg)
  142. except AliPayServiceException as e:
  143. logger.error(repr(e))
  144. return cls.service_error_handler(e.errCode, e.errMsg)
  145. except Exception as e:
  146. logger.exception(e)
  147. return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1001)')
  148. def withdraw_via_bank_in_wechat(gateway, record, bankcard):
  149. # type: (WechatWithdrawGateway, WithdrawRecord, WithdrawBankCard)->WithdrawResult
  150. def error_handler(err_code, err_code_des):
  151. # type:(str, str)->WithdrawResult
  152. remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
  153. if err_code in ['SYSTEMERROR', 'SEND_FAILED']:
  154. return WithdrawResult(
  155. False, 0, False, remarks,
  156. u"商户平台系统错误,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1002)")
  157. elif err_code == 'NOTENOUGH':
  158. from taskmanager.mediator import task_caller
  159. task_caller('withdraw_error_alert', err_type = 'NOTENOUGH')
  160. return WithdrawResult(False, 0, True, remarks,
  161. u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1002)")
  162. elif err_code in ['AMOUNT_LIMIT', 'SENDNUM_LIMIT', 'INVALID_REQUEST', 'PARAM_ERROR', 'SIGNERROR', 'ORDERPAID',
  163. 'FATAL_ERROR', 'FREQUENCY_LIMITED', 'RECV_ACCOUNT_NOT_ALLOWED', 'PAY_CHANNEL_NOT_ALLOWED',
  164. 'NO_AUTH', 'FREQ_LIMIT', 'OPENID_ERROR', 'NOTENOUGH']:
  165. return WithdrawResult(False, 0, True, remarks, err_code_des)
  166. else:
  167. return WithdrawResult(
  168. False, 0, False, remarks,
  169. u"系统异常,无法获取转账状态。平台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1003)")
  170. # 调用付款到银行接口
  171. try:
  172. gateway.withdraw_via_bank(order_no = record.order,
  173. total_amount = record.actualPay,
  174. bank_card = bankcard)
  175. return WithdrawResult(True, 1, False, u'提现申请微信已经受理', u'提现申请微信已经受理')
  176. except WechatNetworkException as e:
  177. logger.error(repr(e))
  178. if e.errMsg:
  179. return WithdrawResult(False, 0, False, e.errMsg, e.errMsg)
  180. else:
  181. return WithdrawResult(False, 0, False, u'微信通讯错误', u'微信通讯错误,请联系客服确认提现是否成功。')
  182. except WeChatPayException as e:
  183. logger.error(repr(e))
  184. return error_handler(e.errCode, e.errMsg)
  185. except NotImplementedError as e:
  186. logger.error(e)
  187. return WithdrawResult(False, 0, True, traceback.format_exc(), u'提现失败')
  188. except Exception as e:
  189. logger.exception(e)
  190. return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1002)')
  191. def withdraw_via_bank(gateway, record, bankcard):
  192. # type: (WithdrawGateway, WithdrawRecord, WithdrawBankCard)->(basestring, WithdrawResult)
  193. if isinstance(gateway, AliPayWithdrawGateway):
  194. return 'alipay', AlipayWithdraw.withdraw_via_bank_in_ali(gateway, record, bankcard)
  195. if isinstance(gateway, WechatWithdrawGateway):
  196. return 'wechat', withdraw_via_bank_in_wechat(gateway, record, bankcard)
  197. raise Exception(u'不支持的提现网关类型')
  198. def withdraw_via_wechat(gateway, record, payOpenId, real_user_name):
  199. # type:(WechatWithdrawGateway, WithdrawRecord, str, str)->WithdrawResult
  200. """
  201. 微信支付到个人微信
  202. :param gateway:
  203. :param record:
  204. :param payOpenId:
  205. :param real_user_name:
  206. :return:
  207. """
  208. def withdraw_via_v1(gateway, record, payOpenId, real_user_name):
  209. def error_handler(err_code, err_code_des):
  210. # type:(str, str)->(bool, bool, str, str)
  211. """
  212. SYSTEMERROR, SEND_FAILED: 不确定是否成功,这个必须必须设置为错误,联系微信确认订单是否成功
  213. 目前已经在微信中明确的错误码可以自动退款,没有明确的其他错误码必须联系微信确认
  214. """
  215. remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
  216. if err_code in ['SYSTEMERROR', 'SEND_FAILED']:
  217. # 该情况不能确认是否成功,需要平台侧查找订单确认
  218. return WithdrawResult(
  219. False, 0, False, remarks,
  220. u"商户平台系统错误,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1003)")
  221. elif err_code == 'NOTENOUGH':
  222. from taskmanager.mediator import task_caller
  223. task_caller('withdraw_error_alert', err_type = 'NOTENOUGH')
  224. return WithdrawResult(False, 0, True, remarks,
  225. u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1003)")
  226. elif err_code == 'NAME_MISMATCH':
  227. return WithdrawResult(False, 2, True, remarks, u"实名校验失败,请确保经销商名字与账户登录微信一致后重试。")
  228. elif err_code == 'V2_ACCOUNT_SIMPLE_BAN':
  229. return WithdrawResult(False, 4, True, remarks, u'您的微信尚未实名认证,请去微信绑定银行卡或身份证完成实名认证。')
  230. elif err_code in ['AMOUNT_LIMIT', 'MONEY_LIMIT', 'SENDNUM_LIMIT', 'NO_AUTH', 'PARAM_ERROR', 'OPENID_ERROR',
  231. 'SIGN_ERROR', 'XML_ERROR', 'FATAL_ERROR', 'FREQUENCY_LIMITED',
  232. 'FREQ_LIMIT', 'CA_ERROR', 'PARAM_IS_NOT_UTF8', 'RECV_ACCOUNT_NOT_ALLOWED',
  233. 'PAY_CHANNEL_NOT_ALLOWED']:
  234. return WithdrawResult(False, 0, True, remarks, err_code_des)
  235. else:
  236. return WithdrawResult(False, 0, False, remarks,
  237. u"系统异常,无法获取转账状态。平台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1005)")
  238. try:
  239. gateway.withdraw_via_changes(amount = record.actualPay,
  240. payOpenId = payOpenId,
  241. order_no = record.order,
  242. real_user_name = real_user_name,
  243. subject = u'服务款项')
  244. return WithdrawResult(True, 1, False, u'提现成功', u'提现成功')
  245. except WechatNetworkException as e:
  246. logger.error(repr(e))
  247. if e.errMsg:
  248. return WithdrawResult(False, 0, False, e.errMsg, e.errMsg)
  249. else:
  250. return WithdrawResult(False, 0, False, u'微信通讯错误', u'微信通讯错误,请联系客服确认提现是否成功。')
  251. except WeChatPayException as e:
  252. logger.error(repr(e))
  253. return error_handler(e.errCode, e.errMsg)
  254. except Exception as e:
  255. logger.exception(e)
  256. return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1003)')
  257. def withdraw_via_v3(gateway, record, payOpenId, real_user_name):
  258. def error_handler(err_code, err_code_des):
  259. # type:(str, str)->(bool, bool, str, str)
  260. """
  261. SYSTEM_ERROR, SEND_FAILED: 不确定是否成功,这个必须必须设置为错误,联系微信确认订单是否成功
  262. 目前已经在微信中明确的错误码可以自动退款,没有明确的其他错误码必须联系微信确认
  263. """
  264. remarks = u'{err_code_des}({err_code})'.format(err_code = err_code, err_code_des = err_code_des)
  265. if err_code in ['SYSTEM_ERROR', 'SEND_FAILED']:
  266. # 该情况不能确认是否成功,需要平台侧查找订单确认
  267. return WithdrawResult(
  268. False, 0, False, remarks,
  269. u"商户平台系统错误,无法获取转账状态。后台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1003)")
  270. elif err_code == 'NOT_ENOUGH':
  271. from taskmanager.mediator import task_caller
  272. task_caller('withdraw_error_alert', err_type = 'NOTENOUGH')
  273. return WithdrawResult(False, 0, True, remarks,
  274. u"商户平台系统繁忙,请过一小时后再试。验证码每天次数有限,频繁提交提现可能消耗完当天验证码次数(1006)")
  275. elif err_code == 'NAME_MISMATCH':
  276. return WithdrawResult(False, 2, True, remarks, u"实名校验失败,请确保经销商名字与账户登录微信一致后重试。")
  277. elif err_code == 'V2_ACCOUNT_SIMPLE_BAN':
  278. return WithdrawResult(False, 4, True, remarks, u'您的微信尚未实名认证,请去微信绑定银行卡或身份证完成实名认证。')
  279. elif err_code in ['AMOUNT_LIMIT', 'MONEY_LIMIT', 'SENDNUM_LIMIT', 'NO_AUTH', 'PARAM_ERROR', 'OPENID_ERROR',
  280. 'SIGN_ERROR', 'XML_ERROR', 'FATAL_ERROR', 'FREQUENCY_LIMITED',
  281. 'FREQ_LIMIT', 'CA_ERROR', 'PARAM_IS_NOT_UTF8', 'RECV_ACCOUNT_NOT_ALLOWED',
  282. 'PAY_CHANNEL_NOT_ALLOWED']:
  283. return WithdrawResult(False, 0, True, remarks, err_code_des)
  284. else:
  285. return WithdrawResult(False, 0, False, remarks,
  286. u"系统异常,无法获取转账状态。平台会每日定时重试,如果3个工作日仍然是失败状态,请联系客服确认结果(1006)")
  287. try:
  288. gateway.withdraw_via_changes(amount = record.actualPay,
  289. payOpenId = payOpenId,
  290. order_no = record.order,
  291. real_user_name = real_user_name,
  292. subject = u'服务款项')
  293. return WithdrawResult(True, 1, False, u'提现成功', u'提现成功')
  294. except WechatNetworkException as e:
  295. logger.error(repr(e))
  296. if e.errMsg:
  297. return WithdrawResult(False, 0, False, e.errMsg, e.errMsg)
  298. else:
  299. return WithdrawResult(False, 0, False, u'微信通讯错误', u'微信通讯错误,请联系客服确认提现是否成功。')
  300. except WeChatPayException as e:
  301. logger.error(repr(e))
  302. return error_handler(e.errCode, e.errMsg)
  303. except Exception as e:
  304. logger.exception(e)
  305. return WithdrawResult(False, 0, False, traceback.format_exc(), u'系统异常,无法确定转账是否成功,请联系客服确认结果(1005)')
  306. if gateway.version == 'v3':
  307. return withdraw_via_v3(gateway, record, payOpenId, real_user_name)
  308. else:
  309. return withdraw_via_v1(gateway, record, payOpenId, real_user_name)
  310. class WithdrawService(object):
  311. def __init__(self, payee, income_type, amount, pay_type, bank_card_no = None):
  312. # type: (CapitalUser, str, RMB, str, Union[str, Optional[str]]) -> None
  313. self.payee = payee
  314. self.income_type = income_type
  315. self.amount = amount
  316. self.pay_type = pay_type
  317. self.bank_card_no = bank_card_no
  318. self.record = None
  319. def execute(self, source_key, recurrent = False):
  320. try:
  321. logger.debug('now to withdraw for {}'.format(source_key))
  322. if not settings.CAN_WITHDRAW_FUND:
  323. logger.info('test environment do not support withdraw.')
  324. raise ServiceException({'result': 0, 'description': u'测试环境不允许提现', 'payload': {}})
  325. if self.amount < RMB(settings.WITHDRAW_MINIMUM):
  326. raise ServiceException(
  327. {'result': 0, 'description': u"提现金额不能少于%s元" % (settings.WITHDRAW_MINIMUM,), 'payload': {}})
  328. if self.amount > RMB(settings.WITHDRAW_MAXIMUM):
  329. raise ServiceException(
  330. {'result': 0, 'description': u"单次提现金额不得大于%s元" % (settings.WITHDRAW_MAXIMUM,), 'payload': {}})
  331. #: 大额提现单预警
  332. if self.amount >= RMB(settings.WHALE_WITHDRAWAL_ORDER_AMOUNT):
  333. from taskmanager.mediator import task_caller
  334. task_caller('whale_withdraw_order_alert')
  335. if self.payee.no_withdraw:
  336. raise ServiceException(
  337. {'result': 0, 'description': u"您的账号权限不足或者异常,暂时不能提现。", 'payload': {}})
  338. if self.payee.abnormal:
  339. raise ServiceException({'result': 0, 'description': u'该帐号资金异常,请联系客服处理', 'payload': {}})
  340. if not self.payee.can_withdraw_today:
  341. raise ServiceException(
  342. {'result': 0, 'description': u"超过每日最大提现次数,请明天再试。", 'payload': {}})
  343. with withdraw_lock(self.payee.role, str(self.payee.id)) as acquired:
  344. if acquired:
  345. if self.payee.sub_balance(self.income_type, source_key) < self.amount:
  346. raise ServiceException({'result': 0, 'description': u'余额不足', 'payload': {}})
  347. is_ledger, withdraw_gateway_list = Agent.withdraw_gateway_list(source_key)
  348. if not is_ledger:
  349. raise ServiceException({'result': 0, 'description': u'暂时不支持提现(1001)', 'payload': {}})
  350. if self.pay_type == WITHDRAW_PAY_TYPE.WECHAT:
  351. if withdraw_gateway_list['wechat'].manual_withdraw:
  352. logger.debug('gateway<{}> is manual withdraw.'.format(repr(withdraw_gateway_list['wechat'])))
  353. raise ServiceException({'result': 0, 'description': u'暂时不支持提现(1002)', 'payload': {}})
  354. pay_entity = WithdrawBankCard(
  355. accountCode = self.payee.withdraw_open_id,
  356. accountName = self.payee.withdraw_wechat_real_name,
  357. bankName = u'微信',
  358. branchBankName = u'微信企业付款')
  359. wechat_withdraw_gateway = withdraw_gateway_list['wechatV3'] or withdraw_gateway_list['wechat']
  360. self.record = self.payee.new_withdraw_record(
  361. withdraw_gateway = wechat_withdraw_gateway,
  362. pay_entity = pay_entity,
  363. source_key = source_key,
  364. income_type = self.income_type,
  365. amount = self.amount,
  366. pay_type = self.pay_type,
  367. manual = False,
  368. recurrent = recurrent)
  369. if not self.record:
  370. raise ServiceException({'result': 0, 'description': u'系统繁忙,请稍后再试', 'payload': {}})
  371. handler = self.payee.new_withdraw_handler(self.record)
  372. updated = self.payee.freeze_balance(self.record.incomeType,
  373. self.record.amount,
  374. self.record.source_key,
  375. self.record.order,
  376. )
  377. if not updated:
  378. handler.revoke(remarks = u'扣款失败', description = u'扣款失败')
  379. raise ServiceException({'result': 0, 'description': u'扣款失败', 'payload': {}})
  380. withdraw_result = withdraw_via_wechat(
  381. gateway = wechat_withdraw_gateway,
  382. record = self.record,
  383. payOpenId = self.payee.withdraw_open_id,
  384. real_user_name = self.record.cardUserName) # type: WithdrawResult
  385. logger.debug(
  386. 'withdraw via wechat. record = %s; result = %s' % (
  387. self.record.order, repr(withdraw_result)))
  388. if withdraw_result.result is True:
  389. handler.approve(finishedTime=datetime.datetime.now())
  390. return {'result': 1, 'description': withdraw_result.show_message,
  391. 'payload': {'paymentId': str(self.record.id)}}
  392. else:
  393. if withdraw_result.refund:
  394. handler.revoke(remarks = withdraw_result.message,
  395. description = withdraw_result.show_message)
  396. else:
  397. handler.fail(remarks = withdraw_result.message,
  398. description = withdraw_result.show_message)
  399. raise ServiceException(
  400. {
  401. 'result': withdraw_result.err_code,
  402. 'description': withdraw_result.show_message,
  403. 'payload': {}
  404. })
  405. elif self.pay_type == WITHDRAW_PAY_TYPE.BANK:
  406. if not self.bank_card_no:
  407. raise ServiceException({'result': 0, 'description': u'银行卡号参数错误', 'payload': {}})
  408. bank_card = self.payee.withdraw_bank_card(self.bank_card_no)
  409. if not bank_card:
  410. raise ServiceException({'result': 0, 'description': u'银行卡不存在', 'payload': {}})
  411. manual = False
  412. withdraw_gateway = withdraw_gateway_list['wechat']
  413. if bank_card.manual:
  414. manual = True
  415. elif bank_card.accountType == WithdrawBankCard.AccountType.PUBLIC:
  416. if withdraw_gateway_list['alipay']:
  417. withdraw_gateway = withdraw_gateway_list['alipay'] # type: WithdrawGateway
  418. if not WithdrawBanks.support(bank_card.bankName):
  419. raise ServiceException({
  420. 'result': 0,
  421. 'description': u'不支持提现到此银行卡或者银行名称错误, 请联系平台客服(1001)',
  422. 'payload': {}})
  423. else:
  424. manual = True
  425. else:
  426. # if withdraw_gateway_list['alipay'] and self.payee.supports('withdraw_alipay'):
  427. if withdraw_gateway_list['alipay']:
  428. withdraw_gateway = withdraw_gateway_list['alipay'] # type: WithdrawGateway
  429. if not WithdrawBanks.support(bank_card.bankName):
  430. raise ServiceException({
  431. 'result': 0,
  432. 'description': u'不支持提现到此银行卡或者银行名称错误, 请联系平台客服(1002)',
  433. 'payload': {}})
  434. else:
  435. if withdraw_gateway_list['wechat'].manual_withdraw:
  436. manual = True
  437. else:
  438. wechat_bank_code = WithdrawBanks.get_wechat_bank_code(bank_card.bankName)
  439. if not wechat_bank_code:
  440. raise ServiceException({
  441. 'result': 0,
  442. 'description': u'不支持提现到此银行卡或者银行名称错误, 请联系平台客服(1002)',
  443. 'payload': {}})
  444. self.record = self.payee.new_withdraw_record(
  445. withdraw_gateway = withdraw_gateway,
  446. pay_entity = bank_card,
  447. source_key = source_key,
  448. income_type = self.income_type,
  449. amount = self.amount,
  450. pay_type = self.pay_type,
  451. manual = manual,
  452. recurrent = recurrent) # type: WithdrawRecord
  453. if not self.record:
  454. raise ServiceException({'result': 0, 'description': u'系统繁忙,请稍后再试(1002)', 'payload': {}})
  455. handler = self.payee.new_withdraw_handler(self.record)
  456. updated = self.payee.freeze_balance(
  457. self.record.incomeType,
  458. self.record.amount,
  459. self.record.source_key,
  460. self.record.order
  461. )
  462. if not updated:
  463. handler.revoke(remarks = u'扣款失败', description = u'扣款失败')
  464. raise ServiceException({'result': 0, 'description': u'扣款失败', 'payload': {}})
  465. # 对公的提现, 或者不支持的银行卡
  466. if manual:
  467. withdraw_result = WithdrawResult(True, 1, False, u'提现申请已经受理', u'提现申请已经受理')
  468. logger.debug(
  469. 'withdraw via bank(manual). record = %s; result = %s' % (
  470. self.record.order, repr(withdraw_result)))
  471. handler.processing(remarks = u'提现申请已经受理', description = u'提现申请已经受理')
  472. return {
  473. 'result': withdraw_result.err_code,
  474. 'description': withdraw_result.show_message,
  475. 'payload': {'paymentId': str(self.record.id)}}
  476. else:
  477. gateway_type, withdraw_result = withdraw_via_bank(withdraw_gateway, self.record, bank_card)
  478. logger.debug(
  479. 'withdraw via bank({}). record = {}; result = {}'.format(
  480. gateway_type, self.record.order, repr(withdraw_result)))
  481. if withdraw_result.result is True:
  482. handler.processing(remarks = u'提现申请已经受理', description = u'提现申请已经受理')
  483. return {
  484. 'result': 1,
  485. 'description': u'提现申请已经受理',
  486. 'payload': {
  487. 'paymentId': str(self.record.id)
  488. }
  489. }
  490. else:
  491. if withdraw_result.refund:
  492. handler.revoke(remarks = withdraw_result.message,
  493. description = withdraw_result.show_message)
  494. else:
  495. handler.fail(remarks = withdraw_result.message,
  496. description = withdraw_result.show_message)
  497. raise ServiceException(
  498. {'result': withdraw_result.err_code, 'description': withdraw_result.show_message,
  499. 'payload': {}})
  500. elif self.pay_type == WITHDRAW_PAY_TYPE.ALIPAY:
  501. pay_entity = WithdrawBankCard(
  502. accountCode = self.payee.withdraw_alipay_login_id,
  503. accountName = self.payee.withdraw_alipay_real_name,
  504. bankName = u'支付宝',
  505. branchBankName = u'支付宝企业付款')
  506. if not pay_entity.accountCode:
  507. raise ServiceException({
  508. 'result': 0,
  509. 'description': u'不支持提现到支付宝账号或者是配置错误(1001)',
  510. 'payload': {}})
  511. withdraw_gateway = withdraw_gateway_list['alipay'] # type: WithdrawGatewayT
  512. if not withdraw_gateway:
  513. raise ServiceException({
  514. 'result': 0,
  515. 'description': u'不支持提现到支付宝账号或者是配置错误(1002)',
  516. 'payload': {}})
  517. self.record = self.payee.new_withdraw_record(
  518. withdraw_gateway = withdraw_gateway,
  519. pay_entity = pay_entity,
  520. source_key = source_key,
  521. income_type = self.income_type,
  522. amount = self.amount,
  523. pay_type = self.pay_type,
  524. manual = False,
  525. recurrent = recurrent) # type: WithdrawRecord
  526. if not self.record:
  527. raise ServiceException({'result': 0, 'description': u'系统繁忙,请稍后再试(1002)', 'payload': {}})
  528. handler = self.payee.new_withdraw_handler(self.record)
  529. updated = self.payee.freeze_balance(
  530. self.record.incomeType,
  531. self.record.amount,
  532. self.record.source_key,
  533. self.record.order,
  534. )
  535. if not updated:
  536. handler.revoke(remarks = u'扣款失败', description = u'扣款失败')
  537. raise ServiceException({'result': 0, 'description': u'扣款失败', 'payload': {}})
  538. withdraw_result = AlipayWithdraw.withdraw_via_changes_in_ali(withdraw_gateway, self.record)
  539. logger.debug(
  540. 'withdraw via alipay. record = %s; result = %s' % (
  541. self.record.order, repr(withdraw_result)))
  542. if withdraw_result.result is True:
  543. finished_time = arrow.get(withdraw_result.callResult['trans_date'], 'YYYY-MM-DD HH:mm:ss',
  544. tzinfo = settings.TIME_ZONE).naive
  545. handler.approve(finishedTime = finished_time, extra = {
  546. 'order_id': withdraw_result.callResult.get('order_id'),
  547. 'pay_fund_order_id': withdraw_result.callResult.get('pay_fund_order_id'),
  548. })
  549. return {
  550. 'result': 1,
  551. 'description': u'提现申请已经受理',
  552. 'payload': {
  553. 'paymentId': str(self.record.id)
  554. }
  555. }
  556. else:
  557. if withdraw_result.refund:
  558. handler.revoke(remarks = withdraw_result.message,
  559. description = withdraw_result.show_message)
  560. else:
  561. handler.fail(remarks = withdraw_result.message,
  562. description = withdraw_result.show_message)
  563. raise ServiceException(
  564. {'result': withdraw_result.err_code, 'description': withdraw_result.show_message,
  565. 'payload': {}})
  566. raise ServiceException({'result': 0, 'description': u'系统错误,请稍后再试', 'payload': {}})
  567. else:
  568. raise ServiceException({'result': 0, 'description': u'操作频繁,请稍后再试', 'payload': {}})
  569. except ServiceException as e:
  570. logger.exception(e)
  571. if self.record:
  572. if 'payload' in e.result:
  573. e.result['payload'].update({'paymentId': str(self.record.id)})
  574. else:
  575. e.result['payload'] = {'paymentId': str(self.record.id)}
  576. return e.result
  577. except WithdrawError as e:
  578. logger.exception(e)
  579. return {
  580. 'result': 0,
  581. 'description': e.message,
  582. 'payload': {'paymentId': str(self.record.id)} if str(self.record.id) else {}
  583. }
  584. class WithdrawRetryService(object):
  585. def __init__(self, record, only_fail = True):
  586. # type: (WithdrawRecord, bool)->None
  587. self.record = record # type : WithdrawRecord
  588. self.only_fail = only_fail
  589. def get_payee(self):
  590. return ROLE.from_role_id(self.record.role, self.record.ownerId)
  591. def check_retry_over(self, handler):
  592. end = self.record.postTime + datetime.timedelta(days = 3) # type: datetime
  593. now = datetime.datetime.now()
  594. if now.year > end.year or now.month > end.month or now.day > end.day:
  595. handler.revoke(remarks = u'重试次数超限,平台退单', description = u'提现失败')
  596. raise ServiceException(
  597. {
  598. 'result': 0,
  599. 'description': u'重试次数超限,平台退单',
  600. 'payload': {}
  601. })
  602. def execute(self):
  603. try:
  604. if self.record.refunded:
  605. raise ServiceException(
  606. {
  607. 'result': 0,
  608. 'description': 'record<id={}> has refunded.'.format(str(self.record.id)),
  609. 'payload': {}
  610. })
  611. if self.record.manual:
  612. raise ServiceException(
  613. {
  614. 'result': 0,
  615. 'description': 'record<id={}> can not be manual.'.format(str(self.record.id)),
  616. 'payload': {}
  617. })
  618. if self.only_fail:
  619. if self.record.status != WithdrawStatus.FAILED:
  620. raise ServiceException(
  621. {
  622. 'result': 0,
  623. 'description': 'record<id={}> must be fail.'.format(str(self.record.id)),
  624. 'payload': {}
  625. })
  626. else:
  627. if self.record.status not in [WithdrawStatus.SUBMITTED, WithdrawStatus.FAILED]:
  628. raise ServiceException(
  629. {
  630. 'result': 0,
  631. 'description': 'record<id={}> must be fail.'.format(str(self.record.id)),
  632. 'payload': {}
  633. })
  634. payee = self.get_payee() # type: CapitalUser
  635. if payee.role != self.record.role:
  636. raise ServiceException(
  637. {
  638. 'result': 0,
  639. 'description': 'role is not same',
  640. 'payload': {}
  641. })
  642. withdraw_gateway = WithdrawGateway.from_withdraw_gateway_key(
  643. self.record.withdrawGatewayKey,
  644. self.record.extras.get('gateway_version', 'v1')) # type: WithdrawGateway
  645. handler = payee.new_withdraw_handler(self.record)
  646. if self.record.payType == WITHDRAW_PAY_TYPE.WECHAT:
  647. try:
  648. query_result = withdraw_gateway.get_transfer_result_via_changes(
  649. self.record.order) # type: WechatWithdrawQueryResult
  650. errcode, errmsg = query_result.error_desc
  651. if query_result.is_failed:
  652. handler.revoke(remarks = errcode,
  653. description = errmsg)
  654. return {
  655. 'result': 1,
  656. 'description': errmsg,
  657. 'payload': {
  658. 'paymentId': str(self.record.id)
  659. }
  660. }
  661. if query_result.is_successful:
  662. handler.approve()
  663. return {
  664. 'result': 1,
  665. 'description': 'SUCCESS',
  666. 'payload': {
  667. 'paymentId': str(self.record.id)
  668. }
  669. }
  670. if query_result.is_processing:
  671. pass
  672. else:
  673. raise ServiceException(
  674. {
  675. 'result': 0,
  676. 'description': u'未知订单状态{}'.format(query_result.order_status),
  677. 'payload': {}
  678. })
  679. except WithdrawOrderNotExist:
  680. logger.warning('withdraw order<orderNo={}> is not exist.'.format(self.record.order))
  681. self.check_retry_over(handler)
  682. payee.freeze_balance(self.record.incomeType,
  683. self.record.amount,
  684. self.record.source_key,
  685. self.record.order,
  686. self.record.is_new_version)
  687. withdraw_result = withdraw_via_wechat(withdraw_gateway,
  688. self.record,
  689. self.record.accountCode,
  690. self.record.cardUserName) # type: WithdrawResult
  691. logger.debug(
  692. 'withdraw via wechat. record = %s; result = %s' % (
  693. self.record.order, repr(withdraw_result)))
  694. if withdraw_result.result is True:
  695. handler.approve()
  696. return {
  697. 'result': 1,
  698. 'description': withdraw_result.show_message,
  699. 'payload': {
  700. 'paymentId': str(self.record.id)
  701. }
  702. }
  703. else:
  704. raise ServiceException(
  705. {
  706. 'result': withdraw_result.err_code,
  707. 'description': withdraw_result.show_message,
  708. 'payload': {}
  709. })
  710. elif self.record.payType == WITHDRAW_PAY_TYPE.BANK:
  711. try:
  712. query_result = withdraw_gateway.get_transfer_result_via_bank(
  713. self.record.order) # type: Union[WechatWithdrawQueryResult, AlipayWithdrawQueryResult]
  714. errcode, errmsg = query_result.error_desc
  715. if query_result.is_failed or query_result.is_refund:
  716. handler.revoke(remarks = errcode, description = errmsg)
  717. return {
  718. 'result': 1,
  719. 'description': errmsg,
  720. 'payload': {
  721. 'paymentId': str(self.record.id)
  722. }
  723. }
  724. if query_result.is_successful:
  725. handler.approve(finishedTime = query_result.finished_time, extra = query_result.extra)
  726. return {
  727. 'result': 1,
  728. 'description': 'SUCCESS',
  729. 'payload': {
  730. 'paymentId': str(self.record.id)
  731. }
  732. }
  733. if query_result.is_processing:
  734. pass
  735. else:
  736. raise ServiceException(
  737. {
  738. 'result': 0,
  739. 'description': u'未知订单状态{}'.format(query_result.order_status),
  740. 'payload': {}
  741. })
  742. except WithdrawOrderNotExist:
  743. logger.warning('withdraw order<orderNo={}> is not exist.'.format(self.record.order))
  744. self.check_retry_over(handler)
  745. bank_card = payee.withdraw_bank_card(self.record.accountCode)
  746. if not bank_card:
  747. raise ServiceException(
  748. {
  749. 'result': 0,
  750. 'description': '银行卡不存在',
  751. 'payload': {}
  752. })
  753. payee.freeze_balance(self.record.incomeType,
  754. self.record.amount,
  755. self.record.source_key,
  756. self.record.order,
  757. )
  758. gateway_type, withdraw_result = withdraw_via_bank(withdraw_gateway, self.record, bank_card)
  759. logger.debug(
  760. 'withdraw via bank({}). record = {}; result = {}'.format(
  761. gateway_type, self.record.order, repr(withdraw_result)))
  762. if withdraw_result.result is True:
  763. handler.processing(remarks=withdraw_result.message, description=withdraw_result.show_message)
  764. return {
  765. 'result': withdraw_result.err_code,
  766. 'description': withdraw_result.show_message,
  767. 'payload': {
  768. 'paymentId': str(self.record.id)
  769. }
  770. }
  771. else:
  772. raise ServiceException(
  773. {
  774. 'result': withdraw_result.err_code,
  775. 'description': withdraw_result.show_message,
  776. 'payload': {}
  777. })
  778. elif self.record.payType == WITHDRAW_PAY_TYPE.ALIPAY:
  779. try:
  780. query_result = withdraw_gateway.get_transfer_result_via_changes(
  781. self.record.order) # type: AlipayWithdrawQueryResult
  782. errcode, errmsg = query_result.error_desc
  783. if query_result.is_failed:
  784. handler.revoke(remarks = errcode, description = errmsg)
  785. return {
  786. 'result': 1,
  787. 'description': errmsg,
  788. 'payload': {
  789. 'paymentId': str(self.record.id)
  790. }
  791. }
  792. if query_result.is_successful:
  793. handler.approve(finishedTime = query_result.finished_time, extra = query_result.extra)
  794. return {
  795. 'result': 1,
  796. 'description': 'SUCCESS',
  797. 'payload': {
  798. 'paymentId': str(self.record.id)
  799. }
  800. }
  801. if query_result.is_processing:
  802. pass
  803. else:
  804. raise ServiceException(
  805. {
  806. 'result': 0,
  807. 'description': u'未知订单状态{}'.format(query_result.order_status),
  808. 'payload': {}
  809. })
  810. except WithdrawOrderNotExist:
  811. logger.warning('withdraw order<orderNo={}> is not exist.'.format(self.record.order))
  812. self.check_retry_over(handler)
  813. updated = payee.freeze_balance(self.record.incomeType,
  814. self.record.amount,
  815. self.record.source_key,
  816. self.record.order,
  817. )
  818. if not updated:
  819. handler.revoke(remarks = u'扣款失败', description = u'扣款失败')
  820. raise ServiceException({'result': 0, 'description': u'扣款失败', 'payload': {}})
  821. withdraw_result = AlipayWithdraw.withdraw_via_changes_in_ali(withdraw_gateway, self.record)
  822. logger.debug(
  823. 'withdraw via alipay. record = %s; result = %s' % (
  824. self.record.order, repr(withdraw_result)))
  825. if withdraw_result.result is True:
  826. finished_time = arrow.get(withdraw_result.callResult['trans_date'], 'YYYY-MM-DD HH:mm:ss',
  827. tzinfo = settings.TIME_ZONE).naive
  828. handler.approve(finishedTime = finished_time, extra = {
  829. 'order_id': withdraw_result.callResult.get('order_id'),
  830. 'pay_fund_order_id': withdraw_result.callResult.get('pay_fund_order_id'),
  831. })
  832. return {
  833. 'result': 1,
  834. 'description': u'提现申请已经受理',
  835. 'payload': {
  836. 'paymentId': str(self.record.id)
  837. }
  838. }
  839. else:
  840. if withdraw_result.refund:
  841. handler.revoke(remarks = withdraw_result.message,
  842. description = withdraw_result.show_message)
  843. else:
  844. handler.fail(remarks = withdraw_result.message,
  845. description = withdraw_result.show_message)
  846. raise ServiceException(
  847. {
  848. 'result': withdraw_result.err_code,
  849. 'description': withdraw_result.show_message,
  850. 'payload': {}
  851. })
  852. else:
  853. logger.error('invalid withdraw type: {}'.format(self.record.payType))
  854. except WechatNetworkException as e:
  855. logger.exception(e)
  856. return {'result': 0, 'description': e.errMsg, 'payload': {}}
  857. except WeChatPayException as e:
  858. logger.exception(e)
  859. return {'result': 0, 'description': e.errMsg, 'payload': {}}
  860. except ServiceException as e:
  861. return e.result
  862. except WithdrawError as e:
  863. logger.exception(e)
  864. return {'result': 0, 'description': e.message, 'payload': {}}
  865. except Exception as e:
  866. logger.exception(e)
  867. return {'result': 0, 'description': e.message, 'payload': {}}