From 3c30456fcb769cdb6e8b81930188e980c1e6b3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Wed, 1 Jun 2022 15:33:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=87=AA=E9=85=8D=E9=80=81,?= =?UTF-8?q?=E7=8A=B6=E6=80=81,=E6=B7=BB=E5=8A=A0=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E6=9C=BA=E6=89=93=E5=8D=B0=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- platformapi/mtwmapi/mtwmapi_test.go | 4 +- platformapi/mtwmapi/order.go | 69 +++++++- platformapi/mtwmapi/order_page_test.go | 8 + platformapi/tiktok/callback.go | 213 +++++++++++++++++++++++++ platformapi/tiktok/refund.go | 43 +++++ platformapi/tiktok/tiktok_pay.go | 4 - 6 files changed, 334 insertions(+), 7 deletions(-) create mode 100644 platformapi/tiktok/callback.go create mode 100644 platformapi/tiktok/refund.go diff --git a/platformapi/mtwmapi/mtwmapi_test.go b/platformapi/mtwmapi/mtwmapi_test.go index d1a4edf0..4339280d 100644 --- a/platformapi/mtwmapi/mtwmapi_test.go +++ b/platformapi/mtwmapi/mtwmapi_test.go @@ -19,10 +19,10 @@ func init() { baseapi.Init(sugarLogger) // 菜市 - //api = New("589", "a81eb3df418d83d6a1a4b7c572156d2f", "", "") + api = New("589", "a81eb3df418d83d6a1a4b7c572156d2f", "", "") // 果园 - api = New("4123", "df2c88338b85f830cebce2a9eab56628", "", "") + //api = New("4123", "df2c88338b85f830cebce2a9eab56628", "", "") //商超 //api = New("5873", "41c479790a76f86326f89e8048964739", "", "token_vI4fNBaB5J1qvft0WmZG_g") //token_nH_IlcWQKAkZBqklwItNRw diff --git a/platformapi/mtwmapi/order.go b/platformapi/mtwmapi/order.go index 16a29782..b189d951 100644 --- a/platformapi/mtwmapi/order.go +++ b/platformapi/mtwmapi/order.go @@ -1,6 +1,7 @@ package mtwmapi import ( + "encoding/json" "errors" "git.rosy.net.cn/baseapi/utils" "time" @@ -413,7 +414,6 @@ func (a *API) OrderDelivering(orderID int64) (err error) { return errors.New("美团系统操作异常,订单查询ng") } } - return err } @@ -697,3 +697,70 @@ func (a *API) OrderStatusAndPsInfo(params map[string]interface{}) (err error) { _, err = a.AccessAPI("ecommerce/order/logistics/sync", false, params) return err } + +// 获取取消跑腿理由刘表 +// https://open-shangou.meituan.com/home/docDetail/482 +func (a *API) GetCancelDeliveryReason(orderId int64, appPoiCode string) ([]*ReasonList, error) { + data, err := a.AccessAPI("order/getCancelDeliveryReason", false, map[string]interface{}{"order_id": orderId, "app_poi_code": appPoiCode}) + if err != nil { + return nil, err + } + + result := &CancelDeliveryReasonList{} + if err := json.Unmarshal([]byte(utils.Interface2String(data)), result); err != nil { + return nil, err + } + return result.Data.ReasonList, nil +} + +type CancelDeliveryReasonList struct { + Data *Data `json:"data"` +} + +type Data struct { + Code int `json:"code"` + Title string `json:"title"` + ReasonList []*ReasonList `json:"reasonList"` + Msg string `json:"msg"` + DeliveryStatus int `json:"deliveryStatus"` +} + +type ReasonList struct { + Code string `json:"code"` + Content string `json:"content"` + PreCancelCode int `json:"preCancelCode"` + PreCancelMsg string `json:"preCancelMsg"` +} + +// 取消跑腿配送 +func (a *API) CancelLogisticsByWmOrderId(param *CancelOrderParam) error { + data, err := a.AccessAPI("order/getCancelDeliveryReason", false, utils.Struct2FlatMap(param)) + if err != nil { + return err + } + + result := &ResultMsg{} + if err := json.Unmarshal([]byte(utils.Interface2String(data)), result); err != nil { + return err + } + if result.Data != "ok" { + return errors.New(result.Error.Msg) + } + return nil +} + +type ResultMsg struct { + Data string `json:"data"` + Error *struct { + Code int `json:"code"` + Msg string `json:"msg"` + } +} + +// 取消跑腿配送请求参数 +type CancelOrderParam struct { + ReasonCode string `json:"reason_code"` // 取消原因code 请开发者一定要先调用 + DetailContent string `json:"detail_content"` // 取消原因 + AppPoiCode string `json:"app_poi_code"` // 门店号 + OrderId string `json:"order_id"` // 订单id +} diff --git a/platformapi/mtwmapi/order_page_test.go b/platformapi/mtwmapi/order_page_test.go index 9efca940..791e1b6a 100644 --- a/platformapi/mtwmapi/order_page_test.go +++ b/platformapi/mtwmapi/order_page_test.go @@ -21,3 +21,11 @@ func TestGetDistributeOrderDetail(t *testing.T) { } baseapi.SugarLogger.Debug(utils.Format4Output(result, false)) } + +func TestCancelReason(t *testing.T) { + result, err := api.GetCancelDeliveryReason(140382470610780245, "14038247") + if err != nil { + t.Fatal(err) + } + baseapi.SugarLogger.Debug(utils.Format4Output(result, false)) +} diff --git a/platformapi/tiktok/callback.go b/platformapi/tiktok/callback.go new file mode 100644 index 00000000..b54690bd --- /dev/null +++ b/platformapi/tiktok/callback.go @@ -0,0 +1,213 @@ +package tiktok + +// +//import ( +// "encoding/base64" +// "encoding/xml" +// "fmt" +// "io/ioutil" +// "net/http" +// "strings" +// +// "git.rosy.net.cn/baseapi/utils" +// "git.rosy.net.cn/jx-callback/globals" +// "github.com/clbanning/mxj" +// "github.com/nanjishidu/gomini/gocrypto" +//) +// +//type CData string +// +//func (c CData) MarshalXTT(e *xml.Encoder, start xml.StartElement) error { +// return e.EncodeElement(struct { +// string `xml:",cdata"` +// }{string(c)}, start) +//} +// +//const ( +// MsgTypeUnkown = 0 // 未知 +// MsgTypePay = 1 // 支付结果回调 +// MsgTypeRefund = 2 // 退款结果回调 +//) +// +//type CallbackResponse struct { +// XMLName xml.Name `json:"-" xml:"xml"` +// +// ReturnCode string `json:"return_code" xml:"return_code"` +// ReturnMsg CData `json:"return_msg" xml:"return_msg"` +//} +// +//type BaseResultMsg struct { +// ReturnCode string `json:"return_code" xml:"return_code"` +// ReturnMsg string `json:"return_msg" xml:"return_msg"` +// +// AppID string `json:"appid" xml:"appid"` +// DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"` +// MchID string `json:"mch_id" xml:"mch_id"` +// NonceStr string `json:"nonce_str" xml:"nonce_str"` +// Sign string `json:"sign" xml:"sign"` +// ResultCode string `json:"result_code" xml:"result_code"` +// ResultMsg string `json:"result_msg" xml:"result_msg"` +// ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"` +// ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"` +//} +// +//type PayResultMsg struct { +// ReturnCode string `json:"return_code" xml:"return_code"` +// ReturnMsg string `json:"return_msg" xml:"return_msg"` +// +// AppID string `json:"appid" xml:"appid"` +// DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"` +// MchID string `json:"mch_id" xml:"mch_id"` +// NonceStr string `json:"nonce_str" xml:"nonce_str"` +// Sign string `json:"sign" xml:"sign"` +// ResultCode string `json:"result_code" xml:"result_code"` +// ResultMsg string `json:"result_msg" xml:"result_msg"` +// ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"` +// ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"` +// +// BankType string `json:"bank_type"` +// CashFee string `json:"cash_fee"` +// FeeType string `json:"fee_type"` +// IsSubscribe string `json:"is_subscribe"` +// OpenID string `json:"openid"` +// OutTradeNo string `json:"out_trade_no"` +// TimeEnd string `json:"time_end"` +// TotalFee string `json:"total_fee"` +// TradeType string `json:"trade_type"` +// TransactionID string `json:"transaction_id"` +//} +// +//type RefundReqInfo struct { +// OutRefundNo string `json:"out_refund_no"` +// OutTradeNo string `json:"out_trade_no"` +// RefundAccount string `json:"refund_account"` +// RefundFee string `json:"refund_fee" xml:"refund_fee"` +// RefundID string `json:"refund_id"` +// RefundRecvAccout string `json:"refund_recv_accout"` +// RefundRequestSource string `json:"refund_request_source"` +// RefundStatus string `json:"refund_status"` +// SettlementRefundFee string `json:"settlement_refund_fee"` +// SettlementTotalFee string `json:"settlement_total_fee"` +// SuccessTime string `json:"success_time"` +// TotalFee string `json:"total_fee"` +// TransactionID string `json:"transaction_id"` +//} +// +//type RefundResultMsg struct { +// ReturnCode string `json:"return_code" xml:"return_code"` +// ReturnMsg string `json:"return_msg" xml:"return_msg"` +// +// AppID string `json:"appid" xml:"appid"` +// DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"` +// MchID string `json:"mch_id" xml:"mch_id"` +// NonceStr string `json:"nonce_str" xml:"nonce_str"` +// Sign string `json:"sign" xml:"sign"` +// ResultCode string `json:"result_code" xml:"result_code"` +// ResultMsg string `json:"result_msg" xml:"result_msg"` +// ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"` +// ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"` +// +// ReqInfoStr string `json:"req_info,omitempty"` +// ReqInfoObj *RefundReqInfo `json:"req_info_obj,omitempty"` +//} +// +//type CallbackMsg struct { +// MsgType int +// MapData map[string]interface{} +// Data interface{} +//} +// +//var ( +// SuccessResponse = &CallbackResponse{ +// ReturnCode: ResponseCodeSuccess, +// ReturnMsg: "OK", +// } +//) +// +//func Err2CallbackResponse(err error, data string) *CallbackResponse { +// if err == nil { +// return SuccessResponse +// } +// returnMsg := err.Error() +// if data != "" { +// returnMsg = fmt.Sprintf("%s,%s", returnMsg, data) +// } +// return &CallbackResponse{ +// ReturnCode: ResponseCodeFail, +// ReturnMsg: CData(returnMsg), +// } +//} +// +//func (a *API) decodeReqInfo(msg string) (decryptedMsg string, err error) { +// binMsg, err := base64.StdEncoding.DecodeString(msg) +// if err == nil { +// // aesKey := []byte(fmt.Sprintf("%x", md5.Sum([]byte(a.appKey)))) +// // binResult, err2 := utils.AESCBCDecpryt(binMsg, aesKey, aesKey[:16]) +// gocrypto.SetAesKey(strings.ToLower(gocrypto.Md5(a.appKey))) +// binResult, err2 := gocrypto.AesECBDecrypt(binMsg) +// if err = err2; err == nil { +// decryptedMsg = string(binResult) +// } +// } +// return decryptedMsg, err +//} +// +//func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackResponse *CallbackResponse) { +// data, err := ioutil.ReadAll(request.Body) +// if err != nil { +// return nil, Err2CallbackResponse(err, "") +// } +// return a.getCallbackMsg(string(data)) +//} +// +//func (a *API) getCallbackMsg(msgBody string) (msg *CallbackMsg, callbackResponse *CallbackResponse) { +// mapData, _, err := a.parseXmlStrAsMap(msgBody) +// if err != nil { +// return nil, Err2CallbackResponse(err, "") +// } +// returnCode := utils.Interface2String(mapData["return_code"]) +// if returnCode != ResponseCodeSuccess { // 如果return_code出错,直接返回 +// return nil, SuccessResponse +// } +// +// reqInfo := utils.Interface2String(mapData["req_info"]) +// transactionID := utils.Interface2String(mapData["transaction_id"]) +// if reqInfo == "" && transactionID != "" { // 对于支付结果通知进行签名验证(退款结果通知不支持验证) +// sigType := utils.Interface2String(mapData[sigTypeKey]) +// sign := utils.Interface2String(mapData[sigKey]) +// desiredSign := a.signParam(sigType, mapData) +// if desiredSign != sign { +// globals.SugarLogger.Debugf("transactionID:%s, sigType:%s, desiredSign:%s <> sign:%s", transactionID, sigType, desiredSign, sign) +// return nil, Err2CallbackResponse(fmt.Errorf("desiredSign:%s <> sign:%s", desiredSign, sign), "") +// } +// } +// +// msg = &CallbackMsg{ +// MapData: mapData, +// } +// if reqInfo != "" { +// msg.MsgType = MsgTypeRefund +// var refundResult *RefundResultMsg +// if err = utils.Map2StructByJson(mapData, &refundResult, false); err == nil { +// if reqInfo, err = a.decodeReqInfo(reqInfo); err == nil { +// mv, err2 := mxj.NewMapXml([]byte(reqInfo)) +// if err = err2; err == nil { +// reqInfoMap := mv["root"].(map[string]interface{}) +// if err = utils.Map2StructByJson(reqInfoMap, &refundResult.ReqInfoObj, false); err == nil { +// msg.Data = refundResult +// } +// } +// } +// } +// } else if transactionID != "" { +// msg.MsgType = MsgTypePay +// var payResult *PayResultMsg +// if err = utils.Map2StructByJson(mapData, &payResult, false); err == nil { +// msg.Data = payResult +// } +// } +// if err != nil { +// callbackResponse = Err2CallbackResponse(err, "") +// } +// return msg, callbackResponse +//} diff --git a/platformapi/tiktok/refund.go b/platformapi/tiktok/refund.go new file mode 100644 index 00000000..cfeb1993 --- /dev/null +++ b/platformapi/tiktok/refund.go @@ -0,0 +1,43 @@ +package tiktok + +import ( + "errors" + "git.rosy.net.cn/baseapi/utils" +) + +// 抖音退款流程 +type RefundOrderReq struct { + AppID string `json:"app_id"` // 小程序APPID + OutOrderNo string `json:"out_order_no"` // 商户分配支付单号,标识进行退款的订单 + OutRefundNo string `json:"out_refund_no"` // 商户分配退款号,保证在商户中唯一 + Reason string `json:"reason"` // 退款原因 + RefundAmount int `json:"refund_amount"` // 退款金额,单位[分] + Sign string `json:"sign"` // 签名,详见 + CpExtra string `json:"cp_extra"` // 开发者自定义字段,回调原样回传 + NotifyURL string `json:"notify_url"` // 商户自定义回调地址 +} + +// 返回值 +type RefundOrderRes struct { + ErrNo int64 `json:"err_no"` // 错误码 + ErrTips string `json:"err_tips"` // 详情 + RefundNo string `json:"refund_no"` // 担保交易服务端退款单号 +} + +// 抖音退款 +func (a *API) RefundOrderTT(param *RefundOrderReq) (string, error) { + param.Sign = a.Sign(utils.Struct2FlatMap(param)) + data, err := a.AccessAPI2(PayUrl, utils.Struct2FlatMap(param)) + if err != nil { + return "", err + } + + result := &RefundOrderRes{} + if err := utils.Map2StructByJson(data, result, false); err != nil { + return "", err + } + if result.ErrNo != 0 { + return "", errors.New(result.ErrTips) + } + return result.RefundNo, nil +} diff --git a/platformapi/tiktok/tiktok_pay.go b/platformapi/tiktok/tiktok_pay.go index 6a021fca..d1d6d61c 100644 --- a/platformapi/tiktok/tiktok_pay.go +++ b/platformapi/tiktok/tiktok_pay.go @@ -30,18 +30,14 @@ func (a *API) Sign(paramsMap map[string]interface{}) string { continue } switch k { - // app_id, thirdparty_id, sign 字段用于标识身份,不参与签名 case "app_id", "thirdparty_id", "sign": default: paramsArr = append(paramsArr, value) } - // paramsArr = append(paramsArr, value) } paramsArr = append(paramsArr, salt) - fmt.Println("==========1", paramsArr) sort.Strings(paramsArr) - fmt.Println("==========2", paramsArr) return fmt.Sprintf("%x", md5.Sum([]byte(strings.Join(paramsArr, "&")))) }