From 8d9e22df6e1a1346817cfce38b2cfdb8a2633b4f Mon Sep 17 00:00:00 2001 From: Patrick Peng Sun Date: Thu, 6 Jul 2017 19:06:36 +1000 Subject: [PATCH] create qr api call tested. --- main.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- qrcode.go | 37 ++++++++++++++++++++++++++++++------- serveEvents.go | 2 ++ server_test.go | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 9965f13..6e79ad1 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,8 @@ import ( "log" "net/http" "net/http/httputil" + "net/url" + "strconv" "time" ) @@ -60,7 +62,7 @@ func setupHTTPHandler() { http.HandleFunc("/MP_verify_6JqVkftKr39GMakA.txt", mpDomainAuthSecret) http.HandleFunc("/profile_newly_register", initialRegistrationHandler) http.HandleFunc("/iapi/getAccessToken", supplyAccessToken) - http.HandleFunc("/iapi/createQr", iapiCreateQrCode) + http.HandleFunc("/iapi/createWechatQr", iapiCreateWechatQrCode) http.ListenAndServe(":65500", nil) } @@ -114,11 +116,53 @@ func supplyAccessToken(w http.ResponseWriter, r *http.Request) { } } -func iapiCreateQrCode(w http.ResponseWriter, r *http.Request) { +func iapiCreateWechatQrCode(w http.ResponseWriter, r *http.Request) { if !checkSignatureByToken(r, IntraAPIConfig.CRMSecrete) { + w.WriteHeader(http.StatusUnauthorized) fmt.Fprint(w, "unauthorized") + return + } + rq := r.URL.RawQuery + m, _ := url.ParseQuery(rq) + + qrValue, qok := m["qrValue"] + expire, eok := m["expire"] + + if !qok || !eok { + w.WriteHeader(http.StatusUnprocessableEntity) + fmt.Fprintf(w, "parameter not correct, bad api call %s", time.Now().Format(getCrmTimeLayout())) + return + } + + if isStrInt(qrValue[0]) && isStrInt(expire[0]) { + intVal, _ := strconv.Atoi(qrValue[0]) + intExpire, _ := strconv.Atoi(expire[0]) + jsonB, err := iapiCreateTempQr(int32(intVal), int32(intExpire)) + if err == nil { + w.Write(jsonB) + } else { + fmt.Fprintf(w, "%s", err) + } + return } + if !isStrInt(qrValue[0]) && expire[0] == "0" { + jsonB, err := isapiCreatePermQr(qrValue[0]) + if err == nil { + w.Write(jsonB) + } else { + fmt.Fprintf(w, "%s", err) + } + return + + } +} + +func isStrInt(v string) bool { + if _, err := strconv.Atoi(v); err == nil { + return true + } + return false } // 用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。 diff --git a/qrcode.go b/qrcode.go index de2a3bf..58e7cd6 100644 --- a/qrcode.go +++ b/qrcode.go @@ -6,13 +6,13 @@ import ( "log" ) -//QRLimitScene 生成永久二维码的信息 -type QRLimitScene struct { +//QRPermanentScene 生成永久二维码的信息 +type QRPermanentScene struct { Name string `json:"action_name"` Info struct { Scene struct { - ID int32 `json:"scene_id"` - IDstr string `json:"scene_str"` //对于永久二维码,我们使用 字符串, 用来区分 数字为主的 临时二维码 + ID int32 `json:"scene_id,omitempty"` + IDstr string `json:"scene_str,omitempty"` //对于永久二维码,我们使用 字符串, 用来区分 数字为主的 临时二维码 } `json:"scene"` } `json:"action_info"` } @@ -38,16 +38,20 @@ type QRSrcInfo struct { //CreateProfileEditQR give user a temp password to login //for editing one's own profile func CreateProfileEditQR() (result QRSrcInfo, err error) { + return createPermanentQR("edit_profile") +} + +func createPermanentQR(value string) (result QRSrcInfo, err error) { // http请求方式: POST // URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKENPOST数据格式:json // POST数据例子:{"action_name": "QR_LIMIT_SCENE", "action_info": {"scene": {"scene_id": 123}}} // 或者也可以使用以下POST数据创建字符串形式的二维码参数: // {"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene": {"scene_str": "123"}}} u := getURL4CreateQR() - s := QRLimitScene{} + s := QRPermanentScene{} //s.Name = "QR_LIMIT_SCENE" //use integer s.Name = "QR_LIMIT_STR_SCENE" //use string - s.Info.Scene.IDstr = "edit_profile" + s.Info.Scene.IDstr = value j, _ := json.Marshal(s) log.Println(string(j)) resp, err := postJSON(j, u) @@ -58,8 +62,8 @@ func CreateProfileEditQR() (result QRSrcInfo, err error) { //image: https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQEm8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyS19UblE5Z3VjU2gxMDAwME0wM04AAgRMYh1ZAwQAAAAA err = json.Unmarshal([]byte(resp), &result) return -} +} func getURL4CreateQR() (URL string) { atk, _ := GetAccessToken() u := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s", atk) @@ -98,3 +102,22 @@ func CreateTempQr(sceneID int32, expire int32) (result QRSrcInfo, err error) { func qrImgByTicket(ticket string) (url string) { return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket } + +func iapiCreateTempQr(qr, expire int32) (jsonB []byte, err error) { + if expire <= 0 { + expire = 30 //30 temp seconds only for 0 value + } + info, err := CreateTempQr(qr, expire) + if err == nil { + jsonB, err = json.Marshal(info) + } + return +} + +func isapiCreatePermQr(qr string) (jsonB []byte, err error) { + info, err := createPermanentQR(qr) + if err == nil { + jsonB, err = json.Marshal(info) + } + return +} diff --git a/serveEvents.go b/serveEvents.go index 95b84ad..be1c690 100644 --- a/serveEvents.go +++ b/serveEvents.go @@ -12,6 +12,8 @@ func (ss *openIDSessionData) serveEvents(in InWechatMsg) (processed bool) { in.replyText("") onUnSubscribe(in) return + case "scancode_waitmsg": + in.replyText("scancode_waitmsg/" + e.EventKey) case "SCAN": in.replyText("SCAN/" + e.EventKey) case "LOCATION": diff --git a/server_test.go b/server_test.go index faf66b1..d8b6f60 100644 --- a/server_test.go +++ b/server_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "encoding/json" "fmt" "log" "net/http" @@ -91,6 +92,20 @@ func TestGetAccesstokenUnAuthorized(t *testing.T) { } +func TestCreatePermenentWechatQr(t *testing.T) { + scene := "edit_profile" //do not create rubbish, create something that we can use later on + expected := "gQEm8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyS19UblE5Z3VjU2gxMDAwME0wM04AAgRMYh1ZAwQAAAAA" + req := buildReqPermQr(scene) + rr, _ := getHTTPResponse(req, iapiCreateWechatQrCode) + m := rr.Body.String() + info := QRSrcInfo{} + err := json.Unmarshal([]byte(m), &info) + AssertEqual(t, err, nil, "decode json should be correct") + log.Println(info) + log.Println(expected) + AssertEqual(t, info.Ticket, expected, "expected ticket not match") +} + func getHTTPResponse(req *http.Request, handler http.HandlerFunc) (rr *httptest.ResponseRecorder, err error) { // Our handlers satisfy http.Handler, so we can call their ServeHTTP method @@ -191,6 +206,23 @@ func buildReqGetAccessTokenUnAuthorized() *http.Request { return req } +func buildReqPermQr(scene string) *http.Request { + req, err := http.NewRequest("GET", "/iapi/createWechatQr", nil) + if err != nil { + log.Fatal(err) + } + q := req.URL.Query() + q.Add("qrValue", scene) + q.Add("expire", "0") + req.URL.RawQuery = q.Encode() + + buildReqCommonSignature(req, IntraAPIConfig.CRMSecrete) + buildReqCommonHeader(req) + + return req + +} + func buildSignature(token string) (signature, timestamp, nonce string) { timestamp = fmt.Sprintf("%d", int32(time.Now().Unix())) nonce = "1461107899" //a randome string cut from previous wechat request