mp

package
v0.0.0-...-71333bb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 12, 2015 License: Apache-2.0 Imports: 20 Imported by: 0

README

微信公众平台 订阅号, 服务号 golang SDK

一个 URL 监听一个公众号的消息
package main

import (
	"fmt"
	"github.com/philsong/wechat2/mp"
	"github.com/philsong/wechat2/mp/menu"
	"github.com/philsong/wechat2/util"
	"net/http"
)

func MenuClickEventHandler(w http.ResponseWriter, r *mp.Request) {
	event := menu.GetClickEvent(r.MixedMsg)
	fmt.Println(event.EventKey)
	return
}

func main() {
	aesKey, err := util.AESKeyDecode("encodedAESKey")
	if err != nil {
		panic(err)
	}

	messageServeMux := mp.NewMessageServeMux()
	messageServeMux.EventHandleFunc(menu.EventTypeClick, MenuClickEventHandler)

	wechatServer := mp.NewDefaultWechatServer("id", "token", "appid", aesKey, messageServeMux)

	wechatServerFrontend := mp.NewWechatServerFrontend(wechatServer, nil)

	http.Handle("/wechat", wechatServerFrontend)
	http.ListenAndServe(":80", nil)
}
一个 URL 监听多个公众号的消息
package main

import (
	"fmt"
	"github.com/philsong/wechat2/mp"
	"github.com/philsong/wechat2/mp/menu"
	"github.com/philsong/wechat2/util"
	"net/http"
)

func MenuClickEventHandler(w http.ResponseWriter, r *mp.Request) {
	event := menu.GetClickEvent(r.MixedMsg)
	fmt.Println(event.EventKey)
	return
}

func main() {
	aesKey1, err := util.AESKeyDecode("encodedAESKey1")
	if err != nil {
		panic(err)
	}

	messageServeMux1 := mp.NewMessageServeMux()
	messageServeMux1.EventHandleFunc(menu.EventTypeClick, MenuClickEventHandler)

	wechatServer1 := mp.NewDefaultWechatServer("id1", "token1", "appid1", aesKey1, messageServeMux1)

	aesKey2, err := util.AESKeyDecode("encodedAESKey2")
	if err != nil {
		panic(err)
	}

	messageServeMux2 := mp.NewMessageServeMux()
	messageServeMux2.EventHandleFunc(menu.EventTypeClick, MenuClickEventHandler)

	wechatServer2 := mp.NewDefaultWechatServer("id2", "token2", "appid2", aesKey2, messageServeMux2)

	var multiWechatServerFrontend mp.MultiWechatServerFrontend
	multiWechatServerFrontend.SetWechatServer("wechat1", wechatServer1)
	multiWechatServerFrontend.SetWechatServer("wechat2", wechatServer2)

	http.Handle("/wechat", &multiWechatServerFrontend)
	http.ListenAndServe(":80", nil)
}

Documentation

Overview

微信公众平台(订阅号, 服务号)SDK

Index

Constants

View Source
const (
	ErrCodeOK                = 0
	ErrCodeInvalidCredential = 40001 // access_token 过期(无效)返回这个错误
	ErrCodeTimeout           = 42001 // access_token 过期(无效)返回这个错误(maybe!!!)
)
View Source
const URLQueryWechatServerKeyName = "wechat_server"

回调 URL 上索引 WechatServer 的 key 的名称.

比如下面的回调地址里面就可以根据 wechat1 来索引对应的 WechatServer.
http://www.xxx.com/?wechat_server=wechat1&signature=XXX&timestamp=123456789&nonce=12345678

Variables

View Source
var DefaultInvalidRequestHandler = InvalidRequestHandlerFunc(func(http.ResponseWriter, *http.Request, error) {})
View Source
var MediaHttpClient = &http.Client{
	Transport: &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   5 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 5 * time.Second,
	},
	Timeout: 300 * time.Second,
}

多媒体上传下载请求的 http.Client

View Source
var TextHttpClient = &http.Client{
	Transport: &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   5 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 5 * time.Second,
	},
	Timeout: 15 * time.Second,
}

