= GoWechat使用文档
:toc: left
//:icons: font

== 零、使用前阅读

=== 综述
image::img/GoWechat_overview.png[]

项目目标：构建一套最简单易用的微信各大平台通用golang API

适用范围：可以与各种平台无缝整合，beego, gin, http （example目录提供完整的使用例子）

开源地址：[Github地址](https://github.com/yaotian/gowechat)

GoDoc: [GoDoc地址](https://godoc.org/github.com/yaotian/gowechat)

[[install,安装]]
=== 安装
  go get github.com/yaotian/gowechat

[[use,使用]]
=== 配置

==== 参数
```go
var config = wxcontext.Config{
  //微信公众平台，商户平台，需要填写的信息
	AppID:          "your app id",
	AppSecret:      "your app secret",
	Token:          "your token",
	EncodingAESKey: "your encoding aes key", 

	//以下是 mch商户平台需要的变量
	SslCertFilePath string //证书公钥文件的路径
	SslKeyFilePath  string //证书私钥文件的路径
	SslCertContent  string //公钥证书的内容
	SslKeyContent   string //私钥证书的内容
	MchID           string //商户ID
	MchAPIKey       string //商户平台设置的api key
}

```

=== 微信平台的操作接口


==== 微信公众平台

```go
wc := gowechat.NewWechat(config)
//微信公众平台
mp, err := wc.MpMgr()
if err != nil {
  return
}
```

==== 微信商户平台

```go
wc := gowechat.NewWechat(config)
//微信商户平台
mch , err := wc.MchMgr()
if err != nil {
  return
}

```

=== 在框架中使用

==== beego中使用的例子
  ./examples/beego 

==== gin中使用的例子
  ./examples/gin

==== net/http中使用的例子 
  ./examples/http

[[mp,mp]]
== 一、微信公众平台

=== 概述 
image::img/GoWechat_mp.png[]


=== 1.获取接口

[source,go]
----
var config = wxcontext.Config{
  //微信公众平台，商户平台，需要填写的信息
	AppID:          "your app id",
	AppSecret:      "your app secret",
	Token:          "your token",
	EncodingAESKey: "your encoding aes key", 
}

wc := gowechat.NewWechat(config)
//微信公众平台
mp, err := wc.MpMgr()
if err != nil {
  return
}
----


=== 2.开发消息服务器
将开发的消息服务器（http://your_domain/wx_server）接入到微信公众平台，有几个步骤

1. 接入你的消息服务器时，微信公众号后台，会发信息给你的服务器，来进行验证。所以你的服务器需要知道如何处理验证消息。    
2. 微信公众平台与你的消息服务器之间通过消息通信来进行同步合作。所以你的消息服务器需要知道如何处理消息与如何发送消息。

本接口将复杂的过程（加密，打包，验证等等）封装了，让你只需要关心业务环节。

将以下的代码，加入到你的controller中，接口自动完成上面所说的两个功能，

[IMPORTANT]
你的controller需要能接收GET与POST两种消息

[source,go]
----
wc := gowechat.NewWechat(config)
//微信公众平台
mp, err := wc.MpMgr()
if err != nil {
  return
}

// 传入request和responseWriter
msgHandler := mp.GetMsgHandler(c.Ctx.Request, c.Ctx.ResponseWriter)

//设置接收消息的处理方法
msgHandler.SetHandleMessageFunc(func(msg message.MixMessage) *message.Reply {
  switch msg.Event {
  case message.EventSubscribe:
    return c.handleSubscribe(&msg)
  case message.EventScan:
    return c.handleScan(&msg)
  case message.EventUnsubscribe:
    return c.handleUnsubscribe(&msg)
  case message.EventClick:
    return c.handleClick(&msg)
  case message.EventLocation:
    return c.handleLocation(&msg)
  }

  switch msg.MsgType {
  case message.MsgTypeText:
    //回复消息：演示回复用户发送的消息
    text := message.NewText(msg.Content)
    return &message.Reply{message.MsgTypeText, text}
  case message.MsgTypeImage:
    return c.handleImage(&msg)
  }

	//处理消息接收以及回复
	err = msgHandler.Handle()
	if err != nil {
		beego.Error(err)
	}

})

----

==== 消息类型
消息分为以下几种：文本、图片、视频、声音、链接、坐标、图文、文章。

===== 文本消息
[source,go]
----
//回复消息：演示回复用户发送的消息
text := message.NewText("your message want to be sent")
return &message.Reply{message.MsgTypeText, text}
----

===== 图片消息
[source,go]
----
pic := message.NewImage("your_pic_mediaID")
return &message.Reply{message.MsgTypeImage, pic}
----

===== 视频消息
[source,go]
----
video := message.NewVideo("your_mediaID", "your_title", "your_description")
return &message.Reply{message.MsgTypeVideo, video}
----

===== 声音消息
[source,go]
----
voice := message.NewVoice("your_mediaID")
return &message.Reply{message.MsgTypeVoice, voice}
----

===== 链接消息
NOTE: 微信目前不支持回复链接消息

===== 坐标消息
NOTE: 微信目前不支持坐标消息

===== 图文消息
[source,go]
----
news := message.NewNews([]*Article{message.NewArticle("your_title", "your_description", "your_picURL", "your_url")})
return &message.Reply{message.MsgTypeNews, news}
----


=== 3.网页授权

[source,go]
----
wc := gowechat.NewWechat(config)
//微信公众平台
mp, err := wc.MpMgr()
if err != nil {
  return
}

oauthHandler := mp.GetPageOAuthHandler(c.Ctx.Request, c.Ctx.ResponseWriter, "http://your_domain/wxoauth")

oauthHandler.SetFuncCheckOpenIDExisting(func(openID string) (existing bool, stopNow bool) {
  //看自己的系统中是否已经存在此openID的用户
  //如果已经存在， 调用自己的Login 方法，设置cookie等，return true
  //如果还不存在，return false, handler会自动去取用户信息
  //your code
  return
})

oauthHandler.SetFuncAfterGetUserInfo(func(user user.Info) (stopNow bool) {
  //已获得用户信息，这里用信息做注册使用
  //调用自己的Login方法，设置cookie等
  //your code
})

oauthHandler.Handle()

----



=== 4.模板消息

[source,go]
----
wc := gowechat.NewWechat(config)
//微信公众平台
mp, err := wc.MpMgr()
if err != nil {
  return
}
api := mp.GetTemplate()

tmplMsg := new(template.Message)
tmplMsg.URL = your_url
tmplMsg.ToUser = your_to_user_openID
tmplMsg.TemplateID = your_templateID
tmplMsg.Data = makeMsgMessageData("your_first", "your_subject", "your_sender_name", "your_msg_content")
_, err = api.Send(tmplMsg)

func makeMsgMessageData(first, subject, sender, remark string) (data map[string]*template.DataItem) {
	data = make(map[string]*template.DataItem)
	data["first"] = &template.DataItem{Value: first, Color: "#173177"}
	data["keyword1"] = &template.DataItem{Value: sender, Color: "#173177"}
	data["keyword2"] = &template.DataItem{Value: subject, Color: "#173177"}
	remark = remark + "\n如果您不想收到此通知，请点击后->[消息]->右上角的设置按钮"
	data["remark"] = &template.DataItem{Value: remark, Color: "#2eb3ff"}
	return
}


----

=== 5.二维码

支持

* 临时二维码
* 永久二维码

输入参数支持 string， int


[source,go]
----
wc := gowechat.NewWechat(config)
//微信公众平台
mp, err := wc.MpMgr()
if err != nil {
  return
}

qrResult, err := mp.GetQrcode().CreateTemporaryQRCodeWithSceneString("your_scene_string", 60)
if err != nil {
  return
}

qrImageURL := qrResult.ImageURL()

----

=== 6.用户

[source,go]
----
wc := gowechat.NewWechat(config)
//微信公众平台
mp, err := wc.MpMgr()
if err != nil {
  return
}

var userInfo = new(user.Info)
userInfo, err = mp.GetUser().GetUserInfo("your_openID")
if err != nil {
  return
}

----

=== 7.菜单

[source,go]
----
wc := gowechat.NewWechat(config)
//微信公众平台
mp, err := wc.MpMgr()
if err != nil {
  return
}

var btnMenus []*menu.Button

btn = new(menu.Button)
btn.Name = m.Name
btn.SetViewButton("your_name", "your_url") //url menu

btnMenus = append(btnMenus,btn)

mp.GetMenu().SetMenu(btnMenus)

----



[[mch,mch]]
== 二、商户平台（微信支付）

=== 概述

image::img/GoWechat_mch.png[]


=== 1.获取接口

[source,go]
----
var config = wxcontext.Config{
  //微信公众平台，商户平台，需要填写的信息
	AppID:          "your app id",
	AppSecret:      "your app secret",
	Token:          "your token",
	EncodingAESKey: "your encoding aes key", 

  //------以下是 mch商户平台需要的变量
  //
  //证书公钥，路径，内容要保证只要有一项设置
	SslCertFilePath: "your cert file path" //证书公钥文件的路径
	SslCertContent:  "your cert content" //公钥证书的内容

  //私钥，路径，内容要保证只要有一项设置
	SslKeyFilePath:  "your key file path" //证书私钥文件的路径
	SslKeyContent:   "your key content" //私钥证书的内容

	MchID: "your mch ID"           //商户ID
	MchAPIKey: "your mch API Key"  //商户平台设置的api key
  //------ End 商户平台设置
}

wc := gowechat.NewWechat(config)
//微信商户平台
mch , err := wc.MchMgr()
if err != nil {
  return
}
----


=== 2.支付订单
[source,golang]
----
var order = pay.OrderInput {
	OpenID: "your openID", //trade_type=JSAPI时（即公众号支付），此参数必传，此参数为微信用户在商户对应appid下的唯一标识
	Body: "购物支付",       //String(128)
	OutTradeNum: "your trade num", //String(32) 20150806125346 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一。
	TotalFee: your_pay_number,     //分为单位
	IP: "your_ip",
	NotifyURL:  "your_notify_URL", //异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数
	ProductID:""    //trade_type=NATIVE时（即扫码支付），此参数必传

	tradeType: "JSAPI" //JSAPI，NATIVE，APP
}
----

公众号支付 H5中调用jssdk支付：
[source,go]
----
//公众号支付（jssdk支付数据）
cfg, err := mch.GetPay().GetJsAPIConfig(order)
----

扫码支付：
[source,go]
----
qrcodeImageURL, err := mch.GetPay().GetNativePayQrcodePicURL(order)
----

=== 3.为两种支付方式生成JS配置

==== a.WeixinJSBridge:
支付数据，json格式，这些数据可以传递到前端模板中使用
[source,go]
----
//公众号支付（jssdk支付数据）
cfg, err := mch.GetPay().GetJsAPIConfig(order)
json := cfg.ToJSON()
----

前端的JS代码例子
[source,javascript]
----
...
WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {{$json}},
       function(res){
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {
                // 使用以上方式判断前端返回,微信团队郑重提示：
                // res.err_msg将在用户支付成功后返回
                // ok，但并不保证它绝对可靠。
           }
       }
   );
...
----

==== b.JSSDK:
支付数据，map格式，这些数据可以传递到前端模板中使用
[source,go]
----
//公众号支付（jssdk支付数据）
cfg, err := mch.GetPay().GetJsAPIConfig(order)
mapData := cfg.ToMap()
----

前端的JS代码例子
[source,javascript]
----
wx.chooseWXPay({
    timestamp: {{$timestamp}},
    nonceStr: {{$nonceStr}},
    package: {{$package}},
    signType: {{$signType}},
    paySign: {{$paySign}}, // 支付签名
    success: function (res) {
        // 支付成功后的回调函数
    }
});
----

=== 4.支付通知
微信支付后，微信服务器会将支付结果回调到你的服务器，服务器地址是在订单参数中设置的NotifyURL,为了安全，过来的数据需要检验正确。
[source,go]
----
wc := gowechat.NewWechat(config)
//微信商户平台
mch , err := wc.MchMgr()
if err != nil {
  return
}

//回调的数据检查，例子
req := c.Ctx.Request
data, err := ioutil.ReadAll(req.Body)
if err != nil {
	return
}
isSuccess, err := mch.GetPay().CheckPayNotifyData(data)
if err != nil {
	return
}
//成功后，你自己的处理

----

=== 5.查询订单

该接口提供所有微信支付订单的查询，商户可以通过该接口主动查询订单状态，完成下一步的业务逻辑。

需要调用查询接口的情况：

* 当商户后台、网络、服务器等出现异常，商户系统最终未接收到支付通知；
* 调用支付接口后，返回系统错误或未知交易状态情况；
* 调用被扫支付API，返回USERPAYING的状态；
* 调用关单或撤销接口API之前，需确认支付状态；

=== 6.关闭订单

=== 7.退款

=== 8.账单

=== 9.发红包

前提条件：发红包前，你必须在商户平台中充值

[source,go]
----

var input paytool.RedPackInput
input.ToOpenID = "your openID"
input.MoneyFen = int(your_money * 100)
input.SendName = "your send Name"
input.ActName = "佣金提现"
input.Wishing = "您的分销佣金"
input.Remark = fmt.Sprintf("佣金提现 User:%s Weixin:%s", "your nickname", "your openID")
input.IP = "your IP"
input.SceneID = paytool.SceneIDAgentBonous

//商户平台接口
mch , err := wc.MchMgr()
if err != nil {
  return
}
_, err = mch.GetPayTool().SendRedPack(input)

----


[[open,open]]
== 三、开放平台

[[corp,corp]]
== 四、企业微信

[[mini,mini]]
== 五、小程序

[[code,code]]
== 六、谁正在使用

[果真免费微社区及推广平台](http://84u6.com)

[新课堂360教育云平台](http://xkt360.com)

如果您正在使用GoWechat，请在github内留言

== 七、微信官方文档参考

.Windtrainer workouts
[width="100%",cols="1,^4,10",options="header"]
|=========================================================
|目录  | 对应     |官方文档 

|/mp | 微信公众平台(订阅号，服务号)| [文档](https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432) 

|/mch | 微信商家平台  |  [文档](https://pay.weixin.qq.com/wiki/doc/api/index.html)

|/open| 微信开放平台|   [文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&lang=zh_CN) 

|/corp | 微信企业号  | [文档](http://qydev.weixin.qq.com/wiki/index.php?title=%E9%A6%96%E9%A1%B5) 

|=========================================================
