GoLang, 原创, 服务器, , ,

微信公众号消息回复Go语言实现

做此接口主要因为自己的网站都使用了go语言重写,而不想因为一个自动回复功能而需要保留php服务。

加密解密的源码已经放置到 GitHub上,请自行获取 https://github.com/chenxue4076/go-wechat-encryption

go mod 导入方式

require github.com/chenxue4076/go-wechat-encryption v1.3.6

微信自动回复的api接口 核心代码如下 接口文件 wechat.go 就是url地址路由到的地址

import (
	"fmt"
	"github.com/kataras/iris/v12"
	"repositories"
)

type WeChatController struct {

}

func (c *WeChatController) Get(ctx iris.Context) interface{} {
	return repositories.WeChatCheckSignature(ctx)
}

func (c *WeChatController) Post(ctx iris.Context) interface{} {
	return repositories.WeChatResponseMsg(ctx)
}

repositories 目录里的处理函数如下:

package repositories

import (
	"crypto/sha1"
	"encoding/hex"
	"encoding/xml"
	"errors"
	go_wechat_encryption "github.com/chenxue4076/go-wechat-encryption"
	"github.com/kataras/iris/v12"
	"go-iris-windigniter/configs"
	"io/ioutil"
	"sort"
	"strconv"
	"strings"
	"time"
)

//验证接口
func WeChatCheckSignature(ctx iris.Context) string {
	wxConfig,err := configs.GetWeChatConfig()
	if err != nil {
		return err.Error()
	}
	wxConfigYuki 			:= wxConfig["TestWechat"]
	serverToken 			:= wxConfigYuki["ServerToken"]

	signature 	:= ctx.URLParam("signature")
	timeStamp 	:= ctx.URLParam("timestamp")
	nonce 		:= ctx.URLParam("nonce")
	echostr 	:= ctx.URLParam("echostr")
	if echostr != "" {
		isValid := checkSignature(signature, timeStamp, nonce, serverToken)
		if isValid {
			return echostr
		}
		return "Error Signature"
	}
	return "Error request"
}

func checkSignature(signature, timeStamp, nonce string, token string) bool {
	paramsArray := []string{token, timeStamp, nonce}
	sort.Strings(paramsArray)
	paramsMsg := ""
	for _,value := range paramsArray {
		//fmt.Println(value)
		paramsMsg += value
	}
	//sha1
	sha1Param := sha1.New()
	sha1Param.Write([]byte(paramsMsg))
	msg := hex.EncodeToString(sha1Param.Sum([]byte("")))
	return msg == signature
}

//微信消息回复接口
func WeChatResponseMsg(ctx iris.Context) string {
	//微信配置信息
	wxConfig,err := configs.GetWeChatConfig()
	if err != nil {
		return err.Error()
	}
	wxConfigYuki 			:= wxConfig["Yuki33521"]
	//解密
	msgDecryptFormat, err := getDecryptXml(ctx, wxConfigYuki)
	if err != nil {
		return err.Error()
	}
	//整理信息
	result, err := dealPostInfo(msgDecryptFormat, ctx, wxConfigYuki)
	//加密返回
	return result
}

func getDecryptXml(ctx iris.Context, wxConfigYuki map[string]string) (msgDecryptFormat go_wechat_encryption.MsgDecryptFormatEvent, err error) {
	serverToken 			:= wxConfigYuki["ServerToken"]
	serverEncodingAesKey 	:= wxConfigYuki["ServerEncodingAesKey"]
	appId 					:= wxConfigYuki["AppId"]
	//responseAttentionText	:= wxConfigYuki["ResponseAttentionText"]
	//responseReplyHelp		:= wxConfigYuki["ResponseReplyHelp"]

	signature 	:= ctx.URLParam("signature")
	timeStamp 	:= ctx.URLParam("timestamp")
	nonce 		:= ctx.URLParam("nonce")
	encryptType := ctx.URLParam("encrypt_type")
	msgSignature := ctx.URLParam("msg_signature")
	postDataXML, err := ioutil.ReadAll(ctx.Request().Body)
	if err != nil {
		return
	}

	wxBizMsgCrypt := go_wechat_encryption.Default(serverToken, serverEncodingAesKey, appId)
	//解密
	xmlMsgBytes, errorCode := wxBizMsgCrypt.DecryptMsg(msgSignature, timeStamp, nonce, postDataXML)
	if errorCode != go_wechat_encryption.WXBizMsgCryptOK {
		err = errors.New(go_wechat_encryption.WXBizMsgErrorMsg(errorCode))
		return
	}
	ctx.Application().Logger().Info("xmlMsgBytes ",string(xmlMsgBytes))
	//解析解密后的xml
	err = xml.Unmarshal(xmlMsgBytes, &msgDecryptFormat)
	if err != nil {
		ctx.Application().Logger().Error("解析XML失败 ", err.Error())
	}
	ctx.Application().Logger().Info("解析XML ",msgDecryptFormat)
	return
}


