From ef1222df31358112b69366d5c3f7173ce965e80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Tue, 15 Mar 2022 19:24:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=9E=E6=BB=9A=E7=BE=8E=E5=9B=A2=E5=A4=96?= =?UTF-8?q?=E5=8D=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- platformapi/fnpsapi_v3/fnps_v3.go | 109 ++++++++++++++++++++++++++++-- platformapi/fnpsapi_v3/fnpsapi.go | 4 +- platformapi/fnpsapi_v3/store.go | 61 ++++++++++------- platformapi/mtwmapi/callback.go | 84 ++++++++++++----------- 4 files changed, 187 insertions(+), 71 deletions(-) diff --git a/platformapi/fnpsapi_v3/fnps_v3.go b/platformapi/fnpsapi_v3/fnps_v3.go index 1f7f40b5..97f3ea61 100644 --- a/platformapi/fnpsapi_v3/fnps_v3.go +++ b/platformapi/fnpsapi_v3/fnps_v3.go @@ -16,10 +16,27 @@ const ( ) //todo 返回参数没做玩 +//type TokenInfo struct { +// AccessToken string `json:"access_token"` +// AppID string `json:"app_id"` +// ExpireTime int64 `json:"expire_time"` +//} + +// 获取token type TokenInfo struct { - AccessToken string `json:"access_token"` - AppID string `json:"app_id"` - ExpireTime int64 `json:"expire_time"` + Sign string `json:"sign"` //返回值签名,详见开放平台侧返回值签名算法 + Code string `json:"code"` //错误码,详见开放平台侧错误码映射表 + Msg string `json:"msg"` //错误信息 + BusinessData string `json:"business_data"` // string +} + +type BusinessData struct { + AppID string `json:"app_id"` //应用id + MerchantID string `json:"merchant_id"` //商户id + AccessToken string `json:"access_token"` //凭证token + RefreshToken string `json:"refresh_token"` //刷新token + ExpireIn string `json:"expire_in"` //access_token剩余有效时间,单位:秒,默认有效期是一年 + ReExpireIn string `json:"re_expire_in"` //refresh_token剩余有效时间 } // 注册请求api @@ -32,14 +49,79 @@ type API struct { timestamp int64 `json:"timestamp"` accessToken string `json:"access_token"` appSecret string `json:"app_secret"` - - locker sync.RWMutex - client *http.Client - config *platformapi.APIConfig + version string `json:"version"` + locker sync.RWMutex + client *http.Client + config *platformapi.APIConfig } +//<-------------------------------------------------------------> +// 请求基础结构体 +type BaseInfo struct { + AccessToken string `json:"access_token"` //凭证token + Signature string `json:"signature"` + MerchantID string `json:"merchant_id"` //商户id + Version string `json:"version"` // 版本固定1.0 + AppID string `json:"app_id"` //应用id + Timestamp int64 `json:"timestamp"` // 当前时间戳 +} + +//#region 获取蜂鸟门店信息 + +// 获取单个门店查询 +type GetOneStoreParam struct { + BaseInfo + BusinessData string `json:"business_data"` // 门店基础数据 +} + +// 获取单个门店基础参数 +type GetOneStore struct { + MerchantID string `json:"merchant_id"` // 商户id + OutShopCode string `json:"out_shop_code"` // 外部门店编码 +} + +// 获取门店返回值信息 +type GetStoreResp struct { + TokenInfo +} + +type GetOneStoreRespData struct { + Address string `json:"address"` // 门店地址 + BranchName string `json:"branch_name"` // 门店分店名 + CategoryID int `json:"category_id"` // 门店类目 + ChainStoreID int `json:"chain_store_id"` // 蜂鸟门店id,创建接口返回的id + ChainstoreType int `json:"chainstore_type"` // 门店类型 1-正式门店;2-测试门店 + ChainstoreTypeDesc string `json:"chainstore_type_desc"` // 门店类型描述 + ContactPhone string `json:"contact_phone"` // 门店联系方式 + CreditCode string `json:"credit_code"` // 统一社会信用代码 + Latitude float64 `json:"latitude"` // 门店纬度[0,90] + Longitude float64 `json:"longitude"` // 门店经度[0,180] + MerchantID int `json:"merchant_id"` // 所属商户id + ModifyStatus int `json:"modify_status"` // 0-无修改,10-资料修改审核中,20-审核通过,30-a审核驳回 + ModifyStatusDesc string `json:"modify_status_desc"` // 门店修改状态描述 + Name string `json:"name"` // 门店主店名 + OutShopCode string `json:"out_shop_code"` // 外部门店编码 + OwnerIDNum string `json:"owner_id_num"` // 门店拥有人身份证号 + OwnerName string `json:"owner_name"` // 门店拥有人姓名 + PositionSource int `json:"position_source"` // 经纬度来源 坐标属性(1:腾讯地图, 2:百度地图, 3:高德地图),蜂鸟建议使用高德地图 + PositionSourceDesc string `json:"position_source_desc"` // 经纬度来源说明 + SettlementAccountID int `json:"settlement_account_id"` // 门店结算账号id + SettlementMode int `json:"settlement_mode"` // 门店结算方式 1:实时结算; 2:账期结算; + SettlementModeDesc string `json:"settlement_mode_desc"` // 结算说明? + Status int `json:"status"` // 0-上架审核中,20-正常(已上架),30-上架审核失败,40-已冻结,50-已下架 + StatusDesc string `json:"status_desc"` // 门店认证状态描述 +} + +//#endregion + // CreateStoreParam 创建门店请求参数 type CreateStoreParam struct { + BaseInfo + BusinessData string `json:"business_data"` // 门店基础数据 +} + +// 创建门店基础数据 +type CreateStoreBaseInfo struct { BranchShopName string `json:"branch_shop_name"` // 门店主店名 必填 ContactPhone string `json:"contact_phone"` // 门店联系方式 OwnerIDPicFrontHash string `json:"owner_id_pic_front_hash"` // 身份证正面 @@ -61,3 +143,16 @@ type CreateStoreParam struct { OwnerIDPicBackHash string `json:"owner_id_pic_back_hash"` // 身份证反面 PositionSource int `json:"position_source"` // 经纬度来源只支持高德(默认值3) } + +type GetStoreResult struct { + ChainStoreCode string `json:"chain_store_code"` + ChainStoreName string `json:"chain_store_name"` + Address string `json:"address"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + PositionSource int `json:"position_source"` + City string `json:"city"` + ContactPhone string `json:"contact_phone"` + ServiceCode string `json:"service_code"` + Status int `json:"status"` //1关店,2开店 +} diff --git a/platformapi/fnpsapi_v3/fnpsapi.go b/platformapi/fnpsapi_v3/fnpsapi.go index 5ce4d84a..f55806b4 100644 --- a/platformapi/fnpsapi_v3/fnpsapi.go +++ b/platformapi/fnpsapi_v3/fnpsapi.go @@ -45,7 +45,7 @@ func New(appID, appSecret, merchantId string, config ...*platformapi.APIConfig) func (a *API) signParam2() (sig string) { sb := new(strings.Builder) sb.WriteString("grant_type=") - sb.WriteString("CODE") //todo + sb.WriteString(a.grantType) sb.WriteString("&code=") sb.WriteString(a.code) //todo sb.WriteString("&app_id=") @@ -104,6 +104,8 @@ func (a *API) AccessAPI(baseUrl, actionApi, method string, bizParams map[string] // 获取access_token func (a *API) GetAccessToken() (tokenInfo *TokenInfo, err error) { + a.grantType = "authorization_code" + a.signature = a.signParam2() result, err := a.AccessAPI(TokenURL, "", RequestPost, nil) if err != nil { return nil, err diff --git a/platformapi/fnpsapi_v3/store.go b/platformapi/fnpsapi_v3/store.go index 92c82065..cc490823 100644 --- a/platformapi/fnpsapi_v3/store.go +++ b/platformapi/fnpsapi_v3/store.go @@ -1,6 +1,7 @@ package fnpsapi import ( + "encoding/json" "fmt" "strings" @@ -19,29 +20,41 @@ func (a *API) CreateStore(createStoreParam *CreateStoreParam) (err error) { return err } -type GetStoreResult struct { - ChainStoreCode string `json:"chain_store_code"` - ChainStoreName string `json:"chain_store_name"` - Address string `json:"address"` - Latitude string `json:"latitude"` - Longitude string `json:"longitude"` - PositionSource int `json:"position_source"` - City string `json:"city"` - ContactPhone string `json:"contact_phone"` - ServiceCode string `json:"service_code"` - Status int `json:"status"` //1关店,2开店 -} - +// 获取门店(单个) func (a *API) GetStore(storeID string) (getStoreResult *GetStoreResult, err error) { - result, err := a.AccessAPI("v2/chain_store/query", URL, map[string]interface{}{ - "chain_store_code": []string{storeID}, - }, true) - if err == nil { - if data, ok := result["data"].([]interface{}); ok { - utils.Map2StructByJson(data[0], &getStoreResult, false) - } else { - err = fmt.Errorf(result["msg"].(string)) - } + params := GetOneStoreParam{ + BaseInfo: BaseInfo{ + AccessToken: a.accessToken, + Signature: a.signature, + MerchantID: a.merchantId, + Version: a.version, + AppID: a.appID, + Timestamp: a.timestamp, + }, + BusinessData: "", + } + + // 序列化请求参数 + data, err := json.Marshal(GetOneStore{MerchantID: a.merchantId, OutShopCode: storeID}) + if err != nil { + return nil, err + } + params.BusinessData = string(data) + paramsMap := utils.Struct2FlatMap(params) + + result, err := a.AccessAPI(ApiURL, "chainstoreQuery", RequestGet, paramsMap) + if err != nil { + return nil, err + } + + if result["code"] != "200" { + return nil, fmt.Errorf("%s", result["msg"]) + } + + if data, ok := result["business_data"].([]interface{}); ok { + utils.Map2StructByJson(data[0], &getStoreResult, false) + } else { + err = fmt.Errorf(result["msg"].(string)) } return getStoreResult, err } @@ -65,7 +78,7 @@ func IsErrShopExist(err error) bool { } func (a *API) UpdateStore(createStoreParam *CreateStoreParam) (err error) { - params := utils.Struct2FlatMap(createStoreParam) - _, err = a.AccessAPI("v2/chain_store/update", URL, params, true) + //params := utils.Struct2FlatMap(createStoreParam) + //_, err = a.AccessAPI("v2/chain_store/update", URL, params, true) return err } diff --git a/platformapi/mtwmapi/callback.go b/platformapi/mtwmapi/callback.go index e233a2f1..c9a6b0c5 100644 --- a/platformapi/mtwmapi/callback.go +++ b/platformapi/mtwmapi/callback.go @@ -91,51 +91,57 @@ func Err2CallbackResponse(err error, data string) *CallbackResponse { } func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackResponse *CallbackResponse) { - if err := request.ParseForm(); err != nil { - baseapi.SugarLogger.Warnf("mtwm GetCallbackMsg ParseForm failed with error:%v", err) - callbackResponse = Err2CallbackResponse(err, "") - return nil, callbackResponse - } + err := request.ParseForm() + if err == nil { + data := utils.URLValues2Map(request.Form) + if dataSign, ok := data[signKey]; ok { + // fullURL := strings.TrimRight(a.callbackURL+request.URL.Path, "/") + // sign := a.signParams(fullURL+"?", data) + cmd := strings.Trim(request.URL.EscapedPath(), "/") + cmd = cmd[strings.LastIndex(cmd, "/")+1:] + // if sign != dataSign.(string) && cmd != MsgTypeStoreStatusChanged { + // baseapi.SugarLogger.Debugf("mtwm GetCallbackMsg my sign:%v, data sign:%v", sign, dataSign.(string)) + // callbackResponse = SignatureIsNotOk + // } else { + msg = &CallbackMsg{ + Cmd: cmd, + } + msg.FormData = make(url.Values) + for k, v := range data { + // 美团外卖的用户催单信息的timestamp是毫秒(其它的事件是秒),统一归一化为秒 + if cmd == MsgTypeUserUrgeOrder && k == "timestamp" { + intV := utils.Str2Int64(v.(string)) + if intV > time.Now().Unix()*2 { + v = utils.Int64ToStr(intV / 1000) + data[k] = v + } + } + msg.FormData.Set(k, v.(string)) + } + msg.Timestamp = utils.Str2Int64(msg.FormData.Get("timestamp")) + msg.Sig = dataSign.(string) + msg.AppID = msg.FormData.Get("app_id") - data := utils.URLValues2Map(request.Form) - if dataSign, ok := data[signKey]; ok { - cmd := strings.Trim(request.URL.EscapedPath(), "/") - cmd = cmd[strings.LastIndex(cmd, "/")+1:] - msg = &CallbackMsg{ - Cmd: cmd, - } - msg.FormData = make(url.Values) - for k, v := range data { - // 美团外卖的用户催单信息的timestamp是毫秒(其它的事件是秒),统一归一化为秒 - if cmd == MsgTypeUserUrgeOrder && k == "timestamp" { - intV := utils.Str2Int64(v.(string)) - if intV > time.Now().Unix()*2 { - v = utils.Int64ToStr(intV / 1000) - data[k] = v + if msg.Cmd == MsgTypeOrderRefund || msg.Cmd == MsgTypeOrderPartialRefund { + var refundData *CallbackRefundInfo + if err = utils.Map2StructByJson(data, &refundData, true); err == nil { + if refundData.Food != "" { + utils.UnmarshalUseNumber([]byte(refundData.Food), &refundData.FoodList) + } + if refundData.Pictures != "" { + utils.UnmarshalUseNumber([]byte(refundData.Pictures), &refundData.PictureList) + } + msg.Data = refundData } } - msg.FormData.Set(k, v.(string)) - } - msg.Timestamp = utils.Str2Int64(msg.FormData.Get("timestamp")) - msg.Sig = dataSign.(string) - msg.AppID = msg.FormData.Get("app_id") - - if msg.Cmd == MsgTypeOrderRefund || msg.Cmd == MsgTypeOrderPartialRefund { - var refundData *CallbackRefundInfo - if err := utils.Map2StructByJson(data, &refundData, true); err == nil { - if refundData.Food != "" { - utils.UnmarshalUseNumber([]byte(refundData.Food), &refundData.FoodList) - } - if refundData.Pictures != "" { - utils.UnmarshalUseNumber([]byte(refundData.Pictures), &refundData.PictureList) - } - msg.Data = refundData - } + // } + } else { + callbackResponse = SuccessResponse // 美团外卖PING消息没有sign } } else { - callbackResponse = SuccessResponse // 美团外卖PING消息没有sign + baseapi.SugarLogger.Warnf("mtwm GetCallbackMsg ParseForm failed with error:%v", err) + callbackResponse = Err2CallbackResponse(err, "") } - return msg, callbackResponse }