preface
Recently, the egg pain has been trying to discard the official account service provided by WeChat, and want to move public service to cloud server. However, it has found awful interface permissions.
1, Basic configuration
It mainly adds url and token. Here, the url must start with http: / / or https: / /, and supports port 80 and port 443 respectively. So the server uses Nginx to monitor port 443, and then forwards it to the port allocated to wechat api (8000 in this paper).
2, Server authentication
When your own server receives a GET request, you need to verify whether the message is sent by the wechat server. The code is as follows.
@app.route('/', methods=['GET', 'POST']) def wechat(): args = request.args signature = args['signature'] timestamp = args['timestamp'] nonce = args['nonce'] token = 'xxxxxxxxx' if not all([signature, timestamp, nonce, echostr]): abort(400) # Construct hashcode according to token, timestamp and nonce list = sorted([token, timestamp, nonce]) s = list[0]+list[1]+list[2] hashcode = hashlib.sha1(s.encode('utf-8')).hexdigest() if request.method=='GET': # GET is only used to check whether hashcode is equal to signature when configuring the server echostr = args['echostr'] if hashcode == signature: return echostr else: return "Sign error."
3, Processing user messages
The user sends it to the wechat server, and the wechat server makes it into XML and sends it to its own server, similar to the following. MsgType is the message type, such as text, picture, applet, video, voice, etc. Process and reply according to MsgType respectively.
<xml> <ToUserName><![CDATA[official account]]></ToUserName> <FromUserName><![CDATA[Fan number]]></FromUserName> <CreateTime>1460537339</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[Big brother muyao nb]]></Content> <MsgId>3272960105994287638</MsgId> </xml>
After receiving the xml, I use xmltodict Parse method converts xml into a similar dictionary for subsequent processing, and finally uses xmltodict The Unparse method converts dict into xml and returns it to the wechat server, which sends it to the user.
The complete code is as follows:
from flask import Flask, jsonify, request, Response, abort from gevent import pywsgi from flask_cors import CORS import time import hashlib import xmltodict app = Flask(__name__) app.config['JSON_AS_ASCII'] = False # Support Chinese app.config['JSON_SORT_KEYS'] = False # Disable json auto sorting CORS(app, supports_credentials=True) # Allow cross domain @app.route('/', methods=['GET', 'POST']) #, strict_slashes=False) def wechat(): args = request.args signature = args['signature'] timestamp = args['timestamp'] nonce = args['nonce'] token = 'xxxxxxxxxxxx' if not all([signature, timestamp, nonce]): abort(400) list = sorted([token, timestamp, nonce]) s = list[0]+list[1]+list[2] hashcode = hashlib.sha1(s.encode('utf-8')).hexdigest() if request.method=='GET': # It is only used for verification when configuring the server echostr = args['echostr'] if hashcode == signature: return echostr else: return "Sign error." if request.method=='POST': # Monitor whether the request is sent by wechat if hashcode != signature: abort(403) xml = request.data if not xml: abort(400) # parse: xml -> dict req = xmltodict.parse(xml)['xml'] print(req) # The user sent text if 'text' == req.get('MsgType'): resp = { 'ToUserName':req.get('FromUserName'), 'FromUserName':req.get('ToUserName'), 'CreateTime':int(time.time()), 'MsgType':'text', 'Content':req.get('Content') } # The user sent voice elif 'voice' == req.get('MsgType'): reg = req.get('Recognition') resp = { 'ToUserName':req.get('FromUserName'), 'FromUserName':req.get('ToUserName'), 'CreateTime':int(time.time()), 'MsgType':'text', 'Content':reg if reg else 'What you said! I cannot understand you' } # The user sent pictures elif 'image' == req.get('MsgType'): resp = { 'ToUserName': req.get('FromUserName'), 'FromUserName': req.get('ToUserName'), 'CreateTime': int(time.time()), 'MsgType': 'text', 'Content': 'Good picture, good picture!' } # The user sent the location elif 'location' == req.get('MsgType'): resp = { 'ToUserName': req.get('FromUserName'), 'FromUserName': req.get('ToUserName'), 'CreateTime': int(time.time()), 'MsgType': 'text', 'Content': f'So you're in(X, Y)=({req.get("Location_X")}, {req.get("Location_Y")})of{req.get("Label")}!' } # The user sent a video elif 'video' == req.get('MsgType'): resp = { 'ToUserName': req.get('FromUserName'), 'FromUserName': req.get('ToUserName'), 'CreateTime': int(time.time()), 'MsgType': 'text', 'Content': 'Don't send anything weird' } # The user sent an event elif 'event' == req.get('MsgType'): if "subscribe" == req.get("Event"): resp = { "ToUserName":req.get("FromUserName", ""), "FromUserName":req.get("ToUserName", ""), "CreateTime":int(time.time()), "MsgType":"text", "Content":u"Thank you for your attention!" } else: print('Take it off!') resp = None # The user sent something else and returned a string casually, otherwise there was a problem with the user's display service else: return "success" # unparse: dict -> xml xml = xmltodict.unparse({'xml':resp if resp else ''}) return xml if __name__ == '__main__': server = pywsgi.WSGIServer(('0.0.0.0', 8000), app) server.serve_forever()
Supplement: authorization of wechat built-in web page
1. Official account permissions set up the domain name of the callback.
2. The user agrees to the authorization and obtains the code (the user accesses the following website, and the REDIRECT_URI is the URL to jump to after clicking the authorization, and the jump will take the code parameter)
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
3. Exchange the code obtained in step 2 for web page authorized access_token. The method is to send a GET request on your own server as follows:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
The returned results are as follows:
{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE" }
4. Take ACCESS_TOKEN and OPENID change the user's basic information, which is also a GET request:
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
The returned json result contains the following fields, and then take it to the front-end display to finish it: