Merge branch 'main' into ticket-bet
This commit is contained in:
commit
f27a75eb66
27
cmd/main.go
27
cmd/main.go
|
|
@ -115,6 +115,7 @@ func main() {
|
||||||
wallet.WalletStore(store),
|
wallet.WalletStore(store),
|
||||||
wallet.TransferStore(store),
|
wallet.TransferStore(store),
|
||||||
notificatioStore,
|
notificatioStore,
|
||||||
|
notificationSvc,
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -206,22 +207,22 @@ func main() {
|
||||||
httpserver.StartTicketCrons(*ticketSvc)
|
httpserver.StartTicketCrons(*ticketSvc)
|
||||||
|
|
||||||
// Fetch companies and branches for live wallet metrics update
|
// Fetch companies and branches for live wallet metrics update
|
||||||
ctx := context.Background()
|
// ctx := context.Background()
|
||||||
|
|
||||||
companies := []domain.GetCompany{
|
// companies := []domain.GetCompany{
|
||||||
{ID: 1, Name: "Company A", WalletBalance: 1000.0},
|
// {ID: 1, Name: "Company A", WalletBalance: 1000.0},
|
||||||
}
|
// }
|
||||||
|
|
||||||
branches := []domain.BranchWallet{
|
// branches := []domain.BranchWallet{
|
||||||
{ID: 10, Name: "Branch Z", CompanyID: 1, Balance: 500.0},
|
// {ID: 10, Name: "Branch Z", CompanyID: 1, Balance: 500.0},
|
||||||
}
|
// }
|
||||||
|
|
||||||
notificationSvc.UpdateLiveWalletMetrics(ctx, companies, branches)
|
// notificationSvc.UpdateLiveWalletMetrics(ctx, companies, branches)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Println("Failed to update live metrics:", err)
|
// log.Println("Failed to update live metrics:", err)
|
||||||
} else {
|
// } else {
|
||||||
log.Println("Live metrics broadcasted successfully")
|
// log.Println("Live metrics broadcasted successfully")
|
||||||
}
|
// }
|
||||||
|
|
||||||
issueReportingRepo := repository.NewReportedIssueRepository(store)
|
issueReportingRepo := repository.NewReportedIssueRepository(store)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -295,7 +295,8 @@ CREATE TABLE IF NOT EXISTS settings (
|
||||||
);
|
);
|
||||||
CREATE TABLE bonus (
|
CREATE TABLE bonus (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
multiplier REAL NOT NULL
|
multiplier REAL NOT NULL,
|
||||||
|
balance_cap BIGINT NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
-- Views
|
-- Views
|
||||||
CREATE VIEW companies_details AS
|
CREATE VIEW companies_details AS
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
-- name: CreateBonusMultiplier :exec
|
-- name: CreateBonusMultiplier :exec
|
||||||
INSERT INTO bonus (multiplier)
|
INSERT INTO bonus (multiplier, balance_cap)
|
||||||
VALUES ($1);
|
VALUES ($1, $2);
|
||||||
|
|
||||||
-- name: GetBonusMultiplier :many
|
-- name: GetBonusMultiplier :many
|
||||||
SELECT id, multiplier
|
SELECT id, multiplier
|
||||||
FROM bonus;
|
FROM bonus;
|
||||||
|
|
||||||
|
-- name: GetBonusBalanceCap :many
|
||||||
|
SELECT id, balance_cap
|
||||||
|
FROM bonus;
|
||||||
|
|
||||||
-- name: UpdateBonusMultiplier :exec
|
-- name: UpdateBonusMultiplier :exec
|
||||||
UPDATE bonus
|
UPDATE bonus
|
||||||
SET multiplier = $1
|
SET multiplier = $1,
|
||||||
WHERE id = $2;
|
balance_cap = $2
|
||||||
|
WHERE id = $3;
|
||||||
|
|
@ -40,7 +40,6 @@ WHERE referrer_id = $1;
|
||||||
|
|
||||||
-- name: GetReferralSettings :one
|
-- name: GetReferralSettings :one
|
||||||
SELECT * FROM referral_settings
|
SELECT * FROM referral_settings
|
||||||
WHERE id = 'default'
|
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
-- name: UpdateReferralSettings :one
|
-- name: UpdateReferralSettings :one
|
||||||
|
|
@ -70,3 +69,9 @@ INSERT INTO referral_settings (
|
||||||
|
|
||||||
-- name: GetReferralByReferredID :one
|
-- name: GetReferralByReferredID :one
|
||||||
SELECT * FROM referrals WHERE referred_id = $1 LIMIT 1;
|
SELECT * FROM referrals WHERE referred_id = $1 LIMIT 1;
|
||||||
|
|
||||||
|
-- name: GetActiveReferralByReferrerID :one
|
||||||
|
SELECT * FROM referrals WHERE referrer_id = $1 AND status = 'PENDING' LIMIT 1;
|
||||||
|
|
||||||
|
-- name: GetReferralCountByID :one
|
||||||
|
SELECT count(*) FROM referrals WHERE referrer_id = $1;
|
||||||
|
|
@ -10,29 +10,69 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const CreateBonusMultiplier = `-- name: CreateBonusMultiplier :exec
|
const CreateBonusMultiplier = `-- name: CreateBonusMultiplier :exec
|
||||||
INSERT INTO bonus (multiplier)
|
INSERT INTO bonus (multiplier, balance_cap)
|
||||||
VALUES ($1)
|
VALUES ($1, $2)
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) CreateBonusMultiplier(ctx context.Context, multiplier float32) error {
|
type CreateBonusMultiplierParams struct {
|
||||||
_, err := q.db.Exec(ctx, CreateBonusMultiplier, multiplier)
|
Multiplier float32 `json:"multiplier"`
|
||||||
|
BalanceCap int64 `json:"balance_cap"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateBonusMultiplier(ctx context.Context, arg CreateBonusMultiplierParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, CreateBonusMultiplier, arg.Multiplier, arg.BalanceCap)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GetBonusBalanceCap = `-- name: GetBonusBalanceCap :many
|
||||||
|
SELECT id, balance_cap
|
||||||
|
FROM bonus
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetBonusBalanceCapRow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
BalanceCap int64 `json:"balance_cap"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetBonusBalanceCap(ctx context.Context) ([]GetBonusBalanceCapRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetBonusBalanceCap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetBonusBalanceCapRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetBonusBalanceCapRow
|
||||||
|
if err := rows.Scan(&i.ID, &i.BalanceCap); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const GetBonusMultiplier = `-- name: GetBonusMultiplier :many
|
const GetBonusMultiplier = `-- name: GetBonusMultiplier :many
|
||||||
SELECT id, multiplier
|
SELECT id, multiplier
|
||||||
FROM bonus
|
FROM bonus
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetBonusMultiplier(ctx context.Context) ([]Bonu, error) {
|
type GetBonusMultiplierRow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Multiplier float32 `json:"multiplier"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetBonusMultiplier(ctx context.Context) ([]GetBonusMultiplierRow, error) {
|
||||||
rows, err := q.db.Query(ctx, GetBonusMultiplier)
|
rows, err := q.db.Query(ctx, GetBonusMultiplier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
var items []Bonu
|
var items []GetBonusMultiplierRow
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i Bonu
|
var i GetBonusMultiplierRow
|
||||||
if err := rows.Scan(&i.ID, &i.Multiplier); err != nil {
|
if err := rows.Scan(&i.ID, &i.Multiplier); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -46,16 +86,18 @@ func (q *Queries) GetBonusMultiplier(ctx context.Context) ([]Bonu, error) {
|
||||||
|
|
||||||
const UpdateBonusMultiplier = `-- name: UpdateBonusMultiplier :exec
|
const UpdateBonusMultiplier = `-- name: UpdateBonusMultiplier :exec
|
||||||
UPDATE bonus
|
UPDATE bonus
|
||||||
SET multiplier = $1
|
SET multiplier = $1,
|
||||||
WHERE id = $2
|
balance_cap = $2
|
||||||
|
WHERE id = $3
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateBonusMultiplierParams struct {
|
type UpdateBonusMultiplierParams struct {
|
||||||
Multiplier float32 `json:"multiplier"`
|
Multiplier float32 `json:"multiplier"`
|
||||||
|
BalanceCap int64 `json:"balance_cap"`
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpdateBonusMultiplier(ctx context.Context, arg UpdateBonusMultiplierParams) error {
|
func (q *Queries) UpdateBonusMultiplier(ctx context.Context, arg UpdateBonusMultiplierParams) error {
|
||||||
_, err := q.db.Exec(ctx, UpdateBonusMultiplier, arg.Multiplier, arg.ID)
|
_, err := q.db.Exec(ctx, UpdateBonusMultiplier, arg.Multiplier, arg.BalanceCap, arg.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ type BetWithOutcome struct {
|
||||||
type Bonu struct {
|
type Bonu struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Multiplier float32 `json:"multiplier"`
|
Multiplier float32 `json:"multiplier"`
|
||||||
|
BalanceCap int64 `json:"balance_cap"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Branch struct {
|
type Branch struct {
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,28 @@ func (q *Queries) CreateReferralSettings(ctx context.Context, arg CreateReferral
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GetActiveReferralByReferrerID = `-- name: GetActiveReferralByReferrerID :one
|
||||||
|
SELECT id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at FROM referrals WHERE referrer_id = $1 AND status = 'PENDING' LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetActiveReferralByReferrerID(ctx context.Context, referrerID string) (Referral, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetActiveReferralByReferrerID, referrerID)
|
||||||
|
var i Referral
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.ReferralCode,
|
||||||
|
&i.ReferrerID,
|
||||||
|
&i.ReferredID,
|
||||||
|
&i.Status,
|
||||||
|
&i.RewardAmount,
|
||||||
|
&i.CashbackAmount,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const GetReferralByCode = `-- name: GetReferralByCode :one
|
const GetReferralByCode = `-- name: GetReferralByCode :one
|
||||||
SELECT id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at FROM referrals
|
SELECT id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at FROM referrals
|
||||||
WHERE referral_code = $1
|
WHERE referral_code = $1
|
||||||
|
|
@ -147,9 +169,19 @@ func (q *Queries) GetReferralByReferredID(ctx context.Context, referredID pgtype
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GetReferralCountByID = `-- name: GetReferralCountByID :one
|
||||||
|
SELECT count(*) FROM referrals WHERE referrer_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetReferralCountByID(ctx context.Context, referrerID string) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetReferralCountByID, referrerID)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
const GetReferralSettings = `-- name: GetReferralSettings :one
|
const GetReferralSettings = `-- name: GetReferralSettings :one
|
||||||
SELECT id, referral_reward_amount, cashback_percentage, bet_referral_bonus_percentage, max_referrals, expires_after_days, updated_by, created_at, updated_at, version FROM referral_settings
|
SELECT id, referral_reward_amount, cashback_percentage, bet_referral_bonus_percentage, max_referrals, expires_after_days, updated_by, created_at, updated_at, version FROM referral_settings
|
||||||
WHERE id = 'default'
|
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,14 @@ type ReferralSettings struct {
|
||||||
Version int32
|
Version int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReferralSettingsReq struct {
|
||||||
|
ReferralRewardAmount float64 `json:"referral_reward_amount" validate:"required"`
|
||||||
|
CashbackPercentage float64 `json:"cashback_percentage" validate:"required"`
|
||||||
|
MaxReferrals int32 `json:"max_referrals" validate:"required"`
|
||||||
|
ExpiresAfterDays int32 `json:"expires_afterdays" validate:"required"`
|
||||||
|
UpdatedBy string `json:"updated_by" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type Referral struct {
|
type Referral struct {
|
||||||
ID int64
|
ID int64
|
||||||
ReferralCode string
|
ReferralCode string
|
||||||
|
|
|
||||||
|
|
@ -269,3 +269,28 @@ type GameRecommendation struct {
|
||||||
Bets []float64 `json:"bets"`
|
Bets []float64 `json:"bets"`
|
||||||
Reason string `json:"reason"` // e.g., "Based on your activity", "Popular", "Random pick"
|
Reason string `json:"reason"` // e.g., "Based on your activity", "Popular", "Random pick"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PopokLaunchRequest struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Platform int `json:"platform"`
|
||||||
|
PartnerID int `json:"partnerId"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Data PopokLaunchRequestData `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PopokLaunchRequestData struct {
|
||||||
|
GameMode string `json:"gameMode"`
|
||||||
|
GameID string `json:"gameId"`
|
||||||
|
Lang string `json:"lang"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
ExitURL string `json:"exitURL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PopokLaunchResponse struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data struct {
|
||||||
|
LauncherURL string `json:"launcherURL"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,25 @@ import (
|
||||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Store) CreateBonusMultiplier(ctx context.Context, multiplier float32) error {
|
func (s *Store) CreateBonusMultiplier(ctx context.Context, multiplier float32, balance_cap int64) error {
|
||||||
return s.queries.CreateBonusMultiplier(ctx, multiplier)
|
return s.queries.CreateBonusMultiplier(ctx, dbgen.CreateBonusMultiplierParams{
|
||||||
|
Multiplier: multiplier,
|
||||||
|
BalanceCap: balance_cap,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) GetBonusMultiplier(ctx context.Context) ([]dbgen.Bonu, error) {
|
func (s *Store) GetBonusMultiplier(ctx context.Context) ([]dbgen.GetBonusMultiplierRow, error) {
|
||||||
return s.queries.GetBonusMultiplier(ctx)
|
return s.queries.GetBonusMultiplier(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32) error {
|
func (s *Store) GetBonusBalanceCap(ctx context.Context) ([]dbgen.GetBonusBalanceCapRow, error) {
|
||||||
|
return s.queries.GetBonusBalanceCap(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32, balance_cap int64) error {
|
||||||
return s.queries.UpdateBonusMultiplier(ctx, dbgen.UpdateBonusMultiplierParams{
|
return s.queries.UpdateBonusMultiplier(ctx, dbgen.UpdateBonusMultiplierParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
Multiplier: mulitplier,
|
Multiplier: mulitplier,
|
||||||
|
BalanceCap: balance_cap,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
|
@ -20,6 +21,8 @@ type ReferralRepository interface {
|
||||||
UpdateSettings(ctx context.Context, settings *domain.ReferralSettings) error
|
UpdateSettings(ctx context.Context, settings *domain.ReferralSettings) error
|
||||||
CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error
|
CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error
|
||||||
GetReferralByReferredID(ctx context.Context, referredID string) (*domain.Referral, error) // New method
|
GetReferralByReferredID(ctx context.Context, referredID string) (*domain.Referral, error) // New method
|
||||||
|
GetReferralCountByID(ctx context.Context, referrerID string) (int64, error)
|
||||||
|
GetActiveReferralByReferrerID(ctx context.Context, referrerID string) (*domain.Referral, error)
|
||||||
UpdateUserReferalCode(ctx context.Context, codedata domain.UpdateUserReferalCode) error
|
UpdateUserReferalCode(ctx context.Context, codedata domain.UpdateUserReferalCode) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,17 +148,17 @@ func (r *ReferralRepo) UpdateSettings(ctx context.Context, settings *domain.Refe
|
||||||
|
|
||||||
func (r *ReferralRepo) CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error {
|
func (r *ReferralRepo) CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error {
|
||||||
rewardAmount := pgtype.Numeric{}
|
rewardAmount := pgtype.Numeric{}
|
||||||
if err := rewardAmount.Scan(settings.ReferralRewardAmount); err != nil {
|
if err := rewardAmount.Scan(fmt.Sprintf("%f", settings.ReferralRewardAmount)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cashbackPercentage := pgtype.Numeric{}
|
cashbackPercentage := pgtype.Numeric{}
|
||||||
if err := cashbackPercentage.Scan(settings.CashbackPercentage); err != nil {
|
if err := cashbackPercentage.Scan(fmt.Sprintf("%f", settings.CashbackPercentage)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
betReferralBonusPercentage := pgtype.Numeric{}
|
betReferralBonusPercentage := pgtype.Numeric{}
|
||||||
if err := betReferralBonusPercentage.Scan(settings.BetReferralBonusPercentage); err != nil {
|
if err := betReferralBonusPercentage.Scan(fmt.Sprintf("%f", settings.BetReferralBonusPercentage)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,6 +186,30 @@ func (r *ReferralRepo) GetReferralByReferredID(ctx context.Context, referredID s
|
||||||
return r.mapToDomainReferral(&dbReferral), nil
|
return r.mapToDomainReferral(&dbReferral), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ReferralRepo) GetReferralCountByID(ctx context.Context, referrerID string) (int64, error) {
|
||||||
|
count, err := r.store.queries.GetReferralCountByID(ctx, referrerID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ReferralRepo) GetActiveReferralByReferrerID(ctx context.Context, referrerID string) (*domain.Referral, error) {
|
||||||
|
referral, err := r.store.queries.GetActiveReferralByReferrerID(ctx, referrerID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return &domain.Referral{}, nil
|
||||||
|
}
|
||||||
|
return &domain.Referral{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.mapToDomainReferral(&referral), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ReferralRepo) mapToDomainReferral(dbRef *dbgen.Referral) *domain.Referral {
|
func (r *ReferralRepo) mapToDomainReferral(dbRef *dbgen.Referral) *domain.Referral {
|
||||||
var referredID *string
|
var referredID *string
|
||||||
if dbRef.ReferredID.Valid {
|
if dbRef.ReferredID.Valid {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type BonusStore interface {
|
type BonusStore interface {
|
||||||
CreateBonusMultiplier(ctx context.Context, multiplier float32) error
|
CreateBonusMultiplier(ctx context.Context, multiplier float32, balance_cap int64) error
|
||||||
GetBonusMultiplier(ctx context.Context) ([]dbgen.Bonu, error)
|
GetBonusMultiplier(ctx context.Context) ([]dbgen.GetBonusMultiplierRow, error)
|
||||||
UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32) error
|
GetBonusBalanceCap(ctx context.Context) ([]dbgen.GetBonusBalanceCapRow, error)
|
||||||
|
UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32, balance_cap int64) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,18 @@ func NewService(bonusStore BonusStore) *Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) CreateBonusMultiplier(ctx context.Context, multiplier float32) error {
|
func (s *Service) CreateBonusMultiplier(ctx context.Context, multiplier float32, balance_cap int64) error {
|
||||||
return s.bonusStore.CreateBonusMultiplier(ctx, multiplier)
|
return s.bonusStore.CreateBonusMultiplier(ctx, multiplier, balance_cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GetBonusMultiplier(ctx context.Context) ([]dbgen.Bonu, error) {
|
func (s *Service) GetBonusMultiplier(ctx context.Context) ([]dbgen.GetBonusMultiplierRow, error) {
|
||||||
return s.bonusStore.GetBonusMultiplier(ctx)
|
return s.bonusStore.GetBonusMultiplier(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32) error {
|
func (s *Service) GetBonusBalanceCap(ctx context.Context) ([]dbgen.GetBonusBalanceCapRow, error) {
|
||||||
return s.bonusStore.UpdateBonusMultiplier(ctx, id, mulitplier)
|
return s.bonusStore.GetBonusBalanceCap(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32, balance_cap int64) error {
|
||||||
|
return s.bonusStore.UpdateBonusMultiplier(ctx, id, mulitplier, balance_cap)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ func NewClient(baseURL, secretKey string) *Client {
|
||||||
|
|
||||||
func (c *Client) InitializePayment(ctx context.Context, req domain.ChapaDepositRequest) (domain.ChapaDepositResponse, error) {
|
func (c *Client) InitializePayment(ctx context.Context, req domain.ChapaDepositRequest) (domain.ChapaDepositResponse, error) {
|
||||||
payload := map[string]interface{}{
|
payload := map[string]interface{}{
|
||||||
"amount": fmt.Sprintf("%.2f", float64(req.Amount)/100),
|
"amount": fmt.Sprintf("%.2f", float64(req.Amount)),
|
||||||
"currency": req.Currency,
|
"currency": req.Currency,
|
||||||
// "email": req.Email,
|
// "email": req.Email,
|
||||||
"first_name": req.FirstName,
|
"first_name": req.FirstName,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ type ReferralStore interface {
|
||||||
ProcessReferral(ctx context.Context, referredID, referralCode string) error
|
ProcessReferral(ctx context.Context, referredID, referralCode string) error
|
||||||
ProcessDepositBonus(ctx context.Context, userID string, amount float64) error
|
ProcessDepositBonus(ctx context.Context, userID string, amount float64) error
|
||||||
GetReferralStats(ctx context.Context, userID string) (*domain.ReferralStats, error)
|
GetReferralStats(ctx context.Context, userID string) (*domain.ReferralStats, error)
|
||||||
|
CreateReferralSettings(ctx context.Context, req domain.ReferralSettingsReq) error
|
||||||
UpdateReferralSettings(ctx context.Context, settings *domain.ReferralSettings) error
|
UpdateReferralSettings(ctx context.Context, settings *domain.ReferralSettings) error
|
||||||
GetReferralSettings(ctx context.Context) (*domain.ReferralSettings, error)
|
GetReferralSettings(ctx context.Context) (*domain.ReferralSettings, error)
|
||||||
|
GetReferralCountByID(ctx context.Context, referrerID string) (int64, error)
|
||||||
ProcessBetReferral(ctx context.Context, userPhone string, betAmount float64) error
|
ProcessBetReferral(ctx context.Context, userPhone string, betAmount float64) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,16 +54,45 @@ func (s *Service) GenerateReferralCode() (string, error) {
|
||||||
|
|
||||||
func (s *Service) CreateReferral(ctx context.Context, userID int64) error {
|
func (s *Service) CreateReferral(ctx context.Context, userID int64) error {
|
||||||
s.logger.Info("Creating referral code for user", "userID", userID)
|
s.logger.Info("Creating referral code for user", "userID", userID)
|
||||||
// TODO: check in user already has an active referral code
|
|
||||||
|
// check if user already has an active referral code
|
||||||
|
referral, err := s.repo.GetActiveReferralByReferrerID(ctx, fmt.Sprintf("%d", userID))
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to check if user alredy has active referral code", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if referral != nil && referral.Status == domain.ReferralPending && referral.ExpiresAt.After(time.Now()) {
|
||||||
|
s.logger.Error("user already has an active referral code", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings, err := s.GetReferralSettings(ctx)
|
||||||
|
if err != nil || settings == nil {
|
||||||
|
s.logger.Error("Failed to fetch referral settings", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check referral count limit
|
||||||
|
referralCount, err := s.GetReferralCountByID(ctx, fmt.Sprintf("%d", userID))
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get referral count", "userID", userID, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("referralCount: ", referralCount)
|
||||||
|
if referralCount == int64(settings.MaxReferrals) {
|
||||||
|
s.logger.Error("referral count limit has been reached", "referralCount", referralCount, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
code, err := s.GenerateReferralCode()
|
code, err := s.GenerateReferralCode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to generate referral code", "error", err)
|
s.logger.Error("Failed to generate referral code", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: get the referral settings from db
|
var rewardAmount float64 = settings.ReferralRewardAmount
|
||||||
var rewardAmount float64 = 100
|
var expireDuration time.Time = time.Now().Add(time.Duration((24 * settings.ExpiresAfterDays)) * time.Hour)
|
||||||
var expireDuration time.Time = time.Now().Add(24 * time.Hour)
|
|
||||||
|
|
||||||
if err := s.repo.CreateReferral(ctx, &domain.Referral{
|
if err := s.repo.CreateReferral(ctx, &domain.Referral{
|
||||||
ReferralCode: code,
|
ReferralCode: code,
|
||||||
|
|
@ -249,6 +278,26 @@ func (s *Service) GetReferralStats(ctx context.Context, userPhone string) (*doma
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateReferralSettings(ctx context.Context, req domain.ReferralSettingsReq) error {
|
||||||
|
s.logger.Info("Creating referral setting")
|
||||||
|
|
||||||
|
if err := s.repo.CreateSettings(ctx, &domain.ReferralSettings{
|
||||||
|
ReferralRewardAmount: req.ReferralRewardAmount,
|
||||||
|
CashbackPercentage: req.CashbackPercentage,
|
||||||
|
MaxReferrals: req.MaxReferrals,
|
||||||
|
ExpiresAfterDays: req.ExpiresAfterDays,
|
||||||
|
UpdatedBy: req.UpdatedBy,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
}); err != nil {
|
||||||
|
s.logger.Error("Failed to create referral setting", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("Referral setting created succesfully")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) UpdateReferralSettings(ctx context.Context, settings *domain.ReferralSettings) error {
|
func (s *Service) UpdateReferralSettings(ctx context.Context, settings *domain.ReferralSettings) error {
|
||||||
s.logger.Info("Updating referral settings", "settingsID", settings.ID)
|
s.logger.Info("Updating referral settings", "settingsID", settings.ID)
|
||||||
|
|
||||||
|
|
@ -272,6 +321,16 @@ func (s *Service) GetReferralSettings(ctx context.Context) (*domain.ReferralSett
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Referral settings retrieved successfully", "settingsID", settings.ID)
|
s.logger.Info("Referral settings retrieved successfully", "settings", settings)
|
||||||
return settings, nil
|
return settings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetReferralCountByID(ctx context.Context, referrerID string) (int64, error) {
|
||||||
|
count, err := s.repo.GetReferralCountByID(ctx, referrerID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get referral count", "userID", referrerID, "error", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -465,7 +465,7 @@ func (s *Service) GenerateReport(ctx context.Context, period string) error {
|
||||||
return fmt.Errorf("fetch data: %w", err)
|
return fmt.Errorf("fetch data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath := fmt.Sprintf("/host-desktop/report_%s_%s.csv", period, time.Now().Format("2006-01-02_15-04"))
|
filePath := fmt.Sprintf("reports/report_%s_%s.csv", period, time.Now().Format("2006-01-02_15-04"))
|
||||||
file, err := os.Create(filePath)
|
file, err := os.Create(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create file: %w", err)
|
return fmt.Errorf("create file: %w", err)
|
||||||
|
|
|
||||||
|
|
@ -43,21 +43,23 @@ func New(repo repository.VirtualGameRepository, walletSvc wallet.Service, store
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) {
|
func (s *service) GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) {
|
||||||
|
// 1. Fetch user
|
||||||
user, err := s.store.GetUserByID(ctx, userID)
|
user, err := s.store.GetUserByID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get user", "userID", userID, "error", err)
|
s.logger.Error("Failed to get user", "userID", userID, "error", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionId := fmt.Sprintf("%d-%s-%d", userID, gameID, time.Now().UnixNano())
|
// 2. Generate session and token
|
||||||
|
sessionID := fmt.Sprintf("%d-%s-%d", userID, gameID, time.Now().UnixNano())
|
||||||
token, err := jwtutil.CreatePopOKJwt(
|
token, err := jwtutil.CreatePopOKJwt(
|
||||||
userID,
|
userID,
|
||||||
user.CompanyID,
|
user.CompanyID,
|
||||||
user.FirstName,
|
user.PhoneNumber,
|
||||||
currency,
|
currency,
|
||||||
"en",
|
"en",
|
||||||
mode,
|
mode,
|
||||||
sessionId,
|
sessionID,
|
||||||
s.config.PopOK.SecretKey,
|
s.config.PopOK.SecretKey,
|
||||||
24*time.Hour,
|
24*time.Hour,
|
||||||
)
|
)
|
||||||
|
|
@ -66,9 +68,9 @@ func (s *service) GenerateGameLaunchURL(ctx context.Context, userID int64, gameI
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record game launch as a transaction (for history and recommendation purposes)
|
// 3. Record virtual game history (optional but recommended)
|
||||||
tx := &domain.VirtualGameHistory{
|
history := &domain.VirtualGameHistory{
|
||||||
SessionID: sessionId, // Optional: populate if session tracking is implemented
|
SessionID: sessionID,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
CompanyID: user.CompanyID.Value,
|
CompanyID: user.CompanyID.Value,
|
||||||
Provider: string(domain.PROVIDER_POPOK),
|
Provider: string(domain.PROVIDER_POPOK),
|
||||||
|
|
@ -76,23 +78,66 @@ func (s *service) GenerateGameLaunchURL(ctx context.Context, userID int64, gameI
|
||||||
TransactionType: "LAUNCH",
|
TransactionType: "LAUNCH",
|
||||||
Amount: 0,
|
Amount: 0,
|
||||||
Currency: currency,
|
Currency: currency,
|
||||||
ExternalTransactionID: sessionId,
|
ExternalTransactionID: sessionID,
|
||||||
Status: "COMPLETED",
|
Status: "COMPLETED",
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
if err := s.repo.CreateVirtualGameHistory(ctx, history); err != nil {
|
||||||
if err := s.repo.CreateVirtualGameHistory(ctx, tx); err != nil {
|
|
||||||
s.logger.Error("Failed to record game launch transaction", "error", err)
|
s.logger.Error("Failed to record game launch transaction", "error", err)
|
||||||
// Do not fail game launch on logging error — just log and continue
|
// Non-fatal: log and continue
|
||||||
}
|
}
|
||||||
|
|
||||||
params := fmt.Sprintf(
|
// 4. Prepare PopOK API request
|
||||||
"partnerId=%s&gameId=%s&gameMode=%s&lang=en&platform=%s&externalToken=%s",
|
timestamp := time.Now().Format("02-01-2006 15:04:05")
|
||||||
s.config.PopOK.ClientID, gameID, mode, s.config.PopOK.Platform, token,
|
partnerID, err := strconv.Atoi(s.config.PopOK.ClientID)
|
||||||
)
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid PopOK ClientID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s?%s", s.config.PopOK.BaseURL, params), nil
|
data := domain.PopokLaunchRequestData{
|
||||||
|
GameMode: mode,
|
||||||
|
GameID: gameID,
|
||||||
|
Lang: "en",
|
||||||
|
Token: token,
|
||||||
|
ExitURL: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := generatePopOKHash(s.config.PopOK.SecretKey, timestamp, data)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate PopOK hash: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
platformInt, err := strconv.Atoi(s.config.PopOK.Platform)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid PopOK Platform: %v", err)
|
||||||
|
}
|
||||||
|
reqBody := domain.PopokLaunchRequest{
|
||||||
|
Action: "getLauncherURL",
|
||||||
|
Platform: platformInt,
|
||||||
|
PartnerID: partnerID,
|
||||||
|
Time: timestamp,
|
||||||
|
Hash: hash,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Make API request
|
||||||
|
bodyBytes, _ := json.Marshal(reqBody)
|
||||||
|
resp, err := http.Post(s.config.PopOK.BaseURL+"/serviceApi.php", "application/json", bytes.NewReader(bodyBytes))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("PopOK POST failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var parsedResp domain.PopokLaunchResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&parsedResp); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse PopOK response: %w", err)
|
||||||
|
}
|
||||||
|
if parsedResp.Code != 0 {
|
||||||
|
return "", fmt.Errorf("PopOK error: %s", parsedResp.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedResp.Data.LauncherURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) HandleCallback(ctx context.Context, callback *domain.PopOKCallback) error {
|
func (s *service) HandleCallback(ctx context.Context, callback *domain.PopOKCallback) error {
|
||||||
|
|
@ -126,7 +171,7 @@ func (s *service) HandleCallback(ctx context.Context, callback *domain.PopOKCall
|
||||||
}
|
}
|
||||||
|
|
||||||
walletID := wallets[0].ID
|
walletID := wallets[0].ID
|
||||||
amount := int64(callback.Amount * 100) // Convert to cents
|
amount := int64(callback.Amount) // Convert to cents
|
||||||
transactionType := callback.Type
|
transactionType := callback.Type
|
||||||
|
|
||||||
switch transactionType {
|
switch transactionType {
|
||||||
|
|
@ -187,7 +232,7 @@ func (s *service) GetPlayerInfo(ctx context.Context, req *domain.PopOKPlayerInfo
|
||||||
return &domain.PopOKPlayerInfoResponse{
|
return &domain.PopOKPlayerInfoResponse{
|
||||||
Country: "ET",
|
Country: "ET",
|
||||||
Currency: claims.Currency,
|
Currency: claims.Currency,
|
||||||
Balance: float64(wallets[0].Balance) / 100, // Convert cents to currency
|
Balance: float64(wallets[0].Balance), // Convert cents to currency
|
||||||
PlayerID: fmt.Sprintf("%d", claims.UserID),
|
PlayerID: fmt.Sprintf("%d", claims.UserID),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +245,7 @@ func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert amount to cents (assuming wallet uses cents)
|
// Convert amount to cents (assuming wallet uses cents)
|
||||||
amountCents := int64(req.Amount * 100)
|
amountCents := int64(req.Amount)
|
||||||
|
|
||||||
// Deduct from wallet
|
// Deduct from wallet
|
||||||
|
|
||||||
|
|
@ -237,7 +282,7 @@ func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (
|
||||||
return &domain.PopOKBetResponse{
|
return &domain.PopOKBetResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
ExternalTrxID: fmt.Sprintf("%v", tx.ID), // Your internal transaction ID
|
ExternalTrxID: fmt.Sprintf("%v", tx.ID), // Your internal transaction ID
|
||||||
Balance: float64(userWallets[0].Balance) / 100,
|
Balance: float64(userWallets[0].Balance),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,7 +308,7 @@ func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (
|
||||||
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
balance := 0.0
|
balance := 0.0
|
||||||
if len(wallets) > 0 {
|
if len(wallets) > 0 {
|
||||||
balance = float64(wallets[0].Balance) / 100
|
balance = float64(wallets[0].Balance)
|
||||||
}
|
}
|
||||||
return &domain.PopOKWinResponse{
|
return &domain.PopOKWinResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
|
|
@ -273,7 +318,7 @@ func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Convert amount to cents
|
// 3. Convert amount to cents
|
||||||
amountCents := int64(req.Amount * 100)
|
amountCents := int64(req.Amount)
|
||||||
|
|
||||||
// 4. Credit to wallet
|
// 4. Credit to wallet
|
||||||
_, err = s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{},
|
_, err = s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{},
|
||||||
|
|
@ -308,10 +353,12 @@ func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (
|
||||||
return nil, fmt.Errorf("transaction recording failed")
|
return nil, fmt.Errorf("transaction recording failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n\n Win balance is:%v\n\n", float64(userWallets[0].Balance))
|
||||||
|
|
||||||
return &domain.PopOKWinResponse{
|
return &domain.PopOKWinResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
ExternalTrxID: fmt.Sprintf("%v", tx.ID),
|
ExternalTrxID: fmt.Sprintf("%v", tx.ID),
|
||||||
Balance: float64(userWallets[0].Balance) / 100,
|
Balance: float64(userWallets[0].Balance),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,7 +381,7 @@ func (s *service) ProcessTournamentWin(ctx context.Context, req *domain.PopOKWin
|
||||||
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
balance := 0.0
|
balance := 0.0
|
||||||
if len(wallets) > 0 {
|
if len(wallets) > 0 {
|
||||||
balance = float64(wallets[0].Balance) / 100
|
balance = float64(wallets[0].Balance)
|
||||||
}
|
}
|
||||||
return &domain.PopOKWinResponse{
|
return &domain.PopOKWinResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
|
|
@ -344,7 +391,7 @@ func (s *service) ProcessTournamentWin(ctx context.Context, req *domain.PopOKWin
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Convert amount to cents
|
// 3. Convert amount to cents
|
||||||
amountCents := int64(req.Amount * 100)
|
amountCents := int64(req.Amount)
|
||||||
|
|
||||||
// 4. Credit user wallet
|
// 4. Credit user wallet
|
||||||
_, err = s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
|
_, err = s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
|
||||||
|
|
@ -379,7 +426,7 @@ func (s *service) ProcessTournamentWin(ctx context.Context, req *domain.PopOKWin
|
||||||
return &domain.PopOKWinResponse{
|
return &domain.PopOKWinResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
ExternalTrxID: fmt.Sprintf("%v", tx.ID),
|
ExternalTrxID: fmt.Sprintf("%v", tx.ID),
|
||||||
Balance: float64(wallets[0].Balance) / 100,
|
Balance: float64(wallets[0].Balance),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -400,7 +447,7 @@ func (s *service) ProcessPromoWin(ctx context.Context, req *domain.PopOKWinReque
|
||||||
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
balance := 0.0
|
balance := 0.0
|
||||||
if len(wallets) > 0 {
|
if len(wallets) > 0 {
|
||||||
balance = float64(wallets[0].Balance) / 100
|
balance = float64(wallets[0].Balance)
|
||||||
}
|
}
|
||||||
return &domain.PopOKWinResponse{
|
return &domain.PopOKWinResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
|
|
@ -440,7 +487,7 @@ func (s *service) ProcessPromoWin(ctx context.Context, req *domain.PopOKWinReque
|
||||||
return &domain.PopOKWinResponse{
|
return &domain.PopOKWinResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
ExternalTrxID: fmt.Sprintf("%v", tx.ID),
|
ExternalTrxID: fmt.Sprintf("%v", tx.ID),
|
||||||
Balance: float64(wallets[0].Balance) / 100,
|
Balance: float64(wallets[0].Balance),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -506,7 +553,7 @@ func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequ
|
||||||
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
balance := 0.0
|
balance := 0.0
|
||||||
if len(wallets) > 0 {
|
if len(wallets) > 0 {
|
||||||
balance = float64(wallets[0].Balance) / 100
|
balance = float64(wallets[0].Balance)
|
||||||
}
|
}
|
||||||
return &domain.PopOKCancelResponse{
|
return &domain.PopOKCancelResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
|
|
@ -567,14 +614,23 @@ func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequ
|
||||||
return &domain.PopOKCancelResponse{
|
return &domain.PopOKCancelResponse{
|
||||||
TransactionID: req.TransactionID,
|
TransactionID: req.TransactionID,
|
||||||
ExternalTrxID: fmt.Sprintf("%v", cancelTx.ID),
|
ExternalTrxID: fmt.Sprintf("%v", cancelTx.ID),
|
||||||
Balance: float64(userWallets[0].Balance) / 100,
|
Balance: float64(userWallets[0].Balance),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) GenerateSignature(params string) string {
|
func generatePopOKHash(privateKey, timestamp string, data domain.PopokLaunchRequestData) (string, error) {
|
||||||
h := hmac.New(sha256.New, []byte(s.config.PopOK.SecretKey))
|
// Marshal data to JSON (compact format, like json_encode in PHP)
|
||||||
h.Write([]byte(params))
|
dataBytes, err := json.Marshal(data)
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate: privateKey + time + json_encoded(data)
|
||||||
|
hashInput := fmt.Sprintf("%s%s%s", privateKey, timestamp, string(dataBytes))
|
||||||
|
|
||||||
|
// SHA-256 hash
|
||||||
|
hash := sha256.Sum256([]byte(hashInput))
|
||||||
|
return hex.EncodeToString(hash[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) verifySignature(callback *domain.PopOKCallback) bool {
|
func (s *service) verifySignature(callback *domain.PopOKCallback) bool {
|
||||||
|
|
@ -607,32 +663,46 @@ func (s *service) GetGameCounts(ctx context.Context, filter domain.ReportFilter)
|
||||||
func (s *service) ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error) {
|
func (s *service) ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error) {
|
||||||
now := time.Now().Format("02-01-2006 15:04:05") // dd-mm-yyyy hh:mm:ss
|
now := time.Now().Format("02-01-2006 15:04:05") // dd-mm-yyyy hh:mm:ss
|
||||||
|
|
||||||
// Calculate hash: sha256(privateKey + time)
|
// Step 1: Construct payload without the hash
|
||||||
rawHash := s.config.PopOK.SecretKey + now
|
data := map[string]interface{}{
|
||||||
hash := fmt.Sprintf("%x", sha256.Sum256([]byte(rawHash)))
|
|
||||||
|
|
||||||
// Construct request payload
|
|
||||||
payload := map[string]interface{}{
|
|
||||||
"action": "gameList",
|
"action": "gameList",
|
||||||
"platform": s.config.PopOK.Platform,
|
"platform": s.config.PopOK.Platform,
|
||||||
"partnerId": s.config.PopOK.ClientID,
|
"partnerId": s.config.PopOK.ClientID,
|
||||||
"currency": currency,
|
"currency": currency,
|
||||||
"time": now,
|
"time": now,
|
||||||
"hash": hash,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyBytes, err := json.Marshal(payload)
|
// Step 2: Marshal data to JSON for hash calculation
|
||||||
|
// dataBytes, err := json.Marshal(data)
|
||||||
|
// if err != nil {
|
||||||
|
// s.logger.Error("Failed to marshal data for hash generation", "error", err)
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Step 3: Calculate hash: sha256(privateKey + time + json(data))
|
||||||
|
rawHash := s.config.PopOK.SecretKey + now
|
||||||
|
hash := fmt.Sprintf("%x", sha256.Sum256([]byte(rawHash)))
|
||||||
|
|
||||||
|
// Step 4: Add the hash to the payload
|
||||||
|
data["hash"] = hash
|
||||||
|
|
||||||
|
// Step 5: Marshal full payload with hash
|
||||||
|
bodyBytes, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to marshal game list request", "error", err)
|
s.logger.Error("Failed to marshal final payload with hash", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 6: Create and send the request
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", s.config.PopOK.BaseURL+"/serviceApi.php", bytes.NewReader(bodyBytes))
|
req, err := http.NewRequestWithContext(ctx, "POST", s.config.PopOK.BaseURL+"/serviceApi.php", bytes.NewReader(bodyBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to create game list request", "error", err)
|
s.logger.Error("Failed to create game list request", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
|
||||||
|
req.Header.Set("Accept", "application/json, text/plain, */*")
|
||||||
|
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
|
@ -642,6 +712,7 @@ func (s *service) ListGames(ctx context.Context, currency string) ([]domain.PopO
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Step 7: Handle response
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
b, _ := io.ReadAll(resp.Body)
|
b, _ := io.ReadAll(resp.Body)
|
||||||
return nil, fmt.Errorf("PopOK game list failed with status %d: %s", resp.StatusCode, string(b))
|
return nil, fmt.Errorf("PopOK game list failed with status %d: %s", resp.StatusCode, string(b))
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
|
@ -23,16 +24,17 @@ type Client struct {
|
||||||
OperatorID string
|
OperatorID string
|
||||||
SecretKey string
|
SecretKey string
|
||||||
BrandID string
|
BrandID string
|
||||||
cfg *config.Config
|
walletSvc *wallet.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(cfg *config.Config) *Client {
|
func NewClient(cfg *config.Config, walletSvc *wallet.Service) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
http: &http.Client{Timeout: 10 * time.Second},
|
http: &http.Client{Timeout: 10 * time.Second},
|
||||||
BaseURL: cfg.VeliGames.BaseURL,
|
BaseURL: cfg.VeliGames.BaseURL,
|
||||||
OperatorID: cfg.VeliGames.OperatorID,
|
OperatorID: cfg.VeliGames.OperatorID,
|
||||||
SecretKey: cfg.VeliGames.SecretKey,
|
SecretKey: cfg.VeliGames.SecretKey,
|
||||||
BrandID: cfg.VeliGames.BrandID,
|
BrandID: cfg.VeliGames.BrandID,
|
||||||
|
walletSvc: walletSvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
|
@ -105,6 +106,17 @@ func (c *Client) ProcessBet(ctx context.Context, req domain.BetRequest) (*domain
|
||||||
|
|
||||||
var res domain.BetResponse
|
var res domain.BetResponse
|
||||||
err := c.post(ctx, "/bet", req, sigParams, &res)
|
err := c.post(ctx, "/bet", req, sigParams, &res)
|
||||||
|
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return &domain.BetResponse{}, fmt.Errorf("invalid PlayerID: %w", err)
|
||||||
|
}
|
||||||
|
wallets, err := c.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
|
||||||
|
if err != nil {
|
||||||
|
return &domain.BetResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.walletSvc.DeductFromWallet(ctx, wallets[0].ID, domain.Currency(req.Amount.Amount), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT)
|
||||||
|
|
||||||
return &res, err
|
return &res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,6 +145,19 @@ func (c *Client) ProcessWin(ctx context.Context, req domain.WinRequest) (*domain
|
||||||
|
|
||||||
var res domain.WinResponse
|
var res domain.WinResponse
|
||||||
err := c.post(ctx, "/win", req, sigParams, &res)
|
err := c.post(ctx, "/win", req, sigParams, &res)
|
||||||
|
|
||||||
|
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return &domain.WinResponse{}, fmt.Errorf("invalid PlayerID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wallets, err := c.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
|
||||||
|
if err != nil {
|
||||||
|
return &domain.WinResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.walletSvc.AddToWallet(ctx, wallets[0].ID, domain.Currency(req.Amount.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
|
||||||
|
|
||||||
return &res, err
|
return &res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,6 +188,18 @@ func (c *Client) ProcessCancel(ctx context.Context, req domain.CancelRequest) (*
|
||||||
|
|
||||||
var res domain.CancelResponse
|
var res domain.CancelResponse
|
||||||
err := c.post(ctx, "/cancel", req, sigParams, &res)
|
err := c.post(ctx, "/cancel", req, sigParams, &res)
|
||||||
|
|
||||||
|
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return &domain.CancelResponse{}, fmt.Errorf("invalid PlayerID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wallets, err := c.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
|
||||||
|
if err != nil {
|
||||||
|
return &domain.CancelResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.walletSvc.AddToWallet(ctx, wallets[0].ID, domain.Currency(req.AdjustmentRefund.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
|
||||||
return &res, err
|
return &res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ func buildNotificationMessage(thresholdPercent int, currentBalance, initialDepos
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"Company wallet balance has reached %d%% of initial deposit. Current balance: %.2f, Initial deposit: %.2f",
|
"Company wallet balance has reached %d%% of initial deposit. Current balance: %.2f, Initial deposit: %.2f",
|
||||||
thresholdPercent,
|
thresholdPercent,
|
||||||
float64(currentBalance)/100, // Assuming currency is in cents
|
float64(currentBalance), // Assuming currency is in cents
|
||||||
float64(initialDeposit)/100,
|
float64(initialDeposit),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,12 @@ type Service struct {
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(walletStore WalletStore, transferStore TransferStore, notificationStore notificationservice.NotificationStore, logger *slog.Logger) *Service {
|
func NewService(walletStore WalletStore, transferStore TransferStore, notificationStore notificationservice.NotificationStore, notificationSvc *notificationservice.Service, logger *slog.Logger) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
walletStore: walletStore,
|
walletStore: walletStore,
|
||||||
transferStore: transferStore,
|
transferStore: transferStore,
|
||||||
notificationStore: notificationStore,
|
notificationStore: notificationStore,
|
||||||
|
notificationSvc: notificationSvc,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ func (s *Service) AddToWallet(
|
||||||
return domain.Transfer{}, err
|
return domain.Transfer{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go s.notificationSvc.UpdateLiveWalletMetricForWallet(ctx, wallet)
|
// go s.notificationSvc.UpdateLiveWalletMetricForWallet(ctx, wallet)
|
||||||
|
|
||||||
// Log the transfer here for reference
|
// Log the transfer here for reference
|
||||||
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
||||||
|
|
@ -135,7 +135,7 @@ func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.
|
||||||
return domain.Transfer{}, nil
|
return domain.Transfer{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
go s.notificationSvc.UpdateLiveWalletMetricForWallet(ctx, wallet)
|
// go s.notificationSvc.UpdateLiveWalletMetricForWallet(ctx, wallet)
|
||||||
|
|
||||||
// Log the transfer here for reference
|
// Log the transfer here for reference
|
||||||
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
func (h *Handler) CreateBonusMultiplier(c *fiber.Ctx) error {
|
func (h *Handler) CreateBonusMultiplier(c *fiber.Ctx) error {
|
||||||
var req struct {
|
var req struct {
|
||||||
Multiplier float32 `json:"multiplier"`
|
Multiplier float32 `json:"multiplier"`
|
||||||
|
BalanceCap int64 `json:"balance_cap"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
|
@ -27,7 +28,7 @@ func (h *Handler) CreateBonusMultiplier(c *fiber.Ctx) error {
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.bonusSvc.CreateBonusMultiplier(c.Context(), req.Multiplier); err != nil {
|
if err := h.bonusSvc.CreateBonusMultiplier(c.Context(), req.Multiplier, req.BalanceCap); err != nil {
|
||||||
h.logger.Error("failed to create bonus multiplier", "error", err)
|
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.StatusInternalServerError, "failed to create bonus mulitplier", nil, nil)
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +50,7 @@ func (h *Handler) UpdateBonusMultiplier(c *fiber.Ctx) error {
|
||||||
var req struct {
|
var req struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Multiplier float32 `json:"multiplier"`
|
Multiplier float32 `json:"multiplier"`
|
||||||
|
BalanceCap int64 `json:"balance_cap"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
|
@ -56,7 +58,7 @@ func (h *Handler) UpdateBonusMultiplier(c *fiber.Ctx) error {
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.bonusSvc.UpdateBonusMultiplier(c.Context(), req.ID, req.Multiplier); err != nil {
|
if err := h.bonusSvc.UpdateBonusMultiplier(c.Context(), req.ID, req.Multiplier, req.BalanceCap); err != nil {
|
||||||
h.logger.Error("failed to update bonus multiplier", "error", err)
|
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.StatusInternalServerError, "failed to update bonus mulitplier", nil, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
|
@ -66,8 +67,16 @@ func (h *Handler) InitiateDeposit(c *fiber.Ctx) error {
|
||||||
multiplier = bonusMultiplier[0].Multiplier
|
multiplier = bonusMultiplier[0].Multiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, domain.ToCurrency(float32(amount)*multiplier), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
|
var balanceCap int64 = 0
|
||||||
fmt.Sprintf("Added %v to static wallet because of deposit bonus using multiplier %v", float32(amount)*multiplier, multiplier),
|
bonusBalanceCap, err := h.bonusSvc.GetBonusBalanceCap(c.Context())
|
||||||
|
if err == nil {
|
||||||
|
balanceCap = bonusBalanceCap[0].BalanceCap
|
||||||
|
}
|
||||||
|
|
||||||
|
capedBalanceAmount := domain.Currency((math.Min(req.Amount, float64(balanceCap)) * float64(multiplier)) * 100)
|
||||||
|
|
||||||
|
_, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, capedBalanceAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
|
||||||
|
fmt.Sprintf("Added %v to static wallet because of deposit bonus using multiplier %v", capedBalanceAmount, multiplier),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error("Failed to add bonus to static wallet", "walletID", wallet.StaticID, "user id", userID, "error", err)
|
h.logger.Error("Failed to add bonus to static wallet", "walletID", wallet.StaticID, "user id", userID, "error", err)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,38 @@ func (h *Handler) CreateReferralCode(c *fiber.Ctx) error {
|
||||||
return response.WriteJSON(c, fiber.StatusOK, "Referral created successfully", nil, nil)
|
return response.WriteJSON(c, fiber.StatusOK, "Referral created successfully", nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) CreateReferralSettings(c *fiber.Ctx) error {
|
||||||
|
var req domain.ReferralSettingsReq
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
h.logger.Error("Failed to parse settings", "error", err)
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
|
}
|
||||||
|
|
||||||
|
if valErrs, ok := h.validator.Validate(c, req); !ok {
|
||||||
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings, err := h.referralSvc.GetReferralSettings(c.Context())
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("Failed to fetch previous referral setting", "error", err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create referral")
|
||||||
|
}
|
||||||
|
|
||||||
|
// only allow one referral setting for now
|
||||||
|
// for future it can be multiple and be able to choose from them
|
||||||
|
if settings != nil {
|
||||||
|
h.logger.Error("referral setting already exists", "error", err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "referral setting already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.referralSvc.CreateReferralSettings(c.Context(), req); err != nil {
|
||||||
|
h.logger.Error("Failed to create referral setting", "error", err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create referral")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.WriteJSON(c, fiber.StatusOK, "Referral created successfully", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// GetReferralStats godoc
|
// GetReferralStats godoc
|
||||||
// @Summary Get referral statistics
|
// @Summary Get referral statistics
|
||||||
// @Description Retrieves referral statistics for the authenticated user
|
// @Description Retrieves referral statistics for the authenticated user
|
||||||
|
|
@ -112,11 +144,12 @@ func (h *Handler) UpdateReferralSettings(c *fiber.Ctx) error {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Router /referral/settings [get]
|
// @Router /referral/settings [get]
|
||||||
func (h *Handler) GetReferralSettings(c *fiber.Ctx) error {
|
func (h *Handler) GetReferralSettings(c *fiber.Ctx) error {
|
||||||
userID, ok := c.Locals("user_id").(int64)
|
// userID, ok := c.Locals("user_id").(int64)
|
||||||
if !ok || userID == 0 {
|
// if !ok || userID == 0 {
|
||||||
h.logger.Error("Invalid user ID in context")
|
// h.logger.Error("Invalid user ID in context")
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Invalid user identification")
|
// return fiber.NewError(fiber.StatusUnauthorized, "Invalid user identification")
|
||||||
}
|
// }
|
||||||
|
userID := int64(2)
|
||||||
|
|
||||||
user, err := h.userSvc.GetUserByID(c.Context(), userID)
|
user, err := h.userSvc.GetUserByID(c.Context(), userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -137,16 +137,28 @@ func parseReportFilter(c *fiber.Ctx) (domain.ReportFilter, error) {
|
||||||
// @Router /api/v1/report-files/download/{filename} [get]
|
// @Router /api/v1/report-files/download/{filename} [get]
|
||||||
func (h *Handler) DownloadReportFile(c *fiber.Ctx) error {
|
func (h *Handler) DownloadReportFile(c *fiber.Ctx) error {
|
||||||
filename := c.Params("filename")
|
filename := c.Params("filename")
|
||||||
if filename == "" {
|
if filename == "" || strings.Contains(filename, "..") {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
Message: "Missing filename parameter",
|
Message: "Invalid filename parameter",
|
||||||
Error: "filename is required",
|
Error: "filename is required and must not contain '..'",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath := fmt.Sprintf("/host-desktop/%s", filename)
|
reportDir := "reports"
|
||||||
|
|
||||||
// Check if file exists
|
// Ensure reports directory exists
|
||||||
|
if _, err := os.Stat(reportDir); os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(reportDir, os.ModePerm); err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to create report directory",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := fmt.Sprintf("%s/%s", reportDir, filename)
|
||||||
|
|
||||||
|
// Check if the report file exists
|
||||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||||
Message: "Report file not found",
|
Message: "Report file not found",
|
||||||
|
|
@ -154,10 +166,11 @@ func (h *Handler) DownloadReportFile(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set download headers and return file
|
// Set download headers
|
||||||
c.Set("Content-Type", "text/csv")
|
c.Set("Content-Type", "text/csv")
|
||||||
c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
||||||
|
|
||||||
|
// Serve the file
|
||||||
if err := c.SendFile(filePath); err != nil {
|
if err := c.SendFile(filePath); err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
Message: "Failed to serve file",
|
Message: "Failed to serve file",
|
||||||
|
|
@ -177,7 +190,17 @@ func (h *Handler) DownloadReportFile(c *fiber.Ctx) error {
|
||||||
// @Failure 500 {object} domain.ErrorResponse "Failed to read report directory"
|
// @Failure 500 {object} domain.ErrorResponse "Failed to read report directory"
|
||||||
// @Router /api/v1/report-files/list [get]
|
// @Router /api/v1/report-files/list [get]
|
||||||
func (h *Handler) ListReportFiles(c *fiber.Ctx) error {
|
func (h *Handler) ListReportFiles(c *fiber.Ctx) error {
|
||||||
reportDir := "/host-desktop"
|
reportDir := "reports"
|
||||||
|
|
||||||
|
// Create the reports directory if it doesn't exist
|
||||||
|
if _, err := os.Stat(reportDir); os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(reportDir, os.ModePerm); err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to create report directory",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
files, err := os.ReadDir(reportDir)
|
files, err := os.ReadDir(reportDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type launchVirtualGameReq struct {
|
type launchVirtualGameReq struct {
|
||||||
GameID string `json:"game_id" validate:"required" example:"crash_001"`
|
GameID string `json:"game_id" validate:"required" example:"1"`
|
||||||
Currency string `json:"currency" validate:"required,len=3" example:"USD"`
|
Currency string `json:"currency" validate:"required,len=3" example:"USD"`
|
||||||
Mode string `json:"mode" validate:"required,oneof=fun real" example:"real"`
|
Mode string `json:"mode" validate:"required,oneof=fun real" example:"real"`
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +108,11 @@ func (h *Handler) HandlePlayerInfo(c *fiber.Ctx) error {
|
||||||
func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
var req domain.PopOKBetRequest
|
var req domain.PopOKBetRequest
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid bet request")
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid bet request",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
// return fiber.NewError(fiber.StatusBadRequest, "Invalid bet request")
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := h.virtualGameSvc.ProcessBet(c.Context(), &req)
|
resp, _ := h.virtualGameSvc.ProcessBet(c.Context(), &req)
|
||||||
|
|
@ -179,7 +183,11 @@ func (h *Handler) GetGameList(c *fiber.Ctx) error {
|
||||||
|
|
||||||
games, err := h.virtualGameSvc.ListGames(c.Context(), currency)
|
games, err := h.virtualGameSvc.ListGames(c.Context(), currency)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadGateway, "failed to fetch games")
|
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Falied to fetch games",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
// return fiber.NewError(fiber.StatusBadGateway, "failed to fetch games")
|
||||||
}
|
}
|
||||||
return c.JSON(games)
|
return c.JSON(games)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func (a *App) initAppRoutes() {
|
||||||
a.fiber.Get("/", func(c *fiber.Ctx) error {
|
a.fiber.Get("/", func(c *fiber.Ctx) error {
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"message": "Welcome to the FortuneBet API",
|
"message": "Welcome to the FortuneBet API",
|
||||||
"version": "1.0dev7",
|
"version": "1.0dev7.5",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -109,7 +109,8 @@ func (a *App) initAppRoutes() {
|
||||||
// Referral Routes
|
// Referral Routes
|
||||||
a.fiber.Post("/referral/create", a.authMiddleware, h.CreateReferralCode)
|
a.fiber.Post("/referral/create", a.authMiddleware, h.CreateReferralCode)
|
||||||
a.fiber.Get("/referral/stats", a.authMiddleware, h.GetReferralStats)
|
a.fiber.Get("/referral/stats", a.authMiddleware, h.GetReferralStats)
|
||||||
a.fiber.Get("/referral/settings", h.GetReferralSettings)
|
a.fiber.Post("/referral/settings", a.authMiddleware, h.CreateReferralSettings)
|
||||||
|
a.fiber.Get("/referral/settings", a.authMiddleware, h.GetReferralSettings)
|
||||||
a.fiber.Patch("/referral/settings", a.authMiddleware, h.UpdateReferralSettings)
|
a.fiber.Patch("/referral/settings", a.authMiddleware, h.UpdateReferralSettings)
|
||||||
|
|
||||||
// Bonus Routes
|
// Bonus Routes
|
||||||
|
|
@ -226,8 +227,8 @@ func (a *App) initAppRoutes() {
|
||||||
|
|
||||||
//Report Routes
|
//Report Routes
|
||||||
group.Get("/reports/dashboard", h.GetDashboardReport)
|
group.Get("/reports/dashboard", h.GetDashboardReport)
|
||||||
group.Get("/report-files/download/:filename", a.authMiddleware, a.SuperAdminOnly, h.DownloadReportFile)
|
group.Get("/report-files/download/:filename", a.authMiddleware, a.OnlyAdminAndAbove, h.DownloadReportFile)
|
||||||
group.Get("/report-files/list", a.authMiddleware, a.SuperAdminOnly, h.ListReportFiles)
|
group.Get("/report-files/list", a.authMiddleware, a.OnlyAdminAndAbove, h.ListReportFiles)
|
||||||
|
|
||||||
//Wallet Monitor Service
|
//Wallet Monitor Service
|
||||||
// group.Get("/debug/wallet-monitor/status", func(c *fiber.Ctx) error {
|
// group.Get("/debug/wallet-monitor/status", func(c *fiber.Ctx) error {
|
||||||
|
|
@ -259,9 +260,9 @@ func (a *App) initAppRoutes() {
|
||||||
group.Post("/veli/start-game", a.authMiddleware, h.StartGame)
|
group.Post("/veli/start-game", a.authMiddleware, h.StartGame)
|
||||||
group.Post("/veli/start-demo-game", a.authMiddleware, h.StartDemoGame)
|
group.Post("/veli/start-demo-game", a.authMiddleware, h.StartDemoGame)
|
||||||
a.fiber.Post("/balance", h.GetBalance)
|
a.fiber.Post("/balance", h.GetBalance)
|
||||||
a.fiber.Post("/bet", h.PlaceBet)
|
// a.fiber.Post("/bet", h.PlaceBet)
|
||||||
a.fiber.Post("/win", h.RegisterWin)
|
// a.fiber.Post("/win", h.RegisterWin)
|
||||||
a.fiber.Post("/cancel", h.CancelTransaction)
|
// a.fiber.Post("/cancel", h.CancelTransaction)
|
||||||
group.Post("/veli/gaming-activity", h.GetGamingActivity)
|
group.Post("/veli/gaming-activity", h.GetGamingActivity)
|
||||||
|
|
||||||
//mongoDB logs
|
//mongoDB logs
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user