wechat.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import xmltodict
  5. from mongoengine import DoesNotExist
  6. from typing import TYPE_CHECKING, Optional
  7. from apps.web.common.transaction.refund import RefundNotifier, RefundPuller
  8. from apps.web.core.models import WechatPayApp
  9. from library.wechatbase.exceptions import WeChatPayException
  10. from library.wechatpy.pay import WeChatPay
  11. from apps.web.user.models import RefundMoneyRecord, RechargeRecord
  12. if TYPE_CHECKING:
  13. from django.core.handlers.wsgi import WSGIRequest
  14. from apps.web.core.payment import PaymentGatewayT
  15. from apps.web.common.transaction.pay import RechargeRecordT, RefundRecordT
  16. class WechatRefundNotifier(RefundNotifier):
  17. def parse_request(self, request): # type:(WSGIRequest) -> dict
  18. payload = xmltodict.parse(request.body)['xml'] # type: dict
  19. # xml 解析之后获取第一步的数据 找到wechat app
  20. app = WechatPayApp.objects.get(appid = payload["appid"], mchid = payload['mch_id']) # type: WechatPayApp
  21. req_info = WeChatPay(appid = app.appid, api_key = app.apikey, mch_id = app.mchid).decrypt(payload["req_info"])
  22. # 所有信息打包成一个字典返回
  23. payload.update(req_info)
  24. return payload
  25. def verify_payload(self, payload): # type:(dict) -> bool
  26. """ 忽略对签名串的校验 """
  27. return True
  28. def handle_refund_order(self, refundOrder, post_pay): # type:(RefundRecordT, callable) -> None
  29. if not self.payload:
  30. refundOrder.fail(errorDesc = u'通知参数错误')
  31. return
  32. if self.payload["return_code"] != "SUCCESS":
  33. refundOrder.fail(errorDesc = self.payload["return_msg"])
  34. return
  35. if self.payload["refund_status"] == "SUCCESS":
  36. payFinishTime = self.payload["success_time"]
  37. datetimeRefund = datetime.datetime.strptime(payFinishTime, "%Y-%m-%d %H:%M:%S")
  38. matched = refundOrder.succeed(tradeRefundNo = self.payload["refund_id"], finishedTime = datetimeRefund)
  39. if not matched:
  40. return
  41. return post_pay(refundOrder, datetimeRefund)
  42. if self.payload["refund_status"] == "REFUNDCLOSE":
  43. refundOrder.closed(tradeRefundNo=self.payload["refund_id"], errorDesc = self.payload["return_msg"])
  44. return
  45. refundOrder.fail(errorDesc = self.payload["return_msg"])
  46. @property
  47. def refund_order_no(self): # type:() -> str
  48. return self.payload['out_refund_no']
  49. @property
  50. def errorResponse(self): # type:() -> str
  51. from apps.web.core.payment.wechat import WechatPaymentGateway
  52. return WechatPaymentGateway.reply("", False)
  53. @property
  54. def successResponse(self): # type:() -> str
  55. from apps.web.core.payment.wechat import WechatPaymentGateway
  56. return WechatPaymentGateway.reply("", True)
  57. class WechatRefundPuller(RefundPuller):
  58. def pull(self, payGateWay, payOrder, post_pay): # type:(PaymentGatewayT, RechargeRecordT, callable) -> None
  59. try:
  60. result = payGateWay.api_refund_query(out_refund_no = self._refundOrder.orderNo)
  61. except WeChatPayException:
  62. # 接口性质的失败 不做任何处理
  63. return
  64. # 找出退款单号
  65. offset = None
  66. for _k, _v in result.items():
  67. if _v == self._refundOrder.orderNo:
  68. offset = _k.rsplit("_", 1)[1]
  69. if not offset:
  70. return
  71. refundStatus = result["refund_status_{}".format(offset)]
  72. if refundStatus == "SUCCESS":
  73. refundTime = result["refund_success_time_{}".format(offset)]
  74. datetimeRefund = datetime.datetime.strptime(refundTime, "%Y-%m-%d %H:%M:%S")
  75. matched = self._refundOrder.succeed(result["refund_id_{}".format(offset)], finishedTime = datetimeRefund)
  76. if not matched:
  77. return
  78. post_pay(self._refundOrder, datetimeRefund)
  79. elif refundStatus == "REFUNDCLOSE":
  80. self._refundOrder.closed(
  81. result["refund_id_{}".format(offset)],
  82. result["err_code"], result["err_code_des"]
  83. )
  84. elif refundStatus == "PROCESSING":
  85. if not (self._refundOrder.is_processing or self._refundOrder.is_closed or self._refundOrder.is_success):
  86. self._refundOrder.processing()
  87. elif refundStatus == "CHANGE":
  88. self._refundOrder.fail(result["err_code"], result["err_code_des"])
  89. else:
  90. pass
  91. return