Files
baseapi/platformapi/elmapi/elmapi.go
邹宗楠 2ed93fe209 1
2022-10-22 22:45:36 +08:00

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
}