ali.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. from typing import TYPE_CHECKING
  5. from apilib.utils_string import cn
  6. from apps.web.user.conf import PAY_NOTIFY_URL
  7. from apps.web.utils import testcase_point
  8. from library.alipay import AliPayServiceException
  9. from apps.web.core import AlipayMixin
  10. from library.alipay import AliPayGatewayException, AliErrorCode, AliException
  11. from apilib.monetary import quantize
  12. from apilib.monetary import RMB
  13. from apps.web.core.payment.base import PaymentGateway, WithdrawGateway
  14. from apps.web.core.models import BankCard
  15. logger = logging.getLogger(__name__)
  16. if TYPE_CHECKING:
  17. from apps.web.core.models import AliApp
  18. class AliPayGateway(PaymentGateway, AlipayMixin):
  19. """
  20. Alipay 支付网关,扩展原库没有的接口 ``alipay.trade.create``
  21. """
  22. @property
  23. def notifyUrl(self):
  24. return PAY_NOTIFY_URL.ALI_PAY_BACK
  25. def __init__(self, app):
  26. # type: (AliApp)->None
  27. super(AliPayGateway, self).__init__(app)
  28. def __repr__(self):
  29. return '<AliPayPaymentGateway(appid=%s, debug=%s)>' % (self.appid, self.debug)
  30. def api_trade_query(self, out_trade_no = None, trade_no = None):
  31. assert out_trade_no or trade_no, 'must input out_trade_no or trade_no'
  32. result = self.client.api_alipay_trade_query(out_trade_no = out_trade_no, trade_no = trade_no)
  33. if result['code'] == u'10000':
  34. return result
  35. elif result['code'] == u'40004':
  36. raise AliPayServiceException(errCode = result['sub_code'], errMsg = result['sub_msg'], client = self)
  37. else:
  38. raise AliPayGatewayException(errCode = result['sub_code'], errMsg = result['sub_msg'], client = self)
  39. def unified_order(self, out_trade_no, openId, money, subject, notifyUrl, **kwargs):
  40. total_amount = quantize(money.amount)
  41. if total_amount != money.amount:
  42. raise AliException(
  43. errCode=AliErrorCode.MY_INVALID_PARAMETER,
  44. errMsg=u'无效的交易金额',
  45. client=self.client
  46. )
  47. extras = {
  48. "buyer_id": openId,
  49. "timeout_express": kwargs.pop("timeout_express", '2m'),
  50. "body": kwargs.pop('body', '')
  51. }
  52. kwargs.update(extras)
  53. return self.client.api_alipay_trade_create(
  54. out_trade_no=out_trade_no, total_amount=str(total_amount), subject=subject, notify_url=notifyUrl, **kwargs
  55. )
  56. def alipay_trade_precreate(self, out_trade_no, money, subject, buyer_id, timeout_express = '2m', body = '',
  57. **kwargs):
  58. """
  59. 当面付的预下单
  60. :param out_trade_no:
  61. :param money:
  62. :param subject:
  63. :param buyer_id:
  64. :param timeout_express:
  65. :param body:
  66. :param kwargs:
  67. :return:
  68. """
  69. total_amount = str(quantize(money.amount, places = '0.01'))
  70. return self.client.api_alipay_trade_precreate(out_trade_no = out_trade_no,
  71. total_amount = total_amount,
  72. subject = subject,
  73. **{"buyer_id": buyer_id,
  74. "timeout_express": timeout_express,
  75. "body": body})
  76. def api_alipay_trade_create(self, out_trade_no,
  77. money,
  78. subject,
  79. buyer_id,
  80. notify_url,
  81. timeout_express = '2m',
  82. body = '',
  83. **kwargs):
  84. # type:(str, RMB, basestring, str, str, str, str, dict)->dict
  85. """
  86. 手机扫码创建订单
  87. :param body:
  88. :param timeout_express: 订单关闭超时时间
  89. :param out_trade_no: 因需要保存到rechargeRecord里,所以需要在方法外生成
  90. :param money: 交易数额 [0.01,100000000]
  91. :param subject: 标题: 支付宝充值1元
  92. :param buyer_id: 正在扫码准备充值的用户
  93. :param kwargs:
  94. :return:
  95. """
  96. total_amount = quantize(money.amount, places = '0.01')
  97. if total_amount != money.amount:
  98. raise AliException(
  99. errCode = AliErrorCode.MY_INVALID_PARAMETER,
  100. errMsg = u'无效的交易金额',
  101. client = self.client)
  102. extras = {
  103. "buyer_id": buyer_id,
  104. "timeout_express": timeout_express,
  105. "body": body
  106. }
  107. extras.update(**kwargs)
  108. logger.debug('alipay kwargs = {}'.format(extras))
  109. logger.debug('alipay kwargs2,out_trade_no=%s,total_amount=%s,subject=%s,notify_url=%s' % (
  110. out_trade_no, str(total_amount), subject, notify_url))
  111. return self.client.api_alipay_trade_create(out_trade_no = out_trade_no,
  112. total_amount = str(total_amount),
  113. subject = subject,
  114. notify_url = notify_url,
  115. **extras)
  116. @testcase_point()
  117. def refund_to_user(self, out_trade_no, out_refund_no, refund_fee, total_fee, refund_reason, **kwargs):
  118. """
  119. :param out_trade_no:
  120. :param out_refund_no:
  121. :param refund_fee:
  122. :param total_fee:
  123. :param refund_reason:
  124. :return:
  125. """
  126. return self.client.api_alipay_trade_refund(out_trade_no = out_trade_no,
  127. out_request_no = out_refund_no,
  128. refund_amount = str(refund_fee),
  129. refund_reason = refund_reason)
  130. def download_bill(self, bill_type = 'trade', bill_date = None):
  131. """
  132. 下载支付宝订单用于对账
  133. """
  134. return self.client.api_alipay_data_dataservice_bill_downloadurl_query(bill_type=bill_type, bill_date=bill_date)
  135. def api_refund_query(self, trade_no, out_trade_no, out_request_no):
  136. return self.client.api_alipay_trade_refund_order_query(trade_no, out_trade_no, out_request_no, ["gmt_refund_pay"])
  137. class AliPayWithdrawGateway(WithdrawGateway, AlipayMixin):
  138. def __init__(self, app, is_ledger = True):
  139. # type: (AliApp, bool)->None
  140. super(AliPayWithdrawGateway, self).__init__(app, is_ledger)
  141. def __repr__(self):
  142. return '<AliPayWithdrawGateway(appid=%s, debug=%s)>' % (self.appid, self.debug)
  143. def withdraw_via_bank(self, order_no, total_amount, bank_card, order_title = u'提现到银行卡'):
  144. # type:(str, RMB, BankCard, str)->dict
  145. """
  146. 经销商提现通过银行卡提现
  147. .. 参考文档 https://opendocs.alipay.com/open/common/transfertocard
  148. :param total_amount:
  149. :param order_no: 商户企业付款单号(本平台订单号) len(order_no) (- [8-32]
  150. :return:
  151. """
  152. payee_info = {
  153. 'identity': bank_card.cardNo,
  154. 'identity_type': 'BANKCARD_ACCOUNT',
  155. 'name': cn(bank_card.holderName)
  156. }
  157. if bank_card.accountType == BankCard.AccountType.PUBLIC:
  158. payee_info['bankcard_ext_info'] = {
  159. 'inst_name': cn(bank_card.bankName),
  160. 'account_type': 1
  161. }
  162. if bank_card.cnapsCode:
  163. payee_info['bankcard_ext_info'].update({
  164. 'bank_code': bank_card.cnapsCode
  165. })
  166. else:
  167. payee_info['bankcard_ext_info'].update({
  168. 'inst_province': cn(bank_card.province),
  169. 'inst_city': cn(bank_card.city),
  170. 'inst_branch_name': cn(bank_card.branchName)
  171. })
  172. else:
  173. payee_info['bankcard_ext_info'] = {
  174. 'account_type': 2
  175. }
  176. result = self.client.api_alipay_fund_trans_uni_transfer(
  177. out_biz_no = order_no, trans_amount = str(total_amount),
  178. payee_info = payee_info, order_title = order_title)
  179. if result['code'] == u'10000':
  180. return result
  181. elif result['code'] == u'40004':
  182. raise AliPayServiceException(errCode = result['sub_code'], errMsg = result['sub_msg'], client = self)
  183. else:
  184. raise AliPayGatewayException(errCode = result['sub_code'], errMsg = result['sub_msg'], client = self)
  185. def get_transfer_result_via_bank(self, order_no):
  186. """
  187. 查询银行卡提现的返回结果
  188. :return:
  189. """
  190. result = self.client.api_alipay_fund_trans_common_query(out_biz_no = order_no)
  191. if result['code'] == u'10000':
  192. return result
  193. elif result['code'] == u'40004':
  194. raise AliPayServiceException(errCode = result['sub_code'], errMsg = result['sub_msg'], client = self)
  195. else:
  196. raise AliPayGatewayException(errCode = result['sub_code'], errMsg = result['sub_msg'], client = self)