blob: 8dca00ac9d776326637e72678467eaa4c9e0d47c [file] [log] [blame]
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) (*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"}.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 {
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 NewSession(appID, appSecret, termID, baseURL, timeout, 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)
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)
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.StatusCode != 200 {
log.Errorf("Request Error %v\n", r.StatusCode)
err = fmt.Errorf("Request Error, StatusCode : %v", r.StatusCode)
return
}
if r.Body != nil {
defer r.Body.Close()
}
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
}