| package swservice |
| |
| import ( |
| "crypto/hmac" |
| "crypto/sha1" |
| "crypto/tls" |
| "encoding/hex" |
| "encoding/json" |
| "errors" |
| "fmt" |
| "io/ioutil" |
| "net" |
| "net/http" |
| "net/url" |
| "strconv" |
| "strings" |
| "sync" |
| "time" |
| |
| "crypto/x509" |
| |
| log "github.com/sirupsen/logrus" |
| "github.com/franela/goreq" |
| ) |
| |
| var ( |
| // ErrBadCAPEM 错误的 CA 文件 |
| ErrBadCAPEM = errors.New("不正确的PEM文件") |
| ) |
| |
| // WebSession web session object |
| type WebSession struct { |
| // AppId app id |
| AppID string |
| // TermId term id |
| TermID string |
| // Appsecret secret |
| AppSecret string |
| // BaseUrl base url |
| BaseURL string |
| // DefaultTimeout default time |
| DefaultTimeout int |
| sessionKey string |
| sslVerify bool |
| httpConnectionPool sync.Pool |
| } |
| |
| func safeGetJSONInt(value interface{}) int { |
| if value == nil { |
| return 0 |
| } |
| s := fmt.Sprintf("%v", value) |
| i, err := strconv.Atoi(s) |
| if err != nil { |
| return 0 |
| } |
| return i |
| } |
| |
| // ServiceResponse service response object |
| type ServiceResponse struct { |
| // RetCode return code |
| RetCode int |
| // RetMsg return message |
| RetMsg string |
| // Result return data |
| Result map[string]interface{} |
| } |
| |
| // NewServiceResponseFromJSON parse json response data |
| func NewServiceResponseFromJSON(jsonData interface{}) *ServiceResponse { |
| if jsonData == nil { |
| return nil |
| } |
| res := &ServiceResponse{} |
| res.Result = jsonData.(map[string]interface{}) |
| res.RetCode = safeGetJSONInt(res.Result["retcode"]) |
| res.RetMsg = res.GetStrValue("retmsg") |
| return res |
| } |
| |
| // GetIntValue get int value |
| func (r *ServiceResponse) GetIntValue(name string) int { |
| return safeGetJSONInt(r.Result[name]) |
| } |
| |
| // GetStrValue get string value |
| func (r *ServiceResponse) GetStrValue(name string) string { |
| s, ok := r.Result[name] |
| if ok { |
| return fmt.Sprintf("%v", s) |
| } |
| return "" |
| } |
| |
| // GetInterfaceValue get value as interface |
| func (r *ServiceResponse) GetInterfaceValue(name string) interface{} { |
| s, ok := r.Result[name] |
| if ok { |
| return s |
| } |
| return nil |
| } |
| |
| // GetFloatValue get float value |
| func (r *ServiceResponse) GetFloatValue(name string) float64 { |
| if s, ok := r.Result[name]; ok { |
| t := fmt.Sprintf("%v", s) |
| f, err := strconv.ParseFloat(t, 64) |
| if err != nil { |
| return 0.0 |
| } |
| return f |
| } |
| return 0.0 |
| } |
| |
| // DoGet send GET request |
| func (w *WebSession) DoGet(uri string, params map[string]string, |
| timeout int) (*goreq.Response, error) { |
| |
| fullURL := w.BaseURL + uri |
| |
| vl := url.Values{} |
| |
| if params != nil { |
| for k, v := range params { |
| vl.Add(k, v) |
| } |
| } |
| fullURL = fullURL + "?" + vl.Encode() |
| return goreq.Request{Uri: fullURL, |
| Method: "GET", |
| Timeout: time.Duration(timeout) * time.Second}.Do() |
| } |
| |
| // GetTimestamp get time stamp format 20160103133455 |
| func (w *WebSession) GetTimestamp() string { |
| t := time.Now() |
| return fmt.Sprintf("%04d%02d%02d%02d%02d%02d", t.Year(), t.Month(), t.Day(), |
| t.Hour(), t.Minute(), t.Second()) |
| } |
| |
| // SignWithKey sign with key |
| func (w *WebSession) SignWithKey(key, message string) string { |
| mac := hmac.New(sha1.New, []byte(key)) |
| mac.Write([]byte(message)) |
| res := mac.Sum(nil) |
| return hex.EncodeToString(res) |
| } |
| |
| // Sign sign data |
| func (w *WebSession) Sign(message string) string { |
| return w.SignWithKey(w.AppSecret, message) |
| } |
| |
| func newTransport(baseurl string, sslVerify bool) *http.Transport { |
| var transport http.Transport |
| if strings.HasPrefix(baseurl, "https://") { |
| var b bool |
| if sslVerify { |
| b = false |
| } else { |
| b = true |
| } |
| transport = http.Transport{MaxIdleConnsPerHost: 0, |
| TLSClientConfig: &tls.Config{InsecureSkipVerify: b}, |
| TLSHandshakeTimeout: time.Duration(1) * time.Second, |
| Dial: func(network, addr string) (net.Conn, error) { |
| defaultTimeout := time.Duration(1) * time.Second |
| return net.DialTimeout(network, addr, defaultTimeout) |
| }} |
| } else if strings.HasPrefix(baseurl, "http://") { |
| transport = http.Transport{MaxIdleConnsPerHost: 0, |
| Dial: func(network, addr string) (net.Conn, error) { |
| defaultTimeout := time.Duration(1) * time.Second |
| return net.DialTimeout(network, addr, defaultTimeout) |
| }} |
| } |
| log.Debugf("创建新连接") |
| return &transport |
| } |
| |
| // DoPost send POST request |
| func (w *WebSession) DoPost(uri string, param map[string]string) (*goreq.Response, error) { |
| param["app_id"] = w.AppID |
| param["term_id"] = w.TermID |
| param["sign_method"] = "HMAC" |
| param["session_key"] = w.sessionKey |
| ts := w.GetTimestamp() |
| param["timestamp"] = ts |
| param["sign"] = w.Sign(w.AppID + w.TermID + w.sessionKey + ts) |
| |
| fullURL := w.BaseURL + uri |
| data, err := json.Marshal(param) |
| if err != nil { |
| return nil, err |
| } |
| var r *goreq.Response |
| r, err = goreq.Request{ |
| Uri: fullURL, |
| Method: "POST", |
| ContentType: "application/json", |
| Body: data, |
| Timeout: time.Duration(3) * time.Second}.Do() |
| if err != nil || r.StatusCode != 200 { |
| log.Errorf("Status=%v, err=%v", r, err) |
| } |
| return r, err |
| } |
| |
| // Auth authorization |
| func (w *WebSession) Auth() error { |
| token, err := w.getAuthToken() |
| if err != nil { |
| return err |
| } |
| err = w.getAppAccessKey(token) |
| if err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| // NewSession new session object |
| func NewSession(appid, appsecret, termid, baseurl string, timeout int, sslVerify bool) *WebSession { |
| if transport, ok := goreq.DefaultTransport.(*http.Transport); ok { |
| transport.TLSClientConfig = nil |
| } |
| return &WebSession{ |
| AppID: appid, |
| AppSecret: appsecret, |
| TermID: termid, |
| BaseURL: baseurl, |
| DefaultTimeout: timeout, |
| sslVerify: sslVerify, |
| httpConnectionPool: sync.Pool{New: func() interface{} { |
| return newTransport(baseurl, sslVerify) |
| }}} |
| } |
| |
| // NewSessionWithCA new session ca |
| func NewSessionWithCA(appID, appSecret, termID, baseURL string, |
| timeout int, ca []byte) (*WebSession, error) { |
| certs := x509.NewCertPool() |
| if !certs.AppendCertsFromPEM(ca) { |
| return nil, ErrBadCAPEM |
| } |
| if transport, ok := goreq.DefaultTransport.(*http.Transport); ok { |
| transport.TLSClientConfig = &tls.Config{RootCAs: certs} |
| } |
| |
| return &WebSession{ |
| AppID: appID, |
| AppSecret: appSecret, |
| TermID: termID, |
| BaseURL: baseURL, |
| DefaultTimeout: timeout, |
| sslVerify: true, |
| }, nil |
| } |
| |
| func (w *WebSession) getAuthToken() (string, error) { |
| type FormJSON struct { |
| AppID string `json:"app_id"` |
| TermID string `json:"term_id"` |
| AccessToken string `json:"access_token"` |
| } |
| |
| uri := fmt.Sprintf("/authservice/getauth/%v/getaccesstoken", w.AppID) |
| |
| params := make(map[string]string) |
| params["term_id"] = w.TermID |
| r, err := w.DoGet(uri, params, 5) |
| |
| if err != nil { |
| return "", err |
| } |
| if r.StatusCode != 200 { |
| return "", errors.New("请求失败") |
| } |
| |
| body, err := ioutil.ReadAll(r.Body) |
| defer r.Body.Close() |
| |
| s := &FormJSON{} |
| err = json.Unmarshal(body, &s) |
| if err != nil { |
| log.Errorf("json unmarshal err %v", err) |
| return "", errors.New("解析失败") |
| } |
| return s.AccessToken, nil |
| } |
| |
| func (w *WebSession) getAppAccessKey(token string) error { |
| type FormJSON struct { |
| AppID string `json:"app_id"` |
| TermID string `json:"term_id"` |
| SessionKey string `json:"session_key"` |
| CardKey string `json:"card_key"` |
| } |
| |
| uri := fmt.Sprintf("/authservice/getauth/%v", w.AppID) |
| |
| params := make(map[string]string) |
| params["term_id"] = w.TermID |
| params["access_token"] = token |
| params["timestamp"] = w.GetTimestamp() |
| params["v"] = "1" |
| params["sign"] = w.Sign(token + params["timestamp"]) |
| params["sign_method"] = "HMAC" |
| |
| r, err := w.DoGet(uri, params, 5) |
| if r != nil { |
| defer r.Body.Close() |
| } |
| if err != nil { |
| log.Errorf("err = %v\n", err) |
| return err |
| } |
| if r.StatusCode != 200 { |
| log.Errorf(" errcode = %v\n", r.StatusCode) |
| return fmt.Errorf("code %v", r.StatusCode) |
| } |
| |
| body, err := ioutil.ReadAll(r.Body) |
| |
| s := &FormJSON{} |
| err = json.Unmarshal(body, &s) |
| if err != nil { |
| log.Errorf("json unmarshal err %v", err) |
| return err |
| } |
| w.sessionKey = s.SessionKey |
| return nil |
| } |
| |
| // CallYKTApi call ykt api function |
| func (w *WebSession) CallYKTApi(request *MessageWriter) (*MessageReader, error) { |
| callData := request.Serialize() |
| params := make(map[string]string) |
| params["funcdata"] = callData |
| r, err := w.DoPost("/ecardservice/ecardapi", params) |
| |
| if r != nil { |
| defer r.Body.Close() |
| } |
| if err != nil { |
| log.Errorf(" err = %v\n", err) |
| return nil, err |
| } |
| |
| if r.StatusCode != 200 { |
| return nil, fmt.Errorf("Request StatusCode:%v", r.StatusCode) |
| } |
| |
| body, err := ioutil.ReadAll(r.Body) |
| return NewMessageReader(body), nil |
| } |
| |
| // CallService call epay service |
| func (w *WebSession) CallService(path string, params map[string]interface{}, |
| signField []string, timeout int) (response *ServiceResponse, err error) { |
| |
| return w.CallService2(path, params, timeout, signField...) |
| } |
| |
| // CallService2 call epay service |
| func (w *WebSession) CallService2(path string, params map[string]interface{}, timeout int, |
| signField ...string) (response *ServiceResponse, err error) { |
| err = nil |
| params["app_id"] = w.AppID |
| params["term_id"] = w.TermID |
| ts := w.GetTimestamp() |
| params["timestamp"] = ts |
| |
| vl := &url.Values{} |
| for k, v := range params { |
| vl.Set(k, fmt.Sprintf("%v", v)) |
| } |
| |
| signData := "" |
| for _, k := range signField { |
| if v, ok := params[k]; ok { |
| signData += fmt.Sprintf("%v", v) |
| } |
| } |
| signData += ts + w.sessionKey |
| vl.Set("sign_method", "HMAC") |
| log.Debugf("Sign: key[%v] data[%v]\n", w.sessionKey, signData) |
| vl.Set("sign", w.SignWithKey(w.AppSecret, signData)) |
| |
| fullURL := w.BaseURL + path |
| log.Debugf("CallService: %v\n", fullURL) |
| var r *goreq.Response |
| r, err = goreq.Request{ |
| Uri: fullURL, |
| Accept: "application/json", |
| ContentType: "application/x-www-form-urlencoded", |
| Method: "POST", |
| Timeout: time.Duration(timeout) * time.Second, |
| Body: vl.Encode()}.Do() |
| if err != nil { |
| log.Errorf("Status=%v, err=%v", r, err) |
| return |
| } |
| if r != nil && r.Body != nil { |
| defer r.Body.Close() |
| } |
| |
| if r.StatusCode != 200 { |
| log.Errorf("Request Error %v\n", r.StatusCode) |
| err = fmt.Errorf("Request Error, StatusCode : %v", r.StatusCode) |
| return |
| } |
| |
| body, err := ioutil.ReadAll(r.Body) |
| var s interface{} |
| err = json.Unmarshal(body, &s) |
| if err != nil { |
| log.Errorf("json unmarshal err %v", err) |
| return |
| } |
| response = NewServiceResponseFromJSON(s) |
| return |
| } |