中国本土应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
6.1KB

  1. # -*- coding: utf-8 -*-
  2. import json
  3. import logging
  4. import xml.etree.cElementTree as ET
  5. from lxml import etree
  6. import sys
  7. from odoo.addons.oec_im_wecom_api.api.wecom_msg_crtpt import WecomMsgCrypt
  8. from odoo import http, models, fields, _
  9. from odoo.http import request
  10. from odoo.http import Response
  11. _logger = logging.getLogger(__name__)
  12. class StripeController(http.Controller):
  13. """ """
  14. @http.route(
  15. ["/wecom_callback", "/wecom_callback/"],
  16. type="http",
  17. auth="public",
  18. methods=["GET", "POST"],
  19. )
  20. def WecomCallbackService(self):
  21. """
  22. 企业微信回调服务
  23. """
  24. @http.route(
  25. ["/wecom_callback/<int:id>/<string:service>", "/wecom_callback/contacts"],
  26. type="http",
  27. auth="public",
  28. methods=["GET", "POST"],
  29. csrf=False,
  30. )
  31. def WecomCallbackService(self, service, id, **kw):
  32. """
  33. 企业微信回调服务
  34. :param id: 公司id
  35. :param service: 回调服务名称 code
  36. 文档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
  37. """
  38. company_id = request.env["res.company"].sudo().search([("id", "=", id)])
  39. sCorpID = company_id.corpid
  40. callback_service = (
  41. company_id.contacts_app_id.app_callback_service_ids.sudo().search(
  42. [
  43. ("app_id", "=", company_id.contacts_app_id.id),
  44. ("code", "=", service),
  45. "|",
  46. ("active", "=", True),
  47. ("active", "=", False),
  48. ]
  49. )
  50. )
  51. if callback_service.active is False:
  52. _logger.info(
  53. _("App [%s] does not have service [%s] enabled")
  54. % (company_id.contacts_app_id.name, callback_service.name)
  55. )
  56. return Response("success", status=200)
  57. else:
  58. wxcpt = WecomMsgCrypt(
  59. callback_service.callback_url_token,
  60. callback_service.callback_aeskey,
  61. sCorpID,
  62. )
  63. # 获取企业微信发送的相关参数
  64. sVerifyMsgSig = kw["msg_signature"]
  65. sVerifyTimeStamp = kw["timestamp"]
  66. sVerifyNonce = kw["nonce"]
  67. if request.httprequest.method == "GET":
  68. # ^ 假设企业的接收消息的URL设置为http://api.3dept.com。
  69. # ^ 企业管理员在保存回调配置信息时,企业微信会发送一条验证消息到填写的URL,请求内容如下:
  70. # ^ 请求方式:GET
  71. # ^ http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS&timestamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR
  72. # ^ 请求参数:
  73. # ^ msg_signature: 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体。
  74. # ^ timestamp: 时间戳。与nonce结合使用,用于防止请求重放攻击。
  75. # ^ nonce: 随机数。用于保证签名不可预测。与timestamp结合使用,用于防止请求重放攻击。
  76. # ^ echostr: 加密的随机字符串。用于保证签名不可预测。
  77. sVerifyEchoStr = kw["echostr"]
  78. ret, msg = wxcpt.VerifyURL(
  79. sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr
  80. )
  81. if ret != 0:
  82. logging.error("ERR: VerifyURL ret: " + str(ret))
  83. sys.exit(1)
  84. return msg
  85. if request.httprequest.method == "POST":
  86. # ^ 假设企业的接收消息的URL设置为http://api.3dept.com。
  87. # ^ 当用户触发回调行为时,企业微信会发送回调消息到填写的URL,请求内容如下:
  88. # ^ 请求方式:POST
  89. # ^ 请求地址 :http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS&timestamp=13500001234&nonce=123412323
  90. # ^ 请求参数:
  91. # ^ msg_signature: 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体。
  92. # ^ timestamp: 时间戳。与nonce结合使用,用于防止请求重放攻击。
  93. # ^ nonce: 随机数。用于保证签名不可预测。与timestamp结合使用,用于防止请求重放攻击。
  94. # ^ ToUserName: 企业微信的CorpID,当为第三方应用回调事件时,CorpID的内容为suiteid
  95. # ^ AgentID: 接收的应用id,可在应用的设置页面获取。仅应用相关的回调会带该字段。
  96. # ^ Encrypt: 消息结构体加密后的字符串
  97. sReqData = request.httprequest.data
  98. ret, msg = wxcpt.DecryptMsg(
  99. sReqData, sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce
  100. )
  101. if ret != 0:
  102. logging.error("ERR: DecryptMsg ret: " + str(ret))
  103. sys.exit(1)
  104. # 解密成功,msg即明文的xml消息结构体
  105. # xml_tree = etree.fromstring(msg) # xml解析为一个xml元素
  106. # print("解密成功", msg)
  107. try:
  108. return (
  109. request.env["wecom.app.event_type"]
  110. .sudo()
  111. .with_context(xml_tree=msg, company_id=company_id)
  112. .handle_event()
  113. ) # 传递xml元素和公司
  114. except:
  115. pass
  116. finally:
  117. # ^ 正确响应企业微信本次的POST请求,企业微信将不会再次发送请求
  118. # ^ ·企业微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次
  119. # ^ ·当接收成功后,http头部返回200表示接收ok,其他错误码企业微信后台会一律当做失败并发起重试
  120. # return Response("success", status=200)
  121. pass
上海开阖软件有限公司 沪ICP备12045867号-1