- big big refactor.

This commit is contained in:
gazebo
2018-06-22 22:26:12 +08:00
parent 062f7a2540
commit 7657966da5
27 changed files with 1277 additions and 1266 deletions

View File

@@ -0,0 +1,69 @@
package elmapi
import (
"fmt"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
"github.com/fatih/structs"
)
// https://open.shop.ele.me/openapi/documents/callback
const (
MsgTypeOrderValid = 10
MsgTypeMerchantValid = 12
MsgTypeOrderCanceled = 14
MsgTypeMerchantInvalid = 15
MsgTypeOrderForceInvalid = 17
MsgTypeOrderFinished = 18
)
type CallbackResponse struct {
Message string `json:"message"`
}
type CallbackMsg struct {
AppID int `json:"appId"`
RequestID string `json:"requestId"`
Type int `json:"type"`
Message string `json:"message"`
ShopID int `json:"shopId"`
Timestamp int64 `json:"timestamp"`
UserID int64 `json:"userId"`
Signature string `json:"signature"`
}
var (
SuccessResponse = &CallbackResponse{"ok"}
)
func (e *API) unmarshalData(data []byte, msg interface{}) (callbackResponse *CallbackResponse) {
err := utils.UnmarshalUseNumber(data, msg)
if err != nil {
return &CallbackResponse{
Message: fmt.Sprintf(platformapi.ErrStrUnmarshalError, data, err),
}
}
return nil
}
func (e *API) CheckCallbackValidation(mapData map[string]interface{}) (callbackResponse *CallbackResponse) {
sign := e.signParamsMap(mapData, "")
if remoteSign, _ := mapData[signKey].(string); sign != remoteSign {
baseapi.SugarLogger.Infof("Signature is not ok, mine:%v, get:%v", sign, remoteSign)
return &CallbackResponse{Message: platformapi.ErrStrCallbackSignatureIsWrong}
}
return nil
}
func (e *API) GetCallbackMsg(data []byte) (msg *CallbackMsg, callbackResponse *CallbackResponse) {
msg = new(CallbackMsg)
if callbackResponse = e.unmarshalData(data, msg); callbackResponse != nil {
return nil, callbackResponse
}
mapData := structs.Map(msg)
callbackResponse = e.CheckCallbackValidation(mapData)
return msg, callbackResponse
}

View File

@@ -0,0 +1,155 @@
package elmapi
import (
"crypto/md5"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"sort"
"strings"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
sandboxURL = "https://open-api-sandbox.shop.ele.me/api/v1/"
prodURL = "https://open-api.shop.ele.me/api/v1/"
signKey = "signature"
)
type ResponseResult struct {
ID string
Result interface{}
Error map[string]interface{}
}
type API struct {
token string
appKey string
secret string
url *url.URL
client *http.Client
config *platformapi.APIConfig
}
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,
client: &http.Client{Timeout: curConfig.ClientTimeout},
config: &curConfig,
}
if isProd {
api.url, _ = url.Parse(prodURL)
} else {
api.url, _ = url.Parse(sandboxURL)
}
return api
}
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.token)
}
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.token,
Nop: "1.0.0",
Metas: metas,
Params: params,
Action: action,
ID: utils.GetUUID(),
}
pl.Signature = a.signParams(action, pl)
request := &http.Request{
Method: "POST",
URL: a.url,
Header: http.Header{
"Content-Type": []string{"application/json; charset=utf-8"},
"Content-Encoding": []string{"gzip, deflate"},
"User-Agent": []string{"eleme-golang-api"},
// "x-eleme-requestid": []string{payload.Id},
},
Body: ioutil.NopCloser(strings.NewReader(string(utils.MustMarshal(pl)))),
}
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)
baseapi.SugarLogger.Warnf("response business code is not ok, data:%v, code:%v", jsonResult1, code)
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
}

View File

@@ -0,0 +1,64 @@
package elmapi
import (
"testing"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/utils"
"go.uber.org/zap"
)
var (
elmapi *API
sugarLogger *zap.SugaredLogger
)
func init() {
logger, _ := zap.NewDevelopment()
sugarLogger = logger.Sugar()
baseapi.Init(sugarLogger)
// sandbox
// elmapi = New("b4f7e424475c3758c111dc60ceec3e2a", "RwT214gAsS", "56afff4b9ebd8a7eb532d18fa33f17be57f9b9db", false)
// prod
elmapi = New("bab2a27f99562f394b411dbb9a6214da", "KLRDcOZGrk", "1fc221f8265506531da36fb613d5f5ad673f2e9a", true)
}
func TestTest(t *testing.T) {
sugarLogger.Debug(utils.GetCurTimeStr())
}
func TestAccessAPI(t *testing.T) {
result, err := elmapi.AccessAPI("eleme.user.getUser", nil)
if err != nil {
t.Fatalf("Error when accessing AccessJDQuery result:%v, error:%v", result, err)
} else {
mapResult := result.Result.(map[string]interface{})
userId := utils.MustInterface2Int64(mapResult["userId"])
if userId != 336072266326420104 || err != nil {
t.Fatalf("userId is not correct:%v", mapResult["userId"])
}
}
}
func TestGetOrder(t *testing.T) {
result, err := elmapi.GetOrder("3023582487561731159")
if err != nil {
t.Fatalf("Error when accessing AccessJDQuery result:%v, error:%v", result, err)
} else {
shopId := int(utils.MustInterface2Int64(result["shopId"]))
if shopId != 157451615 {
t.Fatalf("userId is not correct:%v", shopId)
}
}
}
func TestCallbackSign(t *testing.T) {
jsonStr := `{"requestId":"200016348669063447","type":18,"appId":78247922,"message":"{\"orderId\":\"3024923917769149510\",\"state\":\"settled\",\"shopId\":157492364,\"updateTime\":1529465510,\"role\":1}","shopId":157492364,"timestamp":1529465510255,"signature":"D65F917D93B4F599B85486C799599141","userId":336072266322770688}`
msg, response := elmapi.GetCallbackMsg([]byte(jsonStr))
if response != nil || msg == nil {
t.Fatal(response, msg)
}
}

View File

@@ -0,0 +1,23 @@
package elmapi
const (
OrderStatusPending = "pending"
OrderStatusUnprocessed = "unprocessed"
OrderStatusRefunding = "refunding"
OrderStatusValid = "valid"
OrderStatusInvalid = "invalid"
OrderStatusSettled = "settled"
)
func (a *API) GetOrder(orderID string) (map[string]interface{}, error) {
result, err := a.AccessAPI("eleme.order.getOrder", map[string]interface{}{
"orderId": orderID,
})
if err == nil {
innerResult := result.Result.(map[string]interface{})
return innerResult, nil
}
return nil, err
}