# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import xmltodict from mongoengine import DoesNotExist from typing import TYPE_CHECKING, Optional from apps.web.common.transaction.refund import RefundNotifier, RefundPuller from apps.web.core.models import WechatPayApp from library.wechatbase.exceptions import WeChatPayException from library.wechatpy.pay import WeChatPay from apps.web.user.models import RefundMoneyRecord, RechargeRecord if TYPE_CHECKING: from django.core.handlers.wsgi import WSGIRequest from apps.web.core.payment import PaymentGatewayT from apps.web.common.transaction.pay import RechargeRecordT, 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, post_pay): # type:(RefundRecordT, callable) -> None if not self.payload: refundOrder.fail(errorDesc = u'通知参数错误') return if self.payload["return_code"] != "SUCCESS": refundOrder.fail(errorDesc = self.payload["return_msg"]) 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(tradeRefundNo = self.payload["refund_id"], finishedTime = datetimeRefund) if not matched: return return post_pay(refundOrder, datetimeRefund) if self.payload["refund_status"] == "REFUNDCLOSE": refundOrder.closed(tradeRefundNo=self.payload["refund_id"], errorDesc = self.payload["return_msg"]) return refundOrder.fail(errorDesc = self.payload["return_msg"]) @property def refund_order_no(self): # type:() -> str return 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 pull(self, payGateWay, payOrder, post_pay): # type:(PaymentGatewayT, RechargeRecordT, callable) -> None try: result = payGateWay.api_refund_query(out_refund_no = self._refundOrder.orderNo) except WeChatPayException: # 接口性质的失败 不做任何处理 return # 找出退款单号 offset = None for _k, _v in result.items(): if _v == self._refundOrder.orderNo: offset = _k.rsplit("_", 1)[1] if not offset: return 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(result["refund_id_{}".format(offset)], finishedTime = datetimeRefund) if not matched: return post_pay(self._refundOrder, datetimeRefund) elif refundStatus == "REFUNDCLOSE": self._refundOrder.closed( result["refund_id_{}".format(offset)], result["err_code"], result["err_code_des"] ) elif refundStatus == "PROCESSING": if not (self._refundOrder.is_processing or self._refundOrder.is_closed or self._refundOrder.is_success): self._refundOrder.processing() elif refundStatus == "CHANGE": self._refundOrder.fail(result["err_code"], result["err_code_des"]) else: pass return