一般请求的 http.Client

Functions

func HttpResponseWriter

func HttpResponseWriter(w io.Writer) http.ResponseWriter

将 io.Writer 从语义上实现 http.ResponseWriter.

某些 http 框架可能没有提供 http.ResponseWriter, 而只是提供了 io.Writer.

func ServeHTTP

func ServeHTTP(w http.ResponseWriter, r *http.Request, urlValues url.Values,
	wechatServer WechatServer, invalidRequestHandler InvalidRequestHandler)

ServeHTTP 处理 http 消息请求

NOTE: 调用者保证所有参数有效

func WriteAESResponse

func WriteAESResponse(w http.ResponseWriter, r *Request, msg interface{}) (err error)

回复消息给微信服务器(安全模式).

要求 msg 是有效的消息数据结构(经过 encoding/xml marshal 后符合消息的格式);
如果有必要可以修改 Request 里面的某些值, 比如 TimeStamp.

func WriteRawResponse

func WriteRawResponse(w http.ResponseWriter, r *Request, msg interface{}) (err error)

回复消息给微信服务器(明文模式).

要求 msg 是有效的消息数据结构(经过 encoding/xml marshal 后符合消息的格式);
如果有必要可以修改 Request 里面的某些值, 比如 TimeStamp.

Types

type CommonMessageHeader

type CommonMessageHeader struct {
	ToUserName   string `xml:"ToUserName"   json:"ToUserName"`
	FromUserName string `xml:"FromUserName" json:"FromUserName"`
	CreateTime   int64  `xml:"CreateTime"   json:"CreateTime"`
	MsgType      string `xml:"MsgType"      json:"MsgType"`
}

微信服务器推送过来的消息(事件)通用的消息头

type DefaultTokenServer

type DefaultTokenServer struct {
	// contains filtered or unexported fields
}

TokenServer 的简单实现.

NOTE:
一般用于单进程环境, 因为 DefaultTokenServer 同时也实现了一个简单的中控服务器, 而不是简单的
实现了 TokenServer 接口, 所以整个系统只能存在一个 DefaultTokenServer 实例!!!

func NewDefaultTokenServer

func NewDefaultTokenServer(appid, appsecret string,
	httpClient *http.Client) (srv *DefaultTokenServer)

创建一个新的 DefaultTokenServer.

如果 httpClient == nil 则默认使用 http.DefaultClient.

func (*DefaultTokenServer) Token

func (srv *DefaultTokenServer) Token() (token string, err error)

func (*DefaultTokenServer) TokenRefresh

func (srv *DefaultTokenServer) TokenRefresh() (token string, err error)

type DefaultWechatServer

type DefaultWechatServer struct {
	// contains filtered or unexported fields
}

func NewDefaultWechatServer

func NewDefaultWechatServer(wechatId, token, appId string, AESKey []byte,
	messageHandler MessageHandler) (srv *DefaultWechatServer)

NewDefaultWechatServer 创建一个新的 DefaultWechatServer.

如果不知道自己的 AppId 是多少, 可以先随便填入一个字符串,
这样正常情况下会出现 AppId mismatch 错误, 错误的 have 后面的就是正确的 AppId.

func (*DefaultWechatServer) AppId

func (srv *DefaultWechatServer) AppId() string

func (*DefaultWechatServer) CurrentAESKey

func (srv *DefaultWechatServer) CurrentAESKey() (key [32]byte)

func (*DefaultWechatServer) LastAESKey

func (srv *DefaultWechatServer) LastAESKey() (key [32]byte)

func (*DefaultWechatServer) MessageHandler

func (srv *DefaultWechatServer) MessageHandler() MessageHandler

func (*DefaultWechatServer) Token

func (srv *DefaultWechatServer) Token() string

func (*DefaultWechatServer) UpdateAESKey

func (srv *DefaultWechatServer) UpdateAESKey(AESKey []byte) (err error)

func (*DefaultWechatServer) WechatId

func (srv *DefaultWechatServer) WechatId() string

type Error

type Error struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

func (*Error) Error

func (e *Error) Error() string

