package elmapi import ( "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 { // baseapi.SugarLogger.Debugf("token=%v, appKey=%v, secret=%v", token, appKey, secret) 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 // baseapi.SugarLogger.Debugf("sign str:%v", finalStr) 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) bodyStr := string(utils.MustMarshal(pl)) request, _ := http.NewRequest(http.MethodPost, a.fullURL, strings.NewReader(bodyStr)) 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") err = platformapi.AccessPlatformAPIWithRetry(a.client, request, a.config, func(response *http.Response) (result string, err error) { jsonResult1, err := utils.HTTPResponse2Json(response) if err != nil { return platformapi.ErrLevelGeneralFail, err } 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) { var request *http.Request if method == "POST" { params2 := make(url.Values) for k, v := range params { params2[k] = []string{fmt.Sprint(v)} } request, _ = http.NewRequest("POST", 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 { fullURL, _ := url.Parse(utils.GenerateGetURL(baseURL, "", params)) // baseapi.SugarLogger.Debug(url.String()) request = &http.Request{ Method: "GET", URL: fullURL, } } err = platformapi.AccessPlatformAPIWithRetry(a.client, request, a.config, func(response *http.Response) (result string, err error) { jsonResult1, err := utils.HTTPResponse2Json(response) if err != nil { return platformapi.ErrLevelGeneralFail, err } 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"), "POST") 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 }