From 01bc03cdbf80e2960274778235327a3ef9c3f365 Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Wed, 28 Aug 2024 14:29:18 +0000 Subject: [PATCH 1/4] feat(account-service): add use giftcode --- .../pkg/database/cockroach/accountv2.go | 45 +++++++++++++++++++ controllers/pkg/types/global.go | 32 +++++++++++++ service/account/api/api.go | 41 +++++++++++++++++ service/account/dao/interface.go | 18 ++++++++ service/account/dao/interface_test.go | 20 +++++++++ service/account/helper/common.go | 4 +- service/account/helper/request.go | 35 +++++++++++++++ service/account/router/router.go | 3 +- 8 files changed, 196 insertions(+), 2 deletions(-) diff --git a/controllers/pkg/database/cockroach/accountv2.go b/controllers/pkg/database/cockroach/accountv2.go index 8b8ba95da5e..77970120cf6 100644 --- a/controllers/pkg/database/cockroach/accountv2.go +++ b/controllers/pkg/database/cockroach/accountv2.go @@ -1054,3 +1054,48 @@ func (c *Cockroach) Close() error { } return db.Close() } + +func (c *Cockroach) GetGiftCodeWithCode(code string) (*types.GiftCode, error) { + var giftCode types.GiftCode + if err := c.DB.Where("code = ?", code).First(&giftCode).Error; err != nil { + return nil, fmt.Errorf("failed to get gift code: %w", err) + } + return &giftCode, nil +} + +func (c *Cockroach) UseGiftCode(giftCode *types.GiftCode, userID string) error { + return c.DB.Transaction(func(tx *gorm.DB) error { + ops := &types.UserQueryOpts{ID: userID} + // Update the user's balance + if err := c.updateBalance(tx, ops, giftCode.CreditAmount, false, true); err != nil { + return fmt.Errorf("failed to update user balance: %w", err) + } + + message := "created by gift code" + // Create an AccountTransaction record + accountTransaction := &types.AccountTransaction{ + ID: uuid.New(), + Type: "GiftCode", + UserUID: ops.UID, + DeductionBalance: 0, + Balance: giftCode.CreditAmount, + Message: &message, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + BillingID: giftCode.ID, + } + if err := tx.Create(accountTransaction).Error; err != nil { + return fmt.Errorf("failed to create account transaction: %w", err) + } + + // Mark the gift code as used + giftCode.Used = true + giftCode.UsedBy = ops.UID + giftCode.UsedAt = time.Now() + if err := tx.Save(giftCode).Error; err != nil { + return fmt.Errorf("failed to update gift code: %w", err) + } + + return nil + }) +} diff --git a/controllers/pkg/types/global.go b/controllers/pkg/types/global.go index 69fd3232fc9..8e25623f51c 100644 --- a/controllers/pkg/types/global.go +++ b/controllers/pkg/types/global.go @@ -280,3 +280,35 @@ func (Invoice) TableName() string { func (InvoicePayment) TableName() string { return "InvoicePayment" } + +type GiftCode struct { + ID uuid.UUID `gorm:"column:id;type:uuid;default:gen_random_uuid();primary_key"` + Code string `gorm:"column:code;type:text;not null;unique"` + CreditAmount int64 `gorm:"column:creditAmount;type:bigint;default:0;not null"` + Used bool `gorm:"column:used;type:boolean;default:false;not null"` + UsedBy uuid.UUID `gorm:"column:usedBy;type:uuid"` + UsedAt time.Time `gorm:"column:usedAt;type:timestamp(3) with time zone"` + CreatedAt time.Time `gorm:"column:createdAt;type:timestamp(3) with time zone;default:current_timestamp()"` + ExpiredAt time.Time `gorm:"column:expiredAt;type:timestamp(3) with time zone"` + Comment string `gorm:"column:comment;type:text"` +} + +func (GiftCode) TableName() string { + return "GiftCode" +} + +type AccountTransaction struct { + ID uuid.UUID `gorm:"column:id;type:uuid;default:gen_random_uuid();primary_key"` + Type string `gorm:"column:type;type:text"` + UserUID uuid.UUID `gorm:"column:userUid;type:uuid"` + DeductionBalance int64 `gorm:"column:deduction_balance;type:bigint"` + Balance int64 `gorm:"column:balance;type:bigint"` + Message *string `gorm:"column:message;type:text"` + CreatedAt time.Time `gorm:"column:created_at;type:timestamp(3) with time zone;default:current_timestamp()"` + UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp(3) with time zone;default:current_timestamp()"` + BillingID uuid.UUID `gorm:"column:billing_id;type:uuid"` +} + +func (AccountTransaction) TableName() string { + return "AccountTransaction" +} diff --git a/service/account/api/api.go b/service/account/api/api.go index d65dfa9f7af..773dec9c020 100644 --- a/service/account/api/api.go +++ b/service/account/api/api.go @@ -875,3 +875,44 @@ func GetInvoicePayment(c *gin.Context) { "data": payments, }) } + +// UseGiftCode +// @Summary Use a gift code +// @Description Redeem a gift code and apply the credit to the user's account +// @Tags UseGiftCode +// @Accept json +// @Produce json +// @Param request body helper.UseGiftCodeReq true "Use gift code request" +// @Success 200 {object} helper.UseGiftCodeResp "Successfully redeemed gift code" +// @Failure 400 {object} helper.ErrorMessage "Failed to parse use gift code request" +// @Failure 401 {object} helper.ErrorMessage "Authentication error" +// @Failure 500 {object} helper.ErrorMessage "Failed to redeem gift code" +// @Router /account/v1alpha1/gift-code/use [post] +func UseGiftCode(c *gin.Context) { + // Parse the use gift code request + req, err := helper.ParseUseGiftCodeReq(c) + if err != nil { + c.JSON(http.StatusBadRequest, helper.ErrorMessage{Error: fmt.Sprintf("failed to parse use gift code request: %v", err)}) + return + } + + // Check authentication + if err := CheckAuthAndCalibrate(req.Auth); err != nil { + c.JSON(http.StatusUnauthorized, helper.ErrorMessage{Error: fmt.Sprintf("authenticate error: %v", err)}) + return + } + + // Use the gift code (to be implemented) + if _, err := dao.DBClient.UseGiftCode(req); err != nil { + c.JSON(http.StatusInternalServerError, helper.ErrorMessage{Error: fmt.Sprintf("failed to use gift code: %v", err)}) + return + } + + // Return success response + c.JSON(http.StatusOK, helper.UseGiftCodeResp{ + Data: helper.UseGiftCodeRespData{ + UserID: req.UserID, // 假设 UserID 来自认证信息 + }, + Message: "Gift code successfully redeemed", + }) +} diff --git a/service/account/dao/interface.go b/service/account/dao/interface.go index f4fd86ea790..69e77974e7a 100644 --- a/service/account/dao/interface.go +++ b/service/account/dao/interface.go @@ -55,6 +55,7 @@ type Interface interface { GetUserCrName(ops types.UserQueryOpts) (string, error) GetRegions() ([]types.Region, error) GetLocalRegion() types.Region + UseGiftCode(req *helper.UseGiftCodeReq) (*types.GiftCode, error) } type Account struct { @@ -1358,3 +1359,20 @@ func (m *Account) GetInvoicePayments(invoiceID string) ([]types.Payment, error) func (m *Account) SetStatusInvoice(req *helper.SetInvoiceStatusReq) error { return m.ck.SetInvoiceStatus(req.InvoiceIDList, req.Status) } + +func (m *Account) UseGiftCode(req *helper.UseGiftCodeReq) (*types.GiftCode, error) { + giftCode, err := m.ck.GetGiftCodeWithCode(req.Code) + if err != nil { + return nil, fmt.Errorf("failed to get gift code: %v", err) + } + + if giftCode.Used { + return nil, fmt.Errorf("gift code is already used") + } + + if err = m.ck.UseGiftCode(giftCode, req.UserID); err != nil { + return nil, fmt.Errorf("failed to use gift code: %v", err) + } + + return giftCode, nil +} diff --git a/service/account/dao/interface_test.go b/service/account/dao/interface_test.go index 22b0c6d3aea..a7a87364a70 100644 --- a/service/account/dao/interface_test.go +++ b/service/account/dao/interface_test.go @@ -587,3 +587,23 @@ func init() { os.Setenv("LOCAL_COCKROACH_URI", "") os.Setenv("LOCAL_REGION", "") } + +func TestAccount_UseGiftCode(t *testing.T) { + db, err := newAccountForTest("", os.Getenv("GLOBAL_COCKROACH_URI"), os.Getenv("LOCAL_COCKROACH_URI")) + if err != nil { + t.Fatalf("NewAccountInterface() error = %v", err) + return + } + + giftcode, err := db.UseGiftCode(&helper.UseGiftCodeReq{ + Auth: &helper.Auth{ + UserID: "5uxfy8jl", + }, + }) + + if err != nil { + t.Fatalf("UseGiftCode() error = %v", err) + return + } + t.Logf("giftcode = %+v", giftcode) +} diff --git a/service/account/helper/common.go b/service/account/helper/common.go index 90d9a8b946c..891d5585316 100644 --- a/service/account/helper/common.go +++ b/service/account/helper/common.go @@ -25,11 +25,13 @@ const ( ApplyInvoice = "/invoice/apply" SetStatusInvoice = "/invoice/set-status" GetInvoicePayment = "/invoice/get-payment" + UseGiftCode = "/gift-code/use" ) // env const ( - ConfigPath = "/config/config.json" + //ConfigPath = "/config/config.json" + ConfigPath = "/home/ubuntu/pro/sealos/sealos/service/account/config.json" EnvMongoURI = "MONGO_URI" ENVGlobalCockroach = "GLOBAL_COCKROACH_URI" ENVLocalCockroach = "LOCAL_COCKROACH_URI" diff --git a/service/account/helper/request.go b/service/account/helper/request.go index 8031c7ccfed..e2fe0fb0232 100644 --- a/service/account/helper/request.go +++ b/service/account/helper/request.go @@ -475,3 +475,38 @@ func ParseSetInvoiceStatusReq(c *gin.Context) (*SetInvoiceStatusReq, error) { } return invoiceStatus, nil } + +type UseGiftCodeRespData struct { + UserID string `json:"userID" bson:"userID" example:"user-123"` +} + +type UseGiftCodeResp struct { + Data UseGiftCodeRespData `json:"data,omitempty" bson:"data,omitempty"` + Message string `json:"message,omitempty" bson:"message" example:"Gift code successfully redeemed"` +} + +type UseGiftCodeReq struct { + // @Summary Gift code to be used + // @Description The code of the gift card to be redeemed + // @JSONSchema required + Code string `json:"code" bson:"code" binding:"required" example:"HAPPY2024"` + + // @Summary Authentication information + // @Description Authentication information + // @JSONSchema required + *Auth `json:",inline" bson:",inline"` +} + +func ParseUseGiftCodeReq(c *gin.Context) (*UseGiftCodeReq, error) { + useGiftCode := &UseGiftCodeReq{} + if err := c.ShouldBindJSON(useGiftCode); err != nil { + return nil, fmt.Errorf("bind json error: %v", err) + } + + // Additional validation can be added here if needed + if useGiftCode.Code == "" { + return nil, fmt.Errorf("gift code cannot be empty") + } + + return useGiftCode, nil +} diff --git a/service/account/router/router.go b/service/account/router/router.go index ea1aabf8378..2518012102d 100644 --- a/service/account/router/router.go +++ b/service/account/router/router.go @@ -53,7 +53,8 @@ func RegisterPayRouter() { POST(helper.GetInvoice, api.GetInvoice). POST(helper.ApplyInvoice, api.ApplyInvoice). POST(helper.SetStatusInvoice, api.SetStatusInvoice). - POST(helper.GetInvoicePayment, api.GetInvoicePayment) + POST(helper.GetInvoicePayment, api.GetInvoicePayment). + POST(helper.UseGiftCode, api.UseGiftCode) docs.SwaggerInfo.Host = env.GetEnvWithDefault("SWAGGER_HOST", "localhost:2333") router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) From 40e773d707ec2843ecc9edf48cfa6d1b92ff6a12 Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Thu, 29 Aug 2024 09:02:16 +0000 Subject: [PATCH 2/4] update swagger doc and complete test --- .../pkg/database/cockroach/accountv2.go | 2 +- service/account/api/api.go | 2 +- service/account/dao/interface.go | 4 + service/account/dao/interface_test.go | 19 ++-- service/account/docs/docs.go | 101 ++++++++++++++++++ service/account/docs/swagger.json | 101 ++++++++++++++++++ service/account/docs/swagger.yaml | 71 ++++++++++++ 7 files changed, 289 insertions(+), 11 deletions(-) diff --git a/controllers/pkg/database/cockroach/accountv2.go b/controllers/pkg/database/cockroach/accountv2.go index 77970120cf6..065ae362834 100644 --- a/controllers/pkg/database/cockroach/accountv2.go +++ b/controllers/pkg/database/cockroach/accountv2.go @@ -1071,7 +1071,7 @@ func (c *Cockroach) UseGiftCode(giftCode *types.GiftCode, userID string) error { return fmt.Errorf("failed to update user balance: %w", err) } - message := "created by gift code" + message := "created by use gift code" // Create an AccountTransaction record accountTransaction := &types.AccountTransaction{ ID: uuid.New(), diff --git a/service/account/api/api.go b/service/account/api/api.go index 773dec9c020..e6a40206d9f 100644 --- a/service/account/api/api.go +++ b/service/account/api/api.go @@ -911,7 +911,7 @@ func UseGiftCode(c *gin.Context) { // Return success response c.JSON(http.StatusOK, helper.UseGiftCodeResp{ Data: helper.UseGiftCodeRespData{ - UserID: req.UserID, // 假设 UserID 来自认证信息 + UserID: req.UserID, }, Message: "Gift code successfully redeemed", }) diff --git a/service/account/dao/interface.go b/service/account/dao/interface.go index 69e77974e7a..6e807deac02 100644 --- a/service/account/dao/interface.go +++ b/service/account/dao/interface.go @@ -1366,6 +1366,10 @@ func (m *Account) UseGiftCode(req *helper.UseGiftCodeReq) (*types.GiftCode, erro return nil, fmt.Errorf("failed to get gift code: %v", err) } + if !giftCode.ExpiredAt.IsZero() && time.Now().After(giftCode.ExpiredAt) { + return nil, fmt.Errorf("gift code has expired") + } + if giftCode.Used { return nil, fmt.Errorf("gift code is already used") } diff --git a/service/account/dao/interface_test.go b/service/account/dao/interface_test.go index a7a87364a70..078c723eb4f 100644 --- a/service/account/dao/interface_test.go +++ b/service/account/dao/interface_test.go @@ -580,14 +580,6 @@ func TestAccount_SetStatusInvoice(t *testing.T) { } } -func init() { - // set env - os.Setenv("MONGO_URI", "") - os.Setenv("GLOBAL_COCKROACH_URI", "") - os.Setenv("LOCAL_COCKROACH_URI", "") - os.Setenv("LOCAL_REGION", "") -} - func TestAccount_UseGiftCode(t *testing.T) { db, err := newAccountForTest("", os.Getenv("GLOBAL_COCKROACH_URI"), os.Getenv("LOCAL_COCKROACH_URI")) if err != nil { @@ -596,8 +588,9 @@ func TestAccount_UseGiftCode(t *testing.T) { } giftcode, err := db.UseGiftCode(&helper.UseGiftCodeReq{ + Code: "DfxAffaeEf", Auth: &helper.Auth{ - UserID: "5uxfy8jl", + UserID: "E1xAJ0fy4k", }, }) @@ -607,3 +600,11 @@ func TestAccount_UseGiftCode(t *testing.T) { } t.Logf("giftcode = %+v", giftcode) } + +func init() { + // set env + os.Setenv("MONGO_URI", "") + os.Setenv("GLOBAL_COCKROACH_URI", "") + os.Setenv("LOCAL_COCKROACH_URI", "") + os.Setenv("LOCAL_REGION", "") +} diff --git a/service/account/docs/docs.go b/service/account/docs/docs.go index 3d03f3c9d21..ba8f21858dd 100644 --- a/service/account/docs/docs.go +++ b/service/account/docs/docs.go @@ -761,6 +761,58 @@ const docTemplate = `{ } } }, + "/account/v1alpha1/gift-code/use": { + "post": { + "description": "Redeem a gift code and apply the credit to the user's account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UseGiftCode" + ], + "summary": "Use a gift code", + "parameters": [ + { + "description": "Use gift code request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/helper.UseGiftCodeReq" + } + } + ], + "responses": { + "200": { + "description": "Successfully redeemed gift code", + "schema": { + "$ref": "#/definitions/helper.UseGiftCodeResp" + } + }, + "400": { + "description": "Failed to parse use gift code request", + "schema": { + "$ref": "#/definitions/helper.ErrorMessage" + } + }, + "401": { + "description": "Authentication error", + "schema": { + "$ref": "#/definitions/helper.ErrorMessage" + } + }, + "500": { + "description": "Failed to redeem gift code", + "schema": { + "$ref": "#/definitions/helper.ErrorMessage" + } + } + } + } + }, "/account/v1alpha1/invoice/apply": { "post": { "description": "Apply invoice", @@ -1858,6 +1910,55 @@ const docTemplate = `{ } } }, + "helper.UseGiftCodeReq": { + "type": "object", + "required": [ + "code" + ], + "properties": { + "code": { + "description": "@Summary Gift code to be used\n@Description The code of the gift card to be redeemed\n@JSONSchema required", + "type": "string", + "example": "HAPPY2024" + }, + "kubeConfig": { + "type": "string" + }, + "owner": { + "type": "string", + "example": "admin" + }, + "token": { + "type": "string", + "example": "token" + }, + "userID": { + "type": "string", + "example": "admin" + } + } + }, + "helper.UseGiftCodeResp": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/helper.UseGiftCodeRespData" + }, + "message": { + "type": "string", + "example": "Gift code successfully redeemed" + } + } + }, + "helper.UseGiftCodeRespData": { + "type": "object", + "properties": { + "userID": { + "type": "string", + "example": "user-123" + } + } + }, "helper.UserBaseReq": { "type": "object", "properties": { diff --git a/service/account/docs/swagger.json b/service/account/docs/swagger.json index 43ac3efc1c5..1345e163309 100644 --- a/service/account/docs/swagger.json +++ b/service/account/docs/swagger.json @@ -754,6 +754,58 @@ } } }, + "/account/v1alpha1/gift-code/use": { + "post": { + "description": "Redeem a gift code and apply the credit to the user's account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UseGiftCode" + ], + "summary": "Use a gift code", + "parameters": [ + { + "description": "Use gift code request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/helper.UseGiftCodeReq" + } + } + ], + "responses": { + "200": { + "description": "Successfully redeemed gift code", + "schema": { + "$ref": "#/definitions/helper.UseGiftCodeResp" + } + }, + "400": { + "description": "Failed to parse use gift code request", + "schema": { + "$ref": "#/definitions/helper.ErrorMessage" + } + }, + "401": { + "description": "Authentication error", + "schema": { + "$ref": "#/definitions/helper.ErrorMessage" + } + }, + "500": { + "description": "Failed to redeem gift code", + "schema": { + "$ref": "#/definitions/helper.ErrorMessage" + } + } + } + } + }, "/account/v1alpha1/invoice/apply": { "post": { "description": "Apply invoice", @@ -1851,6 +1903,55 @@ } } }, + "helper.UseGiftCodeReq": { + "type": "object", + "required": [ + "code" + ], + "properties": { + "code": { + "description": "@Summary Gift code to be used\n@Description The code of the gift card to be redeemed\n@JSONSchema required", + "type": "string", + "example": "HAPPY2024" + }, + "kubeConfig": { + "type": "string" + }, + "owner": { + "type": "string", + "example": "admin" + }, + "token": { + "type": "string", + "example": "token" + }, + "userID": { + "type": "string", + "example": "admin" + } + } + }, + "helper.UseGiftCodeResp": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/helper.UseGiftCodeRespData" + }, + "message": { + "type": "string", + "example": "Gift code successfully redeemed" + } + } + }, + "helper.UseGiftCodeRespData": { + "type": "object", + "properties": { + "userID": { + "type": "string", + "example": "user-123" + } + } + }, "helper.UserBaseReq": { "type": "object", "properties": { diff --git a/service/account/docs/swagger.yaml b/service/account/docs/swagger.yaml index 4e9e0ffadc8..9337a3ac540 100644 --- a/service/account/docs/swagger.yaml +++ b/service/account/docs/swagger.yaml @@ -561,6 +561,43 @@ definitions: required: - toUser type: object + helper.UseGiftCodeReq: + properties: + code: + description: |- + @Summary Gift code to be used + @Description The code of the gift card to be redeemed + @JSONSchema required + example: HAPPY2024 + type: string + kubeConfig: + type: string + owner: + example: admin + type: string + token: + example: token + type: string + userID: + example: admin + type: string + required: + - code + type: object + helper.UseGiftCodeResp: + properties: + data: + $ref: '#/definitions/helper.UseGiftCodeRespData' + message: + example: Gift code successfully redeemed + type: string + type: object + helper.UseGiftCodeRespData: + properties: + userID: + example: user-123 + type: string + type: object helper.UserBaseReq: properties: endTime: @@ -1093,6 +1130,40 @@ paths: summary: Get transfer tags: - Transfer + /account/v1alpha1/gift-code/use: + post: + consumes: + - application/json + description: Redeem a gift code and apply the credit to the user's account + parameters: + - description: Use gift code request + in: body + name: request + required: true + schema: + $ref: '#/definitions/helper.UseGiftCodeReq' + produces: + - application/json + responses: + "200": + description: Successfully redeemed gift code + schema: + $ref: '#/definitions/helper.UseGiftCodeResp' + "400": + description: Failed to parse use gift code request + schema: + $ref: '#/definitions/helper.ErrorMessage' + "401": + description: Authentication error + schema: + $ref: '#/definitions/helper.ErrorMessage' + "500": + description: Failed to redeem gift code + schema: + $ref: '#/definitions/helper.ErrorMessage' + summary: Use a gift code + tags: + - UseGiftCode /account/v1alpha1/invoice/apply: post: consumes: From b9235458416319bc1d7d68372d75197709390462 Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Thu, 29 Aug 2024 09:08:46 +0000 Subject: [PATCH 3/4] ok --- service/account/helper/common.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/service/account/helper/common.go b/service/account/helper/common.go index 891d5585316..06c7ae9f487 100644 --- a/service/account/helper/common.go +++ b/service/account/helper/common.go @@ -30,8 +30,7 @@ const ( // env const ( - //ConfigPath = "/config/config.json" - ConfigPath = "/home/ubuntu/pro/sealos/sealos/service/account/config.json" + ConfigPath = "/config/config.json" EnvMongoURI = "MONGO_URI" ENVGlobalCockroach = "GLOBAL_COCKROACH_URI" ENVLocalCockroach = "LOCAL_COCKROACH_URI" From 766657a725ecc60b583f63aa2f42a70f9474f3b8 Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Thu, 29 Aug 2024 09:37:42 +0000 Subject: [PATCH 4/4] ok --- controllers/pkg/database/cockroach/accountv2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/pkg/database/cockroach/accountv2.go b/controllers/pkg/database/cockroach/accountv2.go index 065ae362834..89682fda135 100644 --- a/controllers/pkg/database/cockroach/accountv2.go +++ b/controllers/pkg/database/cockroach/accountv2.go @@ -1057,7 +1057,7 @@ func (c *Cockroach) Close() error { func (c *Cockroach) GetGiftCodeWithCode(code string) (*types.GiftCode, error) { var giftCode types.GiftCode - if err := c.DB.Where("code = ?", code).First(&giftCode).Error; err != nil { + if err := c.DB.Where(&types.GiftCode{Code: code}).First(&giftCode).Error; err != nil { return nil, fmt.Errorf("failed to get gift code: %w", err) } return &giftCode, nil