type EventType

type EventType string

type InvalidRequestHandler

type InvalidRequestHandler interface {
	// err 是错误信息
	ServeInvalidRequest(w http.ResponseWriter, r *http.Request, err error)
}

无效请求(非法或者错误)的处理接口.

type InvalidRequestHandlerFunc

type InvalidRequestHandlerFunc func(http.ResponseWriter, *http.Request, error)

func (InvalidRequestHandlerFunc) ServeInvalidRequest

func (fn InvalidRequestHandlerFunc) ServeInvalidRequest(w http.ResponseWriter, r *http.Request, err error)

type MessageHandler

type MessageHandler interface {
	ServeMessage(w http.ResponseWriter, r *Request)
}

微信服务器推送过来的消息(事件)处理接口

type MessageHandlerFunc

type MessageHandlerFunc func(http.ResponseWriter, *Request)

func (MessageHandlerFunc) ServeMessage

func (fn MessageHandlerFunc) ServeMessage(w http.ResponseWriter, r *Request)

type MessageServeMux

type MessageServeMux struct {
	// contains filtered or unexported fields
}

MessageServeMux 实现了一个简单的消息路由器, 同时也是一个 MessageHandler.

func NewMessageServeMux

func NewMessageServeMux() *MessageServeMux

func (*MessageServeMux) DefaultEventHandle

func (mux *MessageServeMux) DefaultEventHandle(handler MessageHandler)

注册 MessageHandler, 处理未知类型的事件.

func (*MessageServeMux) DefaultEventHandleFunc

func (mux *MessageServeMux) DefaultEventHandleFunc(handler func(http.ResponseWriter, *Request))

注册 MessageHandlerFunc, 处理未知类型的事件.

func (*MessageServeMux) DefaultMessageHandle

func (mux *MessageServeMux) DefaultMessageHandle(handler MessageHandler)

注册 MessageHandler, 处理未知类型的消息.

func (*MessageServeMux) DefaultMessageHandleFunc

func (mux *MessageServeMux) DefaultMessageHandleFunc(handler func(http.ResponseWriter, *Request))

注册 MessageHandlerFunc, 处理未知类型的消息.

func (*MessageServeMux) EventHandle

func (mux *MessageServeMux) EventHandle(eventType EventType, handler MessageHandler)

注册 MessageHandler, 处理特定类型的事件.

func (*MessageServeMux) EventHandleFunc

func (mux *MessageServeMux) EventHandleFunc(eventType EventType, handler func(http.ResponseWriter, *Request))

注册 MessageHandlerFunc, 处理特定类型的事件.

func (*MessageServeMux) MessageHandle

func (mux *MessageServeMux) MessageHandle(msgType MessageType, handler MessageHandler)

注册 MessageHandler, 处理特定类型的消息.

func (*MessageServeMux) MessageHandleFunc

func (mux *MessageServeMux) MessageHandleFunc(msgType MessageType, handler func(http.ResponseWriter, *Request))

注册 MessageHandlerFunc, 处理特定类型的消息.

func (*MessageServeMux) ServeMessage

func (mux *MessageServeMux) ServeMessage(w http.ResponseWriter, r *Request)

MessageServeMux 实现了 MessageHandler 接口.

type MessageType

type MessageType string

type MixedMessage

