247 lines
6.6 KiB
Go
247 lines
6.6 KiB
Go
package elmapi
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"git.rosy.net.cn/baseapi/platformapi"
|
|
"git.rosy.net.cn/baseapi/utils"
|
|
)
|
|
|
|
const (
|
|
sandboxURL = "https://open-api-sandbox.shop.ele.me/api/v1"
|
|
sandboxAuthorizeURL = "https://open-api-sandbox.shop.ele.me/authorize"
|
|
snadboxTokenURL = "https://open-api-sandbox.shop.ele.me/token"
|
|
|
|
prodURL = "https://open-api.shop.ele.me/api/v1"
|
|
prodAuthorizeURL = "https://open-api.shop.ele.me/authorize"
|
|
prodTokenURL = "https://open-api.shop.ele.me/token"
|
|
|
|
signKey = "signature"
|
|
)
|
|
|
|
type ResponseResult struct {
|
|
ID string
|
|
Result interface{}
|
|
Error map[string]interface{}
|
|
}
|
|
|
|
type TokenInfo struct {
|
|
AccessToken string `json:"access_token"`
|
|
TokenType string `json:"token_type"`
|
|
ExpiresIn int `json:"expires_in"`
|
|
}
|
|
|
|
type API struct {
|
|
token string
|
|
appKey string
|
|
secret string
|
|
isProd bool
|
|
fullURL string
|
|
client *http.Client
|
|
config *platformapi.APIConfig
|
|
locker sync.RWMutex
|
|
}
|
|
|
|
type payload struct {
|
|
Token string `json:"token"`
|
|
Nop string `json:"nop"`
|
|
Metas map[string]interface{} `json:"metas"`
|
|
Params map[string]interface{} `json:"params"`
|
|
Action string `json:"action"`
|
|
ID string `json:"id"`
|
|
Signature string `json:"signature"`
|
|
}
|
|
|
|
func New(token, appKey, secret string, isProd bool, config ...*platformapi.APIConfig) *API {
|
|
curConfig := platformapi.DefAPIConfig
|
|
if len(config) > 0 {
|
|
curConfig = *config[0]
|
|
}
|
|
api := &API{
|
|
token: token,
|
|
appKey: appKey,
|
|
secret: secret,
|
|
isProd: isProd,
|
|
client: &http.Client{Timeout: curConfig.ClientTimeout},
|
|
config: &curConfig,
|
|
}
|
|
|
|
if isProd {
|
|
api.fullURL = prodURL + "/"
|
|
} else {
|
|
api.fullURL = sandboxURL + "/"
|
|
}
|
|
return api
|
|
}
|
|
|
|
func (a *API) SetToken(newToken string) (retVal bool) {
|
|
curToken := a.GetToken()
|
|
if curToken != newToken {
|
|
a.locker.Lock()
|
|
defer a.locker.Unlock()
|
|
a.token = newToken
|
|
retVal = true
|
|
// baseapi.SugarLogger.Debugf("elm token changed from %v to %v", curToken, newToken)
|
|
}
|
|
return retVal
|
|
}
|
|
|
|
func (a *API) GetToken() string {
|
|
a.locker.RLock()
|
|
defer a.locker.RUnlock()
|
|
return a.token
|
|
}
|
|
|
|
func (a *API) signParamsMap(mapData map[string]interface{}, prefix string) string {
|
|
keyValues := make([]string, 0)
|
|
for k, v := range mapData {
|
|
if k != signKey {
|
|
vStr := ""
|
|
if prefix == "" { // callback sign
|
|
vStr = fmt.Sprint(v)
|
|
} else { // call sign
|
|
vBytes := utils.MustMarshal(v)
|
|
vStr = string(vBytes)
|
|
}
|
|
keyValues = append(keyValues, k+"="+vStr)
|
|
}
|
|
}
|
|
|
|
sort.Strings(keyValues)
|
|
finalStr := prefix + strings.Join(keyValues, "") + a.secret
|
|
return fmt.Sprintf("%X", md5.Sum([]byte(finalStr)))
|
|
}
|
|
|
|
func (a *API) signParams(action string, pl *payload) string {
|
|
mapData := utils.MergeMaps(pl.Metas, pl.Params)
|
|
return a.signParamsMap(mapData, action+a.GetToken())
|
|
}
|
|
|
|
func (a *API) AccessAPI(action string, params map[string]interface{}) (retVal *ResponseResult, err error) {
|
|
if params == nil {
|
|
params = make(map[string]interface{}, 0)
|
|
}
|
|
metas := map[string]interface{}{
|
|
"app_key": a.appKey,
|
|
"timestamp": utils.GetCurTimestamp(),
|
|
}
|
|
|
|
pl := &payload{
|
|
Token: a.GetToken(),
|
|
Nop: "1.0.0",
|
|
Metas: metas,
|
|
Params: params,
|
|
Action: action,
|
|
ID: utils.GetUUID(),
|
|
}
|
|
|
|
pl.Signature = a.signParams(action, pl)
|
|
plBytes := utils.MustMarshal(pl)
|
|
|
|
err = platformapi.AccessPlatformAPIWithRetry(a.client,
|
|
func() *http.Request {
|
|
request, _ := http.NewRequest(http.MethodPost, a.fullURL, bytes.NewReader(plBytes))
|
|
request.Header.Set("Content-Type", "application/json; charset=utf-8")
|
|
request.Header.Set("Content-Encoding", "gzip, deflate")
|
|
request.Header.Set("User-Agent", "eleme-golang-api")
|
|
return request
|
|
},
|
|
a.config,
|
|
func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
|
|
if jsonResult1 == nil {
|
|
return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
|
|
}
|
|
resultError, _ := jsonResult1["error"].(map[string]interface{})
|
|
retVal = &ResponseResult{
|
|
ID: jsonResult1["id"].(string),
|
|
Error: resultError,
|
|
Result: jsonResult1["result"],
|
|
}
|
|
errinfoMap := retVal.Error
|
|
if errinfoMap == nil {
|
|
return platformapi.ErrLevelSuccess, nil
|
|
}
|
|
code := errinfoMap["code"].(string)
|
|
newErr := utils.NewErrorCode(errinfoMap["message"].(string), code)
|
|
if code == "EXCEED_LIMIT" {
|
|
return platformapi.ErrLevelExceedLimit, newErr
|
|
} else if code == "SERVER_ERROR" || code == "BIZ_SYSTEM_ERROR" || code == "BIZ_1006" || code == "BUSINESS_ERROR" {
|
|
return platformapi.ErrLevelRecoverableErr, newErr
|
|
} else {
|
|
return platformapi.ErrLevelCodeIsNotOK, newErr
|
|
}
|
|
})
|
|
return retVal, err
|
|
}
|
|
|
|
func (a *API) getTokenURL() string {
|
|
if a.isProd {
|
|
return prodTokenURL
|
|
}
|
|
return snadboxTokenURL
|
|
}
|
|
|
|
func (a *API) getAuthorizeURL() string {
|
|
if a.isProd {
|
|
return prodAuthorizeURL
|
|
}
|
|
return sandboxAuthorizeURL
|
|
}
|
|
|
|
func (a *API) AcccessAPI2(baseURL string, params map[string]interface{}, method string) (retVal map[string]interface{}, err error) {
|
|
err = platformapi.AccessPlatformAPIWithRetry(a.client,
|
|
func() *http.Request {
|
|
var request *http.Request
|
|
if method == http.MethodPost {
|
|
params2 := make(url.Values)
|
|
for k, v := range params {
|
|
params2[k] = []string{fmt.Sprint(v)}
|
|
}
|
|
request, _ = http.NewRequest(method, baseURL, strings.NewReader(params2.Encode()))
|
|
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
request.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(a.appKey+":"+a.secret)))
|
|
} else {
|
|
request, _ = http.NewRequest(method, utils.GenerateGetURL(baseURL, "", params), nil)
|
|
}
|
|
return request
|
|
},
|
|
a.config,
|
|
func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
|
|
if jsonResult1 == nil {
|
|
return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
|
|
}
|
|
retVal = jsonResult1
|
|
return platformapi.ErrLevelSuccess, nil
|
|
})
|
|
|
|
return retVal, err
|
|
}
|
|
|
|
func (a *API) RefreshTokenIndividual() (retVal *TokenInfo, err error) {
|
|
result, err := a.AcccessAPI2(a.getTokenURL(), utils.Params2Map("grant_type", "client_credentials", "scope", "all"), http.MethodPost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
retVal = &TokenInfo{}
|
|
|
|
if accessToken, ok := result["access_token"]; ok {
|
|
retVal.AccessToken = accessToken.(string)
|
|
retVal.TokenType = result["token_type"].(string)
|
|
retVal.ExpiresIn = int(utils.MustInterface2Int64(result["expires_in"]))
|
|
|
|
// update my token too.
|
|
a.SetToken(retVal.AccessToken)
|
|
return retVal, nil
|
|
}
|
|
|
|
return nil, platformapi.ErrResponseDataFormatWrong
|
|
}
|