132 lines
6.3 KiB
Python
132 lines
6.3 KiB
Python
# -*- coding: utf-8 -*-
|
||
|
||
import json
|
||
import logging
|
||
import xml.etree.cElementTree as ET
|
||
from lxml import etree
|
||
import sys
|
||
from odoo.addons.wecom_api.api.wecom_msg_crtpt import WecomMsgCrypt # type: ignore
|
||
from odoo import http, models, fields, _
|
||
from odoo.http import request
|
||
from odoo.http import Response
|
||
|
||
_logger = logging.getLogger(__name__)
|
||
|
||
|
||
class StripeController(http.Controller):
|
||
""" """
|
||
|
||
@http.route(
|
||
["/wecom_callback", "/wecom_callback/"],
|
||
type="http",
|
||
auth="public",
|
||
methods=["GET", "POST"],
|
||
)
|
||
def WecomCallbackService(self): # type: ignore
|
||
"""
|
||
企业微信回调服务
|
||
"""
|
||
|
||
@http.route(
|
||
["/wecom_callback/<int:id>/<string:service>", "/wecom_callback/contacts"],
|
||
type="http",
|
||
auth="public",
|
||
methods=["GET", "POST"],
|
||
csrf=False,
|
||
)
|
||
def WecomCallbackService(self, service, id, **kw):
|
||
"""
|
||
企业微信回调服务
|
||
:param id: 公司id
|
||
:param service: 回调服务名称 code
|
||
文档URL: https://work.weixin.qq.com/api/doc/90000/90135/90930#3.2%20%E6%94%AF%E6%8C%81Http%20Post%E8%AF%B7%E6%B1%82%E6%8E%A5%E6%94%B6%E4%B8%9A%E5%8A%A1%E6%95%B0%E6%8D%AE
|
||
"""
|
||
company_id = request.env["res.company"].sudo().search([("id", "=", id)]) # type: ignore
|
||
sCorpID = company_id.corpid
|
||
|
||
callback_service = (
|
||
company_id.contacts_app_id.app_callback_service_ids.sudo().search(
|
||
[
|
||
("app_id", "=", company_id.contacts_app_id.id),
|
||
("code", "=", service),
|
||
"|",
|
||
("active", "=", True),
|
||
("active", "=", False),
|
||
]
|
||
)
|
||
)
|
||
if callback_service.active is False:
|
||
_logger.info(
|
||
_("App [%s] does not have service [%s] enabled")
|
||
% (company_id.contacts_app_id.name, callback_service.name)
|
||
)
|
||
return Response("success", status=200)
|
||
else:
|
||
wxcpt = WecomMsgCrypt(
|
||
callback_service.callback_url_token,
|
||
callback_service.callback_aeskey,
|
||
sCorpID,
|
||
)
|
||
|
||
# 获取企业微信发送的相关参数
|
||
sVerifyMsgSig = kw["msg_signature"]
|
||
sVerifyTimeStamp = kw["timestamp"]
|
||
sVerifyNonce = kw["nonce"]
|
||
|
||
if request.httprequest.method == "GET": # type: ignore
|
||
# ^ 假设企业的接收消息的URL设置为http://api.3dept.com。
|
||
# ^ 企业管理员在保存回调配置信息时,企业微信会发送一条验证消息到填写的URL,请求内容如下:
|
||
# ^ 请求方式:GET
|
||
# ^ http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS×tamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR
|
||
# ^ 请求参数:
|
||
# ^ msg_signature: 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体。
|
||
# ^ timestamp: 时间戳。与nonce结合使用,用于防止请求重放攻击。
|
||
# ^ nonce: 随机数。用于保证签名不可预测。与timestamp结合使用,用于防止请求重放攻击。
|
||
# ^ echostr: 加密的随机字符串。用于保证签名不可预测。
|
||
sVerifyEchoStr = kw["echostr"]
|
||
ret, msg = wxcpt.VerifyURL(
|
||
sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr
|
||
)
|
||
if ret != 0:
|
||
logging.error("ERR: VerifyURL ret: " + str(ret))
|
||
sys.exit(1)
|
||
return msg
|
||
|
||
if request.httprequest.method == "POST": # type: ignore
|
||
# ^ 假设企业的接收消息的URL设置为http://api.3dept.com。
|
||
# ^ 当用户触发回调行为时,企业微信会发送回调消息到填写的URL,请求内容如下:
|
||
# ^ 请求方式:POST
|
||
# ^ 请求地址 :http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS×tamp=13500001234&nonce=123412323
|
||
# ^ 请求参数:
|
||
# ^ msg_signature: 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体。
|
||
# ^ timestamp: 时间戳。与nonce结合使用,用于防止请求重放攻击。
|
||
# ^ nonce: 随机数。用于保证签名不可预测。与timestamp结合使用,用于防止请求重放攻击。
|
||
# ^ ToUserName: 企业微信的CorpID,当为第三方应用回调事件时,CorpID的内容为suiteid
|
||
# ^ AgentID: 接收的应用id,可在应用的设置页面获取。仅应用相关的回调会带该字段。
|
||
# ^ Encrypt: 消息结构体加密后的字符串
|
||
sReqData = request.httprequest.data # type: ignore
|
||
ret, msg = wxcpt.DecryptMsg(
|
||
sReqData, sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce
|
||
)
|
||
if ret != 0:
|
||
logging.error("ERR: DecryptMsg ret: " + str(ret))
|
||
sys.exit(1)
|
||
# 解密成功,msg即明文的xml消息结构体
|
||
# xml_tree = etree.fromstring(msg) # xml解析为一个xml元素
|
||
# print("解密成功", msg)
|
||
try:
|
||
return (
|
||
request.env["wecom.app.event_type"] # type: ignore
|
||
.sudo()
|
||
.with_context(xml_tree=msg, company_id=company_id)
|
||
.handle_event()
|
||
) # 传递xml元素和公司
|
||
except:
|
||
pass
|
||
finally:
|
||
# ^ 正确响应企业微信本次的POST请求,企业微信将不会再次发送请求
|
||
# ^ ·企业微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次
|
||
# ^ ·当接收成功后,http头部返回200表示接收ok,其他错误码企业微信后台会一律当做失败并发起重试
|
||
# return Response("success", status=200)
|
||
pass
|