type MixedMessage struct {
	XMLName struct{} `xml:"xml" json:"-"`
	CommonMessageHeader

	// fuck, MsgId != MsgID
	MsgId int64 `xml:"MsgId" json:"MsgId"`
	MsgID int64 `xml:"MsgID" json:"MsgID"`

	Content      string  `xml:"Content"      json:"Content"`
	MediaId      string  `xml:"MediaId"      json:"MediaId"`
	PicURL       string  `xml:"PicUrl"       json:"PicUrl"`
	Format       string  `xml:"Format"       json:"Format"`
	Recognition  string  `xml:"Recognition"  json:"Recognition"`
	ThumbMediaId string  `xml:"ThumbMediaId" json:"ThumbMediaId"`
	LocationX    float64 `xml:"Location_X"   json:"Location_X"`
	LocationY    float64 `xml:"Location_Y"   json:"Location_Y"`
	Scale        int     `xml:"Scale"        json:"Scale"`
	Label        string  `xml:"Label"        json:"Label"`
	Title        string  `xml:"Title"        json:"Title"`
	Description  string  `xml:"Description"  json:"Description"`
	URL          string  `xml:"Url"          json:"Url"`

	Event    string `xml:"Event"    json:"Event"`
	EventKey string `xml:"EventKey" json:"EventKey"`

	ScanCodeInfo struct {
		ScanType   string `xml:"ScanType"   json:"ScanType"`
		ScanResult string `xml:"ScanResult" json:"ScanResult"`
	} `xml:"ScanCodeInfo" json:"ScanCodeInfo"`

	SendPicsInfo struct {
		Count   int `xml:"Count" json:"Count"`
		PicList []struct {
			PicMD5Sum string `xml:"PicMd5Sum" json:"PicMd5Sum"`
		} `xml:"PicList>item,omitempty" json:"PicList,omitempty"`
	} `xml:"SendPicsInfo" json:"SendPicsInfo"`

	SendLocationInfo struct {
		LocationX float64 `xml:"Location_X" json:"Location_X"`
		LocationY float64 `xml:"Location_Y" json:"Location_Y"`
		Scale     int     `xml:"Scale"      json:"Scale"`
		Label     string  `xml:"Label"      json:"Label"`
		Poiname   string  `xml:"Poiname"    json:"Poiname"`
	} `xml:"SendLocationInfo" json:"SendLocationInfo"`

	Ticket      string  `xml:"Ticket"      json:"Ticket"`
	Latitude    float64 `xml:"Latitude"    json:"Latitude"`
	Longitude   float64 `xml:"Longitude"   json:"Longitude"`
	Precision   float64 `xml:"Precision"   json:"Precision"`
	Status      string  `xml:"Status"      json:"Status"`
	TotalCount  int     `xml:"TotalCount"  json:"TotalCount"`
	FilterCount int     `xml:"FilterCount" json:"FilterCount"`
	SentCount   int     `xml:"SentCount"   json:"SentCount"`
	ErrorCount  int     `xml:"ErrorCount"  json:"ErrorCount"`
	OrderId     string  `xml:"OrderId"     json:"OrderId"`
	OrderStatus int     `xml:"OrderStatus" json:"OrderStatus"`
	ProductId   string  `xml:"ProductId"   json:"ProductId"`
	SKUInfo     string  `xml:"SkuInfo"     json:"SkuInfo"`
}

微信服务器推送过来的消息(事件)的合集.

type MultiWechatServerFrontend

type MultiWechatServerFrontend struct {
	// contains filtered or unexported fields
}

多个 WechatServer 的前端, 负责处理 http 请求, net/http.Handler 的实现

NOTE:
MultiWechatServerFrontend 可以处理多个公众号的消息(事件),但是要求在回调 URL 上加上一个
查询参数,参考常量 URLQueryWechatServerKeyName,这个参数的值就是 MultiWechatServerFrontend
索引 WechatServer 的 key。

例如回调 URL 为 http://www.xxx.com/weixin?wechat_server=1234567890,那么就可以在后端调用

  MultiWechatServerFrontend.SetWechatServer("1234567890", WechatServer)

来增加一个 WechatServer 来处理 wechat_server=1234567890 的消息(事件)。

MultiWechatServerFrontend 并发安全,可以在运行中动态增加和删除 WechatServer。

func (*MultiWechatServerFrontend) DeleteAllWechatServer

func (frontend *MultiWechatServerFrontend) DeleteAllWechatServer()

删除所有的 WechatServer

func (*MultiWechatServerFrontend) DeleteWechatServer

func (frontend *MultiWechatServerFrontend) DeleteWechatServer(serverKey string)

删除 serverKey 对应的 WechatServer

func (*MultiWechatServerFrontend) ServeHTTP

func (frontend *MultiWechatServerFrontend) ServeHTTP(w http.ResponseWriter, r *http.Request)

实现 http.Handler

