mirror of
https://github.com/silenceper/wechat.git
synced 2026-02-12 08:42:28 +08:00
feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法 (#439)
* 添加微信客服SDK * polish:优化签名函数 * polish:优化注释内容 * polish:复用已有的Token以及CommonError,移除无用的输出 * polish:复用已有的消息加解密 * fix:修复错误信息被覆盖的问题 * polish:go fmt 文件 * polish:客服链接支持自定义参数并更新注释文档内容 * feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法
This commit is contained in:
96
work/kf/callback.go
Normal file
96
work/kf/callback.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package kf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SignatureOptions 微信服务器验证参数
|
||||||
|
type SignatureOptions struct {
|
||||||
|
Signature string `form:"msg_signature"`
|
||||||
|
TimeStamp string `form:"timestamp"`
|
||||||
|
Nonce string `form:"nonce"`
|
||||||
|
EchoStr string `form:"echostr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyURL 验证请求参数是否合法并返回解密后的消息内容
|
||||||
|
// //Gin框架的使用示例
|
||||||
|
// r.GET("/v1/event/callback", func(c *gin.Context) {
|
||||||
|
// options := kf.SignatureOptions{}
|
||||||
|
// //获取回调的的校验参数
|
||||||
|
// if = c.ShouldBindQuery(&options); err != nil {
|
||||||
|
// c.String(http.StatusUnauthorized, "参数解析失败")
|
||||||
|
// }
|
||||||
|
// // 调用VerifyURL方法校验当前请求,如果合法则把解密后的内容作为响应返回给微信服务器
|
||||||
|
// echo, err := kfClient.VerifyURL(options)
|
||||||
|
// if err == nil {
|
||||||
|
// c.String(http.StatusOK, echo)
|
||||||
|
// } else {
|
||||||
|
// c.String(http.StatusUnauthorized, "非法请求来源")
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
func (r *Client) VerifyURL(options SignatureOptions) (string, error) {
|
||||||
|
if options.Signature != util.Signature(r.ctx.Token, options.TimeStamp, options.Nonce, options.EchoStr) {
|
||||||
|
return "", NewSDKErr(40015)
|
||||||
|
}
|
||||||
|
_, bData, err := util.DecryptMsg(r.corpID, options.EchoStr, r.encodingAESKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", NewSDKErr(40016)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bData), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原始回调消息内容
|
||||||
|
type callbackOriginMessage struct {
|
||||||
|
ToUserName string // 企业微信的CorpID,当为第三方套件回调事件时,CorpID的内容为suiteid
|
||||||
|
AgentID string // 接收的应用id,可在应用的设置页面获取
|
||||||
|
Encrypt string // 消息结构体加密后的字符串
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallbackMessage 微信客服回调消息
|
||||||
|
type CallbackMessage struct {
|
||||||
|
ToUserName string `json:"to_user_name"` // 微信客服组件ID
|
||||||
|
CreateTime int `json:"create_time"` // 消息创建时间,unix时间戳
|
||||||
|
MsgType string `json:"msgtype"` // 消息的类型,此时固定为 event
|
||||||
|
Event string `json:"event"` // 事件的类型,此时固定为 kf_msg_or_event
|
||||||
|
Token string `json:"token"` // 调用拉取消息接口时,需要传此token,用于校验请求的合法性
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCallbackMessage 获取回调事件中的消息内容
|
||||||
|
// //Gin框架的使用示例
|
||||||
|
// r.POST("/v1/event/callback", func(c *gin.Context) {
|
||||||
|
// var (
|
||||||
|
// message kf.CallbackMessage
|
||||||
|
// body []byte
|
||||||
|
// )
|
||||||
|
// // 读取原始消息内容
|
||||||
|
// body, err = c.GetRawData()
|
||||||
|
// if err != nil {
|
||||||
|
// c.String(http.StatusInternalServerError, err.Error())
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// // 解析原始数据
|
||||||
|
// message, err = kfClient.GetCallbackMessage(body)
|
||||||
|
// if err != nil {
|
||||||
|
// c.String(http.StatusInternalServerError, "消息获取失败")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// fmt.Println(message)
|
||||||
|
// c.String(200, "ok")
|
||||||
|
// })
|
||||||
|
func (r *Client) GetCallbackMessage(encryptedMsg []byte) (msg CallbackMessage, err error) {
|
||||||
|
var origin callbackOriginMessage
|
||||||
|
if err = xml.Unmarshal(encryptedMsg, &origin); err != nil {
|
||||||
|
return msg, err
|
||||||
|
}
|
||||||
|
_, bData, err := util.DecryptMsg(r.corpID, origin.Encrypt, r.encodingAESKey)
|
||||||
|
if err != nil {
|
||||||
|
return msg, NewSDKErr(40016)
|
||||||
|
}
|
||||||
|
if err = xml.Unmarshal(bData, &msg); err != nil {
|
||||||
|
return msg, err
|
||||||
|
}
|
||||||
|
return msg, err
|
||||||
|
}
|
||||||
@@ -21,6 +21,10 @@ const (
|
|||||||
SDKInvalidCorpID Error = "无效的 CorpID"
|
SDKInvalidCorpID Error = "无效的 CorpID"
|
||||||
// SDKAccessTokenInvalid 错误码:40014
|
// SDKAccessTokenInvalid 错误码:40014
|
||||||
SDKAccessTokenInvalid Error = "AccessToken 无效"
|
SDKAccessTokenInvalid Error = "AccessToken 无效"
|
||||||
|
// SDKValidateSignatureFailed 错误码:40015
|
||||||
|
SDKValidateSignatureFailed Error = "校验签名错误"
|
||||||
|
// SDKDecryptMSGFailed 错误码:40016
|
||||||
|
SDKDecryptMSGFailed Error = "消息解密失败"
|
||||||
// SDKAccessTokenMissing 错误码:41001
|
// SDKAccessTokenMissing 错误码:41001
|
||||||
SDKAccessTokenMissing Error = "缺少AccessToken参数"
|
SDKAccessTokenMissing Error = "缺少AccessToken参数"
|
||||||
// SDKAccessTokenExpired 错误码:42001
|
// SDKAccessTokenExpired 错误码:42001
|
||||||
@@ -53,6 +57,10 @@ func NewSDKErr(code int64, msgList ...string) Error {
|
|||||||
return SDKInvalidCorpID
|
return SDKInvalidCorpID
|
||||||
case 40014:
|
case 40014:
|
||||||
return SDKAccessTokenInvalid
|
return SDKAccessTokenInvalid
|
||||||
|
case 40015:
|
||||||
|
return SDKValidateSignatureFailed
|
||||||
|
case 40016:
|
||||||
|
return SDKDecryptMSGFailed
|
||||||
case 45009:
|
case 45009:
|
||||||
return SDKApiFreqOutOfLimit
|
return SDKApiFreqOutOfLimit
|
||||||
case 95011:
|
case 95011:
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package syncmsg
|
|
||||||
|
|
||||||
// Event 微信客服回调事件
|
|
||||||
type Event struct {
|
|
||||||
ToUserName string `json:"to_user_name"` // 微信客服组件ID
|
|
||||||
CreateTime int `json:"create_time"` // 消息创建时间,unix时间戳
|
|
||||||
MsgType string `json:"msgtype"` // 消息的类型,此时固定为 event
|
|
||||||
Event string `json:"event"` // 事件的类型,此时固定为 kf_msg_or_event
|
|
||||||
Token string `json:"token"` // 调用拉取消息接口时,需要传此token,用于校验请求的合法性
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user