- big big refactor.
This commit is contained in:
69
platformapi/elmapi/callback.go
Normal file
69
platformapi/elmapi/callback.go
Normal 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
|
||||
}
|
||||
155
platformapi/elmapi/elmapi.go
Normal file
155
platformapi/elmapi/elmapi.go
Normal 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
|
||||
}
|
||||
64
platformapi/elmapi/elmapi_test.go
Normal file
64
platformapi/elmapi/elmapi_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
23
platformapi/elmapi/order.go
Normal file
23
platformapi/elmapi/order.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user