func (*MultiWechatServerFrontend) SetInvalidRequestHandler

func (frontend *MultiWechatServerFrontend) SetInvalidRequestHandler(handler InvalidRequestHandler)

设置 InvalidRequestHandler, 如果 handler == nil 则使用默认的 DefaultInvalidRequestHandler

func (*MultiWechatServerFrontend) SetWechatServer

func (frontend *MultiWechatServerFrontend) SetWechatServer(serverKey string, server WechatServer)

设置 serverKey-WechatServer pair. 如果 serverKey == "" 或者 server == nil 则不做任何操作

type Request

type Request struct {
	HttpRequest *http.Request // 可以为 nil, 因为某些 http 框架没有提供此参数

	// 下面的字段必须提供, 如果有的话
	Signature string        // 请求 URL 中的签名: signature
	TimeStamp int64         // 请求 URL 中的时间戳: timestamp
	Nonce     string        // 请求 URL 中的随机数: nonce
	RawMsgXML []byte        // "明文"消息的 XML 文本
	MixedMsg  *MixedMessage // RawMsgXML 解析后的消息

	// 下面的字段是 AES 模式才有的
	MsgSignature string   // 请求 URL 中的消息体签名: msg_signature
	EncryptType  string   // 请求 URL 中的加密方式: encrypt_type
	AESKey       [32]byte // 当前消息 AES 加密的 key
	Random       []byte   // 当前消息加密时所用的 random, 16 bytes

	// 下面字段是公众号的基本信息
	WechatId    string // 公众号的原始 id, 等于 MixedMessage.ToUserName
	WechatToken string
	WechatAppId string
}

消息(事件)请求信息

type RequestHttpBody

type RequestHttpBody struct {
	XMLName      struct{} `xml:"xml" json:"-"`
	MixedMessage          // ToUserName 始终有效
	EncryptedMsg string   `xml:"Encrypt" json:"Encrypt"`
}

安全模式 和 兼容模式, 微信服务器推送过来的 http body

type ResponseHttpBody

type ResponseHttpBody struct {
	XMLName      struct{} `xml:"xml" json:"-"`
	EncryptedMsg string   `xml:"Encrypt"`
	MsgSignature string   `xml:"MsgSignature"`
	TimeStamp    int64    `xml:"TimeStamp"`
	Nonce        string   `xml:"Nonce"`
}

安全模式回复消息的 http body

type TokenServer

type TokenServer interface {
	// 从中控服务器获取 access_token, 该 access_token 一般缓存在某个地方.
	Token() (token string, err error)

	// 请求 access_token 中控服务器到微信服务器刷新 access_token.
	// 建议从微信服务器获取一次 access_token 之后的5秒内再次调用该函数不再获取, 而是直接返回之前的结果.
	TokenRefresh() (token string, err error)
}

access_token 中控服务器接口, see token_server.png.

type WechatClient

type WechatClient struct {
	TokenServer TokenServer
	HttpClient  *http.Client
	// contains filtered or unexported fields
}

微信公众号"主动"请求功能的基本封装.

func (*WechatClient) GetCallbackIP

func (clt *WechatClient) GetCallbackIP() (ipList []string, err error)

如果公众号基于安全等考虑,需要获知微信服务器的IP地址列表,以便进行相关限制, 可以通过该接口获得微信服务器IP地址列表。

func (*WechatClient) GetJSON

func (clt *WechatClient) GetJSON(incompleteURL string, response interface{}) (err error)

GET 微信资源, 然后将微信服务器返回的 JSON 用 encoding/json 解析到 response.

NOTE:
1. 一般不用调用这个方法, 请直接调用高层次的封装方法;
2. 最终的 URL == incompleteURL + access_token;
3. response 要求是 struct 的指针, 并且该 struct 拥有属性:
   ErrCode int `json:"errcode"` (可以是直接属性, 也可以是匿名属性里的属性)

func (*WechatClient) GetNewToken

func (clt *WechatClient) GetNewToken() (token string, err error)

当 WechatClient.Token() 返回的 access_token 失效时获取新的 access_token.