func dealPostInfo(msgDecryptFormat go_wechat_encryption.MsgDecryptFormatEvent, ctx iris.Context, wxConfigYuki map[string]string) (msg string, err error) {
	if msgDecryptFormat.MsgType == "text" {
		keyword := msgDecryptFormat.Content
		msg,err = showTextResponse(ctx, msgDecryptFormat, keyword, wxConfigYuki)
		return
	} else if msgDecryptFormat.MsgType == "event" {

	}
	err = errors.New("Other message type")
	return
}

func showTextResponse(ctx iris.Context, msgDecryptFormat go_wechat_encryption.MsgDecryptFormatEvent, text string, wxConfigYuki map[string]string) (msg string, err error) {
	if text == "" {
		text = wxConfigYuki["ResponseReplyHelp"]
	}
	timeStampReply := strconv.FormatInt(time.Now().Unix(), 10)
	msgType := "text"

	msgReplyFormatText := go_wechat_encryption.MsgReplyFormatText{
		msgDecryptFormat.FromUserName,
		msgDecryptFormat.ToUserName,
		timeStampReply,
		msgType,
		text,
	}
	resultBytes, err := xml.Marshal(msgReplyFormatText)
	if err != nil {
		return
	}
	resultStr := strings.ReplaceAll(string(resultBytes), "MsgReplyFormatText", "xml")

	/*textTpl := `<xml>
	<ToUserName><![CDATA[%s]]></ToUserName>
	<FromUserName><![CDATA[%s]]></FromUserName>
	<CreateTime>%s</CreateTime>
	<MsgType><![CDATA[%s]]></MsgType>
	<Content><![CDATA[%s]]></Content>
	<FuncFlag>0</FuncFlag>
</xml>`
	//记得交换 ToUserName 和 FromUserName
	resultStr := fmt.Sprintf(textTpl, msgDecryptFormat.FromUserName, msgDecryptFormat.ToUserName, timeStamp, msgType, text)*/
	//获取配置
	serverToken 			:= wxConfigYuki["ServerToken"]
	serverEncodingAesKey 	:= wxConfigYuki["ServerEncodingAesKey"]
	appId 					:= wxConfigYuki["AppId"]

	timeStamp 	:= ctx.URLParam("timestamp")
	nonce 		:= ctx.URLParam("nonce")
	//加密
	wxBizMsgCrypt := go_wechat_encryption.Default(serverToken, serverEncodingAesKey, appId)
	msgBytes,errorCode := wxBizMsgCrypt.EncryptMsg(resultStr, timeStamp, nonce)
	if errorCode != 0 {
		err = errors.New(go_wechat_encryption.WXBizMsgErrorMsg(errorCode))
		return
	}
	msg = string(msgBytes)
	return
}

附件:wechat配置参数

TestWechat:
    AppId:  wxb9be04dc961ddd0f
    AppSecret:  0e06f47e20b436ad6ca2e31762915eba
    ServerToken:  Windigniter.com
    ServerEncodingAesKey: PddAWYnWvqDArOgGelTYk4fqpBcH4fn0JeJuLv68SSe
    WeChatId: gh_d29d816879e7
    ResponseAttentionText:  终于等到你了
    ResponseReplyHelp:  欢迎关注公众号,回复内容

贴出的代码帮助参考使用,另外 https://github.com/chenxue4076/go-wechat-encryption 中的 demo也可以验证 加密解密的正确性

(710)

Related Post