package mtwmapi import ( "encoding/json" "net/http" "net/url" "strings" "time" "git.rosy.net.cn/baseapi" "git.rosy.net.cn/baseapi/utils" ) const ( MsgPoiStatusOpened = 121 // 121=营业 MsgPoiStatusClosed = 120 // 120=休息 MsgPoiStatusOnline = 18 // 18=上线 MsgPoiStatusOffline = 19 // 19=下线 ) const ( MsgTypeWaybillStatus = "waybillStatus" MsgTypeNewOrder = "newOrder" MsgTypeOrderAccepted = "orderAccepted" MsgTypeOrderFinished = "orderFinished" MsgTypeOrderFinancial = "orderFinancial" MsgTypeOrderCanceled = "orderCanceled" MsgTypeUserUrgeOrder = "userUrgeOrder" MsgTypePrivateNumberDowngrade = "numberDowngrade" MsgTypeOrderModified = "orderModified" // 这个不是订单调整,是订单信息(地址、联系人等)变化后推送的消息 MsgTypeOrderRefund = "orderRefund" // 订单退款(全额) MsgTypeOrderPartialRefund = "orderPartialRefund" // /订单部分退款 MsgTypeOrderFinishedPickup = "orderFinishedPickup" MsgTypeOrderDeliveryFeeChange = "deliveryFeeChange" MsgTypeStoreStatusChanged = "storeStatusChanged" MsgTypeStoreAuditStatusChanged = "storeAuditStatusChanged" MsgTypeSkuDelete = "skuDelete" // 线上商品删除回调 MsgTypeSkuUpdate = "skuUpdate" // 线上修改商品回调(非api操作商品回调) MsgTypeStoreBind = "storeBind" MsgTypeOrderAgree = "agree" // 同意退款回调 ) type CallbackResponse struct { Data string `json:"data"` } // 全额退款没有Food, FoodList, Money项 type CallbackRefundInfo struct { Timestamp int64 `json:"timestamp"` AppID string `json:"app_id"` Sig string `json:"sig"` OrderID int64 `json:"order_id"` NotifyType string `json:"notify_type"` RefundID int64 `json:"refund_id"` Ctime int64 `json:"ctime"` Reason string `json:"reason"` ResType int `json:"res_type"` IsAppeal int `json:"is_appeal"` Pictures string `json:"pictures"` PictureList []string `json:"pictureList"` Status string `json:"status"` Food string `json:"food"` FoodList []*RefundSkuDetail `json:"foodList"` Money float32 `json:"money"` } type CallbackMsg struct { Cmd string Timestamp int64 `json:"timestamp"` AppID string `json:"app_id"` Sig string `json:"sig"` FormData url.Values Data interface{} } var ( SuccessResponse = &CallbackResponse{Data: "ok"} SignatureIsNotOk = &CallbackResponse{Data: "sign failed"} ) func Err2CallbackResponse(err error, data string) *CallbackResponse { if err == nil { return SuccessResponse } if data == "" { data = err.Error() } return &CallbackResponse{ Data: data, } } func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackResponse *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 { // 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") 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 { baseapi.SugarLogger.Warnf("mtwm GetCallbackMsg ParseForm failed with error:%v", err) callbackResponse = Err2CallbackResponse(err, "") } return msg, callbackResponse } func (a *API) GetRefundSkuDetailFromMsg(msg *CallbackMsg) (refundSkuDetail []*RefundSkuDetail) { utils.UnmarshalUseNumber([]byte(msg.FormData.Get("food")), &refundSkuDetail) return refundSkuDetail } // 单独处理im回调 func (a *API) GetIMCallbackMsg(request *http.Request) (msg *ImCallbackMsg, callbackResponse *CallbackResponse) { var ( err = request.ParseForm() pushContent PushContentReq tData string ) msg = &ImCallbackMsg{} msg.FormData = make(url.Values) if err == nil { data := utils.URLValues2Map(request.Form) if dataSign, ok := data[signKey]; ok { for k, v := range data { if k == "push_content" { //tData, _ = json.Marshal(v) tData = v.(string) continue } msg.FormData.Set(k, v.(string)) } msg.Timestamp = utils.Str2Int(msg.FormData.Get("timestamp")) msg.Sig = dataSign.(string) msg.AppID = msg.FormData.Get("app_id") if err = json.Unmarshal([]byte(tData), &pushContent); err == nil { msg.PushContent = pushContent } //if err = utils.Map2StructByJson(tData, &pushContent, true); err == nil { // msg.PushContent = pushContent //} } else { callbackResponse = SuccessResponse } } else { baseapi.SugarLogger.Warnf("mtwm GetCallbackMsg ParseForm failed with error:%v", err) callbackResponse = Err2CallbackResponse(err, "") } return msg, callbackResponse } // 平台修改商品消息通知(目前只接收上下架状态) type VendorUpdateSkuCallback struct { AppFoodCode string `json:"app_food_code"` AppId int `json:"app_id"` AppPoiCode string `json:"app_poi_code"` AppSpuCode string `json:"app_spu_code"` CategoryName string `json:"categoryName"` Ctime int `json:"ctime"` DiffContents struct { Skus []struct { Desc string `json:"desc"` DiffContentMap struct { SkuIsSoldOut struct { Result int `json:"result"` Origin int `json:"origin"` } `json:"sku_is_sold_out"` } `json:"diffContentMap"` Id string `json:"id"` OpType int `json:"opType"` OtherExtend struct { } `json:"otherExtend"` } `json:"skus"` IsSoldOut struct { Result int `json:"result"` Origin int `json:"origin"` } `json:"is_sold_out"` } `json:"diffContents"` Name string `json:"name"` OpAppKey string `json:"opAppKey"` OpName string `json:"opName"` OpReason string `json:"opReason"` Timestamp int `json:"timestamp"` Utime int `json:"utime"` }