func (*WechatClient) PostJSON

func (clt *WechatClient) PostJSON(incompleteURL string, request interface{}, response interface{}) (err error)

用 encoding/json 把 request marshal 为 JSON, 放入 http 请求的 body 中, POST 到微信服务器, 然后将微信服务器返回的 JSON 用 encoding/json 解析到 response.

NOTE:
1. 一般不用调用这个方法, 请直接调用高层次的封装方法;
2. 最终的 URL == incompleteURL + access_token;
3. response 要求是 struct 的指针, 并且该 struct 拥有属性:
   ErrCode int `json:"errcode"` (可以是直接属性, 也可以是匿名属性里的属性)

func (*WechatClient) Token

func (clt *WechatClient) Token() (token string, err error)

获取 access_token.

func (*WechatClient) TokenRefresh

func (clt *WechatClient) TokenRefresh() (token string, err error)

请求微信服务器更新 access_token.

NOTE:
1. 一般情况下无需调用该函数, 请使用 Token() 获取 access_token.
2. 即使 access_token 失效(错误代码 40001, 正常情况下不会出现),
   也请谨慎调用 TokenRefresh, 建议直接返回错误! 因为很有可能高并发情况下造成雪崩效应!
3. 再次强调, 调用这个函数你应该知道发生了什么!!!

func (*WechatClient) UploadFromReader

func (clt *WechatClient) UploadFromReader(incompleteURL, filename string,
	reader io.Reader, response interface{}) (err error)

通用上传接口.

NOTE:
1. 一般不用调用这个方法, 请直接调用高层次的封装方法;
2. 最终的 URL == incompleteURL + access_token;
3. 参数 filename 不是文件路径, 是指定 multipart/form-data 里面文件名称
4. response 要求是 struct 的指针, 并且该 struct 拥有属性:
   ErrCode int `json:"errcode"` (可以是直接属性, 也可以是匿名属性里的属性)

type WechatServer

type WechatServer interface {
	WechatId() string // 获取公众号的原始ID, 等于后台中的 公众号设置-->帐号详情-->原始ID
	Token() string    // 获取公众号的Token, 和后台中的设置相等
	AppId() string    // 获取公众号的 AppId

	CurrentAESKey() [32]byte // 获取当前有效的 AES 加密 Key
	LastAESKey() [32]byte    // 获取最后一个有效的 AES 加密 Key

	MessageHandler() MessageHandler // 获取 MessageHandler
}

公众号服务端接口, 处理单个公众号的消息(事件)请求.

type WechatServerFrontend

type WechatServerFrontend struct {
	// contains filtered or unexported fields
}

实现了 http.Handler, 处理一个公众号的消息(事件)请求.

func NewWechatServerFrontend

func NewWechatServerFrontend(server WechatServer, handler InvalidRequestHandler) *WechatServerFrontend

func (*WechatServerFrontend) ServeHTTP

func (frontend *WechatServerFrontend) ServeHTTP(w http.ResponseWriter, r *http.Request)

实现 http.Handler.

Directories

Path Synopsis
帐号管理接口.
帐号管理接口.
数据统计接口.
数据统计接口.
多客服接口.
多客服接口.
js-sdk 服务器端接口.
js-sdk 服务器端接口.
媒体相关操作.
媒体相关操作.
自定义菜单接口.
自定义菜单接口.
消息(接收和发送)接口.
消息(接收和发送)接口.
custom
客服主动回复消息.
客服主动回复消息.
mass
群发消息.
群发消息.
mass/masstoall
群发消息给所有用户.
群发消息给所有用户.
mass/masstogroup
根据分组进行群发消息.
根据分组进行群发消息.
mass/masstousers
根据OpenID列表群发消息.
根据OpenID列表群发消息.
mass/preview
预览接口.
预览接口.
request
被动接收基本的消息(事件).
被动接收基本的消息(事件).
response
被动回复用户消息.
被动回复用户消息.
template
模板消息接口.
模板消息接口.
用户管理接口.
用户管理接口.
oauth2
网页授权获取用户基本信息.
网页授权获取用户基本信息.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL