| @@ -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 | |||
| } | |||
| // 用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。 | |||
| @@ -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 | |||
| } | |||
| @@ -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": | |||
| @@ -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 | |||