diff --git a/business/jxstore/misc/misc.go b/business/jxstore/misc/misc.go index a4016209f..523455b84 100644 --- a/business/jxstore/misc/misc.go +++ b/business/jxstore/misc/misc.go @@ -182,18 +182,18 @@ func Init() { delivery.GetOrderRiderInfoToPlatform("", 0) }, 10*time.Second, 4*time.Minute) - //每天晚上23:00更新抖店 审核状态 - ScheduleTimerFunc("UpdateStorePoiStatus", func() { - cms.UpdateStorePoiStatus(jxcontext.AdminCtx) - }, []string{ - "22:00:00", - }) - //每天晚上23:00获取门店 电子围栏、仓库、限售模板、运费模板ID同步进数据库 - ScheduleTimerFunc("UpdateStoreRelInformation", func() { - cms.UpdateStoreRelInformation(jxcontext.AdminCtx) - }, []string{ - "22:00:00", - }) + //每天晚上23:00更新抖店 审核状态(收费注释) + //ScheduleTimerFunc("UpdateStorePoiStatus", func() { + // cms.UpdateStorePoiStatus(jxcontext.AdminCtx) + //}, []string{ + // "22:00:00", + //}) + //每天晚上23:00获取门店 电子围栏、仓库、限售模板、运费模板ID同步进数据库(收费注释) + //ScheduleTimerFunc("UpdateStoreRelInformation", func() { + // cms.UpdateStoreRelInformation(jxcontext.AdminCtx) + //}, []string{ + // "22:00:00", + //}) // 更新抖店订单的结算信息 ScheduleTimerFunc("UpdateTiktokShopTotalMoney", func() { orderman.UpdateTiktokShopTotalMoney() @@ -264,7 +264,7 @@ func Init() { if beego.BConfig.RunMode != "jxgy" { ScheduleTimerFuncByInterval(func() { cms.RefreshTiktokShopToken(jxcontext.AdminCtx) - }, 60*time.Second, 30*time.Minute) + }, 60*time.Second, 60*time.Minute) } // 定时任务刷新当前订单的物流信息 @@ -368,10 +368,10 @@ func Init() { ScheduleTimerFunc("GetNewVendorPopActs", func() { act.GetNewVendorPopActs(jxcontext.AdminCtx) }, dailyHeartbeat) - //企业微信群人数通告 - ScheduleTimerFunc("SendQywxPeopleCount", func() { - cms.SendQywxPeopleCount(jxcontext.AdminCtx) - }, dailyHeartbeat) + //企业微信群人数通告(暂时取消) + //ScheduleTimerFunc("SendQywxPeopleCount", func() { + // cms.SendQywxPeopleCount(jxcontext.AdminCtx) + //}, dailyHeartbeat) ScheduleTimerFunc("doDailyWork1", func() { //同步商品额外前缀和水印图(打标记) cms.SyncSkuExperfixAndWatermark(jxcontext.AdminCtx) diff --git a/business/model/api_config.go b/business/model/api_config.go index cdb2bf2a9..298283ac0 100644 --- a/business/model/api_config.go +++ b/business/model/api_config.go @@ -22,7 +22,8 @@ const ( VendorGoMei = 12 // 国美 VendorIDTT = 13 // 抖音平台小程序 VendorIDDD = 14 // 抖店 - VendorIDPurchaseEnd = 15 + VendorIDKS = 15 // 快手小程序 + VendorIDPurchaseEnd = 20 VendorIDWXPay = 51 // 微信支付 diff --git a/business/model/order.go b/business/model/order.go index c2ac52093..74303f634 100644 --- a/business/model/order.go +++ b/business/model/order.go @@ -11,9 +11,10 @@ const ( ) const ( - PayTypeWX = 1 // 微信支付 - PayTypeTL = 2 // 通联宝支付 - PayTypeTicTok = 3 // 抖音支付 + PayTypeWX = 1 // 微信支付 + PayTypeTL = 2 // 通联宝支付 + PayTypeTicTok = 3 // 抖音支付 + PayTypeKuaiShou = 4 // 快手支付 PayTypeTL_DiscountCard = 3 // 通联宝支付(会员折扣卡) PayTypeTL_StoreAcctPay = 4 // 通联宝支付(门店账户充值) diff --git a/business/partner/purchase/ebai/store_sku2.go b/business/partner/purchase/ebai/store_sku2.go index 1f2ceacb9..c2fdee9a7 100644 --- a/business/partner/purchase/ebai/store_sku2.go +++ b/business/partner/purchase/ebai/store_sku2.go @@ -515,6 +515,9 @@ func genSkuParamsFromStoreSkuInfo2(storeSku *dao.StoreSkuSyncInfo, isCreate, isE if storeSku.MinOrderCount > 0 { params["minimum"] = utils.Int2Float64(storeSku.MinOrderCount) } + if storeSku.MinOrderCount == 0 { + params["minimum"] = utils.Int2Float64(1) + } return params } diff --git a/business/partner/purchase/jx/localjx/kuaishou_pay.go b/business/partner/purchase/jx/localjx/kuaishou_pay.go new file mode 100644 index 000000000..6a54e5e3a --- /dev/null +++ b/business/partner/purchase/jx/localjx/kuaishou_pay.go @@ -0,0 +1,191 @@ +package localjx + +import ( + "errors" + "fmt" + "git.rosy.net.cn/baseapi/platformapi/kuaishou_mini" + "strings" + "time" + + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/auth2/authprovider/kuaishou" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" + "git.rosy.net.cn/jx-callback/globals" + "git.rosy.net.cn/jx-callback/globals/api" +) + +func getOrderBriefKs(order *model.GoodsOrder) string { + sku := make([]string, len(order.Skus)) + for _, v := range order.Skus { + sku = append(sku, fmt.Sprintf("%s x %d件商品", v.SkuName, v.Count)) + } + return strings.Join(sku, ",") +} + +func pay4OrderByKs(ctx *jxcontext.Context, order *model.GoodsOrder, vendorPayType, subAppID string) (orderPay *model.OrderPay, err error) { + // 获取用户快手OpenId + var ( + db = dao.GetDB() + ) + authBindList, err := dao.GetUserBindAuthInfo(db, ctx.GetUserID(), model.AuthBindTypeAuth, []string{kuaishou.AuthTypeKuaiShouMini}, "", "", nil) + if err != nil { + return nil, err + } + if len(authBindList) == model.NO { + return nil, errors.New("用户未绑定快手,无法快手支付") + } + + param := &kuaishou_mini.PreCreateOrderReq{ + OutOrderNo: utils.Int64ToStr(GenPayOrderID(order)), + OpenId: authBindList[0].AuthID, + TotalAmount: order.ActualPayPrice, + Subject: "蔬菜水果日用品", + Detail: getOrderBriefKs(order), + TypeDetail: 1832, // 蔬菜:费率2%,水果:1833%2 + ExpireTime: 60 * 10, + Sign: "", + Attach: "", + NotifyUrl: "http://callback.jxc4.com/kuaishou/KuaiShouCallback", + GoodsId: "", + GoodsDetailUrl: "", + MultiCopiesGoodsInfo: "", + CancelOrder: 0, + } + + // 预下单 + prePayInfo, err := api.KuaiShouApi.PreCreateOrder(param) + if err == nil { + orderPay = &model.OrderPay{ + PayOrderID: order.VendorOrderID, // 抖音订单id + PayType: model.PayTypeKuaiShou, + VendorPayType: vendorPayType, + VendorOrderID: order.VendorOrderID, + VendorID: order.VendorID, + Status: 0, + PayCreatedAt: time.Now(), + PrepayID: "", + CodeURL: prePayInfo, // 抖音支付token + TotalFee: int(order.ActualPayPrice), + } + } + return orderPay, err +} + +func OnKSPayCallback(msg *kuaishou_mini.CallBackDetail, refund *kuaishou_mini.RefundCallBack, payType string) (err error) { + switch payType { + case kuaishou_mini.CallbackTypePay: // 支付回调 + err = onKSPayFinished(msg) + case kuaishou_mini.CallbackTypeRefund: // 退款回调 + err = onKSPayRefund(refund) + } + return err +} + +func onKSPayFinished(msg *kuaishou_mini.CallBackDetail) (err error) { + orderPay := &model.OrderPay{ + PayOrderID: msg.OutOrderNo, + PayType: model.PayTypeTicTok, + } + orderPay.DeletedAt = utils.DefaultTimeValue + db := dao.GetDB() + if err = dao.GetEntity(db, orderPay, "PayOrderID", "PayType", "DeletedAt"); err == nil { + orderPay.PayFinishedAt = utils.Time2Pointer(time.Now()) + orderPay.TransactionID = msg.TradeNo + orderPay.OriginalData = utils.Format4Output(msg, true) + switch msg.Status { + case kuaishou_mini.OrderPayStatusHandleing: + orderPay.Status = model.PayStatusNo + case kuaishou_mini.OrderPayStatusSuccess: + orderPay.Status = model.PayStatusYes + case kuaishou_mini.OrderPayStatusFailed: + orderPay.Status = model.PayStatusFailed + } + dao.UpdateEntity(db, orderPay) + + if msg.Status == kuaishou_mini.OrderPayStatusSuccess { + err = OnPayFinished(orderPay) + } + } else { + globals.SugarLogger.Debugf("onKSPayFinished msg:%s, err:%v", utils.Format4Output(msg, true), err) + } + return err +} + +func onKSPayRefund(msg *kuaishou_mini.RefundCallBack) (err error) { + orderPayRefund := &model.OrderPayRefund{ + RefundID: msg.OutRefundNo, + } + db := dao.GetDB() + if err = dao.GetEntity(db, orderPayRefund, "RefundID"); err == nil { + switch msg.Status { + case kuaishou_mini.OrderPayStatusHandleing: + orderPayRefund.Status = model.PayStatusNo + case kuaishou_mini.OrderPayStatusSuccess: + orderPayRefund.Status = model.PayStatusYes + case kuaishou_mini.OrderPayStatusFailed: + orderPayRefund.Status = model.PayStatusFailed + } + + orderPayRefund.OriginalData = utils.Format4Output(msg, true) + dao.UpdateEntity(db, orderPayRefund) + } else if dao.IsNoRowsError(err) { + globals.SugarLogger.Warnf("收到异常的退款事件, msg:%s", utils.Format4Output(msg, true)) + } + + orderPay := &model.OrderPay{ + VendorOrderID: orderPayRefund.VendorOrderID, + VendorID: jxutils.GetPossibleVendorIDFromVendorOrderID(orderPayRefund.VendorOrderID), + PayType: model.PayTypeKuaiShou, + Status: model.PayStatusYes, + } + orderPay.DeletedAt = utils.DefaultTimeValue + if err = dao.GetEntity(db, orderPay, "VendorOrderID", "VendorID", "PayType", "Status", "DeletedAt"); err == nil { + orderPay.Status = model.PayStatusRefund + dao.UpdateEntity(db, orderPay) + } + return err +} + +// RefundOrderByKS 申请退款 +func RefundOrderByKS(ctx *jxcontext.Context, orderPay *model.OrderPay, refundID string, refundFee int, refundDesc string) (orderPayRefund *model.OrderPayRefund, err error) { + + param := kuaishou_mini.RefundParam{ + OutOrderNo: orderPay.VendorOrderID, + OutRefundNo: refundID, + Reason: refundDesc, + Attach: "", + NotifyUrl: "http://callback.jxc4.com/kuaishou/kuaiShouCallback", + RefundAmount: int64(orderPay.TotalFee), + Sign: "", + MultiCopiesGoodsInfo: "", + } + result, err := api.KuaiShouApi.RefundOrder(¶m) + if err == nil { + orderPayRefund = &model.OrderPayRefund{ + RefundID: refundID, + VendorRefundID: result, + VendorOrderID: orderPay.VendorOrderID, + VendorID: orderPay.VendorID, + Status: model.RefundStatusNo, + TransactionID: orderPay.TransactionID, + RefundFee: refundFee, + RefundCreatedAt: time.Now(), + } + dao.WrapAddIDCULDEntity(orderPayRefund, ctx.GetUserName()) + db := dao.GetDB() + if result != "" { + orderPayRefund.Status = model.RefundStatusYes + } else { + orderPayRefund.Status = model.RefundStatusFailed + } + orderPayRefund.OriginalData = utils.Format4Output(result, true) + dao.CreateEntity(db, orderPayRefund) + + orderPay.Status = model.PayStatusRefund + dao.UpdateEntity(db, orderPay) + } + return orderPayRefund, err +} diff --git a/business/partner/purchase/jx/localjx/order.go b/business/partner/purchase/jx/localjx/order.go index bcc1b1600..e4c2b6751 100644 --- a/business/partner/purchase/jx/localjx/order.go +++ b/business/partner/purchase/jx/localjx/order.go @@ -335,6 +335,11 @@ func Pay4Order(ctx *jxcontext.Context, orderID int64, payType int, vendorPayType dao.WrapAddIDCULDEntity(orderPay, ctx.GetUserName()) err = dao.CreateEntity(dao.GetDB(), orderPay) } + case model.PayTypeKuaiShou: + if orderPay, err = pay4OrderByKs(ctx, order, vendorPayType, subAppID); err == nil && orderPay != nil { + dao.WrapAddIDCULDEntity(orderPay, ctx.GetUserName()) + err = dao.CreateEntity(dao.GetDB(), orderPay) + } default: err = fmt.Errorf("支付方式:%d当前不支持", payType) } @@ -1393,7 +1398,8 @@ func CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) // refundID := utils.Int64ToStr(GenRefundID(order)) refundID := order.VendorOrderID var orderPayRefund *model.OrderPayRefund - if orderPay.PayType == model.PayTypeWX { + switch orderPay.PayType { + case model.PayTypeWX: orderPayRefund, err = refundOrderByWX(ctx, orderPay, refundID, orderPay.TotalFee, reason) if err == nil { dao.WrapAddIDCULDEntity(orderPayRefund, ctx.GetUserName()) @@ -1401,17 +1407,23 @@ func CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) } else { errList.AddErr(err) } - } else if orderPay.PayType == model.PayTypeTL { + case model.PayTypeTL: orderPayRefund, err = RefundOrderByTL(ctx, orderPay, refundID, orderPay.TotalFee, reason) if err != nil { errList.AddErr(err) } - } else if orderPay.PayType == model.PayTypeTicTok { + case model.PayTypeTicTok: orderPayRefund, err = RefundOrderByTT(ctx, orderPay, refundID, orderPay.TotalFee, reason) if err != nil { errList.AddErr(err) } + case model.PayTypeKuaiShou: + orderPayRefund, err = RefundOrderByKS(ctx, orderPay, refundID, orderPay.TotalFee, reason) + if err != nil { + errList.AddErr(err) + } } + if err == nil { //如果用了优惠券,状态要刷回去 if order.CouponIDs != "" { diff --git a/business/partner/purchase/jx/localjx/tiktokPay.go b/business/partner/purchase/jx/localjx/tiktokPay.go index 7acd5f8cd..132d120ce 100644 --- a/business/partner/purchase/jx/localjx/tiktokPay.go +++ b/business/partner/purchase/jx/localjx/tiktokPay.go @@ -58,6 +58,7 @@ func getTikTok(appID string) (TikTokMini *tiktok.API) { } return TikTokMini } + func OnTTPayCallback(msg *tiktok.DetailCallBackMessage, refund *tiktok.DetailCallBackMessage2Refund, payType string) (err error) { switch payType { case tiktok.PayStatus: // 支付回调 diff --git a/controllers/jx_order.go b/controllers/jx_order.go index 2b139d834..faf94ec9f 100644 --- a/controllers/jx_order.go +++ b/controllers/jx_order.go @@ -852,6 +852,39 @@ func (c *OrderController) PartRefundOrder() { }) } +// @Title 小程序用户申请售后(退款) +// @Description 小程序用户申请售后(退款) +// @Param token header string true "认证token" +// @Param vendorOrderID formData string true "订单ID" +// @Param vendorID formData int true "订单所属厂商ID" +// @Param refundSkuList formData string true "要去除的商品信息,只有skuID与Count字段有效" +// @Param reason formData string true "原因" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /ApplyRefundOrder [post] +func (c *OrderController) ApplyRefundOrder() { + c.callApplyRefundOrder(func(params *tOrderApplyRefundOrderParams) (retVal interface{}, errCode string, err error) { + //var skuList []*model.OrderSku + //err = jxutils.Strings2Objs(params.RefundSkuList, &skuList) + //if err == nil { + // _, _, skuList = skuList2Map(skuList) + // var order *model.GoodsOrder + // order, err = partner.CurOrderManager.LoadOrder(params.VendorOrderID, params.VendorID) + // if err == nil { + // removedAll, err2 := fillSkuList(skuList, order.Skus) + // if err = err2; err == nil { + // if removedAll { + // err = defsch.FixedScheduler.RefundOrder(params.Ctx, order, params.Reason) + // } else { + // err = defsch.FixedScheduler.PartRefundOrder(params.Ctx, order, skuList, params.Reason) + // } + // } + // } + //} + return retVal, "", err + }) +} + func fillSkuList(skuList, orderSkuList []*model.OrderSku) (isSame bool, err error) { skuCount, orderSkuMap, _ := skuList2Map(orderSkuList) skuCount2 := 0 diff --git a/controllers/kuaishou_callback.go b/controllers/kuaishou_callback.go new file mode 100644 index 000000000..2e4e0b17b --- /dev/null +++ b/controllers/kuaishou_callback.go @@ -0,0 +1,72 @@ +package controllers + +import ( + "git.rosy.net.cn/baseapi/platformapi/kuaishou_mini" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/partner/purchase/jx/localjx" + "git.rosy.net.cn/jx-callback/globals" + "git.rosy.net.cn/jx-callback/globals/api" + "github.com/astaxie/beego/server/web" +) + +type KuaiShouController struct { + web.Controller +} + +// KuaiShouCallBack 快手回调 +func (c *KuaiShouController) KuaiShouCallback() { + payOrder, refundOrder, payType, msgId, err := api.KuaiShouApi.KauiShouCallback(c.Ctx.Request) + globals.SugarLogger.Debugf("KuaiShouCallBack payOrder =: %s", utils.Format4Output(payOrder, false)) + globals.SugarLogger.Debugf("KuaiShouCallBack refundOrder=: %s", refundOrder) + globals.SugarLogger.Debugf("KuaiShouCallBack payType=: %s", payType) + globals.SugarLogger.Debugf("KuaiShouCallBack err=: %s", utils.Format4Output(err, false)) + if err != nil { + c.Data["json"] = CallBackFail(msgId) + c.ServeJSON() + return + } + + switch payType { + case kuaishou_mini.CallbackTypePay: + err = localjx.OnKSPayCallback(payOrder, nil, payType) + case kuaishou_mini.CallbackTypeRefund: + err = localjx.OnKSPayCallback(nil, refundOrder, payType) + case kuaishou_mini.CallbackTypeSettle: + c.Data["json"] = CallBackFail(msgId) + c.ServeJSON() + return + default: + c.Data["json"] = CallBackFail(msgId) + c.ServeJSON() + return + } + + if err != nil { + c.Data["json"] = CallBackFail(msgId) + c.ServeJSON() + return + } + + c.Data["json"] = CallBackSuccess(msgId) + c.ServeJSON() + return +} + +type CallBackResult struct { + Result string `json:"result"` + MessageId string `json:"message_id"` +} + +func CallBackSuccess(msgId string) *CallBackResult { + return &CallBackResult{ + Result: "1", + MessageId: msgId, + } +} + +func CallBackFail(msgId string) *CallBackResult { + return &CallBackResult{ + Result: "-1", + MessageId: msgId, + } +} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 1bdfbe821..810c99afb 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -1466,6 +1466,15 @@ func init() { Filters: nil, Params: nil}) + web.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(web.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], + web.ControllerComments{ + Method: "ApplyRefundOrder", + Router: `/ApplyRefundOrder`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + web.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(web.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], web.ControllerComments{ Method: "PrintOrder", diff --git a/routers/router.go b/routers/router.go index ef6e2191d..9b4ef8fab 100644 --- a/routers/router.go +++ b/routers/router.go @@ -193,6 +193,7 @@ func init() { web.AutoRouter(&controllers.TiktokController{}) // 订单 web.AutoRouter(&controllers.TiktokShopController{}) // 门店授权 web.AutoRouter(&controllers.LogisticsController{}) // 抖音快递信息同步 + web.AutoRouter(&controllers.KuaiShouController{}) // 快手回调 // 如下都是用于检测存活的空接口 web.Any("/", func(ctx *beecontext.Context) { ctx.WriteString("pong\n")