From dbf500813c0db541c72eb6631ccf3b85a42ea193 Mon Sep 17 00:00:00 2001 From: Hiven Date: Wed, 26 Jul 2023 19:23:16 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E6=94=AF=E4=BB=98=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- constants/pay.go | 14 ++ model/gmodel/fs_cart_logic.go | 7 + model/gmodel/fs_order_logic.go | 15 ++ model/gmodel/fs_pay_logic.go | 70 +++++- model/gmodel/fs_product_design_logic.go | 4 + model/gmodel/fs_resources_gen.go | 26 ++ model/gmodel/fs_resources_logic.go | 2 + model/gmodel/fs_user_gen.go | 2 +- model/gmodel/var_gen.go | 2 + server/pay/internal/config/config.go | 5 +- server/pay/internal/handler/routes.go | 5 + .../internal/handler/stripewebhookhandler.go | 35 +++ .../internal/logic/orderpaymentintentlogic.go | 50 ++-- .../pay/internal/logic/stripewebhooklogic.go | 227 ++++++++++++++++++ server/pay/internal/types/types.go | 3 + server_api/pay.api | 9 + utils/encryption_decryption/md5.go | 36 +++ 17 files changed, 482 insertions(+), 30 deletions(-) create mode 100644 model/gmodel/fs_resources_gen.go create mode 100644 model/gmodel/fs_resources_logic.go create mode 100644 server/pay/internal/handler/stripewebhookhandler.go create mode 100644 server/pay/internal/logic/stripewebhooklogic.go create mode 100644 utils/encryption_decryption/md5.go diff --git a/constants/pay.go b/constants/pay.go index cd39d457..91826170 100644 --- a/constants/pay.go +++ b/constants/pay.go @@ -6,3 +6,17 @@ const ( PAYMETHOD_STRIPE PayMethod = 1 PAYMETHOD_PAYPAL PayMethod = 2 ) + +type PayStatus int64 + +const ( + PAYSTATUS_SUCCESS PayStatus = 1 + PAYSTATUS_UNSUCCESS PayStatus = 0 +) + +type PayStage int64 + +const ( + PAYSTAGE_DEPOSIT PayStage = 1 // 首付 + PAYSTAGE_REMAINING PayStage = 2 // 尾款 +) diff --git a/model/gmodel/fs_cart_logic.go b/model/gmodel/fs_cart_logic.go index 34824d94..b18f7672 100755 --- a/model/gmodel/fs_cart_logic.go +++ b/model/gmodel/fs_cart_logic.go @@ -87,3 +87,10 @@ func (c *FsCartModel) GetUserCartsByIds(ctx context.Context, userId int64, ids [ err = c.db.WithContext(ctx).Model(&FsCart{}).Where("`id` in (?) and `user_id` = ?", ids, userId).Find(&resp).Error return resp, err } + +func (c *FsCartModel) DeleteCartsByIds(ctx context.Context, ids []int64) ( err error) { + if len(ids) == 0 { + return + } + return c.db.WithContext(ctx).Model(&FsCart{}).Where("`id` in (?)", ids).Delete(&FsCart{}).Error +} diff --git a/model/gmodel/fs_order_logic.go b/model/gmodel/fs_order_logic.go index 16c9a527..b96e4d90 100755 --- a/model/gmodel/fs_order_logic.go +++ b/model/gmodel/fs_order_logic.go @@ -136,6 +136,21 @@ func (m *FsOrderModel) FindCount(ctx context.Context, countBuilder *gorm.DB, fil } } +func (m *FsOrderModel) FindOneByQuery(ctx context.Context, rowBuilder *gorm.DB, filterMap map[string]string) (*FsOrderRel, error) { + var resp FsOrderRel + + if filterMap != nil { + rowBuilder = rowBuilder.Scopes(handler.FilterData(filterMap)) + } + + result := rowBuilder.WithContext(ctx).Limit(1).Find(&resp) + if result.Error != nil { + return nil, result.Error + } else { + return &resp, nil + } +} + // 事务 func (m *FsOrderModel) Trans(ctx context.Context, fn func(ctx context.Context, connGorm *gorm.DB) error) error { tx := m.db.Table(m.name).WithContext(ctx).Begin() diff --git a/model/gmodel/fs_pay_logic.go b/model/gmodel/fs_pay_logic.go index 885c7dbc..d470f2e9 100644 --- a/model/gmodel/fs_pay_logic.go +++ b/model/gmodel/fs_pay_logic.go @@ -1,6 +1,11 @@ package gmodel -import "context" +import ( + "context" + "fusenapi/utils/handler" + + "gorm.io/gorm" +) func (p *FsPayModel) GetListByOrderNumber(ctx context.Context, sn string) (resp []FsPay, err error) { err = p.db.WithContext(ctx).Model(&FsPay{}).Where("`order_number` = ? ", sn).Find(&resp).Error @@ -33,6 +38,69 @@ func (p *FsPayModel) CreateOrUpdate(ctx context.Context, req *FsPay) (resp *FsPa return req, err } +func (m *FsPayModel) RowSelectBuilder(selectData []string) *gorm.DB { + var rowBuilder = m.db.Table(m.name) + + if selectData != nil { + rowBuilder = rowBuilder.Select(selectData) + } else { + rowBuilder = rowBuilder.Select("*") + } + return rowBuilder +} + +func (m *FsPayModel) FindCount(ctx context.Context, countBuilder *gorm.DB, filterMap map[string]string) (int64, error) { + var count int64 + + // 过滤 + if filterMap != nil { + countBuilder = countBuilder.Scopes(handler.FilterData(filterMap)) + } + + result := countBuilder.WithContext(ctx).Limit(1).Count(&count) + if result.Error != nil { + return 0, result.Error + } else { + return count, nil + } +} + +func (m *FsPayModel) FindOneByQuery(ctx context.Context, rowBuilder *gorm.DB, filterMap map[string]string) (*FsPay, error) { + var resp FsPay + + if filterMap != nil { + rowBuilder = rowBuilder.Scopes(handler.FilterData(filterMap)) + } + + result := rowBuilder.WithContext(ctx).Limit(1).Find(&resp) + if result.Error != nil { + return nil, result.Error + } else { + return &resp, nil + } +} + +// 事务 +func (m *FsPayModel) Trans(ctx context.Context, fn func(ctx context.Context, connGorm *gorm.DB) error) error { + tx := m.db.Table(m.name).WithContext(ctx).Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err := tx.Error; err != nil { + return err + } + + if err := fn(ctx, tx); err != nil { + tx.Rollback() + return err + } + + return tx.Commit().Error +} + func (m *FsPayModel) TableName() string { return m.name } diff --git a/model/gmodel/fs_product_design_logic.go b/model/gmodel/fs_product_design_logic.go index a1b4fe62..71f26d17 100755 --- a/model/gmodel/fs_product_design_logic.go +++ b/model/gmodel/fs_product_design_logic.go @@ -35,6 +35,10 @@ func (d *FsProductDesignModel) UpdateBySn(ctx context.Context, sn string, data * return d.db.WithContext(ctx).Model(&FsProductDesign{}).Where("`sn` = ?", sn).Updates(&data).Error } +func (d *FsProductDesignModel) UpdateByIds(ctx context.Context, ids []int64, data *FsProductDesign) error { + return d.db.Table(d.name).WithContext(ctx).Model(&FsProductDesign{}).Where("`id` in ?", ids).Updates(&data).Error +} + func (m *FsProductDesignModel) TableName() string { return m.name } diff --git a/model/gmodel/fs_resources_gen.go b/model/gmodel/fs_resources_gen.go new file mode 100644 index 00000000..432a8113 --- /dev/null +++ b/model/gmodel/fs_resources_gen.go @@ -0,0 +1,26 @@ +package gmodel + +import ( + "gorm.io/gorm" + "time" +) + +// fs_resources 资源表 +type FsResources struct { + ResourceId string `gorm:"primary_key;default:'';" json:"resource_id"` // 资源 ID + UserId *int64 `gorm:"index;default:0;" json:"user_id"` // 用户 ID + GuestId *int64 `gorm:"index;default:0;" json:"guest_id"` // 访客 ID + ResourceType *string `gorm:"index;default:'';" json:"resource_type"` // 资源类型 + ResourceUrl *string `gorm:"default:'';" json:"resource_url"` // 资源 URL + UploadedAt *time.Time `gorm:"index;default:'0000-00-00 00:00:00';" json:"uploaded_at"` // 上传时间 + Metadata *string `gorm:"default:'';" json:"metadata"` // 元数据,json格式,存储图像分率 + MetaKey1 *string `gorm:"index;default:'';" json:"meta_key1"` // 需要关键信息查询的自定义属性1,可以动态增加 +} +type FsResourcesModel struct { + db *gorm.DB + name string +} + +func NewFsResourcesModel(db *gorm.DB) *FsResourcesModel { + return &FsResourcesModel{db: db, name: "fs_resources"} +} diff --git a/model/gmodel/fs_resources_logic.go b/model/gmodel/fs_resources_logic.go new file mode 100644 index 00000000..e68225aa --- /dev/null +++ b/model/gmodel/fs_resources_logic.go @@ -0,0 +1,2 @@ +package gmodel +// TODO: 使用model的属性做你想做的 \ No newline at end of file diff --git a/model/gmodel/fs_user_gen.go b/model/gmodel/fs_user_gen.go index 05b8cdd1..81d0f008 100644 --- a/model/gmodel/fs_user_gen.go +++ b/model/gmodel/fs_user_gen.go @@ -30,7 +30,7 @@ type FsUser struct { IsPhoneAdvertisement *int64 `gorm:"default:0;" json:"is_phone_advertisement"` // 是否接收短信广告 IsOpenRender *int64 `gorm:"default:0;" json:"is_open_render"` // 是否打开个性化渲染(1:开启,0:关闭) IsThousandFace *int64 `gorm:"default:0;" json:"is_thousand_face"` // 是否已经存在千人千面(1:存在,0:不存在) - IsLowRendering *int64 `gorm:"default:0;" json:"is_low_rendering"` // + IsLowRendering *int64 `gorm:"default:0;" json:"is_low_rendering"` // 是否开启低渲染模型渲染 IsRemoveBg *int64 `gorm:"default:1;" json:"is_remove_bg"` // 用户上传logo是否去除背景 } type FsUserModel struct { diff --git a/model/gmodel/var_gen.go b/model/gmodel/var_gen.go index a5916431..2fac48a5 100644 --- a/model/gmodel/var_gen.go +++ b/model/gmodel/var_gen.go @@ -79,6 +79,7 @@ type AllModelsGen struct { FsQuotationRemarkTemplate *FsQuotationRemarkTemplateModel // fs_quotation_remark_template 报价单备注模板 FsQuotationSaler *FsQuotationSalerModel // fs_quotation_saler 报价单业务员表 FsRefundReason *FsRefundReasonModel // fs_refund_reason + FsResources *FsResourcesModel // fs_resources 资源表 FsStandardLogo *FsStandardLogoModel // fs_standard_logo 标准logo FsTags *FsTagsModel // fs_tags 产品分类表 FsToolLogs *FsToolLogsModel // fs_tool_logs 3d设计工具日志表 @@ -169,6 +170,7 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsQuotationRemarkTemplate: NewFsQuotationRemarkTemplateModel(gdb), FsQuotationSaler: NewFsQuotationSalerModel(gdb), FsRefundReason: NewFsRefundReasonModel(gdb), + FsResources: NewFsResourcesModel(gdb), FsStandardLogo: NewFsStandardLogoModel(gdb), FsTags: NewFsTagsModel(gdb), FsToolLogs: NewFsToolLogsModel(gdb), diff --git a/server/pay/internal/config/config.go b/server/pay/internal/config/config.go index d032023a..0b7a2dcb 100644 --- a/server/pay/internal/config/config.go +++ b/server/pay/internal/config/config.go @@ -10,12 +10,11 @@ type Config struct { rest.RestConf SourceMysql string Auth types.Auth - - PayConfig struct { + PayConfig struct { Stripe struct { Key string - SuccessURL string CancelURL string + SuccessURL string } } } diff --git a/server/pay/internal/handler/routes.go b/server/pay/internal/handler/routes.go index 13f9ba4c..454d6715 100644 --- a/server/pay/internal/handler/routes.go +++ b/server/pay/internal/handler/routes.go @@ -17,6 +17,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/api/pay/payment-intent", Handler: OrderPaymentIntentHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/api/pay/stripe-webhook", + Handler: StripeWebhookHandler(serverCtx), + }, }, ) } diff --git a/server/pay/internal/handler/stripewebhookhandler.go b/server/pay/internal/handler/stripewebhookhandler.go new file mode 100644 index 00000000..1e0da8fc --- /dev/null +++ b/server/pay/internal/handler/stripewebhookhandler.go @@ -0,0 +1,35 @@ +package handler + +import ( + "net/http" + "reflect" + + "fusenapi/utils/basic" + + "fusenapi/server/pay/internal/logic" + "fusenapi/server/pay/internal/svc" + "fusenapi/server/pay/internal/types" +) + +func StripeWebhookHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var req types.StripeWebhookReq + userinfo, err := basic.RequestParse(w, r, svcCtx, &req) + if err != nil { + return + } + + // 创建一个业务逻辑层实例 + l := logic.NewStripeWebhookLogic(r.Context(), svcCtx) + + rl := reflect.ValueOf(l) + basic.BeforeLogic(w, r, rl) + + resp := l.StripeWebhook(&req, userinfo) + + if !basic.AfterLogic(w, r, rl, resp) { + basic.NormalAfterLogic(w, r, resp) + } + } +} diff --git a/server/pay/internal/logic/orderpaymentintentlogic.go b/server/pay/internal/logic/orderpaymentintentlogic.go index 98c5f0d0..a9d13c22 100644 --- a/server/pay/internal/logic/orderpaymentintentlogic.go +++ b/server/pay/internal/logic/orderpaymentintentlogic.go @@ -84,7 +84,7 @@ func (l *OrderPaymentIntentLogic) OrderPaymentIntent(req *types.OrderPaymentInte } // 判断订单状态以及该支付金额 - // 未支付 + var nowAt int64 = time.Now().Unix() var payAmount int64 if *orderInfo.Status == int64(constants.STATUS_NEW_NOT_PAY) { payAmount = *orderInfo.TotalAmount / 2 @@ -97,37 +97,17 @@ func (l *OrderPaymentIntentLogic) OrderPaymentIntent(req *types.OrderPaymentInte payConfig := &pay.Config{} var generatePrepaymentReq = &pay.GeneratePrepaymentReq{ - ProductName: "aa", + ProductName: "支付标题", Amount: payAmount, Currency: "eur", Quantity: 1, - ProductDescription: "ddddddddddddddddddddddd", + ProductDescription: "支付描述", } - if constants.PayMethod(req.PayMethod) == constants.PAYMETHOD_STRIPE { - payConfig.Stripe.Key = l.svcCtx.Config.PayConfig.Stripe.Key - generatePrepaymentReq.SuccessURL = l.svcCtx.Config.PayConfig.Stripe.SuccessURL - generatePrepaymentReq.CancelURL = l.svcCtx.Config.PayConfig.Stripe.CancelURL - } - payDriver := pay.NewPayDriver(req.PayMethod, payConfig) var resData types.OrderPaymentIntentRes // 事务处理 err = orderModel.Trans(l.ctx, func(ctx context.Context, connGorm *gorm.DB) error { - // 支付预付--生成 - prepaymentRes, err := payDriver.GeneratePrepayment(generatePrepaymentReq) - if err != nil { - return err - } - resData.RedirectUrl = prepaymentRes.URL - - // 订单信息--修改 - err = gmodel.NewFsOrderModel(connGorm).Update(ctx, orderInfo) - if err != nil { - return err - } - // 支付记录--处理 //支付记录改为一条订单多条,分首款尾款 - var createdAt int64 = time.Now().Unix() var payStatus int64 = 0 var orderSource int64 = 1 var payStage int64 @@ -150,14 +130,34 @@ func (l *OrderPaymentIntentLogic) OrderPaymentIntent(req *types.OrderPaymentInte } payStage = 2 } + + // 支付预付--生成 + if constants.PayMethod(req.PayMethod) == constants.PAYMETHOD_STRIPE { + payConfig.Stripe.Key = l.svcCtx.Config.PayConfig.Stripe.Key + generatePrepaymentReq.SuccessURL = l.svcCtx.Config.PayConfig.Stripe.SuccessURL + generatePrepaymentReq.CancelURL = l.svcCtx.Config.PayConfig.Stripe.CancelURL + } + payDriver := pay.NewPayDriver(req.PayMethod, payConfig) + prepaymentRes, err := payDriver.GeneratePrepayment(generatePrepaymentReq) + if err != nil { + return err + } + resData.RedirectUrl = prepaymentRes.URL + + // 订单信息--修改 + err = gmodel.NewFsOrderModel(connGorm).Update(ctx, orderInfo) + if err != nil { + return err + } + if fspay == nil { fspay = &gmodel.FsPay{ UserId: orderInfo.UserId, OrderNumber: orderInfo.Sn, - CreatedAt: &createdAt, + CreatedAt: &nowAt, } } else { - fspay.UpdatedAt = &createdAt + fspay.UpdatedAt = &nowAt } fspay.PayAmount = &payAmount fspay.PayStage = &payStage diff --git a/server/pay/internal/logic/stripewebhooklogic.go b/server/pay/internal/logic/stripewebhooklogic.go new file mode 100644 index 00000000..82c5275b --- /dev/null +++ b/server/pay/internal/logic/stripewebhooklogic.go @@ -0,0 +1,227 @@ +package logic + +import ( + "encoding/json" + "errors" + "fusenapi/constants" + "fusenapi/model/gmodel" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "io" + "net/http" + "time" + + "context" + + "fusenapi/server/pay/internal/svc" + "fusenapi/server/pay/internal/types" + + "github.com/stripe/stripe-go/v74" + "github.com/zeromicro/go-zero/core/logx" + "gorm.io/gorm" +) + +type StripeWebhookLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext + Payload []byte +} + +func NewStripeWebhookLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StripeWebhookLogic { + return &StripeWebhookLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +// 处理进入前逻辑w,r +func (l *StripeWebhookLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) { + const MaxBodyBytes = int64(65536) + r.Body = http.MaxBytesReader(w, r.Body, MaxBodyBytes) + defer r.Body.Close() + payload, err := io.ReadAll(r.Body) + if err != nil { + logx.Error(err) + w.WriteHeader(http.StatusServiceUnavailable) + return + } + l.Payload = payload +} + +// 处理逻辑后 w,r 如:重定向, resp 必须重新处理 +// func (l *StripeWebhookLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) { +// // httpx.OkJsonCtx(r.Context(), w, resp) +// } + +func (l *StripeWebhookLogic) StripeWebhook(req *types.StripeWebhookReq, userinfo *auth.UserInfo) (resp *basic.Response) { + // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) + // userinfo 传入值时, 一定不为null + + event := stripe.Event{} + + if err := json.Unmarshal(l.Payload, &event); err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail") + } + + // Unmarshal the event data into an appropriate struct depending on its Type + switch event.Type { + case "payment_intent.succeeded": + var paymentIntent stripe.PaymentIntent + err := json.Unmarshal(event.Data.Raw, &paymentIntent) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type payment_intent.succeeded") + } + err = l.handlePaymentIntentSucceeded(&paymentIntent) + if err != nil { + return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type Unhandled") + } + case "payment_method.attached": + var paymentMethod stripe.PaymentMethod + err := json.Unmarshal(event.Data.Raw, &paymentMethod) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type payment_method.attached") + } + // ... handle other event types + default: + logx.Error("Unhandled event") + return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type Unhandled") + } + + return resp.SetStatus(basic.CodeOK) +} + +// 成功的付款 +func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe.PaymentIntent) error { + + // 查询支付记录 + payModel := gmodel.NewFsPayModel(l.svcCtx.MysqlConn) + rsbPay := payModel.RowSelectBuilder(nil) + rsbPay = rsbPay.Where("trade_no = ?", paymentIntent.ID) + payInfo, err := payModel.FindOneByQuery(l.ctx, rsbPay, nil) + if err != nil { + return err + } + if *payInfo.PayStatus == 1 { + return errors.New("pay status 1") + } + //订单信息 + orderDetailTemplateModel := gmodel.NewFsOrderDetailTemplateModel(l.svcCtx.MysqlConn) + orderModel := gmodel.NewFsOrderModel(l.svcCtx.MysqlConn) + rsbOrder := orderModel.RowSelectBuilder(nil) + rsbOrder = rsbOrder.Where("trade_no =?", paymentIntent.ID).Preload("FsOrderDetails") + rsbOrder = rsbOrder.Preload("FsOrderDetails", func(dbPreload *gorm.DB) *gorm.DB { + return dbPreload.Table(orderModel.TableName()).Preload("FsOrderDetailTemplateInfo", func(dbPreload *gorm.DB) *gorm.DB { + return dbPreload.Table(orderDetailTemplateModel.TableName()).Preload("FsProductDesignInfo") + }) + }) + fsOrderRel, err := orderModel.FindOneByQuery(l.ctx, rsbOrder, nil) + if err != nil { + return err + } + + var designIds []int64 + var cartIds []int64 + if len(fsOrderRel.FsOrderDetails) > 0 { + for _, fsOrderDetail := range fsOrderRel.FsOrderDetails { + if fsOrderDetail.FsOrderDetailTemplateInfo.FsProductDesignInfo.Id != 0 { + designIds = append(designIds, fsOrderDetail.FsOrderDetailTemplateInfo.FsProductDesignInfo.Id) + } + cartIds = append(cartIds, *fsOrderDetail.CartId) + } + } + + var nowTime int64 = time.Now().Unix() + + // 支付成功 + if paymentIntent.Status == "succeeded" { + var card string = paymentIntent.LatestCharge.PaymentMethodDetails.Card.Last4 + var brand string = string(paymentIntent.LatestCharge.PaymentMethodDetails.Card.Brand) + + err = orderModel.Trans(l.ctx, func(ctx context.Context, connGorm *gorm.DB) (err error) { + // 更新支付信息 + payModelT := gmodel.NewFsPayModel(connGorm) + *payInfo.PayStatus = 1 + *payInfo.PayTime = nowTime + *payInfo.CardNo = card + *payInfo.Brand = brand + _, err = payModelT.CreateOrUpdate(ctx, payInfo) + if err != nil { + return err + } + + // 更新设计数据 + productDesignModelT := gmodel.NewFsProductDesignModel(connGorm) + var isPay int64 = 1 + err = productDesignModelT.UpdateByIds(ctx, designIds, &gmodel.FsProductDesign{IsPay: &isPay}) + if err != nil { + return err + } + + var orderInfo = &gmodel.FsOrder{} + + // 支付记录是首款 + if *payInfo.PayStage == int64(constants.PAYSTAGE_DEPOSIT) { + *orderInfo.Status = int64(constants.STATUS_NEW_PART_PAY) + *orderInfo.IsPartPay = 1 + *orderInfo.PayedAmount = paymentIntent.Amount + // 删除购物车 + cartModelT := gmodel.NewFsCartModel(connGorm) + err = cartModelT.DeleteCartsByIds(ctx, cartIds) + if err != nil { + return err + } + } + + // 支付记录是尾款 + if *payInfo.PayStage == int64(constants.PAYSTAGE_REMAINING) { + if *orderInfo.Status < int64(constants.STATUS_NEW_PAY_COMPLETED) { + *orderInfo.Status = int64(constants.STATUS_NEW_PAY_COMPLETED) + } + *orderInfo.IsPayCompleted = 1 + *orderInfo.PayedAmount = *orderInfo.PayedAmount + paymentIntent.Amount + } + + // 更新订单信息 + *orderInfo.Ptime = nowTime + orderModelT := gmodel.NewFsOrderModel(connGorm) + err = orderModelT.Update(ctx, orderInfo) + if err != nil { + return err + } + return err + }) + if err != nil { + return err + } + + //千人千面的处理 + // $renderServer = (new RenderService()); + // $renderServer->thousandsFacesV2($order->id); + // //清除用户最新的设计 + // $cache = \Yii::$app->cache; + // $cache->delete(CacheConfigHelper::LAST_DESIGN . $order->user_id); + // //缓存最新订单编号 + // $cache->set(CacheConfigHelper::USER_ORDERNO . $order->user_id, $order->sn); + + // //查询用户邮箱信息 + // $user = \api\models\User::find()->where(['id' => $order->user_id])->one(); + // $redisData = [ + // 'key' => 'receipt_download', + // 'param' => [ + // 'email' => $user->email, + // 'order_id' => $order->id, + // 'pay_id' => $pay->id, + // 'type' => 1,//付款成功为1 + // ] + // ]; + // Email::timely($redisData); + } + + // 订单记录 + return nil +} diff --git a/server/pay/internal/types/types.go b/server/pay/internal/types/types.go index 13a18778..c070431b 100644 --- a/server/pay/internal/types/types.go +++ b/server/pay/internal/types/types.go @@ -16,6 +16,9 @@ type OrderPaymentIntentRes struct { RedirectUrl string `json:"redirect_url"` } +type StripeWebhookReq struct { +} + type Request struct { } diff --git a/server_api/pay.api b/server_api/pay.api index f94d77a9..e110c5d4 100644 --- a/server_api/pay.api +++ b/server_api/pay.api @@ -13,6 +13,9 @@ service pay { @handler OrderPaymentIntentHandler post /api/pay/payment-intent(OrderPaymentIntentReq) returns (response); + + @handler StripeWebhookHandler + get /api/pay/stripe-webhook(StripeWebhookReq) returns (response); } // 生成预付款 @@ -26,4 +29,10 @@ type ( OrderPaymentIntentRes { RedirectUrl string `json:"redirect_url"` } +) + +// StripeWebhook支付通知 +type ( + StripeWebhookReq { + } ) \ No newline at end of file diff --git a/utils/encryption_decryption/md5.go b/utils/encryption_decryption/md5.go new file mode 100644 index 00000000..f2a4a450 --- /dev/null +++ b/utils/encryption_decryption/md5.go @@ -0,0 +1,36 @@ +package encryption_decryption + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "encoding/json" + "sort" + "strings" +) + +func MakeSign(params map[string]interface{}) string { + // 排序 + keys := make([]string, len(params)) + i := 0 + for k, _ := range params { + keys[i] = k + i++ + } + sort.Strings(keys) + byteBuf := bytes.NewBuffer([]byte{}) + encoder := json.NewEncoder(byteBuf) + encoder.SetEscapeHTML(false) + + err := encoder.Encode(params) + + if err != nil { + panic(err) + } + + data := byteBuf.String() + + h := md5.New() + h.Write([]byte(strings.TrimRight(data, "\n"))) + return hex.EncodeToString(h.Sum(nil)) +} From cf30fc2b88f79bc35c481f15e9369c53f50161a3 Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Thu, 27 Jul 2023 17:07:54 +0800 Subject: [PATCH 02/15] fix --- constants/websocket.go | 6 +- server/websocket/internal/handler/routes.go | 5 ++ .../handler/thirdpartyloginnotifyhandler.go | 35 ++++++++ .../logic/thirdpartyloginnotifylogic.go | 86 +++++++++++++++++++ server/websocket/internal/types/types.go | 15 ++++ server_api/websocket.api | 18 +++- 6 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 server/websocket/internal/handler/thirdpartyloginnotifyhandler.go create mode 100644 server/websocket/internal/logic/thirdpartyloginnotifylogic.go diff --git a/constants/websocket.go b/constants/websocket.go index 7b4c9ca3..34e28eb7 100644 --- a/constants/websocket.go +++ b/constants/websocket.go @@ -12,8 +12,12 @@ const ( WEBSOCKET_RENDER_IMAGE = "WEBSOCKET_RENDER_IMAGE" //数据格式错误 WEBSOCKET_ERR_DATA_FORMAT = "WEBSOCKET_ERR_DATA_FORMAT" - // + //第三方登录通知 + WEBSOCKET_THIRD_PARTY_LOGIN_NOTIFY = "WEBSOCKET_THIRD_PARTY_LOGIN_NOTIFY" ) // 云渲染完成通知api需要的签名字符串 const RENDER_NOTIFY_SIGN_KEY = "fusen-render-notify-%s-%d" + +// 第三方登录通知api需要的签名字符串 +const THIRD_PARTY_LOGIN_NOTIFY_SIGN_KEY = "fusen-render-notify-%s-%d" diff --git a/server/websocket/internal/handler/routes.go b/server/websocket/internal/handler/routes.go index 859ecc5b..6649e3c3 100644 --- a/server/websocket/internal/handler/routes.go +++ b/server/websocket/internal/handler/routes.go @@ -22,6 +22,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/api/websocket/render_notify", Handler: RenderNotifyHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/api/websocket/third_party_login_notify", + Handler: ThirdPartyLoginNotifyHandler(serverCtx), + }, }, ) } diff --git a/server/websocket/internal/handler/thirdpartyloginnotifyhandler.go b/server/websocket/internal/handler/thirdpartyloginnotifyhandler.go new file mode 100644 index 00000000..ae16b24c --- /dev/null +++ b/server/websocket/internal/handler/thirdpartyloginnotifyhandler.go @@ -0,0 +1,35 @@ +package handler + +import ( + "net/http" + "reflect" + + "fusenapi/utils/basic" + + "fusenapi/server/websocket/internal/logic" + "fusenapi/server/websocket/internal/svc" + "fusenapi/server/websocket/internal/types" +) + +func ThirdPartyLoginNotifyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var req types.ThirdPartyLoginNotifyReq + userinfo, err := basic.RequestParse(w, r, svcCtx, &req) + if err != nil { + return + } + + // 创建一个业务逻辑层实例 + l := logic.NewThirdPartyLoginNotifyLogic(r.Context(), svcCtx) + + rl := reflect.ValueOf(l) + basic.BeforeLogic(w, r, rl) + + resp := l.ThirdPartyLoginNotify(&req, userinfo) + + if !basic.AfterLogic(w, r, rl, resp) { + basic.NormalAfterLogic(w, r, resp) + } + } +} diff --git a/server/websocket/internal/logic/thirdpartyloginnotifylogic.go b/server/websocket/internal/logic/thirdpartyloginnotifylogic.go new file mode 100644 index 00000000..ce077259 --- /dev/null +++ b/server/websocket/internal/logic/thirdpartyloginnotifylogic.go @@ -0,0 +1,86 @@ +package logic + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "fusenapi/constants" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "time" + + "context" + + "fusenapi/server/websocket/internal/svc" + "fusenapi/server/websocket/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type ThirdPartyLoginNotifyLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewThirdPartyLoginNotifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ThirdPartyLoginNotifyLogic { + return &ThirdPartyLoginNotifyLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +// 处理进入前逻辑w,r +// func (l *ThirdPartyLoginNotifyLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) { +// } + +// 处理逻辑后 w,r 如:重定向, resp 必须重新处理 +// func (l *ThirdPartyLoginNotifyLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) { +// // httpx.OkJsonCtx(r.Context(), w, resp) +// } + +func (l *ThirdPartyLoginNotifyLogic) ThirdPartyLoginNotify(req *types.ThirdPartyLoginNotifyReq, userinfo *auth.UserInfo) (resp *basic.Response) { + if time.Now().Unix()-120 > req.Time /*|| req.Time > time.Now().Unix() */ { + return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param: time is invalid") + } + if req.Info.WebsocketId <= 0 { + return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:websocket_id is required") + } + if req.Info.Token == "" { + return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:token is required") + } + //验证签名 sha256 + notifyByte, _ := json.Marshal(req.Info) + h := sha256.New() + h.Write([]byte(fmt.Sprintf(constants.THIRD_PARTY_LOGIN_NOTIFY_SIGN_KEY, string(notifyByte), req.Time))) + signHex := h.Sum(nil) + sign := hex.EncodeToString(signHex) + //fmt.Println(sign) + if req.Sign != sign { + return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid sign") + } + //查询对应websocket连接 + val, ok := mapConnPool.Load(req.Info.WebsocketId) + if !ok { + return resp.SetStatusWithMessage(basic.CodeOK, "success:websocket connection is not exists") + } + ws, ok := val.(wsConnectItem) + if !ok { + return resp.SetStatusWithMessage(basic.CodeServiceErr, "type of websocket connect object is err") + } + data := types.DataTransferData{ + T: constants.WEBSOCKET_THIRD_PARTY_LOGIN_NOTIFY, + D: types.ThirdPartyLoginRspMsg{ + Token: req.Info.Token, + }, + } + b, _ := json.Marshal(data) + select { + case <-ws.closeChan: + return resp.SetStatusWithMessage(basic.CodeOK, "websocket connect object is closed") + case ws.outChan <- b: + return resp.SetStatusWithMessage(basic.CodeOK, "success") + } +} diff --git a/server/websocket/internal/types/types.go b/server/websocket/internal/types/types.go index c8aa0da8..dbeff4db 100644 --- a/server/websocket/internal/types/types.go +++ b/server/websocket/internal/types/types.go @@ -23,6 +23,10 @@ type RenderImageRspMsg struct { Image string `json:"image"` //渲染后的图片 } +type ThirdPartyLoginRspMsg struct { + Token string `json:"token"` +} + type RenderNotifyReq struct { Sign string `json:"sign"` Time int64 `json:"time"` @@ -36,6 +40,17 @@ type NotifyInfo struct { Image string `json:"image"` } +type ThirdPartyLoginNotifyReq struct { + Sign string `json:"sign"` + Time int64 `json:"time"` + Info ThirdPartyLoginNotify `json:"info"` +} + +type ThirdPartyLoginNotify struct { + WebsocketId uint64 `json:"websocket_id"` + Token string `json:"token"` +} + type Request struct { } diff --git a/server_api/websocket.api b/server_api/websocket.api index c2dc6430..379f3e45 100644 --- a/server_api/websocket.api +++ b/server_api/websocket.api @@ -12,9 +12,12 @@ service websocket { //websocket数据交互 @handler DataTransferHandler get /api/websocket/data_transfer(request) returns (response); - //渲染完了通知接口 + //云渲染完了通知接口 @handler RenderNotifyHandler post /api/websocket/render_notify(RenderNotifyReq) returns (response); + //第三方登录通知接口 + @handler ThirdPartyLoginNotifyHandler + post /api/websocket/third_party_login_notify(ThirdPartyLoginNotifyReq) returns (response); } //websocket数据交互 @@ -33,6 +36,9 @@ type RenderImageRspMsg { //websocket发送渲染完的数据 AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本 Image string `json:"image"` //渲染后的图片 } +type ThirdPartyLoginRspMsg { //websocket三方登录的通知数据 + Token string `json:"token"` +} //渲染完了通知接口 type RenderNotifyReq { Sign string `json:"sign"` @@ -44,4 +50,14 @@ type NotifyInfo { TemplateTagId int64 `json:"template_tag_id"` //模板标签id AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本 Image string `json:"image"` +} +//第三方登录通知接口 +type ThirdPartyLoginNotifyReq { + Sign string `json:"sign"` + Time int64 `json:"time"` + Info ThirdPartyLoginNotify `json:"info"` +} +type ThirdPartyLoginNotify { + WebsocketId uint64 `json:"websocket_id"` + Token string `json:"token"` } \ No newline at end of file From 9f9f7d3fd6668cd2572b294529e08d898425dc84 Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Thu, 27 Jul 2023 17:33:50 +0800 Subject: [PATCH 03/15] fix --- .../internal/logic/datatransferlogic.go | 21 +++++++++++++----- .../internal/logic/rendernotifylogic.go | 22 ++++++------------- .../logic/thirdpartyloginnotifylogic.go | 18 +++++---------- .../internal/logic/ws_render_image_logic.go | 10 +++++++-- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index 999bd381..f72dca20 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -79,12 +79,14 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp } defer conn.Close() w.Header().Set("Connection", "Upgrade") - rsp := types.DataTransferData{} //鉴权不成功10秒后断开 /*isAuth, _ := l.checkAuth(svcCtx, r) if !isAuth { time.Sleep(time.Second) //兼容下火狐 - rsp.T = constants.WEBSOCKET_UNAUTH + rsp := types.DataTransferData{ + T: constants.WEBSOCKET_UNAUTH, + D: nil, + } b, _ := json.Marshal(rsp) //先发一条正常信息 _ = conn.WriteMessage(websocket.TextMessage, b) @@ -110,9 +112,7 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp defer ws.close() //把连接成功消息发回去 time.Sleep(time.Second) //兼容下火狐 - rsp.T = constants.WEBSOCKET_CONNECT_SUCCESS - rsp.D = uniqueId - b, _ := json.Marshal(rsp) + b := ws.respondDataFormat(constants.WEBSOCKET_CONNECT_SUCCESS, uniqueId) _ = conn.WriteMessage(websocket.TextMessage, b) //循环读客户端信息 go ws.readLoop() @@ -247,11 +247,22 @@ func (w *wsConnectItem) getRenderImageMapKey(productId, templateTagId int64, alg return fmt.Sprintf("%d-%d-%s", productId, templateTagId, algorithmVersion) } +// 格式化返回数据 +func (w *wsConnectItem) respondDataFormat(msgType string, data interface{}) []byte { + d := types.DataTransferData{ + T: msgType, + D: data, + } + b, _ := json.Marshal(d) + return b +} + // 处理接受到的数据 func (w *wsConnectItem) dealwithReciveData(data []byte) { var parseInfo types.DataTransferData if err := json.Unmarshal(data, &parseInfo); err != nil { logx.Error("invalid format of websocket message") + w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket message") return } d, _ := json.Marshal(parseInfo.D) diff --git a/server/websocket/internal/logic/rendernotifylogic.go b/server/websocket/internal/logic/rendernotifylogic.go index 06ab1172..b0088855 100644 --- a/server/websocket/internal/logic/rendernotifylogic.go +++ b/server/websocket/internal/logic/rendernotifylogic.go @@ -1,10 +1,6 @@ package logic import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" "fusenapi/constants" "fusenapi/utils/basic" "time" @@ -45,7 +41,7 @@ func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq) (resp *basi return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid param time") } //验证签名 sha256 - notifyByte, _ := json.Marshal(req.Info) + /*notifyByte, _ := json.Marshal(req.Info) h := sha256.New() h.Write([]byte(fmt.Sprintf(constants.RENDER_NOTIFY_SIGN_KEY, string(notifyByte), req.Time))) signHex := h.Sum(nil) @@ -53,7 +49,7 @@ func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq) (resp *basi //fmt.Println(sign) if req.Sign != sign { return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid sign") - } + }*/ //遍历websocket链接把数据传进去 mapConnPool.Range(func(key, value any) bool { //断言连接 @@ -67,15 +63,11 @@ func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq) (resp *basi if !ok { return true } - rspData := types.DataTransferData{ - T: constants.WEBSOCKET_RENDER_IMAGE, - D: types.RenderImageRspMsg{ - ProductId: req.Info.ProductId, - TemplateTagId: req.Info.TemplateTagId, - Image: req.Info.Image, - }, - } - b, _ := json.Marshal(rspData) + b := ws.respondDataFormat(constants.WEBSOCKET_RENDER_IMAGE, types.RenderImageRspMsg{ + ProductId: req.Info.ProductId, + TemplateTagId: req.Info.TemplateTagId, + Image: req.Info.Image, + }) //删除对应的需要渲染的图片map ws.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{ Option: 0, //0删除 1添加 diff --git a/server/websocket/internal/logic/thirdpartyloginnotifylogic.go b/server/websocket/internal/logic/thirdpartyloginnotifylogic.go index ce077259..d363ffd9 100644 --- a/server/websocket/internal/logic/thirdpartyloginnotifylogic.go +++ b/server/websocket/internal/logic/thirdpartyloginnotifylogic.go @@ -1,10 +1,6 @@ package logic import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" "fusenapi/constants" "fusenapi/utils/auth" "fusenapi/utils/basic" @@ -52,7 +48,7 @@ func (l *ThirdPartyLoginNotifyLogic) ThirdPartyLoginNotify(req *types.ThirdParty return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:token is required") } //验证签名 sha256 - notifyByte, _ := json.Marshal(req.Info) + /*notifyByte, _ := json.Marshal(req.Info) h := sha256.New() h.Write([]byte(fmt.Sprintf(constants.THIRD_PARTY_LOGIN_NOTIFY_SIGN_KEY, string(notifyByte), req.Time))) signHex := h.Sum(nil) @@ -60,7 +56,7 @@ func (l *ThirdPartyLoginNotifyLogic) ThirdPartyLoginNotify(req *types.ThirdParty //fmt.Println(sign) if req.Sign != sign { return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid sign") - } + }*/ //查询对应websocket连接 val, ok := mapConnPool.Load(req.Info.WebsocketId) if !ok { @@ -70,13 +66,9 @@ func (l *ThirdPartyLoginNotifyLogic) ThirdPartyLoginNotify(req *types.ThirdParty if !ok { return resp.SetStatusWithMessage(basic.CodeServiceErr, "type of websocket connect object is err") } - data := types.DataTransferData{ - T: constants.WEBSOCKET_THIRD_PARTY_LOGIN_NOTIFY, - D: types.ThirdPartyLoginRspMsg{ - Token: req.Info.Token, - }, - } - b, _ := json.Marshal(data) + b := ws.respondDataFormat(constants.WEBSOCKET_THIRD_PARTY_LOGIN_NOTIFY, types.ThirdPartyLoginRspMsg{ + Token: req.Info.Token, + }) select { case <-ws.closeChan: return resp.SetStatusWithMessage(basic.CodeOK, "websocket connect object is closed") diff --git a/server/websocket/internal/logic/ws_render_image_logic.go b/server/websocket/internal/logic/ws_render_image_logic.go index 634b6267..4803a1f4 100644 --- a/server/websocket/internal/logic/ws_render_image_logic.go +++ b/server/websocket/internal/logic/ws_render_image_logic.go @@ -2,6 +2,7 @@ package logic import ( "encoding/json" + "fusenapi/constants" "fusenapi/server/websocket/internal/types" "github.com/zeromicro/go-zero/core/logx" ) @@ -22,8 +23,13 @@ type renderImageControlChanItem struct { func (w *wsConnectItem) SendToCloudRender(data []byte) { var renderImageData types.RenderImageReqMsg if err := json.Unmarshal(data, &renderImageData); err != nil { - logx.Error("invalid format of websocket render image message", err) - return + select { + case <-w.closeChan: + return + case w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket render image message"): + logx.Error("invalid format of websocket render image message", err) + return + } } logx.Info("收到请求云渲染图片数据:", renderImageData) //把需要渲染的图片任务加进去 From 64a284bd77fddd90391654dcf1f07799b8d84839 Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Thu, 27 Jul 2023 17:37:56 +0800 Subject: [PATCH 04/15] fix --- server/websocket/internal/logic/datatransferlogic.go | 2 +- server/websocket/internal/logic/ws_render_image_logic.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index f72dca20..e2056ba4 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -262,7 +262,7 @@ func (w *wsConnectItem) dealwithReciveData(data []byte) { var parseInfo types.DataTransferData if err := json.Unmarshal(data, &parseInfo); err != nil { logx.Error("invalid format of websocket message") - w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket message") + w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket message:"+string(data)) return } d, _ := json.Marshal(parseInfo.D) diff --git a/server/websocket/internal/logic/ws_render_image_logic.go b/server/websocket/internal/logic/ws_render_image_logic.go index 4803a1f4..ea929c26 100644 --- a/server/websocket/internal/logic/ws_render_image_logic.go +++ b/server/websocket/internal/logic/ws_render_image_logic.go @@ -26,7 +26,7 @@ func (w *wsConnectItem) SendToCloudRender(data []byte) { select { case <-w.closeChan: return - case w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket render image message"): + case w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket render image message:"+string(data)): logx.Error("invalid format of websocket render image message", err) return } From 687c18641f8026186949bbabfff11be5a13d952f Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Thu, 27 Jul 2023 17:41:36 +0800 Subject: [PATCH 05/15] fix --- .../internal/logic/ws_render_image_logic.go | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/server/websocket/internal/logic/ws_render_image_logic.go b/server/websocket/internal/logic/ws_render_image_logic.go index ea929c26..1f1c6557 100644 --- a/server/websocket/internal/logic/ws_render_image_logic.go +++ b/server/websocket/internal/logic/ws_render_image_logic.go @@ -19,36 +19,6 @@ type renderImageControlChanItem struct { Key string //map的key } -// 渲染请求数据处理发送云渲染服务处理 -func (w *wsConnectItem) SendToCloudRender(data []byte) { - var renderImageData types.RenderImageReqMsg - if err := json.Unmarshal(data, &renderImageData); err != nil { - select { - case <-w.closeChan: - return - case w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket render image message:"+string(data)): - logx.Error("invalid format of websocket render image message", err) - return - } - } - logx.Info("收到请求云渲染图片数据:", renderImageData) - //把需要渲染的图片任务加进去 - for _, productId := range renderImageData.ProductIds { - select { - case <-w.closeChan: //连接关闭了 - return - default: - //加入渲染任务 - key := w.getRenderImageMapKey(productId, renderImageData.TemplateTagId, renderImageData.AlgorithmVersion) - w.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{ - Option: 1, //0删除 1添加 - Key: key, - } - // TODO 数据发送给云渲染服务器 - } - } -} - // 操作连接中渲染任务的增加/删除 func (w *wsConnectItem) operationRenderTask() { for { @@ -67,3 +37,29 @@ func (w *wsConnectItem) operationRenderTask() { } } } + +// 渲染请求数据处理发送云渲染服务处理 +func (w *wsConnectItem) SendToCloudRender(data []byte) { + var renderImageData types.RenderImageReqMsg + if err := json.Unmarshal(data, &renderImageData); err != nil { + w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket render image message:"+string(data)) + logx.Error("invalid format of websocket render image message", err) + return + } + logx.Info("收到请求云渲染图片数据:", renderImageData) + //把需要渲染的图片任务加进去 + for _, productId := range renderImageData.ProductIds { + select { + case <-w.closeChan: //连接关闭了 + return + default: + //加入渲染任务 + key := w.getRenderImageMapKey(productId, renderImageData.TemplateTagId, renderImageData.AlgorithmVersion) + w.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{ + Option: 1, //0删除 1添加 + Key: key, + } + // TODO 数据发送给云渲染服务器 + } + } +} From 18bbc6788fb5da5d0f8200693ff94db28f9ae3bf Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Thu, 27 Jul 2023 19:30:40 +0800 Subject: [PATCH 06/15] fix --- server/websocket/internal/logic/datatransferlogic.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index e2056ba4..b4ffae61 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -44,14 +44,17 @@ var ( } //升级websocket upgrade = websocket.Upgrader{ - ReadBufferSize: 1024 * 10, //最大可读取大小 10M + //最大可读取大小 10M + ReadBufferSize: 1024 * 10, //握手超时时间15s HandshakeTimeout: time.Second * 15, //允许跨域 CheckOrigin: func(r *http.Request) bool { return true }, - WriteBufferPool: &buffPool, + //写的缓存池 + WriteBufferPool: &buffPool, + //是否支持压缩 EnableCompression: true, } //websocket连接存储 From 2c537bf82356710ed72c4230a342fe4596ee8f07 Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Thu, 27 Jul 2023 19:33:29 +0800 Subject: [PATCH 07/15] fix --- server/websocket/internal/logic/datatransferlogic.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index b4ffae61..74f14691 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -103,8 +103,8 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp conn: conn, uniqueId: uniqueId, closeChan: make(chan struct{}, 1), - inChan: make(chan []byte, 100), - outChan: make(chan []byte, 100), + inChan: make(chan []byte, 1000), + outChan: make(chan []byte, 1000), renderProperty: renderProperty{ renderImageTask: make(map[string]struct{}), renderImageTaskCtlChan: make(chan renderImageControlChanItem, 100), From 64e7361d509e726e29f3d4bbac0c46191c2976bc Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Fri, 28 Jul 2023 10:02:40 +0800 Subject: [PATCH 08/15] fix --- server/websocket/internal/logic/datatransferlogic.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index 74f14691..e14a687a 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -81,7 +81,6 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp return } defer conn.Close() - w.Header().Set("Connection", "Upgrade") //鉴权不成功10秒后断开 /*isAuth, _ := l.checkAuth(svcCtx, r) if !isAuth { From 164c737dd35c5520651f2370b9a46d4241c50756 Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Fri, 28 Jul 2023 10:20:35 +0800 Subject: [PATCH 09/15] fix --- server/websocket/internal/logic/datatransferlogic.go | 4 ++-- server/websocket/internal/logic/ws_render_image_logic.go | 2 +- server/websocket/internal/types/types.go | 3 +++ server_api/websocket.api | 3 +++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index e14a687a..44ba0abb 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -245,8 +245,8 @@ func (w *wsConnectItem) sendToOutChan(data []byte) { } // 获取需要渲染图片的map key -func (w *wsConnectItem) getRenderImageMapKey(productId, templateTagId int64, algorithmVersion string) string { - return fmt.Sprintf("%d-%d-%s", productId, templateTagId, algorithmVersion) +func (w *wsConnectItem) getRenderImageMapKey(productId, templateTagId, logoId int64, algorithmVersion string) string { + return fmt.Sprintf("%d-%d-%d-%s", productId, templateTagId, logoId, algorithmVersion) } // 格式化返回数据 diff --git a/server/websocket/internal/logic/ws_render_image_logic.go b/server/websocket/internal/logic/ws_render_image_logic.go index 1f1c6557..d58def77 100644 --- a/server/websocket/internal/logic/ws_render_image_logic.go +++ b/server/websocket/internal/logic/ws_render_image_logic.go @@ -54,7 +54,7 @@ func (w *wsConnectItem) SendToCloudRender(data []byte) { return default: //加入渲染任务 - key := w.getRenderImageMapKey(productId, renderImageData.TemplateTagId, renderImageData.AlgorithmVersion) + key := w.getRenderImageMapKey(productId, renderImageData.TemplateTagId, renderImageData.LogoId, renderImageData.AlgorithmVersion) w.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{ Option: 1, //0删除 1添加 Key: key, diff --git a/server/websocket/internal/types/types.go b/server/websocket/internal/types/types.go index dbeff4db..901b8123 100644 --- a/server/websocket/internal/types/types.go +++ b/server/websocket/internal/types/types.go @@ -13,6 +13,7 @@ type DataTransferData struct { type RenderImageReqMsg struct { ProductIds []int64 `json:"product_ids"` //产品 id TemplateTagId int64 `json:"template_tag_id"` //模板标签id + LogoId int64 `json:"logo_id"` //logoid AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本 } @@ -20,6 +21,7 @@ type RenderImageRspMsg struct { ProductId int64 `json:"product_id"` //产品 id TemplateTagId int64 `json:"template_tag_id"` //模板标签id AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本 + LogoId int64 `json:"logo_id"` //logoid Image string `json:"image"` //渲染后的图片 } @@ -37,6 +39,7 @@ type NotifyInfo struct { ProductId int64 `json:"product_id"` //产品id TemplateTagId int64 `json:"template_tag_id"` //模板标签id AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本 + LogoId int64 `json:"logo_id"` //logoid Image string `json:"image"` } diff --git a/server_api/websocket.api b/server_api/websocket.api index 379f3e45..e24e2874 100644 --- a/server_api/websocket.api +++ b/server_api/websocket.api @@ -28,12 +28,14 @@ type DataTransferData { type RenderImageReqMsg { //websocket接受要云渲染处理的数据 ProductIds []int64 `json:"product_ids"` //产品 id TemplateTagId int64 `json:"template_tag_id"` //模板标签id + LogoId int64 `json:"logo_id"` //logoid AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本 } type RenderImageRspMsg { //websocket发送渲染完的数据 ProductId int64 `json:"product_id"` //产品 id TemplateTagId int64 `json:"template_tag_id"` //模板标签id AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本 + LogoId int64 `json:"logo_id"` //logoid Image string `json:"image"` //渲染后的图片 } type ThirdPartyLoginRspMsg { //websocket三方登录的通知数据 @@ -49,6 +51,7 @@ type NotifyInfo { ProductId int64 `json:"product_id"` //产品id TemplateTagId int64 `json:"template_tag_id"` //模板标签id AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本 + LogoId int64 `json:"logo_id"` //logoid Image string `json:"image"` } //第三方登录通知接口 From aa39146cfbc70715bd21d146d1bd2ad6b86bdc14 Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Fri, 28 Jul 2023 10:22:01 +0800 Subject: [PATCH 10/15] fix --- server/websocket/internal/logic/rendernotifylogic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/websocket/internal/logic/rendernotifylogic.go b/server/websocket/internal/logic/rendernotifylogic.go index b0088855..8323e8f6 100644 --- a/server/websocket/internal/logic/rendernotifylogic.go +++ b/server/websocket/internal/logic/rendernotifylogic.go @@ -57,7 +57,7 @@ func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq) (resp *basi if !ok { return true } - renderKey := ws.getRenderImageMapKey(req.Info.ProductId, req.Info.TemplateTagId, req.Info.AlgorithmVersion) + renderKey := ws.getRenderImageMapKey(req.Info.ProductId, req.Info.TemplateTagId, req.Info.LogoId, req.Info.AlgorithmVersion) //查询有无该渲染任务 _, ok = ws.renderProperty.renderImageTask[renderKey] if !ok { From a158f562b6a8befb875da4b838b5302427968321 Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Fri, 28 Jul 2023 10:37:23 +0800 Subject: [PATCH 11/15] fix --- .../internal/logic/getproducttemplatetagslogic.go | 1 + server/product-template-tag/internal/types/types.go | 1 + server_api/product-template-tag.api | 1 + 3 files changed, 3 insertions(+) diff --git a/server/product-template-tag/internal/logic/getproducttemplatetagslogic.go b/server/product-template-tag/internal/logic/getproducttemplatetagslogic.go index 68c62363..815ef07c 100644 --- a/server/product-template-tag/internal/logic/getproducttemplatetagslogic.go +++ b/server/product-template-tag/internal/logic/getproducttemplatetagslogic.go @@ -47,6 +47,7 @@ func (l *GetProductTemplateTagsLogic) GetProductTemplateTags(req *types.GetProdu list := make([]types.GetProductTemplateTagsRsp, 0, len(productTemplateTags)) for _, v := range productTemplateTags { list = append(list, types.GetProductTemplateTagsRsp{ + Id: v.Id, Tag: *v.Title, Cover: *v.CoverImg, }) diff --git a/server/product-template-tag/internal/types/types.go b/server/product-template-tag/internal/types/types.go index 39866da6..a89ea882 100644 --- a/server/product-template-tag/internal/types/types.go +++ b/server/product-template-tag/internal/types/types.go @@ -10,6 +10,7 @@ type GetProductTemplateTagsReq struct { } type GetProductTemplateTagsRsp struct { + Id int64 `json:"id"` Tag string `json:"tag"` Cover string `json:"cover"` } diff --git a/server_api/product-template-tag.api b/server_api/product-template-tag.api index ae62099a..3b057025 100644 --- a/server_api/product-template-tag.api +++ b/server_api/product-template-tag.api @@ -20,6 +20,7 @@ type GetProductTemplateTagsReq { Limit int `form:"limit"` } type GetProductTemplateTagsRsp { + Id int64 `json:"id"` Tag string `json:"tag"` Cover string `json:"cover"` } \ No newline at end of file From 15b2a80f58eee6e7ace1ca7f8a9f8114e8c2ebcf Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Fri, 28 Jul 2023 10:42:04 +0800 Subject: [PATCH 12/15] fix --- server/product-template-tag/internal/handler/routes.go | 2 +- server_api/product-template-tag.api | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/product-template-tag/internal/handler/routes.go b/server/product-template-tag/internal/handler/routes.go index b2c8f181..24a5c707 100644 --- a/server/product-template-tag/internal/handler/routes.go +++ b/server/product-template-tag/internal/handler/routes.go @@ -14,7 +14,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { []rest.Route{ { Method: http.MethodGet, - Path: "/api/product-template/get_product_template_tags", + Path: "/api/product-template-tag/get_product_template_tags", Handler: GetProductTemplateTagsHandler(serverCtx), }, }, diff --git a/server_api/product-template-tag.api b/server_api/product-template-tag.api index 3b057025..c8b1a2cc 100644 --- a/server_api/product-template-tag.api +++ b/server_api/product-template-tag.api @@ -12,7 +12,7 @@ import "basic.api" service product-template-tag { //获取产品模板标签列表 @handler GetProductTemplateTagsHandler - get /api/product-template/get_product_template_tags(GetProductTemplateTagsReq) returns (response); + get /api/product-template-tag/get_product_template_tags(GetProductTemplateTagsReq) returns (response); } //获取产品模板标签列表 From 8f5bcc76ec2ddd6da18e3c1114475357651d763e Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Fri, 28 Jul 2023 11:13:47 +0800 Subject: [PATCH 13/15] fix --- server/websocket/internal/logic/rendernotifylogic.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/websocket/internal/logic/rendernotifylogic.go b/server/websocket/internal/logic/rendernotifylogic.go index 8323e8f6..9e5e0c9e 100644 --- a/server/websocket/internal/logic/rendernotifylogic.go +++ b/server/websocket/internal/logic/rendernotifylogic.go @@ -57,6 +57,10 @@ func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq) (resp *basi if !ok { return true } + //关闭标识 + if ws.isClose { + return true + } renderKey := ws.getRenderImageMapKey(req.Info.ProductId, req.Info.TemplateTagId, req.Info.LogoId, req.Info.AlgorithmVersion) //查询有无该渲染任务 _, ok = ws.renderProperty.renderImageTask[renderKey] From 334ea4aa3f8f5363b81d5170720c4b5cdd68eb56 Mon Sep 17 00:00:00 2001 From: Hiven Date: Fri, 28 Jul 2023 11:15:42 +0800 Subject: [PATCH 14/15] =?UTF-8?q?=E6=94=AF=E4=BB=98=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/gmodel/fs_resource_gen.go | 27 ++++ model/gmodel/fs_resource_logic.go | 2 + model/gmodel/var_gen.go | 2 + server/pay/etc/pay.yaml | 2 + server/pay/internal/config/config.go | 7 +- server/pay/internal/handler/routes.go | 2 +- .../internal/handler/stripewebhookhandler.go | 19 ++- .../internal/logic/orderpaymentintentlogic.go | 7 +- .../pay/internal/logic/stripewebhooklogic.go | 139 +++++++++++++----- server/pay/internal/types/types.go | 5 +- server_api/pay.api | 7 +- utils/pay/pay.go | 7 +- utils/pay/stripe.go | 24 ++- 13 files changed, 199 insertions(+), 51 deletions(-) create mode 100644 model/gmodel/fs_resource_gen.go create mode 100644 model/gmodel/fs_resource_logic.go diff --git a/model/gmodel/fs_resource_gen.go b/model/gmodel/fs_resource_gen.go new file mode 100644 index 00000000..23623ee0 --- /dev/null +++ b/model/gmodel/fs_resource_gen.go @@ -0,0 +1,27 @@ +package gmodel + +import ( + "gorm.io/gorm" + "time" +) + +// fs_resource 资源表 +type FsResource struct { + ResourceId string `gorm:"primary_key;default:'';" json:"resource_id"` // 资源 ID + UserId *int64 `gorm:"index;default:0;" json:"user_id"` // 用户 ID + GuestId *int64 `gorm:"index;default:0;" json:"guest_id"` // 访客 ID + ResourceType *string `gorm:"index;default:'';" json:"resource_type"` // 资源类型 + ResourceUrl *string `gorm:"default:'';" json:"resource_url"` // 资源 URL + Version *string `gorm:"index;default:'0';" json:"version"` // 版本信息 + UploadedAt *time.Time `gorm:"index;default:'0000-00-00 00:00:00';" json:"uploaded_at"` // 上传时间 + Metadata *string `gorm:"default:'';" json:"metadata"` // 元数据,json格式,存储图像分率 + MetaKey1 *string `gorm:"index;default:'';" json:"meta_key1"` // 需要关键信息查询的自定义属性1,可以动态增加 +} +type FsResourceModel struct { + db *gorm.DB + name string +} + +func NewFsResourceModel(db *gorm.DB) *FsResourceModel { + return &FsResourceModel{db: db, name: "fs_resource"} +} diff --git a/model/gmodel/fs_resource_logic.go b/model/gmodel/fs_resource_logic.go new file mode 100644 index 00000000..e68225aa --- /dev/null +++ b/model/gmodel/fs_resource_logic.go @@ -0,0 +1,2 @@ +package gmodel +// TODO: 使用model的属性做你想做的 \ No newline at end of file diff --git a/model/gmodel/var_gen.go b/model/gmodel/var_gen.go index 2fac48a5..191a9898 100644 --- a/model/gmodel/var_gen.go +++ b/model/gmodel/var_gen.go @@ -79,6 +79,7 @@ type AllModelsGen struct { FsQuotationRemarkTemplate *FsQuotationRemarkTemplateModel // fs_quotation_remark_template 报价单备注模板 FsQuotationSaler *FsQuotationSalerModel // fs_quotation_saler 报价单业务员表 FsRefundReason *FsRefundReasonModel // fs_refund_reason + FsResource *FsResourceModel // fs_resource 资源表 FsResources *FsResourcesModel // fs_resources 资源表 FsStandardLogo *FsStandardLogoModel // fs_standard_logo 标准logo FsTags *FsTagsModel // fs_tags 产品分类表 @@ -170,6 +171,7 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsQuotationRemarkTemplate: NewFsQuotationRemarkTemplateModel(gdb), FsQuotationSaler: NewFsQuotationSalerModel(gdb), FsRefundReason: NewFsRefundReasonModel(gdb), + FsResource: NewFsResourceModel(gdb), FsResources: NewFsResourcesModel(gdb), FsStandardLogo: NewFsStandardLogoModel(gdb), FsTags: NewFsTagsModel(gdb), diff --git a/server/pay/etc/pay.yaml b/server/pay/etc/pay.yaml index dcc6a6fe..841753e3 100644 --- a/server/pay/etc/pay.yaml +++ b/server/pay/etc/pay.yaml @@ -1,6 +1,7 @@ Name: pay Host: 0.0.0.0 Port: 9915 +Timeout: 15000 SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest Auth: AccessSecret: fusen2023 @@ -9,5 +10,6 @@ Auth: PayConfig: Stripe: Key: "sk_test_51IisojHygnIJZeghPVSBhkwySfcyDV4SoAduIxu3J7bvSJ9cZMD96LY1LO6SpdbYquLJX5oKvgEBB67KT9pecfCy00iEC4pp9y" + EndpointSecret: "whsec_f5f9a121d43af3789db7459352f08cf523eb9e0fbf3381f91ba6c97c324c174d" SuccessURL: "http://www.baidu.com" CancelURL: "http://www.baidu.com" \ No newline at end of file diff --git a/server/pay/internal/config/config.go b/server/pay/internal/config/config.go index 0b7a2dcb..6676984a 100644 --- a/server/pay/internal/config/config.go +++ b/server/pay/internal/config/config.go @@ -12,9 +12,10 @@ type Config struct { Auth types.Auth PayConfig struct { Stripe struct { - Key string - CancelURL string - SuccessURL string + EndpointSecret string + Key string + CancelURL string + SuccessURL string } } } diff --git a/server/pay/internal/handler/routes.go b/server/pay/internal/handler/routes.go index 454d6715..28292f5f 100644 --- a/server/pay/internal/handler/routes.go +++ b/server/pay/internal/handler/routes.go @@ -18,7 +18,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Handler: OrderPaymentIntentHandler(serverCtx), }, { - Method: http.MethodGet, + Method: http.MethodPost, Path: "/api/pay/stripe-webhook", Handler: StripeWebhookHandler(serverCtx), }, diff --git a/server/pay/internal/handler/stripewebhookhandler.go b/server/pay/internal/handler/stripewebhookhandler.go index 1e0da8fc..b4447ba7 100644 --- a/server/pay/internal/handler/stripewebhookhandler.go +++ b/server/pay/internal/handler/stripewebhookhandler.go @@ -1,6 +1,7 @@ package handler import ( + "io" "net/http" "reflect" @@ -14,19 +15,31 @@ import ( func StripeWebhookHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var req types.StripeWebhookReq - userinfo, err := basic.RequestParse(w, r, svcCtx, &req) + const MaxBodyBytes = int64(65536) + r.Body = http.MaxBytesReader(w, r.Body, MaxBodyBytes) + defer r.Body.Close() + payload, err := io.ReadAll(r.Body) if err != nil { return } + var req types.StripeWebhookReq + + // userinfo, err := basic.RequestParse(w, r, svcCtx, &req) + // if err != nil { + // return + // } + + req.Payload = payload + req.StripeSignature = r.Header.Get("Stripe-Signature") + // 创建一个业务逻辑层实例 l := logic.NewStripeWebhookLogic(r.Context(), svcCtx) rl := reflect.ValueOf(l) basic.BeforeLogic(w, r, rl) - resp := l.StripeWebhook(&req, userinfo) + resp := l.StripeWebhook(&req, nil) if !basic.AfterLogic(w, r, rl, resp) { basic.NormalAfterLogic(w, r, resp) diff --git a/server/pay/internal/logic/orderpaymentintentlogic.go b/server/pay/internal/logic/orderpaymentintentlogic.go index a9d13c22..a5cb71bc 100644 --- a/server/pay/internal/logic/orderpaymentintentlogic.go +++ b/server/pay/internal/logic/orderpaymentintentlogic.go @@ -97,6 +97,7 @@ func (l *OrderPaymentIntentLogic) OrderPaymentIntent(req *types.OrderPaymentInte payConfig := &pay.Config{} var generatePrepaymentReq = &pay.GeneratePrepaymentReq{ + OrderSn: req.Sn, ProductName: "支付标题", Amount: payAmount, Currency: "eur", @@ -142,7 +143,6 @@ func (l *OrderPaymentIntentLogic) OrderPaymentIntent(req *types.OrderPaymentInte if err != nil { return err } - resData.RedirectUrl = prepaymentRes.URL // 订单信息--修改 err = gmodel.NewFsOrderModel(connGorm).Update(ctx, orderInfo) @@ -161,7 +161,7 @@ func (l *OrderPaymentIntentLogic) OrderPaymentIntent(req *types.OrderPaymentInte } fspay.PayAmount = &payAmount fspay.PayStage = &payStage - fspay.TradeNo = &prepaymentRes.TradeNo + //fspay.TradeNo = &prepaymentRes.TradeNo fspay.PaymentMethod = &req.PayMethod fspay.OrderSource = &orderSource fspay.PayStatus = &payStatus @@ -171,6 +171,9 @@ func (l *OrderPaymentIntentLogic) OrderPaymentIntent(req *types.OrderPaymentInte return err } + resData.RedirectUrl = prepaymentRes.URL + resData.ClientSecret = prepaymentRes.ClientSecret + return nil }) diff --git a/server/pay/internal/logic/stripewebhooklogic.go b/server/pay/internal/logic/stripewebhooklogic.go index 82c5275b..048b78e7 100644 --- a/server/pay/internal/logic/stripewebhooklogic.go +++ b/server/pay/internal/logic/stripewebhooklogic.go @@ -7,8 +7,6 @@ import ( "fusenapi/model/gmodel" "fusenapi/utils/auth" "fusenapi/utils/basic" - "io" - "net/http" "time" "context" @@ -17,15 +15,15 @@ import ( "fusenapi/server/pay/internal/types" "github.com/stripe/stripe-go/v74" + "github.com/stripe/stripe-go/v74/webhook" "github.com/zeromicro/go-zero/core/logx" "gorm.io/gorm" ) type StripeWebhookLogic struct { logx.Logger - ctx context.Context - svcCtx *svc.ServiceContext - Payload []byte + ctx context.Context + svcCtx *svc.ServiceContext } func NewStripeWebhookLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StripeWebhookLogic { @@ -37,18 +35,9 @@ func NewStripeWebhookLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Str } // 处理进入前逻辑w,r -func (l *StripeWebhookLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) { - const MaxBodyBytes = int64(65536) - r.Body = http.MaxBytesReader(w, r.Body, MaxBodyBytes) - defer r.Body.Close() - payload, err := io.ReadAll(r.Body) - if err != nil { - logx.Error(err) - w.WriteHeader(http.StatusServiceUnavailable) - return - } - l.Payload = payload -} +// func (l *StripeWebhookLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) { + +// } // 处理逻辑后 w,r 如:重定向, resp 必须重新处理 // func (l *StripeWebhookLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) { @@ -59,15 +48,45 @@ func (l *StripeWebhookLogic) StripeWebhook(req *types.StripeWebhookReq, userinfo // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) // userinfo 传入值时, 一定不为null + stripe.Key = l.svcCtx.Config.PayConfig.Stripe.Key event := stripe.Event{} - if err := json.Unmarshal(l.Payload, &event); err != nil { + if err := json.Unmarshal(req.Payload, &event); err != nil { logx.Error(err) return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail") } + endpointSecret := l.svcCtx.Config.PayConfig.Stripe.EndpointSecret + signatureHeader := req.StripeSignature + event, err := webhook.ConstructEvent(req.Payload, signatureHeader, endpointSecret) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "Webhook signature verification failed") + } + // Unmarshal the event data into an appropriate struct depending on its Type switch event.Type { + case "charge.succeeded": + // var charge stripe.Charge + // err := json.Unmarshal(event.Data.Raw, &charge) + // if err != nil { + // logx.Error(err) + // return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type charge.succeeded") + // } + + case "checkout.session.completed": + // checkout checkout.session.completed 处理逻辑 + // var session stripe.CheckoutSession + // err := json.Unmarshal(event.Data.Raw, &session) + // if err != nil { + // logx.Error(err) + // return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type payment_intent.succeeded") + // } + // fmt.Println("checkout.session.completed") + // err = l.handlePaymentSessionCompleted(session.ID, session.PaymentIntent.ID) + // if err != nil { + // return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "checkout.session.completed fail") + // } case "payment_intent.succeeded": var paymentIntent stripe.PaymentIntent err := json.Unmarshal(event.Data.Raw, &paymentIntent) @@ -95,13 +114,39 @@ func (l *StripeWebhookLogic) StripeWebhook(req *types.StripeWebhookReq, userinfo return resp.SetStatus(basic.CodeOK) } +// session完成 +// func (l *StripeWebhookLogic) handlePaymentSessionCompleted(sessionId string, tradeNo string) (err error) { +// // 查询支付记录 +// payModel := gmodel.NewFsPayModel(l.svcCtx.MysqlConn) +// rsbPay := payModel.RowSelectBuilder(nil) +// rsbPay = rsbPay.Where("session_id = ?", sessionId) +// payInfo, err := payModel.FindOneByQuery(l.ctx, rsbPay, nil) +// if err != nil { +// return err +// } +// if *payInfo.PayStatus == 0 { +// *payInfo.TradeNo = tradeNo +// _, err = payModel.CreateOrUpdate(l.ctx, payInfo) +// if err != nil { +// return err +// } +// } else { +// return errors.New("pay status 1") +// } +// return err +// } + // 成功的付款 func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe.PaymentIntent) error { + orderSn, ok := paymentIntent.Metadata["order_sn"] + if !ok || orderSn == "" { + return errors.New("order_sn not found") + } // 查询支付记录 payModel := gmodel.NewFsPayModel(l.svcCtx.MysqlConn) rsbPay := payModel.RowSelectBuilder(nil) - rsbPay = rsbPay.Where("trade_no = ?", paymentIntent.ID) + rsbPay = rsbPay.Where("order_number = ?", orderSn) payInfo, err := payModel.FindOneByQuery(l.ctx, rsbPay, nil) if err != nil { return err @@ -112,22 +157,27 @@ func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe. //订单信息 orderDetailTemplateModel := gmodel.NewFsOrderDetailTemplateModel(l.svcCtx.MysqlConn) orderModel := gmodel.NewFsOrderModel(l.svcCtx.MysqlConn) + fsOrderDetailModel := gmodel.NewFsOrderDetailModel(l.svcCtx.MysqlConn) + fsProductDesignModel := gmodel.NewFsProductDesignModel(l.svcCtx.MysqlConn) + rsbOrder := orderModel.RowSelectBuilder(nil) - rsbOrder = rsbOrder.Where("trade_no =?", paymentIntent.ID).Preload("FsOrderDetails") + rsbOrder = rsbOrder.Where("sn =?", orderSn).Preload("FsOrderDetails") rsbOrder = rsbOrder.Preload("FsOrderDetails", func(dbPreload *gorm.DB) *gorm.DB { - return dbPreload.Table(orderModel.TableName()).Preload("FsOrderDetailTemplateInfo", func(dbPreload *gorm.DB) *gorm.DB { - return dbPreload.Table(orderDetailTemplateModel.TableName()).Preload("FsProductDesignInfo") + return dbPreload.Table(fsOrderDetailModel.TableName()).Preload("FsOrderDetailTemplateInfo", func(dbPreload *gorm.DB) *gorm.DB { + return dbPreload.Table(orderDetailTemplateModel.TableName()).Preload("FsProductDesignInfo", func(dbPreload *gorm.DB) *gorm.DB { + return dbPreload.Table(fsProductDesignModel.TableName()) + }) }) }) - fsOrderRel, err := orderModel.FindOneByQuery(l.ctx, rsbOrder, nil) + fsOrderRelInfo, err := orderModel.FindOneByQuery(l.ctx, rsbOrder, nil) if err != nil { return err } var designIds []int64 var cartIds []int64 - if len(fsOrderRel.FsOrderDetails) > 0 { - for _, fsOrderDetail := range fsOrderRel.FsOrderDetails { + if len(fsOrderRelInfo.FsOrderDetails) > 0 { + for _, fsOrderDetail := range fsOrderRelInfo.FsOrderDetails { if fsOrderDetail.FsOrderDetailTemplateInfo.FsProductDesignInfo.Id != 0 { designIds = append(designIds, fsOrderDetail.FsOrderDetailTemplateInfo.FsProductDesignInfo.Id) } @@ -139,8 +189,18 @@ func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe. // 支付成功 if paymentIntent.Status == "succeeded" { - var card string = paymentIntent.LatestCharge.PaymentMethodDetails.Card.Last4 - var brand string = string(paymentIntent.LatestCharge.PaymentMethodDetails.Card.Brand) + var card string + var brand string + if paymentIntent.LatestCharge.PaymentMethodDetails != nil { + if paymentIntent.LatestCharge.PaymentMethodDetails.Card != nil { + if paymentIntent.LatestCharge.PaymentMethodDetails.Card.Last4 != "" { + card = paymentIntent.LatestCharge.PaymentMethodDetails.Card.Last4 + } + if paymentIntent.LatestCharge.PaymentMethodDetails.Card.Brand != "" { + brand = string(paymentIntent.LatestCharge.PaymentMethodDetails.Card.Brand) + } + } + } err = orderModel.Trans(l.ctx, func(ctx context.Context, connGorm *gorm.DB) (err error) { // 更新支付信息 @@ -163,12 +223,17 @@ func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe. } var orderInfo = &gmodel.FsOrder{} - + var orderStatus int64 + var orderIsPartPay int64 + var orderPayedAmount int64 + var orderIsPayCompleted int64 // 支付记录是首款 if *payInfo.PayStage == int64(constants.PAYSTAGE_DEPOSIT) { - *orderInfo.Status = int64(constants.STATUS_NEW_PART_PAY) - *orderInfo.IsPartPay = 1 - *orderInfo.PayedAmount = paymentIntent.Amount + orderStatus = int64(constants.STATUS_NEW_PART_PAY) + orderIsPartPay = 1 + orderInfo.IsPartPay = &orderIsPartPay + orderPayedAmount = paymentIntent.Amount + // 删除购物车 cartModelT := gmodel.NewFsCartModel(connGorm) err = cartModelT.DeleteCartsByIds(ctx, cartIds) @@ -180,14 +245,18 @@ func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe. // 支付记录是尾款 if *payInfo.PayStage == int64(constants.PAYSTAGE_REMAINING) { if *orderInfo.Status < int64(constants.STATUS_NEW_PAY_COMPLETED) { - *orderInfo.Status = int64(constants.STATUS_NEW_PAY_COMPLETED) + orderStatus = int64(constants.STATUS_NEW_PAY_COMPLETED) } - *orderInfo.IsPayCompleted = 1 - *orderInfo.PayedAmount = *orderInfo.PayedAmount + paymentIntent.Amount + orderIsPayCompleted = 1 + orderInfo.IsPayCompleted = &orderIsPayCompleted + orderPayedAmount = *orderInfo.PayedAmount + paymentIntent.Amount } // 更新订单信息 - *orderInfo.Ptime = nowTime + orderInfo.Id = fsOrderRelInfo.Id + orderInfo.Status = &orderStatus + orderInfo.Ptime = &nowTime + orderInfo.PayedAmount = &orderPayedAmount orderModelT := gmodel.NewFsOrderModel(connGorm) err = orderModelT.Update(ctx, orderInfo) if err != nil { diff --git a/server/pay/internal/types/types.go b/server/pay/internal/types/types.go index c070431b..76c0341e 100644 --- a/server/pay/internal/types/types.go +++ b/server/pay/internal/types/types.go @@ -13,10 +13,13 @@ type OrderPaymentIntentReq struct { } type OrderPaymentIntentRes struct { - RedirectUrl string `json:"redirect_url"` + RedirectUrl string `json:"redirect_url"` + ClientSecret string `json:"clientSecret"` } type StripeWebhookReq struct { + Payload []byte `json:"base_byte_slice,optional"` + StripeSignature string `json:"Stripe-Signature"` } type Request struct { diff --git a/server_api/pay.api b/server_api/pay.api index e110c5d4..b7a1b0d1 100644 --- a/server_api/pay.api +++ b/server_api/pay.api @@ -15,7 +15,7 @@ service pay { post /api/pay/payment-intent(OrderPaymentIntentReq) returns (response); @handler StripeWebhookHandler - get /api/pay/stripe-webhook(StripeWebhookReq) returns (response); + post /api/pay/stripe-webhook(StripeWebhookReq) returns (response); } // 生成预付款 @@ -27,12 +27,15 @@ type ( PayMethod int64 `form:"pay_method"` //支付方式 } OrderPaymentIntentRes { - RedirectUrl string `json:"redirect_url"` + RedirectUrl string `json:"redirect_url"` + ClientSecret string `json:"clientSecret"` } ) // StripeWebhook支付通知 type ( StripeWebhookReq { + Payload []byte `json:"base_byte_slice,optional"` + StripeSignature string `json:"Stripe-Signature"` } ) \ No newline at end of file diff --git a/utils/pay/pay.go b/utils/pay/pay.go index 005d700d..a378e4a8 100644 --- a/utils/pay/pay.go +++ b/utils/pay/pay.go @@ -24,6 +24,7 @@ type Pay interface { } type GeneratePrepaymentReq struct { + OrderSn string `json:"order_sn"` // 订单编号 Amount int64 `json:"amount"` // 支付金额 Currency string `json:"currency"` // 支付货币 ProductName string `json:"product_name"` // 商品名称 @@ -35,6 +36,8 @@ type GeneratePrepaymentReq struct { } type GeneratePrepaymentRes struct { - URL string `json:"url"` // 支付重定向地址 - TradeNo string `json:"trade_no"` //交易ID + URL string `json:"url"` // 支付重定向地址 + TradeNo string `json:"trade_no"` //交易ID + ClientSecret string `json:"clientSecret"` //交易密钥 + SessionId string `json:"session_id"` //SessionId } diff --git a/utils/pay/stripe.go b/utils/pay/stripe.go index 9dc5a2cf..cc8127f7 100644 --- a/utils/pay/stripe.go +++ b/utils/pay/stripe.go @@ -12,6 +12,7 @@ type Stripe struct { // 生成预付款 func (stripePay *Stripe) GeneratePrepayment(req *GeneratePrepaymentReq) (res *GeneratePrepaymentRes, err error) { var productData stripe.CheckoutSessionLineItemPriceDataProductDataParams + // productData.Metadata = map[string]string{"order_id": "33333333333333"} if req.ProductName != "" { productData.Name = stripe.String(req.ProductName) @@ -30,7 +31,14 @@ func (stripePay *Stripe) GeneratePrepayment(req *GeneratePrepaymentReq) (res *Ge // productData.Images = images stripe.Key = stripePay.Key + // session 方式 params := &stripe.CheckoutSessionParams{ + PaymentIntentData: &stripe.CheckoutSessionPaymentIntentDataParams{Metadata: map[string]string{"order_sn": req.OrderSn}}, + // Params: stripe.Params{Metadata: map[string]string{"order_id": "1111111111111"}}, + PaymentMethodTypes: stripe.StringSlice([]string{ + "card", + // "ideal", + }), LineItems: []*stripe.CheckoutSessionLineItemParams{ { PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{ @@ -47,12 +55,24 @@ func (stripePay *Stripe) GeneratePrepayment(req *GeneratePrepaymentReq) (res *Ge } result, err := session.New(params) + // 密钥方式 + // params := &stripe.PaymentIntentParams{ + // Amount: stripe.Int64(req.Amount), + // Currency: stripe.String(string(req.Currency)), + // AutomaticPaymentMethods: &stripe.PaymentIntentAutomaticPaymentMethodsParams{ + // Enabled: stripe.Bool(true), + // }, + // } + // result, err := paymentintent.New(params) + if err != nil { return nil, err } return &GeneratePrepaymentRes{ - URL: result.URL, - TradeNo: result.ID, + URL: result.URL, + //TradeNo: result.ID, + SessionId: result.ID, + // ClientSecret: result.ClientSecret, }, err } From ae717f932eee8b2f810d0ee94615f3be84489bc9 Mon Sep 17 00:00:00 2001 From: Hiven Date: Fri, 28 Jul 2023 11:24:10 +0800 Subject: [PATCH 15/15] =?UTF-8?q?=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/gmodel/var_gen.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/model/gmodel/var_gen.go b/model/gmodel/var_gen.go index 741c6b05..191a9898 100644 --- a/model/gmodel/var_gen.go +++ b/model/gmodel/var_gen.go @@ -79,10 +79,7 @@ type AllModelsGen struct { FsQuotationRemarkTemplate *FsQuotationRemarkTemplateModel // fs_quotation_remark_template 报价单备注模板 FsQuotationSaler *FsQuotationSalerModel // fs_quotation_saler 报价单业务员表 FsRefundReason *FsRefundReasonModel // fs_refund_reason -<<<<<<< HEAD FsResource *FsResourceModel // fs_resource 资源表 -======= ->>>>>>> develop FsResources *FsResourcesModel // fs_resources 资源表 FsStandardLogo *FsStandardLogoModel // fs_standard_logo 标准logo FsTags *FsTagsModel // fs_tags 产品分类表 @@ -174,10 +171,7 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen { FsQuotationRemarkTemplate: NewFsQuotationRemarkTemplateModel(gdb), FsQuotationSaler: NewFsQuotationSalerModel(gdb), FsRefundReason: NewFsRefundReasonModel(gdb), -<<<<<<< HEAD FsResource: NewFsResourceModel(gdb), -======= ->>>>>>> develop FsResources: NewFsResourcesModel(gdb), FsStandardLogo: NewFsStandardLogoModel(gdb), FsTags: NewFsTagsModel(gdb),