123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import xmltodict
- from typing import TYPE_CHECKING
- from apps.web.common.transaction.refund import RefundNotifier, RefundPuller
- from apps.web.core.models import WechatPayApp
- from library.wechatbase.exceptions import WeChatException, WechatNetworkException
- from library.wechatpy.pay import WeChatPay
- logger = logging.getLogger(__name__)
- if TYPE_CHECKING:
- from django.core.handlers.wsgi import WSGIRequest
- from apps.web.core.payment.type_checking import PaymentGatewayT
- from apps.web.common.transaction.pay import RefundRecordT
- class WechatRefundNotifier(RefundNotifier):
- def parse_request(self, request): # type:(WSGIRequest) -> dict
- payload = xmltodict.parse(request.body)['xml'] # type: dict
- # xml 解析之后获取第一步的数据 找到wechat app
- app = WechatPayApp.objects.get(appid = payload["appid"], mchid = payload['mch_id']) # type: WechatPayApp
- req_info = WeChatPay(appid = app.appid, api_key = app.apikey, mch_id = app.mchid).decrypt(payload["req_info"])
- # 所有信息打包成一个字典返回
- payload.update(req_info)
- return payload
- def verify_payload(self, payload): # type:(dict) -> bool
- """ 忽略对签名串的校验 """
- return True
- def handle_refund_order(self, refundOrder, refund_post_callable): # type:(RefundRecordT, callable) -> None
- if not self.payload or self.payload["return_code"] != "SUCCESS":
- return
- if self.payload["refund_status"] == "SUCCESS":
- payFinishTime = self.payload["success_time"]
- datetimeRefund = datetime.datetime.strptime(payFinishTime, "%Y-%m-%d %H:%M:%S")
- matched = refundOrder.succeed(finishedTime = datetimeRefund, **{
- 'tradeRefundNo': self.payload["refund_id"]
- })
- if not matched:
- return
- return refund_post_callable(refundOrder, True)
- elif self.payload["refund_status"] == "REFUNDCLOSE":
- refundOrder.fail(
- errorCode = 'REFUNDCLOSE',
- errorDesc = self.payload["return_msg"],
- **{
- 'tradeRefundNo': self.payload.get("refund_id")
- })
- return
- elif self.payload["refund_status"] == "CHANGE":
- refundOrder.fail(
- errorCode = 'CHANGE',
- errorDesc = u'退款异常',
- **{
- 'tradeRefundNo': self.payload.get("refund_id")
- })
- else:
- pass
- @property
- def refund_order_filter(self): # type:() -> dict
- return {'orderNo': self.payload["out_refund_no"]}
- @property
- def errorResponse(self): # type:() -> str
- from apps.web.core.payment.wechat import WechatPaymentGateway
- return WechatPaymentGateway.reply("", False)
- @property
- def successResponse(self): # type:() -> str
- from apps.web.core.payment.wechat import WechatPaymentGateway
- return WechatPaymentGateway.reply("", True)
- class WechatRefundPuller(RefundPuller):
- def parse_error(self, errorCode, errorDesc, refund_post_callable):
- if errorCode == 'REFUNDNOTEXIST':
- self._refundOrder.no_order(errorCode = errorCode, errorDesc = errorDesc)
- elif errorCode in ['TRADE_OVERDUE', 'USER_ACCOUNT_ABNORMAL']:
- # USER_ACCOUNT_ABNORMAL: 用户账户异常或已注销,不能原路退回,请使用其他方式进行退款。
- # TRADE_OVERDUE: 超期订单无法退款
- matched = self._refundOrder.closed(errorCode = errorCode, errorDesc = errorDesc)
- if matched:
- refund_post_callable(self._refundOrder, False)
- return True
- else:
- if errorCode in ['NOTENOUGH']:
- # NOTENOUGH: 基本账户余额不足,请充值后重新发起
- self._refundOrder.fail(errorCode = errorCode, errorDesc = errorDesc)
- else:
- # 其他暂时不处理,逐步补充
- logger.warning('RefundOrder<orderNo={}>, errorCode = {}, errorDesc = {}'.format(
- self._refundOrder.orderNo, errorCode, errorDesc))
- return True
- return False
- def pull(self, refund_post_callable, **kwargs): # type:(callable, dict) -> bool
- payGateway = self._refundOrder.my_payment_gateway # type: PaymentGatewayT
- try:
- result = payGateway.api_refund_query(out_refund_no = self._refundOrder.orderNo)
- except WechatNetworkException as e:
- # return_code不为SUCCESS的情况下
- raise e
- except WeChatException as e:
- # result_code不为SUCCESS的情况下, 抛出异常
- return self.parse_error(e.errCode, e.errMsg, refund_post_callable)
- else:
- # 找出退款单号
- offset = None
- for _k, _v in result.items():
- if _v == self._refundOrder.orderNo:
- offset = _k.rsplit("_", 1)[1]
- break
- if not offset:
- return False
- refundStatus = result["refund_status_{}".format(offset)]
- if refundStatus == "SUCCESS":
- refundTime = result["refund_success_time_{}".format(offset)]
- datetimeRefund = datetime.datetime.strptime(refundTime, "%Y-%m-%d %H:%M:%S")
- matched = self._refundOrder.succeed(
- finishedTime = datetimeRefund, tradeRefundNo = result["refund_id_{}".format(offset)])
- if matched:
- refund_post_callable(self._refundOrder, True)
- return True
- elif refundStatus == "REFUNDCLOSE":
- self._refundOrder.fail(
- errorCode = "REFUNDCLOSE",
- errorDesc = '{}({})'.format(result.get("err_code_des"), result.get("err_code")))
- elif refundStatus == "PROCESSING":
- if not (self._refundOrder.is_processing or self._refundOrder.is_closed or self._refundOrder.is_success):
- self._refundOrder.processing()
- return True
- elif refundStatus == "CHANGE":
- matched = self._refundOrder.closed(
- errorCode = 'CHANGE',
- errorDesc = '{}({})'.format(result.get("err_code_des"), result.get("err_code")),
- **{
- 'tradeRefundNo': result.get("refund_id_{}".format(offset))
- })
- if matched:
- refund_post_callable(self._refundOrder, False)
- return True
- else:
- pass
- return False
|