From eca084f7f8e946ca79e1abc520213d4d6eb9e0f4 Mon Sep 17 00:00:00 2001 From: Asher Samuel Date: Sat, 28 Jun 2025 20:43:17 +0300 Subject: [PATCH] deposit bonus service --- cmd/main.go | 3 ++ db/migrations/000001_fortune.up.sql | 4 ++ db/query/bonus.sql | 12 +++++ gen/db/bonus.sql.go | 61 ++++++++++++++++++++++ gen/db/models.go | 5 ++ internal/repository/bonus.go | 22 ++++++++ internal/services/bonus/port.go | 13 +++++ internal/services/bonus/service.go | 29 +++++++++++ internal/web_server/app.go | 14 +++-- internal/web_server/handlers/bonus.go | 65 ++++++++++++++++++++++++ internal/web_server/handlers/chapa.go | 21 ++++++++ internal/web_server/handlers/handlers.go | 4 ++ internal/web_server/routes.go | 6 +++ 13 files changed, 254 insertions(+), 5 deletions(-) create mode 100644 db/query/bonus.sql create mode 100644 gen/db/bonus.sql.go create mode 100644 internal/repository/bonus.go create mode 100644 internal/services/bonus/port.go create mode 100644 internal/services/bonus/service.go create mode 100644 internal/web_server/handlers/bonus.go diff --git a/cmd/main.go b/cmd/main.go index 00bc4a3..b13d7b9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -30,6 +30,7 @@ import ( // "github.com/SamuelTariku/FortuneBet-Backend/internal/router" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bonus" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" @@ -124,6 +125,7 @@ func main() { ticketSvc := ticket.NewService(store, eventSvc, *oddsSvc, domain.MongoDBLogger, *settingSvc, notificationSvc) betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, logger, domain.MongoDBLogger) resultSvc := result.NewService(store, cfg, logger, *betSvc, *oddsSvc, eventSvc, leagueSvc, notificationSvc) + bonusSvc := bonus.NewService(store) referalRepo := repository.NewReferralRepository(store) vitualGameRepo := repository.NewVirtualGameRepository(store) recommendationRepo := repository.NewRecommendationRepository(store) @@ -255,6 +257,7 @@ func main() { eventSvc, leagueSvc, referalSvc, + bonusSvc, virtualGameSvc, aleaService, // veliService, diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index 6ed5000..3456bb4 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -287,6 +287,10 @@ CREATE TABLE IF NOT EXISTS settings ( created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); +CREATE TABLE bonus ( + id BIGSERIAL PRIMARY KEY, + multiplier REAL NOT NULL +); -- Views CREATE VIEW companies_details AS SELECT companies.*, diff --git a/db/query/bonus.sql b/db/query/bonus.sql new file mode 100644 index 0000000..c516162 --- /dev/null +++ b/db/query/bonus.sql @@ -0,0 +1,12 @@ +-- name: CreateBonusMultiplier :exec +INSERT INTO bonus (multiplier) +VALUES ($1); + +-- name: GetBonusMultiplier :many +SELECT id, multiplier +FROM bonus; + +-- name: UpdateBonusMultiplier :exec +UPDATE bonus +SET multiplier = $1 +WHERE id = $2; \ No newline at end of file diff --git a/gen/db/bonus.sql.go b/gen/db/bonus.sql.go new file mode 100644 index 0000000..21ef5c7 --- /dev/null +++ b/gen/db/bonus.sql.go @@ -0,0 +1,61 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 +// source: bonus.sql + +package dbgen + +import ( + "context" +) + +const CreateBonusMultiplier = `-- name: CreateBonusMultiplier :exec +INSERT INTO bonus (multiplier) +VALUES ($1) +` + +func (q *Queries) CreateBonusMultiplier(ctx context.Context, multiplier float32) error { + _, err := q.db.Exec(ctx, CreateBonusMultiplier, multiplier) + return err +} + +const GetBonusMultiplier = `-- name: GetBonusMultiplier :many +SELECT id, multiplier +FROM bonus +` + +func (q *Queries) GetBonusMultiplier(ctx context.Context) ([]Bonu, error) { + rows, err := q.db.Query(ctx, GetBonusMultiplier) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Bonu + for rows.Next() { + var i Bonu + if err := rows.Scan(&i.ID, &i.Multiplier); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const UpdateBonusMultiplier = `-- name: UpdateBonusMultiplier :exec +UPDATE bonus +SET multiplier = $1 +WHERE id = $2 +` + +type UpdateBonusMultiplierParams struct { + Multiplier float32 `json:"multiplier"` + ID int64 `json:"id"` +} + +func (q *Queries) UpdateBonusMultiplier(ctx context.Context, arg UpdateBonusMultiplierParams) error { + _, err := q.db.Exec(ctx, UpdateBonusMultiplier, arg.Multiplier, arg.ID) + return err +} diff --git a/gen/db/models.go b/gen/db/models.go index 3ba6c5e..b99623f 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -128,6 +128,11 @@ type BetWithOutcome struct { Outcomes []BetOutcome `json:"outcomes"` } +type Bonu struct { + ID int64 `json:"id"` + Multiplier float32 `json:"multiplier"` +} + type Branch struct { ID int64 `json:"id"` Name string `json:"name"` diff --git a/internal/repository/bonus.go b/internal/repository/bonus.go new file mode 100644 index 0000000..b253ad2 --- /dev/null +++ b/internal/repository/bonus.go @@ -0,0 +1,22 @@ +package repository + +import ( + "context" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" +) + +func (s *Store) CreateBonusMultiplier(ctx context.Context, multiplier float32) error { + return s.queries.CreateBonusMultiplier(ctx, multiplier) +} + +func (s *Store) GetBonusMultiplier(ctx context.Context) ([]dbgen.Bonu, error) { + return s.queries.GetBonusMultiplier(ctx) +} + +func (s *Store) UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32) error { + return s.queries.UpdateBonusMultiplier(ctx, dbgen.UpdateBonusMultiplierParams{ + ID: id, + Multiplier: mulitplier, + }) +} diff --git a/internal/services/bonus/port.go b/internal/services/bonus/port.go new file mode 100644 index 0000000..02b59ca --- /dev/null +++ b/internal/services/bonus/port.go @@ -0,0 +1,13 @@ +package bonus + +import ( + "context" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" +) + +type BonusStore interface { + CreateBonusMultiplier(ctx context.Context, multiplier float32) error + GetBonusMultiplier(ctx context.Context) ([]dbgen.Bonu, error) + UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32) error +} diff --git a/internal/services/bonus/service.go b/internal/services/bonus/service.go new file mode 100644 index 0000000..f55107c --- /dev/null +++ b/internal/services/bonus/service.go @@ -0,0 +1,29 @@ +package bonus + +import ( + "context" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" +) + +type Service struct { + bonusStore BonusStore +} + +func NewService(bonusStore BonusStore) *Service { + return &Service{ + bonusStore: bonusStore, + } +} + +func (s *Service) CreateBonusMultiplier(ctx context.Context, multiplier float32) error { + return s.bonusStore.CreateBonusMultiplier(ctx, multiplier) +} + +func (s *Service) GetBonusMultiplier(ctx context.Context) ([]dbgen.Bonu, error) { + return s.bonusStore.GetBonusMultiplier(ctx) +} + +func (s *Service) UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32) error { + return s.bonusStore.UpdateBonusMultiplier(ctx, id, mulitplier) +} diff --git a/internal/web_server/app.go b/internal/web_server/app.go index 61bc682..a9dd1b2 100644 --- a/internal/web_server/app.go +++ b/internal/web_server/app.go @@ -7,6 +7,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bonus" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" @@ -50,6 +51,7 @@ type App struct { logger *slog.Logger NotidicationStore *notificationservice.Service referralSvc referralservice.ReferralStore + bonusSvc *bonus.Service port int settingSvc *settings.Service authSvc *authentication.Service @@ -96,6 +98,7 @@ func NewApp( eventSvc event.Service, leagueSvc league.Service, referralSvc referralservice.ReferralStore, + bonusSvc *bonus.Service, virtualGameSvc virtualgameservice.VirtualGameService, aleaVirtualGameService alea.AleaVirtualGameService, // veliVirtualGameService veli.VeliVirtualGameService, @@ -119,11 +122,11 @@ func NewApp( })) s := &App{ - issueReportingSvc: issueReportingSvc, - instSvc: instSvc, - currSvc: currSvc, - fiber: app, - port: port, + issueReportingSvc: issueReportingSvc, + instSvc: instSvc, + currSvc: currSvc, + fiber: app, + port: port, settingSvc: settingSvc, authSvc: authSvc, @@ -141,6 +144,7 @@ func NewApp( companySvc: companySvc, NotidicationStore: notidicationStore, referralSvc: referralSvc, + bonusSvc: bonusSvc, Logger: logger, prematchSvc: prematchSvc, eventSvc: eventSvc, diff --git a/internal/web_server/handlers/bonus.go b/internal/web_server/handlers/bonus.go new file mode 100644 index 0000000..19f0e4f --- /dev/null +++ b/internal/web_server/handlers/bonus.go @@ -0,0 +1,65 @@ +package handlers + +import ( + "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" + "github.com/gofiber/fiber/v2" +) + +func (h *Handler) CreateBonusMultiplier(c *fiber.Ctx) error { + var req struct { + Multiplier float32 `json:"multiplier"` + } + + if err := c.BodyParser(&req); err != nil { + h.logger.Error("failed to parse bonus multiplier", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) + } + + // currently only one multiplier is allowed + // we can add an active bool in the db and have mulitple bonus if needed + multipliers, err := h.bonusSvc.GetBonusMultiplier(c.Context()) + if err != nil { + h.logger.Error("failed to get bonus multiplier", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) + } + + if len(multipliers) > 0 { + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) + } + + if err := h.bonusSvc.CreateBonusMultiplier(c.Context(), req.Multiplier); err != nil { + h.logger.Error("failed to create bonus multiplier", "error", err) + return response.WriteJSON(c, fiber.StatusInternalServerError, "failed to create bonus mulitplier", nil, nil) + } + + return response.WriteJSON(c, fiber.StatusOK, "Create bonus mulitplier successfully", nil, nil) +} + +func (h *Handler) GetBonusMultiplier(c *fiber.Ctx) error { + multipliers, err := h.bonusSvc.GetBonusMultiplier(c.Context()) + if err != nil { + h.logger.Error("failed to get bonus multiplier", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) + } + + return response.WriteJSON(c, fiber.StatusOK, "Fetched bonus mulitplier successfully", multipliers, nil) +} + +func (h *Handler) UpdateBonusMultiplier(c *fiber.Ctx) error { + var req struct { + ID int64 `json:"id"` + Multiplier float32 `json:"multiplier"` + } + + if err := c.BodyParser(&req); err != nil { + h.logger.Error("failed to parse bonus multiplier", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) + } + + if err := h.bonusSvc.UpdateBonusMultiplier(c.Context(), req.ID, req.Multiplier); err != nil { + h.logger.Error("failed to update bonus multiplier", "error", err) + return response.WriteJSON(c, fiber.StatusInternalServerError, "failed to update bonus mulitplier", nil, nil) + } + + return response.WriteJSON(c, fiber.StatusOK, "Updated bonus mulitplier successfully", nil, nil) +} diff --git a/internal/web_server/handlers/chapa.go b/internal/web_server/handlers/chapa.go index 5e9ad56..ddfb32d 100644 --- a/internal/web_server/handlers/chapa.go +++ b/internal/web_server/handlers/chapa.go @@ -51,6 +51,27 @@ func (h *Handler) InitiateDeposit(c *fiber.Ctx) error { }) } + // get static wallet of user + wallet, err := h.walletSvc.GetCustomerWallet(c.Context(), userID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ + Error: err.Error(), + Message: "Failed to initiate Chapa deposit", + }) + } + + var multiplier float32 = 1 + bonusMultiplier, err := h.bonusSvc.GetBonusMultiplier(c.Context()) + if err == nil { + multiplier = bonusMultiplier[0].Multiplier + } + + _, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, domain.ToCurrency(float32(amount)*multiplier), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}) + if err != nil { + h.logger.Error("Failed to add bonus to static wallet", "walletID", wallet.StaticID, "user id", userID, "error", err) + return err + } + return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Chapa deposit process initiated successfully", Data: checkoutURL, diff --git a/internal/web_server/handlers/handlers.go b/internal/web_server/handlers/handlers.go index bb2a792..104a297 100644 --- a/internal/web_server/handlers/handlers.go +++ b/internal/web_server/handlers/handlers.go @@ -6,6 +6,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bonus" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" @@ -41,6 +42,7 @@ type Handler struct { notificationSvc *notificationservice.Service userSvc *user.Service referralSvc referralservice.ReferralStore + bonusSvc *bonus.Service reportSvc report.ReportStore chapaSvc *chapa.Service walletSvc *wallet.Service @@ -76,6 +78,7 @@ func New( chapaSvc *chapa.Service, walletSvc *wallet.Service, referralSvc referralservice.ReferralStore, + bonusSvc *bonus.Service, virtualGameSvc virtualgameservice.VirtualGameService, aleaVirtualGameSvc alea.AleaVirtualGameService, // veliVirtualGameSvc veli.VeliVirtualGameService, @@ -106,6 +109,7 @@ func New( chapaSvc: chapaSvc, walletSvc: walletSvc, referralSvc: referralSvc, + bonusSvc: bonusSvc, validator: validator, userSvc: userSvc, transactionSvc: transactionSvc, diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index bfd971e..71bc5eb 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -31,6 +31,7 @@ func (a *App) initAppRoutes() { a.chapaSvc, a.walletSvc, a.referralSvc, + a.bonusSvc, a.virtualGameSvc, a.aleaVirtualGameService, // a.veliVirtualGameService, @@ -111,6 +112,11 @@ func (a *App) initAppRoutes() { a.fiber.Get("/referral/settings", h.GetReferralSettings) a.fiber.Patch("/referral/settings", a.authMiddleware, h.UpdateReferralSettings) + // Bonus Routes + a.fiber.Get("/bonus", a.authMiddleware, h.GetBonusMultiplier) + a.fiber.Post("/bonus/create", a.authMiddleware, h.CreateBonusMultiplier) + a.fiber.Put("/bonus/update", a.authMiddleware, h.UpdateBonusMultiplier) + a.fiber.Get("/cashiers", a.authMiddleware, h.GetAllCashiers) a.fiber.Get("/cashiers/:id", a.authMiddleware, h.GetCashierByID) a.fiber.Post("/cashiers", a.authMiddleware, h.CreateCashier)