diff --git a/platform/common/common.go b/platform/common/common.go index 45e432eb..5c282fc2 100644 --- a/platform/common/common.go +++ b/platform/common/common.go @@ -6,6 +6,8 @@ import ( "net/http" "time" + "github.com/fatih/structs" + "git.rosy.net.cn/baseapi" "git.rosy.net.cn/baseapi/utils" ) @@ -40,6 +42,10 @@ var ( CBErrMsgUnmarshal = "can not unmarshal data:%v, data:%v" ) +func init() { + structs.DefaultTagName = "json" +} + func AccessPlatformAPIWithRetry(params *AccessPlatformAPIWithRetryParams, handleResponse func(response *http.Response) (string, error)) error { exceedLimitRetryCount := 0 recoverableErrorRetryCount := 0 diff --git a/platform/dadaapi/callback.go b/platform/dadaapi/callback.go new file mode 100644 index 00000000..ec1bb15d --- /dev/null +++ b/platform/dadaapi/callback.go @@ -0,0 +1,83 @@ +package dadaapi + +import ( + "crypto/md5" + "fmt" + "sort" + "strings" + + "github.com/fatih/structs" + + "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/baseapi/utils" +) + +const ( + DadaResponseHttpCodeSuccess = 200 + DadaResponseHttpCodeGeneralErr = 555 +) + +type DadaCallbackMsg struct { + ClientId string `json:"client_id"` + OrderId string `json:"order_id"` + OrderStatus int `json:"order_status"` + CancelReason string `json:"cancel_reason"` + CancelFrom int `json:"cancel_from"` + UpdateTime int `json:"update_time"` + Signature string `json:"signature"` + DmId int `json:"dm_id"` + DmName string `json:"dm_name"` + DmMobile string `json:"dm_mobile"` +} + +type DadaCallbackResponse struct { + Code int + Dummy string `json:"dummy"` +} + +var ( + SuccessResponse = &DadaCallbackResponse{Code: DadaResponseHttpCodeSuccess} + FailedResponse = &DadaCallbackResponse{Code: DadaResponseHttpCodeGeneralErr} +) + +func (d *DadaAPI) signParamsCallback(mapData map[string]interface{}) string { + values := make([]string, 0) + for k, v := range mapData { + if k != signKey { + values = append(values, fmt.Sprint(v)) + } + } + sort.Strings(values) + + finalStr := strings.Join(values, "") + // baseapi.SugarLogger.Debugf("sign str:%v", finalStr) + return fmt.Sprintf("%x", md5.Sum([]byte(finalStr))) +} + +func (d *DadaAPI) unmarshalData(data []byte, msg interface{}) (callbackResponse *DadaCallbackResponse) { + err := utils.UnmarshalUseNumber(data, msg) + if err != nil { + return FailedResponse + } + return nil +} + +func (d *DadaAPI) CheckCallbackValidation(mapData map[string]interface{}) (callbackResponse *DadaCallbackResponse) { + sign := d.signParamsCallback(mapData) + if remoteSign, _ := mapData[signKey].(string); sign != remoteSign { + baseapi.SugarLogger.Infof("Signature is not ok, mine:%v, get:%v", sign, remoteSign) + return FailedResponse + } + return nil +} + +func (d *DadaAPI) GetOrderCallbackMsg(data []byte) (orderMsg *DadaCallbackMsg, callbackResponse *DadaCallbackResponse) { + orderMsg = new(DadaCallbackMsg) + if callbackResponse = d.unmarshalData(data, orderMsg); callbackResponse != nil { + return nil, FailedResponse + } + + mapData := structs.Map(orderMsg) + callbackResponse = d.CheckCallbackValidation(mapData) + return orderMsg, callbackResponse +} diff --git a/platform/dadaapi/dadaapi.go b/platform/dadaapi/dadaapi.go new file mode 100644 index 00000000..ec62b3be --- /dev/null +++ b/platform/dadaapi/dadaapi.go @@ -0,0 +1,141 @@ +package dadaapi + +import ( + "bytes" + "crypto/md5" + "fmt" + "net/http" + "sort" + "time" + + "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/baseapi/platform/common" + "git.rosy.net.cn/baseapi/utils" +) + +const ( + dadaSandboxURL = "http://newopen.qa.imdada.cn/" + dadaProdURL = "http://newopen.imdada.cn/" + signKey = "signature" +) + +const ( + clientTimeout = time.Second * 10 + sleepSecondWhenLimited = 6 * time.Second + maxRetryCountWhenNetworkException = 3 + maxRetryCountWhenReachLimited = 10 +) + +const ( + DadaCodeException = -1 + DadaCodeSuccess = 0 + DadaCodeSignErr = 2003 + DadaCodeRetryLater = 2012 + DadaCodeNetworkErr = 2455 +) + +type DadaAPI struct { + appKey string + appSecret string + sourceId string + url string + callbackURL string + client *http.Client +} + +type DadaResult struct { + Status string `json:"status"` + Code int `json:"code"` + Msg string `json:"msg"` + Result map[string]interface{} `json:"result"` + ErrorCode int `json:"errorCode"` +} + +func NewDadaAPI(appKey, appSecret, sourceId, callbackURL string, isProd bool) *DadaAPI { + api := &DadaAPI{ + appKey: appKey, + appSecret: appSecret, + sourceId: sourceId, + callbackURL: callbackURL, + client: &http.Client{Timeout: clientTimeout}, + } + if isProd { + api.url = dadaProdURL + } else { + api.url = dadaSandboxURL + } + return api +} + +func (d *DadaAPI) signParams(mapData map[string]interface{}) string { + keys := make([]string, 0) + for k := range mapData { + keys = append(keys, k) + } + sort.Strings(keys) + + finalStr := d.appSecret + for _, k := range keys { + if k != signKey { + finalStr += k + fmt.Sprint(mapData[k]) + } + } + + finalStr += d.appSecret + // baseapi.SugarLogger.Debugf("sign str:%v", finalStr) + return fmt.Sprintf("%X", md5.Sum([]byte(finalStr))) +} + +func (d *DadaAPI) AccessDada(action string, params map[string]interface{}) (retVal *DadaResult, err error) { + params2 := make(map[string]interface{}) + + params2["app_key"] = d.appKey + params2["timestamp"] = utils.Int64ToStr(utils.GetCurTimestamp()) + params2["format"] = "json" + params2["v"] = "1.0" + params2["source_id"] = d.sourceId + if params == nil { + params2["body"] = "" + } else { + params2["body"] = string(utils.MustMarshal(params)) + } + params2[signKey] = d.signParams(params2) + params2Bytes := utils.MustMarshal(params2) + request, _ := http.NewRequest("POST", d.url+action, bytes.NewReader(params2Bytes)) + request.Header.Set("Content-Type", "application/json") + apiAccess := &common.AccessPlatformAPIWithRetryParams{ + MaxExceedLimitRetryCount: maxRetryCountWhenReachLimited, + MaxRecoverableRetryCount: maxRetryCountWhenNetworkException, + SleepSecondWhenExceedLimit: sleepSecondWhenLimited, + Client: d.client, + Request: request, + } + + err = common.AccessPlatformAPIWithRetry(apiAccess, func(response *http.Response) (result string, err error) { + jsonResult1, err := utils.HttpResponse2Json(response) + if err != nil { + baseapi.SugarLogger.Warnf("HttpResponse2Json return:%v", err) + return common.PAErrorLevelGeneralFail, err + } + code := int(utils.MustInterface2Int64(jsonResult1["code"])) + retVal = &DadaResult{ + Code: code, + ErrorCode: code, + Msg: jsonResult1["msg"].(string), + Status: jsonResult1["status"].(string), + } + + if code == DadaCodeSuccess { + retVal.Result = jsonResult1["result"].(map[string]interface{}) + return common.PAErrorLevelSuccess, nil + } + baseapi.SugarLogger.Debug(jsonResult1) + if code == DadaCodeRetryLater || code == DadaCodeNetworkErr { + return common.PAErrorLevelRecoverable, nil + } + + return common.PAErrorLevelGeneralFail, utils.NewErrorIntCode(retVal.Msg, code) + }) + + return retVal, err +} diff --git a/platform/dadaapi/dadaapi_test.go b/platform/dadaapi/dadaapi_test.go new file mode 100644 index 00000000..60ca2ec8 --- /dev/null +++ b/platform/dadaapi/dadaapi_test.go @@ -0,0 +1,51 @@ +package dadaapi + +import ( + "testing" + + "git.rosy.net.cn/baseapi" + + "git.rosy.net.cn/baseapi/utils" + "go.uber.org/zap" +) + +var ( + dadaapi *DadaAPI + sugarLogger *zap.SugaredLogger +) + +func init() { + logger, _ := zap.NewDevelopment() + sugarLogger = logger.Sugar() + baseapi.Init(sugarLogger) + + // sandbox + dadaapi = NewDadaAPI("dada9623324449cd250", "30c2abbfe8a8780ad5aace46300c64b9", "73753", "", false) + + // prod +} + +func TestTest(t *testing.T) { + sugarLogger.Debug(utils.GetCurTimeStr()) +} + +func TestAccessDada(t *testing.T) { + body := make(map[string]interface{}) + body["order_id"] = "fakeorderid" + result, err := dadaapi.AccessDada("api/order/status/query", body) + + failed := true + if err != nil { + if err2, ok := err.(*utils.ErrorWithCode); ok { + if err2.IntCode() != DadaCodeSignErr { + failed = false + } + } + } else { + failed = false + } + + if failed { + t.Fatalf("Error when accessing api result:%v, error:%v", result, err) + } +} diff --git a/platform/dadaapi/order.go b/platform/dadaapi/order.go new file mode 100644 index 00000000..6bf1f9cd --- /dev/null +++ b/platform/dadaapi/order.go @@ -0,0 +1,85 @@ +package dadaapi + +import ( + "git.rosy.net.cn/baseapi/utils" + "github.com/fatih/structs" +) + +type OperateOrderRequiredParams struct { + ShopNo string `json:"shop_no"` + OriginId string `json:"origin_id"` + CityCode string `json:"city_code"` + CargoPrice float64 `json:"cargo_price"` + IsPrepay int `json:"is_prepay"` + ReceiverName string `json:"receiver_name"` + ReceiverAddress string `json:"receiver_address"` + ReceiverLat float64 `json:"receiver_lat"` + ReceiverLng float64 `json:"receiver_lng"` +} + +type CreateOrderResponse struct { + Distance float64 + Fee float64 + DeliverFee float64 + CouponFee float64 + Tips float64 + InsuranceFee float64 +} + +func (d *DadaAPI) QueryOrderInfo(orderId string) (retVal map[string]interface{}, err error) { + params := make(map[string]interface{}) + params["order_id"] = orderId + result, err := d.AccessDada("api/order/status/query", params) + if err != nil { + return nil, err + } + return result.Result, nil +} + +func map2CreateOrderResponse(mapData map[string]interface{}) *CreateOrderResponse { + retVal := new(CreateOrderResponse) + retVal.Distance = utils.MustInterface2Float64(mapData["distance"]) + retVal.Fee = utils.MustInterface2Float64(mapData["fee"]) + retVal.DeliverFee = utils.MustInterface2Float64(mapData["deliverFee"]) + + if value, ok := mapData["couponFee"]; ok { + retVal.CouponFee = utils.MustInterface2Float64(value) + } + if value, ok := mapData["tips"]; ok { + retVal.CouponFee = utils.MustInterface2Float64(value) + } + if value, ok := mapData["insuranceFee"]; ok { + retVal.CouponFee = utils.MustInterface2Float64(value) + } + + return retVal +} + +func (d *DadaAPI) operateOrder(action string, orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) { + params := structs.Map(orderInfo) + params["callback"] = d.callbackURL + allParams := utils.MergeMaps(params, addParams) + + result, err := d.AccessDada(action, allParams) + if err != nil { + return nil, err + } + + return map2CreateOrderResponse(result.Result), nil +} + +func (d *DadaAPI) AddOrder(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) { + return d.operateOrder("api/order/addOrder", orderInfo, addParams) +} + +func (d *DadaAPI) ReaddOrder(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) { + return d.operateOrder("api/order/reAddOrder", orderInfo, addParams) +} + +func (d *DadaAPI) QueryDeliverFee(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) { + return d.operateOrder("api/order/queryDeliverFee", orderInfo, addParams) +} + +func (d *DadaAPI) AddOrderAfterQuery(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) { + return d.operateOrder("api/order/addAfterQuery", orderInfo, addParams) +} diff --git a/platform/elmapi/callback.go b/platform/elmapi/callback.go index 135506b4..f2d212ab 100644 --- a/platform/elmapi/callback.go +++ b/platform/elmapi/callback.go @@ -23,14 +23,14 @@ type ELMCallbackResponse struct { } type ELMCallbackMsg struct { - AppId int `json:"appId" structs:"appId"` - RequestId string `json:"requestId" structs:"requestId"` - Type int `json:"type" structs:"type"` - Message string `json:"message" structs:"message"` - ShopId int `json:"shopId" structs:"shopId"` - Timestamp int64 `json:"timestamp" structs:"timestamp"` - UserId int64 `json:"userId" structs:"userId"` - Signature string `json:"signature" structs:"signature"` + 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 ( @@ -47,23 +47,22 @@ func (e *ELMAPI) unmarshalData(data []byte, msg interface{}) (callbackResponse * return nil } -func (e *ELMAPI) CheckRequestValidation(mapData map[string]interface{}) (callbackResponse *ELMCallbackResponse) { +func (e *ELMAPI) CheckCallbackValidation(mapData map[string]interface{}) (callbackResponse *ELMCallbackResponse) { sign := e.signParamsMap(mapData, "") - if remoteSign, ok := mapData[signKey].(string); ok && sign != remoteSign { + if remoteSign, _ := mapData[signKey].(string); sign != remoteSign { baseapi.SugarLogger.Infof("Signature is not ok, mine:%v, get:%v", sign, remoteSign) return &ELMCallbackResponse{Message: "signature is invalid"} } return nil } -func (e *ELMAPI) GetMsgFromData(data []byte) (msg *ELMCallbackMsg, callbackResponse *ELMCallbackResponse) { +func (e *ELMAPI) GetCallbackMsg(data []byte) (msg *ELMCallbackMsg, callbackResponse *ELMCallbackResponse) { msg = new(ELMCallbackMsg) - callbackResponse = e.unmarshalData(data, msg) - if callbackResponse != nil { + if callbackResponse = e.unmarshalData(data, msg); callbackResponse != nil { return nil, callbackResponse } mapData := structs.Map(msg) - callbackResponse = e.CheckRequestValidation(mapData) + callbackResponse = e.CheckCallbackValidation(mapData) return msg, callbackResponse } diff --git a/platform/elmapi/elmapi_test.go b/platform/elmapi/elmapi_test.go index cb0b4955..19a56b57 100644 --- a/platform/elmapi/elmapi_test.go +++ b/platform/elmapi/elmapi_test.go @@ -20,10 +20,10 @@ func init() { baseapi.Init(sugarLogger) // sandbox - elmapi = NewELMAPI("2d2b583447b04b6bba5a6f3faed3559b", "RwT214gAsS", "56afff4b9ebd8a7eb532d18fa33f17be57f9b9db", false) + // elmapi = NewELMAPI("b4f7e424475c3758c111dc60ceec3e2a", "RwT214gAsS", "56afff4b9ebd8a7eb532d18fa33f17be57f9b9db", false) // prod - // elmapi = NewELMAPI("bab2a27f99562f394b411dbb9a6214da", "KLRDcOZGrk", "1fc221f8265506531da36fb613d5f5ad673f2e9a", true) + elmapi = NewELMAPI("bab2a27f99562f394b411dbb9a6214da", "KLRDcOZGrk", "1fc221f8265506531da36fb613d5f5ad673f2e9a", true) } func TestTest(t *testing.T) { @@ -57,7 +57,7 @@ func TestGetOrder(t *testing.T) { 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.GetMsgFromData([]byte(jsonStr)) + msg, response := elmapi.GetCallbackMsg([]byte(jsonStr)) if response != nil || msg == nil { t.Fatal("Something wrong") } diff --git a/platform/jdapi/callback.go b/platform/jdapi/callback.go index 0453555e..9cd56a54 100644 --- a/platform/jdapi/callback.go +++ b/platform/jdapi/callback.go @@ -56,7 +56,7 @@ func (j *JDAPI) unmarshalData(strData string, msg interface{}) (callbackResponse return nil } -func (j *JDAPI) CheckRequestValidation(request *http.Request) (callbackResponse *JDCallbackResponse) { +func (j *JDAPI) CheckCallbackValidation(request *http.Request) (callbackResponse *JDCallbackResponse) { mapData := make(map[string]string) mapData["token"] = request.FormValue("token") mapData["app_key"] = request.FormValue("app_key") @@ -78,53 +78,44 @@ func (j *JDAPI) CheckRequestValidation(request *http.Request) (callbackResponse return nil } -func (j *JDAPI) getCommonOrderMsg(request *http.Request, needDecode bool) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) { - if callbackResponse = j.CheckRequestValidation(request); callbackResponse != nil { - return nil, callbackResponse +func (j *JDAPI) getCommonOrderCallbackMsg(request *http.Request, msg interface{}, needDecode bool) (callbackResponse *JDCallbackResponse) { + if callbackResponse = j.CheckCallbackValidation(request); callbackResponse != nil { + return callbackResponse } - msg = new(JDOrderMsg) jdParamJSON := request.FormValue(JD_PARAM_JSON) if needDecode { if jdParamJSON2, err := url.QueryUnescape(jdParamJSON); err == nil { jdParamJSON = jdParamJSON2 + } else { + return &JDCallbackResponse{ + Code: JDerrorCodeAbnormalParam, + Msg: fmt.Sprintf(common.CBErrMsgUnescape, jdParamJSON, err), + Data: jdParamJSON, + } } } - callbackResponse = j.unmarshalData(jdParamJSON, msg) - if callbackResponse != nil { - return nil, callbackResponse + + if callbackResponse = j.unmarshalData(jdParamJSON, msg); callbackResponse != nil { + return callbackResponse } - return msg, nil + return nil } -func (j *JDAPI) GetOrderMsg(request *http.Request) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) { - return j.getCommonOrderMsg(request, false) +func (j *JDAPI) GetOrderCallbackMsg(request *http.Request) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) { + msg = new(JDOrderMsg) + callbackResponse = j.getCommonOrderCallbackMsg(request, msg, false) + return msg, callbackResponse } -func (j *JDAPI) GetOrderApplyCancelMsg(request *http.Request) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) { - return j.getCommonOrderMsg(request, true) +func (j *JDAPI) GetOrderApplyCancelCallbackMsg(request *http.Request) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) { + msg = new(JDOrderMsg) + callbackResponse = j.getCommonOrderCallbackMsg(request, msg, true) + return msg, callbackResponse } -func (j *JDAPI) GetOrderDeliveryMsg(request *http.Request) (msg *JDDeliveryStatusMsg, callbackResponse *JDCallbackResponse) { - if callbackResponse = j.CheckRequestValidation(request); callbackResponse != nil { - return nil, callbackResponse - } - +func (j *JDAPI) GetOrderDeliveryCallbackMsg(request *http.Request) (msg *JDDeliveryStatusMsg, callbackResponse *JDCallbackResponse) { msg = new(JDDeliveryStatusMsg) - jdParamJSON := request.FormValue(JD_PARAM_JSON) - jdParamJSON2, err := url.QueryUnescape(jdParamJSON) - if err != nil { - return nil, &JDCallbackResponse{ - Code: JDerrorCodeAbnormalParam, - Msg: fmt.Sprintf(common.CBErrMsgUnescape, jdParamJSON, err), - Data: jdParamJSON, - } - } - jdParamJSON = jdParamJSON2 - - callbackResponse = j.unmarshalData(jdParamJSON, msg) - if callbackResponse != nil { - return nil, callbackResponse - } - return msg, nil + callbackResponse = j.getCommonOrderCallbackMsg(request, msg, true) + return msg, callbackResponse } diff --git a/platform/mtpsapi/callback.go b/platform/mtpsapi/callback.go index bb299f5b..02e7695e 100644 --- a/platform/mtpsapi/callback.go +++ b/platform/mtpsapi/callback.go @@ -39,7 +39,7 @@ var ( SignatureIsNotOk = &MtpsCallbackResponse{Code: -1} ) -func (m *MTPSAPI) CheckRequestValidation(request *http.Request) (callbackResponse *MtpsCallbackResponse) { +func (m *MTPSAPI) CheckCallbackValidation(request *http.Request) (callbackResponse *MtpsCallbackResponse) { request.ParseForm() sign := m.signParams(request.PostForm) if sign != request.FormValue(signKey) { @@ -59,8 +59,7 @@ func (m *MTPSAPI) CheckRequestValidation(request *http.Request) (callbackRespons } func (m *MTPSAPI) GetOrderCallbackMsg(request *http.Request) (orderMsg *MtpsCallbackOrderMsg, callbackResponse *MtpsCallbackResponse) { - callbackResponse = m.CheckRequestValidation(request) - if callbackResponse != nil { + if callbackResponse = m.CheckCallbackValidation(request); callbackResponse != nil { return nil, callbackResponse } orderMsg = &MtpsCallbackOrderMsg{ @@ -84,8 +83,7 @@ func (m *MTPSAPI) GetOrderCallbackMsg(request *http.Request) (orderMsg *MtpsCall } func (m *MTPSAPI) GetOrderExceptionCallbackMsg(request *http.Request) (orderMsg *MtpsCallbackOrderExceptionMsg, callbackResponse *MtpsCallbackResponse) { - callbackResponse = m.CheckRequestValidation(request) - if callbackResponse != nil { + if callbackResponse = m.CheckCallbackValidation(request); callbackResponse != nil { return nil, callbackResponse } orderMsg = &MtpsCallbackOrderExceptionMsg{ diff --git a/platform/mtpsapi/mtpsapi.go b/platform/mtpsapi/mtpsapi.go index 3fd263c8..8ff5b9ba 100644 --- a/platform/mtpsapi/mtpsapi.go +++ b/platform/mtpsapi/mtpsapi.go @@ -107,20 +107,20 @@ type MTPSResult struct { } type MtpsCreateOrderByShopInfo struct { - DeliveryId int64 `structs:"delivery_id"` - OrderId string `structs:"order_id"` - ShopId string `structs:"shop_id"` - DeliveryServiceCode int `structs:"delivery_service_code"` - ReceiverName string `structs:"receiver_name"` - ReceiverAddress string `structs:"receiver_address"` - ReceiverPhone string `structs:"receiver_phone"` - ReceiverLng int `structs:"receiver_lng"` - ReceiverLat int `structs:"receiver_lat"` - CoordinateType int `structs:"coordinate_type"` - GoodsValue float64 `structs:"goods_value"` - GoodsWeight float64 `structs:"goods_weight"` - ExpectedDeliveryTime int64 `structs:"expected_delivery_time"` - OrderType int `structs:"order_type"` + DeliveryId int64 `json:"delivery_id"` + OrderId string `json:"order_id"` + ShopId string `json:"shop_id"` + DeliveryServiceCode int `json:"delivery_service_code"` + ReceiverName string `json:"receiver_name"` + ReceiverAddress string `json:"receiver_address"` + ReceiverPhone string `json:"receiver_phone"` + ReceiverLng int `json:"receiver_lng"` + ReceiverLat int `json:"receiver_lat"` + CoordinateType int `json:"coordinate_type"` + GoodsValue float64 `json:"goods_value"` + GoodsWeight float64 `json:"goods_weight"` + ExpectedDeliveryTime int64 `json:"expected_delivery_time"` + OrderType int `json:"order_type"` } type MTPSAPI struct {