fix: transfer not showing online bet issue
This commit is contained in:
parent
49527cbf2a
commit
1557a3141b
8
.env
8
.env
|
|
@ -1,8 +1,8 @@
|
|||
# REPORT_EXPORT_PATH="C:\\ProgramData\\FortuneBet\\exported_reports" #prod env
|
||||
REPORT_EXPORT_PATH ="./exported_reports" #dev env
|
||||
|
||||
RESEND_SENDER_EMAIL=email
|
||||
RESEND_API_KEY=123
|
||||
RESEND_SENDER_EMAIL=customer@fortunebets.net
|
||||
RESEND_API_KEY=re_GSTRa9Pp_JkRWBpST9MvaCVULJF8ybGKE
|
||||
|
||||
ENV=development
|
||||
PORT=8080
|
||||
|
|
@ -11,8 +11,8 @@ REFRESH_EXPIRY=2592000
|
|||
JWT_KEY=mysecretkey
|
||||
ACCESS_EXPIRY=600
|
||||
LOG_LEVEL=debug
|
||||
AFRO_SMS_API_KEY=1
|
||||
AFRO_SMS_SENDER_NAME=
|
||||
AFRO_SMS_API_KEY=eyJhbGciOiJIUzI1NiJ9.eyJpZGVudGlmaWVyIjoiQlR5ZDFIYmJFYXZ6YUo3dzZGell1RUlieGozSElJeTYiLCJleHAiOjE4OTYwMTM5MTksImlhdCI6MTczODI0NzUxOSwianRpIjoiOWIyNTJkNWQtODcxOC00NGYzLWIzMDQtMGYxOTRhY2NiNTU3In0.XPw8s6mCx1Tp1CfxGmjFRROmdkVnghnqfmsniB-Ze8I
|
||||
AFRO_SMS_SENDER_NAME=FortuneBets
|
||||
|
||||
AFRO_SMS_RECEIVER_PHONE_NUMBER=
|
||||
BET365_TOKEN=158046-hesJDP2Cay2M5G
|
||||
|
|
|
|||
17
db.sql
Normal file
17
db.sql
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
psql (16.8)
|
||||
Type "help" for help.
|
||||
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
gh=#
|
||||
|
|
@ -125,7 +125,7 @@ CREATE TABLE IF NOT EXISTS wallet_transfer (
|
|||
id BIGSERIAL PRIMARY KEY,
|
||||
amount BIGINT NOT NULL,
|
||||
type VARCHAR(255) NOT NULL,
|
||||
receiver_wallet_id BIGINT NOT NULL,
|
||||
receiver_wallet_id BIGINT,
|
||||
sender_wallet_id BIGINT,
|
||||
cashier_id BIGINT,
|
||||
verified BOOLEAN NOT NULL DEFAULT false,
|
||||
|
|
@ -263,7 +263,6 @@ FROM companies
|
|||
JOIN wallets ON wallets.id = companies.wallet_id
|
||||
JOIN users ON users.id = companies.admin_id;
|
||||
;
|
||||
|
||||
CREATE VIEW branch_details AS
|
||||
SELECT branches.*,
|
||||
CONCAT(users.first_name, ' ', users.last_name) AS manager_name,
|
||||
|
|
@ -290,40 +289,39 @@ FROM tickets
|
|||
LEFT JOIN ticket_outcomes ON tickets.id = ticket_outcomes.ticket_id
|
||||
GROUP BY tickets.id;
|
||||
-- Foreign Keys
|
||||
|
||||
ALTER TABLE users
|
||||
ADD CONSTRAINT unique_email UNIQUE (email),
|
||||
ADD CONSTRAINT unique_email UNIQUE (email),
|
||||
ADD CONSTRAINT unique_phone_number UNIQUE (phone_number);
|
||||
ALTER TABLE refresh_tokens
|
||||
ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
ALTER TABLE bets
|
||||
ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
ADD CONSTRAINT fk_bets_branches FOREIGN KEY (branch_id) REFERENCES branches(id);
|
||||
ALTER TABLE wallets
|
||||
ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
ALTER TABLE customer_wallets
|
||||
ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users(id),
|
||||
ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users(id),
|
||||
ADD CONSTRAINT fk_customer_wallets_regular_wallet FOREIGN KEY (regular_wallet_id) REFERENCES wallets(id),
|
||||
ADD CONSTRAINT fk_customer_wallets_static_wallet FOREIGN KEY (static_wallet_id) REFERENCES wallets(id);
|
||||
ALTER TABLE wallet_transfer
|
||||
ADD CONSTRAINT fk_wallet_transfer_receiver_wallet FOREIGN KEY (receiver_wallet_id) REFERENCES wallets(id),
|
||||
ADD CONSTRAINT fk_wallet_transfer_receiver_wallet FOREIGN KEY (receiver_wallet_id) REFERENCES wallets(id),
|
||||
ADD CONSTRAINT fk_wallet_transfer_sender_wallet FOREIGN KEY (sender_wallet_id) REFERENCES wallets(id),
|
||||
ADD CONSTRAINT fk_wallet_transfer_cashier FOREIGN KEY (cashier_id) REFERENCES users(id);
|
||||
ALTER TABLE transactions
|
||||
ADD CONSTRAINT fk_transactions_branches FOREIGN KEY (branch_id) REFERENCES branches(id),
|
||||
ADD CONSTRAINT fk_transactions_branches FOREIGN KEY (branch_id) REFERENCES branches(id),
|
||||
ADD CONSTRAINT fk_transactions_cashiers FOREIGN KEY (cashier_id) REFERENCES users(id),
|
||||
ADD CONSTRAINT fk_transactions_bets FOREIGN KEY (bet_id) REFERENCES bets(id);
|
||||
ALTER TABLE branches
|
||||
ADD CONSTRAINT fk_branches_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id),
|
||||
ADD CONSTRAINT fk_branches_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id),
|
||||
ADD CONSTRAINT fk_branches_manager FOREIGN KEY (branch_manager_id) REFERENCES users(id);
|
||||
ALTER TABLE branch_operations
|
||||
ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERENCES supported_operations(id) ON DELETE CASCADE,
|
||||
ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERENCES supported_operations(id) ON DELETE CASCADE,
|
||||
ADD CONSTRAINT fk_branch_operations_branches FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE;
|
||||
ALTER TABLE branch_cashiers
|
||||
ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE;
|
||||
ALTER TABLE companies
|
||||
ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id),
|
||||
ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id),
|
||||
ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE;
|
||||
----------------------------------------------seed data-------------------------------------------------------------
|
||||
-------------------------------------- DO NOT USE IN PRODUCTION-------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
CREATE TABLE virtual_games (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
provider VARCHAR(100) NOT NULL,
|
||||
category VARCHAR(100) NOT NULL,
|
||||
min_bet DECIMAL(15,2) NOT NULL,
|
||||
max_bet DECIMAL(15,2) NOT NULL,
|
||||
volatility VARCHAR(50) NOT NULL,
|
||||
rtp DECIMAL(5,2) NOT NULL,
|
||||
is_featured BOOLEAN DEFAULT false,
|
||||
popularity_score INTEGER DEFAULT 0,
|
||||
thumbnail_url TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
-- CREATE TABLE virtual_games (
|
||||
-- id BIGSERIAL PRIMARY KEY,
|
||||
-- name VARCHAR(255) NOT NULL,
|
||||
-- provider VARCHAR(100) NOT NULL,
|
||||
-- category VARCHAR(100) NOT NULL,
|
||||
-- min_bet DECIMAL(15,2) NOT NULL,
|
||||
-- max_bet DECIMAL(15,2) NOT NULL,
|
||||
-- volatility VARCHAR(50) NOT NULL,
|
||||
-- rtp DECIMAL(5,2) NOT NULL,
|
||||
-- is_featured BOOLEAN DEFAULT false,
|
||||
-- popularity_score INTEGER DEFAULT 0,
|
||||
-- thumbnail_url TEXT,
|
||||
-- created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
-- updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
-- );
|
||||
|
||||
CREATE TABLE user_game_interactions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id),
|
||||
game_id BIGINT NOT NULL REFERENCES virtual_games(id),
|
||||
interaction_type VARCHAR(50) NOT NULL, -- 'view', 'play', 'bet', 'favorite'
|
||||
amount DECIMAL(15,2),
|
||||
interaction_type VARCHAR(50) NOT NULL,
|
||||
-- 'view', 'play', 'bet', 'favorite'
|
||||
amount DECIMAL(15, 2),
|
||||
duration_seconds INTEGER,
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_user_game_interactions_user ON user_game_interactions(user_id);
|
||||
CREATE INDEX idx_user_game_interactions_game ON user_game_interactions(game_id);
|
||||
|
|
@ -48,16 +48,20 @@ VALUES (
|
|||
SELECT *
|
||||
FROM bet_with_outcomes
|
||||
wHERE (
|
||||
branch_id = $1
|
||||
OR $1 IS NULL
|
||||
branch_id = sqlc.narg('branch_id')
|
||||
OR sqlc.narg('branch_id') IS NULL
|
||||
)
|
||||
AND (
|
||||
company_id = $2
|
||||
OR $2 IS NULL
|
||||
company_id = sqlc.narg('company_id')
|
||||
OR sqlc.narg('company_id') IS NULL
|
||||
)
|
||||
AND (
|
||||
user_id = $3
|
||||
OR $3 IS NULL
|
||||
user_id = sqlc.narg('user_id')
|
||||
OR sqlc.narg('user_id') IS NULL
|
||||
)
|
||||
AND (
|
||||
is_shop_bet = sqlc.narg('is_shop_bet')
|
||||
OR sqlc.narg('is_shop_bet') IS NULL
|
||||
);
|
||||
-- name: GetBetByID :one
|
||||
SELECT *
|
||||
|
|
|
|||
|
|
@ -23,7 +23,15 @@ VALUES ($1, $2)
|
|||
RETURNING *;
|
||||
-- name: GetAllBranches :many
|
||||
SELECT *
|
||||
FROM branch_details;
|
||||
FROM branch_details
|
||||
WHERE (
|
||||
company_id = sqlc.narg('company_id')
|
||||
OR sqlc.narg('company_id') IS NULL
|
||||
)
|
||||
AND (
|
||||
is_active = sqlc.narg('is_active')
|
||||
OR sqlc.narg('is_active') IS NULL
|
||||
);
|
||||
-- name: GetBranchByID :one
|
||||
SELECT *
|
||||
FROM branch_details
|
||||
|
|
|
|||
|
|
@ -125,16 +125,26 @@ wHERE (
|
|||
user_id = $3
|
||||
OR $3 IS NULL
|
||||
)
|
||||
AND (
|
||||
is_shop_bet = $4
|
||||
OR $4 IS NULL
|
||||
)
|
||||
`
|
||||
|
||||
type GetAllBetsParams struct {
|
||||
BranchID pgtype.Int8 `json:"branch_id"`
|
||||
CompanyID pgtype.Int8 `json:"company_id"`
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
IsShopBet pgtype.Bool `json:"is_shop_bet"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWithOutcome, error) {
|
||||
rows, err := q.db.Query(ctx, GetAllBets, arg.BranchID, arg.CompanyID, arg.UserID)
|
||||
rows, err := q.db.Query(ctx, GetAllBets,
|
||||
arg.BranchID,
|
||||
arg.CompanyID,
|
||||
arg.UserID,
|
||||
arg.IsShopBet,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,10 +157,23 @@ func (q *Queries) DeleteBranchOperation(ctx context.Context, arg DeleteBranchOpe
|
|||
const GetAllBranches = `-- name: GetAllBranches :many
|
||||
SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance
|
||||
FROM branch_details
|
||||
WHERE (
|
||||
company_id = $1
|
||||
OR $1 IS NULL
|
||||
)
|
||||
AND (
|
||||
is_active = $2
|
||||
OR $2 IS NULL
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllBranches(ctx context.Context) ([]BranchDetail, error) {
|
||||
rows, err := q.db.Query(ctx, GetAllBranches)
|
||||
type GetAllBranchesParams struct {
|
||||
CompanyID pgtype.Int8 `json:"company_id"`
|
||||
IsActive pgtype.Bool `json:"is_active"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAllBranches(ctx context.Context, arg GetAllBranchesParams) ([]BranchDetail, error) {
|
||||
rows, err := q.db.Query(ctx, GetAllBranches, arg.CompanyID, arg.IsActive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -421,12 +421,13 @@ type VirtualGame struct {
|
|||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Category string `json:"category"`
|
||||
Category pgtype.Text `json:"category"`
|
||||
MinBet pgtype.Numeric `json:"min_bet"`
|
||||
MaxBet pgtype.Numeric `json:"max_bet"`
|
||||
Volatility string `json:"volatility"`
|
||||
Volatility pgtype.Text `json:"volatility"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Rtp pgtype.Numeric `json:"rtp"`
|
||||
IsFeatured pgtype.Bool `json:"is_featured"`
|
||||
IsFeatured bool `json:"is_featured"`
|
||||
PopularityScore pgtype.Int4 `json:"popularity_score"`
|
||||
ThumbnailUrl pgtype.Text `json:"thumbnail_url"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
|
|
@ -483,7 +484,7 @@ type WalletTransfer struct {
|
|||
ID int64 `json:"id"`
|
||||
Amount int64 `json:"amount"`
|
||||
Type string `json:"type"`
|
||||
ReceiverWalletID int64 `json:"receiver_wallet_id"`
|
||||
ReceiverWalletID pgtype.Int8 `json:"receiver_wallet_id"`
|
||||
SenderWalletID pgtype.Int8 `json:"sender_wallet_id"`
|
||||
CashierID pgtype.Int8 `json:"cashier_id"`
|
||||
Verified bool `json:"verified"`
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ RETURNING id, amount, type, receiver_wallet_id, sender_wallet_id, cashier_id, ve
|
|||
type CreateTransferParams struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Type string `json:"type"`
|
||||
ReceiverWalletID int64 `json:"receiver_wallet_id"`
|
||||
ReceiverWalletID pgtype.Int8 `json:"receiver_wallet_id"`
|
||||
SenderWalletID pgtype.Int8 `json:"sender_wallet_id"`
|
||||
CashierID pgtype.Int8 `json:"cashier_id"`
|
||||
Verified bool `json:"verified"`
|
||||
|
|
@ -159,7 +159,7 @@ WHERE receiver_wallet_id = $1
|
|||
OR sender_wallet_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID int64) ([]WalletTransfer, error) {
|
||||
func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID pgtype.Int8) ([]WalletTransfer, error) {
|
||||
rows, err := q.db.Query(ctx, GetTransfersByWallet, receiverWalletID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ type BetFilter struct {
|
|||
BranchID ValidInt64 // Can Be Nullable
|
||||
CompanyID ValidInt64 // Can Be Nullable
|
||||
UserID ValidInt64 // Can Be Nullable
|
||||
IsShopBet ValidBool
|
||||
}
|
||||
|
||||
type GetBet struct {
|
||||
|
|
@ -173,4 +174,3 @@ func ConvertBet(bet GetBet) BetRes {
|
|||
CreatedAt: bet.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ type Branch struct {
|
|||
IsSelfOwned bool
|
||||
}
|
||||
|
||||
type BranchFilter struct {
|
||||
CompanyID ValidInt64
|
||||
IsSuspended ValidBool
|
||||
}
|
||||
|
||||
type BranchDetail struct {
|
||||
ID int64
|
||||
Name string
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ type DeliveryChannel string
|
|||
const (
|
||||
NotificationTypeCashOutSuccess NotificationType = "cash_out_success"
|
||||
NotificationTypeDepositSuccess NotificationType = "deposit_success"
|
||||
NotificationTypeWithdrawSuccess NotificationType = "withdraw_success"
|
||||
NotificationTypeBetPlaced NotificationType = "bet_placed"
|
||||
NotificationTypeDailyReport NotificationType = "daily_report"
|
||||
NotificationTypeHighLossOnBet NotificationType = "high_loss_on_bet"
|
||||
|
|
@ -23,7 +24,8 @@ const (
|
|||
NotificationTypeSignUpWelcome NotificationType = "signup_welcome"
|
||||
NotificationTypeOTPSent NotificationType = "otp_sent"
|
||||
NOTIFICATION_TYPE_WALLET NotificationType = "wallet_threshold"
|
||||
NOTIFICATION_TYPE_TRANSFER NotificationType = "transfer_failed"
|
||||
NOTIFICATION_TYPE_TRANSFER_FAIL NotificationType = "transfer_failed"
|
||||
NOTIFICATION_TYPE_TRANSFER_SUCCESS NotificationType = "transfer_success"
|
||||
NOTIFICATION_TYPE_ADMIN_ALERT NotificationType = "admin_alert"
|
||||
NOTIFICATION_RECEIVER_ADMIN NotificationRecieverSide = "admin"
|
||||
|
||||
|
|
@ -91,3 +93,18 @@ func FromJSON(data []byte) (*Notification, error) {
|
|||
}
|
||||
return &n, nil
|
||||
}
|
||||
|
||||
func ReceiverFromRole(role Role) NotificationRecieverSide {
|
||||
|
||||
if role == RoleAdmin {
|
||||
return NotificationRecieverSideAdmin
|
||||
} else if role == RoleCashier {
|
||||
return NotificationRecieverSideCashier
|
||||
} else if role == RoleBranchManager {
|
||||
return NotificationRecieverSideBranchManager
|
||||
} else if role == RoleCustomer {
|
||||
return NotificationRecieverSideCustomer
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ const (
|
|||
|
||||
type PaymentMethod string
|
||||
|
||||
// Info on why the wallet was modified
|
||||
// If its internal system modification then, its always direct
|
||||
const (
|
||||
TRANSFER_DIRECT PaymentMethod = "direct"
|
||||
TRANSFER_CASH PaymentMethod = "cash"
|
||||
TRANSFER_BANK PaymentMethod = "bank"
|
||||
TRANSFER_CHAPA PaymentMethod = "chapa"
|
||||
|
|
@ -22,16 +25,21 @@ const (
|
|||
TRANSFER_OTHER PaymentMethod = "other"
|
||||
)
|
||||
|
||||
// There is always a receiving wallet id
|
||||
// There is a sender wallet id only if wallet transfer type
|
||||
// Info for the payment providers
|
||||
type PaymentDetails struct {
|
||||
ReferenceNumber ValidString
|
||||
BankNumber ValidString
|
||||
}
|
||||
|
||||
// A Transfer is logged for every modification of ALL wallets and wallet types
|
||||
type Transfer struct {
|
||||
ID int64
|
||||
Amount Currency
|
||||
Verified bool
|
||||
Type TransferType
|
||||
PaymentMethod PaymentMethod
|
||||
ReceiverWalletID int64
|
||||
SenderWalletID int64
|
||||
ReceiverWalletID ValidInt64
|
||||
SenderWalletID ValidInt64
|
||||
ReferenceNumber string
|
||||
CashierID ValidInt64
|
||||
CreatedAt time.Time
|
||||
|
|
@ -42,8 +50,8 @@ type CreateTransfer struct {
|
|||
Amount Currency
|
||||
Verified bool
|
||||
ReferenceNumber string
|
||||
ReceiverWalletID int64
|
||||
SenderWalletID int64
|
||||
ReceiverWalletID ValidInt64
|
||||
SenderWalletID ValidInt64
|
||||
CashierID ValidInt64
|
||||
Type TransferType
|
||||
PaymentMethod PaymentMethod
|
||||
|
|
|
|||
|
|
@ -57,3 +57,11 @@ type CreateCustomerWallet struct {
|
|||
RegularWalletID int64
|
||||
StaticWalletID int64
|
||||
}
|
||||
|
||||
type WalletType string
|
||||
|
||||
const (
|
||||
CustomerWalletType WalletType = "customer_wallet"
|
||||
BranchWalletType WalletType = "branch_wallet"
|
||||
CompanyWalletType WalletType = "company_wallet"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
func InitLogger() (*zap.Logger, error) {
|
||||
mongoCore, err := NewMongoCore(
|
||||
"mongodb://root:secret@mongo:27017/?authSource=admin",
|
||||
"mongodb://root:secret@localhost:27017/?authSource=admin",
|
||||
"logdb",
|
||||
"applogs",
|
||||
zapcore.InfoLevel,
|
||||
|
|
|
|||
|
|
@ -209,6 +209,10 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma
|
|||
Int64: filter.UserID.Value,
|
||||
Valid: filter.UserID.Valid,
|
||||
},
|
||||
IsShopBet: pgtype.Bool{
|
||||
Bool: filter.IsShopBet.Value,
|
||||
Valid: filter.IsShopBet.Valid,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
domain.MongoDBLogger.Error("failed to get all bets",
|
||||
|
|
|
|||
|
|
@ -128,8 +128,13 @@ func (s *Store) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]do
|
|||
return branches, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error) {
|
||||
dbBranches, err := s.queries.GetAllBranches(ctx)
|
||||
func (s *Store) GetAllBranches(ctx context.Context, filter domain.BranchFilter) ([]domain.BranchDetail, error) {
|
||||
dbBranches, err := s.queries.GetAllBranches(ctx, dbgen.GetAllBranchesParams{
|
||||
CompanyID: pgtype.Int8{
|
||||
Int64: filter.CompanyID.Value,
|
||||
Valid: filter.CompanyID.Valid,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,14 @@ func convertDBTransfer(transfer dbgen.WalletTransfer) domain.Transfer {
|
|||
Amount: domain.Currency(transfer.Amount),
|
||||
Type: domain.TransferType(transfer.Type),
|
||||
Verified: transfer.Verified,
|
||||
ReceiverWalletID: transfer.ReceiverWalletID,
|
||||
SenderWalletID: transfer.SenderWalletID.Int64,
|
||||
ReceiverWalletID: domain.ValidInt64{
|
||||
Value: transfer.ReceiverWalletID.Int64,
|
||||
Valid: transfer.ReceiverWalletID.Valid,
|
||||
},
|
||||
SenderWalletID: domain.ValidInt64{
|
||||
Value: transfer.SenderWalletID.Int64,
|
||||
Valid: transfer.SenderWalletID.Valid,
|
||||
},
|
||||
CashierID: domain.ValidInt64{
|
||||
Value: transfer.CashierID.Int64,
|
||||
Valid: transfer.CashierID.Valid,
|
||||
|
|
@ -28,10 +34,13 @@ func convertCreateTransfer(transfer domain.CreateTransfer) dbgen.CreateTransferP
|
|||
return dbgen.CreateTransferParams{
|
||||
Amount: int64(transfer.Amount),
|
||||
Type: string(transfer.Type),
|
||||
ReceiverWalletID: transfer.ReceiverWalletID,
|
||||
ReceiverWalletID: pgtype.Int8{
|
||||
Int64: transfer.ReceiverWalletID.Value,
|
||||
Valid: transfer.ReceiverWalletID.Valid,
|
||||
},
|
||||
SenderWalletID: pgtype.Int8{
|
||||
Int64: transfer.SenderWalletID,
|
||||
Valid: true,
|
||||
Int64: transfer.SenderWalletID.Value,
|
||||
Valid: transfer.SenderWalletID.Valid,
|
||||
},
|
||||
CashierID: pgtype.Int8{
|
||||
Int64: transfer.CashierID.Value,
|
||||
|
|
@ -62,7 +71,10 @@ func (s *Store) GetAllTransfers(ctx context.Context) ([]domain.Transfer, error)
|
|||
return result, nil
|
||||
}
|
||||
func (s *Store) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.Transfer, error) {
|
||||
transfers, err := s.queries.GetTransfersByWallet(ctx, walletID)
|
||||
transfers, err := s.queries.GetTransfersByWallet(ctx, pgtype.Int8{
|
||||
Int64: walletID,
|
||||
Valid: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -241,7 +241,11 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
|
|||
}
|
||||
|
||||
deductedAmount := req.Amount / 10
|
||||
err = s.walletSvc.DeductFromWallet(ctx, branch.WalletID, domain.ToCurrency(deductedAmount))
|
||||
_, err = s.walletSvc.DeductFromWallet(ctx,
|
||||
branch.WalletID, domain.ToCurrency(deductedAmount), domain.BranchWalletType, domain.ValidInt64{
|
||||
Value: userID,
|
||||
Valid: true,
|
||||
}, domain.TRANSFER_DIRECT)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("failed to deduct from wallet",
|
||||
zap.Int64("wallet_id", branch.WalletID),
|
||||
|
|
@ -274,7 +278,10 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
|
|||
}
|
||||
|
||||
deductedAmount := req.Amount / 10
|
||||
err = s.walletSvc.DeductFromWallet(ctx, branch.WalletID, domain.ToCurrency(deductedAmount))
|
||||
_, err = s.walletSvc.DeductFromWallet(ctx, branch.WalletID, domain.ToCurrency(deductedAmount), domain.BranchWalletType, domain.ValidInt64{
|
||||
Value: userID,
|
||||
Valid: true,
|
||||
}, domain.TRANSFER_DIRECT)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("wallet deduction failed",
|
||||
zap.Int64("wallet_id", branch.WalletID),
|
||||
|
|
@ -300,7 +307,8 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
|
|||
}
|
||||
|
||||
userWallet := wallets[0]
|
||||
err = s.walletSvc.DeductFromWallet(ctx, userWallet.ID, domain.ToCurrency(req.Amount))
|
||||
_, err = s.walletSvc.DeductFromWallet(ctx, userWallet.ID,
|
||||
domain.ToCurrency(req.Amount), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("wallet deduction failed for customer",
|
||||
zap.Int64("wallet_id", userWallet.ID),
|
||||
|
|
@ -676,7 +684,8 @@ func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.Outc
|
|||
amount = bet.Amount
|
||||
}
|
||||
|
||||
err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount)
|
||||
_, err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
|
||||
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("failed to add winnings to wallet",
|
||||
zap.Int64("wallet_id", customerWallet.RegularID),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ type BranchStore interface {
|
|||
GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error)
|
||||
GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error)
|
||||
GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error)
|
||||
GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error)
|
||||
GetAllBranches(ctx context.Context, filter domain.BranchFilter) ([]domain.BranchDetail, error)
|
||||
SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error)
|
||||
UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error)
|
||||
DeleteBranch(ctx context.Context, id int64) error
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package branch
|
||||
package branch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -42,8 +42,8 @@ func (s *Service) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]
|
|||
func (s *Service) GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) {
|
||||
return s.branchStore.GetBranchOperations(ctx, branchID)
|
||||
}
|
||||
func (s *Service) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error) {
|
||||
return s.branchStore.GetAllBranches(ctx)
|
||||
func (s *Service) GetAllBranches(ctx context.Context, filter domain.BranchFilter) ([]domain.BranchDetail, error) {
|
||||
return s.branchStore.GetAllBranches(ctx, filter)
|
||||
}
|
||||
|
||||
func (s *Service) GetBranchByCashier(ctx context.Context, userID int64) (domain.Branch, error) {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,10 @@ func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount doma
|
|||
PaymentMethod: domain.TRANSFER_CHAPA,
|
||||
ReferenceNumber: reference,
|
||||
// ReceiverWalletID: 1,
|
||||
SenderWalletID: senderWallet.ID,
|
||||
SenderWalletID: domain.ValidInt64{
|
||||
Value: senderWallet.ID,
|
||||
Valid: true,
|
||||
},
|
||||
Verified: false,
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +122,11 @@ func (s *Service) VerifyDeposit(ctx context.Context, reference string) error {
|
|||
return ErrPaymentNotFound
|
||||
}
|
||||
|
||||
// just making sure that the sender id is valid
|
||||
if !payment.SenderWalletID.Valid {
|
||||
return fmt.Errorf("sender wallet is invalid %v \n", payment.SenderWalletID)
|
||||
}
|
||||
|
||||
// Skip if already completed
|
||||
if payment.Verified {
|
||||
return nil
|
||||
|
|
@ -137,7 +145,7 @@ func (s *Service) VerifyDeposit(ctx context.Context, reference string) error {
|
|||
|
||||
// If payment is completed, credit user's wallet
|
||||
if verification.Status == domain.PaymentStatusCompleted {
|
||||
if err := s.walletStore.UpdateBalance(ctx, payment.SenderWalletID, payment.Amount); err != nil {
|
||||
if err := s.walletStore.UpdateBalance(ctx, payment.SenderWalletID.Value, payment.Amount); err != nil {
|
||||
return fmt.Errorf("failed to credit user wallet: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +164,11 @@ func (s *Service) ManualVerifyPayment(ctx context.Context, txRef string) (*domai
|
|||
}, nil
|
||||
}
|
||||
|
||||
// just making sure that the sender id is valid
|
||||
if !transfer.SenderWalletID.Valid {
|
||||
return nil, fmt.Errorf("sender wallet id is invalid: %v \n", transfer.SenderWalletID)
|
||||
}
|
||||
|
||||
// If not verified or not found, verify with Chapa
|
||||
verification, err := s.chapaClient.VerifyPayment(ctx, txRef)
|
||||
if err != nil {
|
||||
|
|
@ -170,7 +183,7 @@ func (s *Service) ManualVerifyPayment(ctx context.Context, txRef string) (*domai
|
|||
}
|
||||
|
||||
// Credit user's wallet
|
||||
err = s.walletStore.UpdateBalance(ctx, transfer.SenderWalletID, transfer.Amount)
|
||||
err = s.walletStore.UpdateBalance(ctx, transfer.SenderWalletID.Value, transfer.Amount)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update wallet balance: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ func (s *Service) ProcessReferral(ctx context.Context, referredPhone, referralCo
|
|||
|
||||
walletID := wallets[0].ID
|
||||
currentBonus := float64(wallets[0].Balance)
|
||||
err = s.walletSvc.AddToWallet(ctx, walletID, domain.Currency(int64((currentBonus+referral.RewardAmount)*100)))
|
||||
_, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBonus+referral.RewardAmount)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to add referral reward to wallet", "walletID", walletID, "referrerID", referrerID, "error", err)
|
||||
return err
|
||||
|
|
@ -162,7 +162,7 @@ func (s *Service) ProcessDepositBonus(ctx context.Context, userPhone string, amo
|
|||
walletID := wallets[0].ID
|
||||
bonus := amount * (settings.CashbackPercentage / 100)
|
||||
currentBonus := float64(wallets[0].Balance)
|
||||
err = s.walletSvc.AddToWallet(ctx, walletID, domain.Currency(int64((currentBonus+bonus)*100)))
|
||||
_, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBonus+bonus)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to add deposit bonus to wallet", "walletID", walletID, "userID", userID, "bonus", bonus, "error", err)
|
||||
return err
|
||||
|
|
@ -216,7 +216,7 @@ func (s *Service) ProcessBetReferral(ctx context.Context, userPhone string, betA
|
|||
|
||||
walletID := wallets[0].ID
|
||||
currentBalance := float64(wallets[0].Balance)
|
||||
err = s.walletSvc.AddToWallet(ctx, walletID, domain.Currency(int64((currentBalance+bonus)*100)))
|
||||
_, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBalance+bonus)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to add bet referral bonus to wallet", "walletID", walletID, "referrerID", referrerID, "bonus", bonus, "error", err)
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ func (s *AleaPlayService) processTransaction(ctx context.Context, tx *domain.Vir
|
|||
}
|
||||
tx.WalletID = wallets[0].ID
|
||||
|
||||
if err := s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount)); err != nil {
|
||||
if _, err := s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil {
|
||||
return fmt.Errorf("wallet update failed: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ func (s *service) HandleCallback(ctx context.Context, callback *domain.PopOKCall
|
|||
return errors.New("unknown transaction type")
|
||||
}
|
||||
|
||||
err = s.walletSvc.AddToWallet(ctx, walletID, domain.Currency(amount))
|
||||
_, err = s.walletSvc.AddToWallet(ctx, walletID, domain.Currency(amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update wallet", "walletID", walletID, "userID", session.UserID, "amount", amount, "error", err)
|
||||
return err
|
||||
|
|
@ -184,7 +184,7 @@ func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (
|
|||
return &domain.PopOKBetResponse{}, fmt.Errorf("Failed to read user wallets")
|
||||
}
|
||||
|
||||
if err := s.walletSvc.DeductFromWallet(ctx, claims.UserID, domain.Currency(amountCents)); err != nil {
|
||||
if _, err := s.walletSvc.DeductFromWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT); err != nil {
|
||||
return nil, fmt.Errorf("insufficient balance")
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +245,7 @@ func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (
|
|||
|
||||
// 4. Credit to wallet
|
||||
|
||||
if err := s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents)); err != nil {
|
||||
if _, err := s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil {
|
||||
s.logger.Error("Failed to credit wallet", "userID", claims.UserID, "error", err)
|
||||
return nil, fmt.Errorf("wallet credit failed")
|
||||
}
|
||||
|
|
@ -316,7 +316,7 @@ func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequ
|
|||
// 5. Refund the bet amount (absolute value since bet amount is negative)
|
||||
refundAmount := -originalBet.Amount
|
||||
|
||||
if err := s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(refundAmount)); err != nil {
|
||||
if _, err := s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(refundAmount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil {
|
||||
s.logger.Error("Failed to refund bet", "userID", claims.UserID, "error", err)
|
||||
return nil, fmt.Errorf("refund failed")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ func (s *VeliPlayService) processTransaction(ctx context.Context, tx *domain.Vir
|
|||
}
|
||||
tx.WalletID = wallets[0].ID
|
||||
|
||||
if err := s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount)); err != nil {
|
||||
if _, err := s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil {
|
||||
return fmt.Errorf("wallet update failed: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,85 +14,7 @@ var (
|
|||
)
|
||||
|
||||
func (s *Service) CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) {
|
||||
senderWallet, err := s.walletStore.GetWalletByID(ctx, transfer.SenderWalletID)
|
||||
receiverWallet, err := s.walletStore.GetWalletByID(ctx, transfer.ReceiverWalletID)
|
||||
if err != nil {
|
||||
return domain.Transfer{}, fmt.Errorf("failed to get sender wallet: %w", err)
|
||||
}
|
||||
|
||||
// Check if wallet has sufficient balance
|
||||
if senderWallet.Balance < transfer.Amount || senderWallet.Balance == 0 {
|
||||
// Send notification to customer
|
||||
customerNotification := &domain.Notification{
|
||||
RecipientID: receiverWallet.UserID,
|
||||
Type: domain.NOTIFICATION_TYPE_TRANSFER,
|
||||
Level: domain.NotificationLevelError,
|
||||
Reciever: domain.NotificationRecieverSideCustomer,
|
||||
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "Service Temporarily Unavailable",
|
||||
Message: "Our payment system is currently under maintenance. Please try again later.",
|
||||
},
|
||||
Priority: 2,
|
||||
Metadata: []byte(fmt.Sprintf(`{
|
||||
"transfer_amount": %d,
|
||||
"current_balance": %d,
|
||||
"wallet_id": %d,
|
||||
"notification_type": "customer_facing"
|
||||
}`, transfer.Amount, senderWallet.Balance, transfer.SenderWalletID)),
|
||||
}
|
||||
|
||||
// Send notification to admin team
|
||||
adminNotification := &domain.Notification{
|
||||
RecipientID: senderWallet.UserID,
|
||||
Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT,
|
||||
Level: domain.NotificationLevelError,
|
||||
Reciever: domain.NotificationRecieverSideAdmin,
|
||||
DeliveryChannel: domain.DeliveryChannelEmail, // Or any preferred admin channel
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "CREDIT WARNING: System Running Out of Funds",
|
||||
Message: fmt.Sprintf(
|
||||
"Wallet ID %d has insufficient balance for transfer. Current balance: %.2f, Attempted transfer: %.2f",
|
||||
transfer.SenderWalletID,
|
||||
float64(senderWallet.Balance)/100,
|
||||
float64(transfer.Amount)/100,
|
||||
),
|
||||
},
|
||||
Priority: 1, // High priority for admin alerts
|
||||
Metadata: fmt.Appendf(nil, `{
|
||||
"wallet_id": %d,
|
||||
"balance": %d,
|
||||
"required_amount": %d,
|
||||
"notification_type": "admin_alert"
|
||||
}`, transfer.SenderWalletID, senderWallet.Balance, transfer.Amount),
|
||||
}
|
||||
|
||||
// Send both notifications
|
||||
if err := s.notificationStore.SendNotification(ctx, customerNotification); err != nil {
|
||||
s.logger.Error("failed to send customer notification",
|
||||
"user_id", "",
|
||||
"error", err)
|
||||
}
|
||||
|
||||
// Get admin recipients and send to all
|
||||
adminRecipients, err := s.notificationStore.ListRecipientIDs(ctx, domain.NotificationRecieverSideAdmin)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get admin recipients", "error", err)
|
||||
} else {
|
||||
for _, adminID := range adminRecipients {
|
||||
adminNotification.RecipientID = adminID
|
||||
if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
|
||||
s.logger.Error("failed to send admin notification",
|
||||
"admin_id", adminID,
|
||||
"error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return domain.Transfer{}, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
// Proceed with transfer if balance is sufficient
|
||||
// This is just a transfer log when
|
||||
return s.transferStore.CreateTransfer(ctx, transfer)
|
||||
}
|
||||
|
||||
|
|
@ -116,43 +38,12 @@ func (s *Service) UpdateTransferVerification(ctx context.Context, id int64, veri
|
|||
return s.transferStore.UpdateTransferVerification(ctx, id, verified)
|
||||
}
|
||||
|
||||
func (s *Service) RefillWallet(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) {
|
||||
receiverWallet, err := s.GetWalletByID(ctx, transfer.ReceiverWalletID)
|
||||
if err != nil {
|
||||
return domain.Transfer{}, err
|
||||
}
|
||||
|
||||
// Add to receiver
|
||||
senderWallet, err := s.GetWalletByID(ctx, transfer.SenderWalletID)
|
||||
if err != nil {
|
||||
return domain.Transfer{}, err
|
||||
} else if senderWallet.Balance < transfer.Amount {
|
||||
return domain.Transfer{}, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
err = s.walletStore.UpdateBalance(ctx, receiverWallet.ID, receiverWallet.Balance+transfer.Amount)
|
||||
if err != nil {
|
||||
return domain.Transfer{}, err
|
||||
}
|
||||
|
||||
// Log the transfer so that if there is a mistake, it can be reverted
|
||||
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
||||
CashierID: transfer.CashierID,
|
||||
ReceiverWalletID: receiverWallet.ID,
|
||||
Amount: transfer.Amount,
|
||||
Type: domain.DEPOSIT,
|
||||
PaymentMethod: transfer.PaymentMethod,
|
||||
Verified: true,
|
||||
})
|
||||
if err != nil {
|
||||
return domain.Transfer{}, err
|
||||
}
|
||||
|
||||
return newTransfer, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiverID int64, amount domain.Currency, paymentMethod domain.PaymentMethod, cashierID domain.ValidInt64) (domain.Transfer, error) {
|
||||
func (s *Service) TransferToWallet(ctx context.Context,
|
||||
senderID int64, receiverID int64,
|
||||
amount domain.Currency, paymentMethod domain.PaymentMethod,
|
||||
cashierID domain.ValidInt64) (domain.Transfer, error) {
|
||||
|
||||
senderWallet, err := s.GetWalletByID(ctx, senderID)
|
||||
if err != nil {
|
||||
|
|
@ -191,10 +82,16 @@ func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiver
|
|||
}
|
||||
|
||||
// Log the transfer so that if there is a mistake, it can be reverted
|
||||
transfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
||||
SenderWalletID: senderID,
|
||||
transfer, err := s.CreateTransfer(ctx, domain.CreateTransfer{
|
||||
SenderWalletID: domain.ValidInt64{
|
||||
Value: senderID,
|
||||
Valid: true,
|
||||
},
|
||||
ReceiverWalletID: domain.ValidInt64{
|
||||
Value: receiverID,
|
||||
Valid: true,
|
||||
},
|
||||
CashierID: cashierID,
|
||||
ReceiverWalletID: receiverID,
|
||||
Amount: amount,
|
||||
Type: domain.WALLET,
|
||||
PaymentMethod: paymentMethod,
|
||||
|
|
@ -206,3 +103,66 @@ func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiver
|
|||
|
||||
return transfer, nil
|
||||
}
|
||||
|
||||
func (s *Service) SendTransferNotification(ctx context.Context, senderWallet domain.Wallet, receiverWallet domain.Wallet,
|
||||
senderRole domain.Role, receiverRole domain.Role, amount domain.Currency) error {
|
||||
// Send notification to sender (this could be any role) that money was transferred
|
||||
senderWalletReceiverSide := domain.ReceiverFromRole(senderRole)
|
||||
|
||||
senderNotify := &domain.Notification{
|
||||
RecipientID: senderWallet.UserID,
|
||||
Type: domain.NOTIFICATION_TYPE_TRANSFER_SUCCESS,
|
||||
Level: domain.NotificationLevelSuccess,
|
||||
Reciever: senderWalletReceiverSide,
|
||||
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "Wallet has been deducted",
|
||||
Message: fmt.Sprintf(`ETB %d has been transferred from your wallet`),
|
||||
},
|
||||
Priority: 2,
|
||||
Metadata: []byte(fmt.Sprintf(`{
|
||||
"transfer_amount": %d,
|
||||
"current_balance": %d,
|
||||
"wallet_id": %d,
|
||||
"notification_type": "customer_facing"
|
||||
}`, amount, senderWallet.Balance, senderWallet.ID)),
|
||||
}
|
||||
|
||||
// Sender notifications
|
||||
if err := s.notificationStore.SendNotification(ctx, senderNotify); err != nil {
|
||||
s.logger.Error("failed to send sender notification",
|
||||
"user_id", "",
|
||||
"error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
receiverWalletReceiverSide := domain.ReceiverFromRole(receiverRole)
|
||||
|
||||
receiverNotify := &domain.Notification{
|
||||
RecipientID: receiverWallet.UserID,
|
||||
Type: domain.NOTIFICATION_TYPE_TRANSFER_SUCCESS,
|
||||
Level: domain.NotificationLevelSuccess,
|
||||
Reciever: receiverWalletReceiverSide,
|
||||
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "Wallet has been credited",
|
||||
Message: fmt.Sprintf(`ETB %d has been transferred to your wallet`),
|
||||
},
|
||||
Priority: 2,
|
||||
Metadata: []byte(fmt.Sprintf(`{
|
||||
"transfer_amount": %d,
|
||||
"current_balance": %d,
|
||||
"wallet_id": %d,
|
||||
"notification_type": "customer_facing"
|
||||
}`, amount, receiverWallet.Balance, receiverWallet.ID)),
|
||||
}
|
||||
// Sender notifications
|
||||
if err := s.notificationStore.SendNotification(ctx, receiverNotify); err != nil {
|
||||
s.logger.Error("failed to send sender notification",
|
||||
"user_id", "",
|
||||
"error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package wallet
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
|
@ -68,28 +69,152 @@ func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Cu
|
|||
return s.walletStore.UpdateBalance(ctx, id, balance)
|
||||
}
|
||||
|
||||
func (s *Service) AddToWallet(ctx context.Context, id int64, amount domain.Currency) error {
|
||||
func (s *Service) AddToWallet(
|
||||
ctx context.Context, id int64, amount domain.Currency, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod, paymentDetails domain.PaymentDetails) (domain.Transfer, error) {
|
||||
wallet, err := s.GetWalletByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
return domain.Transfer{}, err
|
||||
}
|
||||
|
||||
return s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount)
|
||||
err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount)
|
||||
if err != nil {
|
||||
return domain.Transfer{}, err
|
||||
}
|
||||
|
||||
// Log the transfer here for reference
|
||||
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
||||
Amount: amount,
|
||||
Verified: true,
|
||||
ReceiverWalletID: domain.ValidInt64{
|
||||
Value: wallet.ID,
|
||||
Valid: true,
|
||||
},
|
||||
CashierID: cashierID,
|
||||
PaymentMethod: paymentMethod,
|
||||
Type: domain.DEPOSIT,
|
||||
ReferenceNumber: paymentDetails.ReferenceNumber.Value,
|
||||
})
|
||||
|
||||
return newTransfer, err
|
||||
}
|
||||
|
||||
func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.Currency) error {
|
||||
func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.Currency, walletType domain.WalletType, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod) (domain.Transfer, error) {
|
||||
wallet, err := s.GetWalletByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
return domain.Transfer{}, err
|
||||
}
|
||||
|
||||
if wallet.Balance < amount {
|
||||
return ErrBalanceInsufficient
|
||||
// Send Wallet low to admin
|
||||
if walletType == domain.CompanyWalletType || walletType == domain.BranchWalletType {
|
||||
s.SendAdminWalletLowNotification(ctx, wallet, amount)
|
||||
}
|
||||
return domain.Transfer{}, ErrBalanceInsufficient
|
||||
}
|
||||
|
||||
return s.walletStore.UpdateBalance(ctx, id, wallet.Balance-amount)
|
||||
err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance-amount)
|
||||
|
||||
if err != nil {
|
||||
return domain.Transfer{}, nil
|
||||
}
|
||||
|
||||
// Log the transfer here for reference
|
||||
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
||||
Amount: amount,
|
||||
Verified: true,
|
||||
SenderWalletID: domain.ValidInt64{
|
||||
Value: wallet.ID,
|
||||
Valid: true,
|
||||
},
|
||||
CashierID: cashierID,
|
||||
PaymentMethod: paymentMethod,
|
||||
Type: domain.WITHDRAW,
|
||||
ReferenceNumber: "",
|
||||
})
|
||||
|
||||
return newTransfer, err
|
||||
}
|
||||
|
||||
// Directly Refilling wallet without
|
||||
// func (s *Service) RefillWallet(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) {
|
||||
// receiverWallet, err := s.GetWalletByID(ctx, transfer.ReceiverWalletID)
|
||||
// if err != nil {
|
||||
// return domain.Transfer{}, err
|
||||
// }
|
||||
|
||||
// // Add to receiver
|
||||
// senderWallet, err := s.GetWalletByID(ctx, transfer.SenderWalletID)
|
||||
// if err != nil {
|
||||
// return domain.Transfer{}, err
|
||||
// } else if senderWallet.Balance < transfer.Amount {
|
||||
// return domain.Transfer{}, ErrInsufficientBalance
|
||||
// }
|
||||
|
||||
// err = s.walletStore.UpdateBalance(ctx, receiverWallet.ID, receiverWallet.Balance+transfer.Amount)
|
||||
// if err != nil {
|
||||
// return domain.Transfer{}, err
|
||||
// }
|
||||
|
||||
// // Log the transfer so that if there is a mistake, it can be reverted
|
||||
// newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
|
||||
// CashierID: transfer.CashierID,
|
||||
// ReceiverWalletID: transfer.ReceiverWalletID,
|
||||
// Amount: transfer.Amount,
|
||||
// Type: domain.DEPOSIT,
|
||||
// PaymentMethod: transfer.PaymentMethod,
|
||||
// Verified: true,
|
||||
// })
|
||||
// if err != nil {
|
||||
// return domain.Transfer{}, err
|
||||
// }
|
||||
|
||||
// return newTransfer, nil
|
||||
// }
|
||||
|
||||
func (s *Service) UpdateWalletActive(ctx context.Context, id int64, isActive bool) error {
|
||||
return s.walletStore.UpdateWalletActive(ctx, id, isActive)
|
||||
}
|
||||
|
||||
func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet, amount domain.Currency) error {
|
||||
// Send notification to admin team
|
||||
adminNotification := &domain.Notification{
|
||||
RecipientID: adminWallet.UserID,
|
||||
Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT,
|
||||
Level: domain.NotificationLevelError,
|
||||
Reciever: domain.NotificationRecieverSideAdmin,
|
||||
DeliveryChannel: domain.DeliveryChannelEmail, // Or any preferred admin channel
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "CREDIT WARNING: System Running Out of Funds",
|
||||
Message: fmt.Sprintf(
|
||||
"Wallet ID %d has insufficient balance for transfer. Current balance: %.2f, Attempted transfer: %.2f",
|
||||
adminWallet.ID,
|
||||
adminWallet.Balance.Float32(),
|
||||
amount.Float32(),
|
||||
),
|
||||
},
|
||||
Priority: 1, // High priority for admin alerts
|
||||
Metadata: fmt.Appendf(nil, `{
|
||||
"wallet_id": %d,
|
||||
"balance": %d,
|
||||
"required_amount": %d,
|
||||
"notification_type": "admin_alert"
|
||||
}`, adminWallet.ID, adminWallet.Balance, amount),
|
||||
}
|
||||
|
||||
// Get admin recipients and send to all
|
||||
adminRecipients, err := s.notificationStore.ListRecipientIDs(ctx, domain.NotificationRecieverSideAdmin)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get admin recipients", "error", err)
|
||||
return err
|
||||
} else {
|
||||
for _, adminID := range adminRecipients {
|
||||
adminNotification.RecipientID = adminID
|
||||
if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
|
||||
s.logger.Error("failed to send admin notification",
|
||||
"admin_id", adminID,
|
||||
"error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,30 +62,30 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
|
|||
// }
|
||||
// },
|
||||
// },
|
||||
{
|
||||
spec: "0 */5 * * * *", // Every 5 Minutes
|
||||
task: func() {
|
||||
log.Println("Updating expired events status...")
|
||||
// {
|
||||
// spec: "0 */5 * * * *", // Every 5 Minutes
|
||||
// task: func() {
|
||||
// log.Println("Updating expired events status...")
|
||||
|
||||
if _, err := resultService.CheckAndUpdateExpiredEvents(context.Background()); err != nil {
|
||||
log.Printf("Failed to update events: %v", err)
|
||||
} else {
|
||||
log.Printf("Successfully updated expired events")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
spec: "0 */15 * * * *", // Every 15 Minutes
|
||||
task: func() {
|
||||
log.Println("Fetching results for upcoming events...")
|
||||
// if _, err := resultService.CheckAndUpdateExpiredEvents(context.Background()); err != nil {
|
||||
// log.Printf("Failed to update events: %v", err)
|
||||
// } else {
|
||||
// log.Printf("Successfully updated expired events")
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// spec: "0 */15 * * * *", // Every 15 Minutes
|
||||
// task: func() {
|
||||
// log.Println("Fetching results for upcoming events...")
|
||||
|
||||
if err := resultService.FetchAndProcessResults(context.Background()); err != nil {
|
||||
log.Printf("Failed to process result: %v", err)
|
||||
} else {
|
||||
log.Printf("Successfully processed all outcomes")
|
||||
}
|
||||
},
|
||||
},
|
||||
// if err := resultService.FetchAndProcessResults(context.Background()); err != nil {
|
||||
// log.Printf("Failed to process result: %v", err)
|
||||
// } else {
|
||||
// log.Printf("Successfully processed all outcomes")
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
}
|
||||
|
||||
for _, job := range schedule {
|
||||
|
|
|
|||
|
|
@ -36,15 +36,14 @@ type loginCustomerRes struct {
|
|||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /auth/login [post]
|
||||
func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
|
||||
|
||||
var req loginCustomerReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
h.logger.Error("Failed to parse LoginCustomer request", "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)
|
||||
if _, ok := h.validator.Validate(c, req); !ok {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Request")
|
||||
}
|
||||
|
||||
successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
|
@ -24,7 +23,6 @@ import (
|
|||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /bet [post]
|
||||
func (h *Handler) CreateBet(c *fiber.Ctx) error {
|
||||
fmt.Printf("Calling leagues")
|
||||
// Get user_id from middleware
|
||||
userID := c.Locals("user_id").(int64)
|
||||
role := c.Locals("role").(domain.Role)
|
||||
|
|
@ -161,12 +159,29 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error {
|
|||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /bet [get]
|
||||
func (h *Handler) GetAllBet(c *fiber.Ctx) error {
|
||||
|
||||
// role := c.Locals("role").(domain.Role)
|
||||
companyID := c.Locals("company_id").(domain.ValidInt64)
|
||||
branchID := c.Locals("branch_id").(domain.ValidInt64)
|
||||
|
||||
var isShopBet domain.ValidBool
|
||||
isShopBetQuery := c.Query("is_shop")
|
||||
|
||||
|
||||
if isShopBetQuery != "" {
|
||||
isShopBetParse, err := strconv.ParseBool(isShopBetQuery)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_shop_bet")
|
||||
}
|
||||
isShopBet = domain.ValidBool{
|
||||
Value: isShopBetParse,
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
bets, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{
|
||||
BranchID: branchID,
|
||||
CompanyID: companyID,
|
||||
IsShopBet: isShopBet,
|
||||
})
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to get bets", "error", err)
|
||||
|
|
|
|||
|
|
@ -381,8 +381,23 @@ func (h *Handler) GetBranchByCompanyID(c *fiber.Ctx) error {
|
|||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /branch [get]
|
||||
func (h *Handler) GetAllBranches(c *fiber.Ctx) error {
|
||||
// TODO: Limit the get all branches to only the companies for branch manager and cashiers
|
||||
branches, err := h.branchSvc.GetAllBranches(c.Context())
|
||||
companyID := c.Locals("company_id").(domain.ValidInt64)
|
||||
isActiveParam := c.Params("is_active")
|
||||
isActiveValid := isActiveParam != ""
|
||||
isActive, err := strconv.ParseBool(isActiveParam)
|
||||
|
||||
if isActiveValid && err != nil {
|
||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid is_active param", err, nil)
|
||||
}
|
||||
|
||||
branches, err := h.branchSvc.GetAllBranches(c.Context(),
|
||||
domain.BranchFilter{
|
||||
CompanyID: companyID,
|
||||
IsSuspended: domain.ValidBool{
|
||||
Value: isActive,
|
||||
Valid: isActiveValid,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to get branches", "error", err)
|
||||
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get branches", err, nil)
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
|
|||
role := c.Locals("role").(domain.Role)
|
||||
companyId := c.Locals("company_id").(domain.ValidInt64)
|
||||
|
||||
// Checking to make sure that admin user has a company id in the token
|
||||
if role != domain.RoleSuperAdmin && !companyId.Valid {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID")
|
||||
}
|
||||
|
|
@ -182,32 +183,38 @@ func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
|
|||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /managers/{id} [get]
|
||||
func (h *Handler) GetManagerByID(c *fiber.Ctx) error {
|
||||
// branchId := int64(12) //c.Locals("branch_id").(int64)
|
||||
// filter := user.Filter{
|
||||
// Role: string(domain.RoleUser),
|
||||
// BranchId: user.ValidBranchId{
|
||||
// Value: branchId,
|
||||
// Valid: true,
|
||||
// },
|
||||
// Page: c.QueryInt("page", 1),
|
||||
// PageSize: c.QueryInt("page_size", 10),
|
||||
// }
|
||||
// valErrs, ok := validator.Validate(c, filter)
|
||||
// if !ok {
|
||||
// return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
|
||||
// }
|
||||
role := c.Locals("role").(domain.Role)
|
||||
companyId := c.Locals("company_id").(domain.ValidInt64)
|
||||
requestUserID := c.Locals("user_id").(int64)
|
||||
|
||||
// Only Super Admin / Admin / Branch Manager can view this route
|
||||
if role != domain.RoleSuperAdmin && role != domain.RoleAdmin && role != domain.RoleBranchManager {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Role Unauthorized")
|
||||
}
|
||||
|
||||
if role != domain.RoleSuperAdmin && !companyId.Valid {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID")
|
||||
}
|
||||
|
||||
userIDstr := c.Params("id")
|
||||
userID, err := strconv.ParseInt(userIDstr, 10, 64)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to fetch user using UserID", "error", err)
|
||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid managers ID", nil, nil)
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid managers ID")
|
||||
}
|
||||
|
||||
user, err := h.userSvc.GetUserByID(c.Context(), userID)
|
||||
if err != nil {
|
||||
h.logger.Error("Get User By ID failed", "error", err)
|
||||
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get managers", nil, nil)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get managers")
|
||||
}
|
||||
|
||||
// A Branch Manager can only fetch his own branch info
|
||||
if role == domain.RoleBranchManager && user.ID != requestUserID {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "User Access Not Allowed")
|
||||
}
|
||||
|
||||
// Check that only admin from company can view this route
|
||||
if role != domain.RoleSuperAdmin && user.CompanyID.Value != companyId.Value {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Only company user can view manager info")
|
||||
}
|
||||
|
||||
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
|
||||
|
|
@ -259,7 +266,9 @@ type updateManagerReq struct {
|
|||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /managers/{id} [put]
|
||||
func (h *Handler) UpdateManagers(c *fiber.Ctx) error {
|
||||
|
||||
var req updateManagerReq
|
||||
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
h.logger.Error("UpdateManagers failed", "error", err)
|
||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", nil, nil)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
func GetLogsHandler(appCtx context.Context) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
client, err := mongo.Connect(appCtx, options.Client().ApplyURI("mongodb://root:secret@mongo:27017/?authSource=admin"))
|
||||
client, err := mongo.Connect(appCtx, options.Client().ApplyURI("mongodb://root:secret@localhost:27017/?authSource=admin"))
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "MongoDB connection failed: "+err.Error())
|
||||
}
|
||||
|
|
@ -32,7 +32,6 @@ func GetLogsHandler(appCtx context.Context) fiber.Handler {
|
|||
return fiber.NewError(fiber.StatusInternalServerError, "Cursor decoding error: "+err.Error())
|
||||
}
|
||||
|
||||
|
||||
return c.JSON(logs)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ type TransferWalletRes struct {
|
|||
Verified bool `json:"verified" example:"true"`
|
||||
Type string `json:"type" example:"transfer"`
|
||||
PaymentMethod string `json:"payment_method" example:"bank"`
|
||||
ReceiverWalletID int64 `json:"receiver_wallet_id" example:"1"`
|
||||
ReceiverWalletID *int64 `json:"receiver_wallet_id" example:"1"`
|
||||
SenderWalletID *int64 `json:"sender_wallet_id" example:"1"`
|
||||
CashierID *int64 `json:"cashier_id" example:"789"`
|
||||
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
|
||||
|
|
@ -27,7 +27,7 @@ type RefillRes struct {
|
|||
Verified bool `json:"verified" example:"true"`
|
||||
Type string `json:"type" example:"transfer"`
|
||||
PaymentMethod string `json:"payment_method" example:"bank"`
|
||||
ReceiverWalletID int64 `json:"receiver_wallet_id" example:"1"`
|
||||
ReceiverWalletID *int64 `json:"receiver_wallet_id" example:"1"`
|
||||
SenderWalletID *int64 `json:"sender_wallet_id" example:"1"`
|
||||
CashierID *int64 `json:"cashier_id" example:"789"`
|
||||
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
|
||||
|
|
@ -35,13 +35,20 @@ type RefillRes struct {
|
|||
}
|
||||
|
||||
func convertTransfer(transfer domain.Transfer) TransferWalletRes {
|
||||
var senderWalletID *int64
|
||||
senderWalletID = &transfer.SenderWalletID
|
||||
|
||||
var cashierID *int64
|
||||
if transfer.CashierID.Valid {
|
||||
cashierID = &transfer.CashierID.Value
|
||||
}
|
||||
var receiverID *int64
|
||||
if transfer.ReceiverWalletID.Valid {
|
||||
receiverID = &transfer.ReceiverWalletID.Value
|
||||
}
|
||||
|
||||
var senderId *int64
|
||||
if transfer.SenderWalletID.Valid {
|
||||
senderId = &transfer.SenderWalletID.Value
|
||||
}
|
||||
|
||||
return TransferWalletRes{
|
||||
ID: transfer.ID,
|
||||
|
|
@ -49,8 +56,8 @@ func convertTransfer(transfer domain.Transfer) TransferWalletRes {
|
|||
Verified: transfer.Verified,
|
||||
Type: string(transfer.Type),
|
||||
PaymentMethod: string(transfer.PaymentMethod),
|
||||
ReceiverWalletID: transfer.ReceiverWalletID,
|
||||
SenderWalletID: senderWalletID,
|
||||
ReceiverWalletID: receiverID,
|
||||
SenderWalletID: senderId,
|
||||
CashierID: cashierID,
|
||||
CreatedAt: transfer.CreatedAt,
|
||||
UpdatedAt: transfer.UpdatedAt,
|
||||
|
|
@ -126,16 +133,16 @@ func (h *Handler) TransferToWallet(c *fiber.Ctx) error {
|
|||
}
|
||||
// Get sender ID from the cashier
|
||||
userID := c.Locals("user_id").(int64)
|
||||
role := string(c.Locals("role").(domain.Role))
|
||||
role := c.Locals("role").(domain.Role)
|
||||
companyID := c.Locals("company_id").(int64)
|
||||
|
||||
var senderID int64
|
||||
|
||||
//TODO: check to make sure that the cashiers aren't transferring TO branch wallet
|
||||
if role == string(domain.RoleCustomer) {
|
||||
if role == domain.RoleCustomer {
|
||||
h.logger.Error("Unauthorized access", "userID", userID, "role", role)
|
||||
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
|
||||
} else if role == string(domain.RoleBranchManager) || role == string(domain.RoleAdmin) || role == string(domain.RoleSuperAdmin) {
|
||||
} else if role == domain.RoleBranchManager || role == domain.RoleAdmin || role == domain.RoleSuperAdmin {
|
||||
company, err := h.companySvc.GetCompanyByID(c.Context(), companyID)
|
||||
if err != nil {
|
||||
return response.WriteJSON(c, fiber.StatusInternalServerError, "Error fetching company", err, nil)
|
||||
|
|
@ -190,6 +197,7 @@ func (h *Handler) RefillWallet(c *fiber.Ctx) error {
|
|||
|
||||
receiverIDString := c.Params("id")
|
||||
|
||||
userID := c.Locals("user_id").(int64)
|
||||
receiverID, err := strconv.ParseInt(receiverIDString, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -197,13 +205,6 @@ func (h *Handler) RefillWallet(c *fiber.Ctx) error {
|
|||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
|
||||
}
|
||||
// Get sender ID from the cashier
|
||||
userID := c.Locals("user_id").(int64)
|
||||
role := string(c.Locals("role").(domain.Role))
|
||||
|
||||
if role != string(domain.RoleSuperAdmin) {
|
||||
h.logger.Error("Unauthorized access", "userID", userID, "role", role)
|
||||
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
|
||||
}
|
||||
|
||||
var req CreateRefillReq
|
||||
|
||||
|
|
@ -217,16 +218,11 @@ func (h *Handler) RefillWallet(c *fiber.Ctx) error {
|
|||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
|
||||
}
|
||||
|
||||
transfer, err := h.walletSvc.RefillWallet(c.Context(), domain.CreateTransfer{
|
||||
Amount: domain.ToCurrency(req.Amount),
|
||||
PaymentMethod: domain.TRANSFER_BANK,
|
||||
ReceiverWalletID: receiverID,
|
||||
CashierID: domain.ValidInt64{
|
||||
transfer, err := h.walletSvc.AddToWallet(
|
||||
c.Context(), receiverID, domain.ToCurrency(req.Amount), domain.ValidInt64{
|
||||
Value: userID,
|
||||
Valid: true,
|
||||
},
|
||||
Type: domain.TransferType("deposit"),
|
||||
})
|
||||
}, domain.TRANSFER_BANK, domain.PaymentDetails{})
|
||||
|
||||
if !ok {
|
||||
return response.WriteJSON(c, fiber.StatusInternalServerError, "Creating Transfer Failed", err, nil)
|
||||
|
|
|
|||
|
|
@ -192,7 +192,8 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
// TODO: Remove later
|
||||
err = h.walletSvc.AddToWallet(c.Context(), newWallet.ID, domain.ToCurrency(100.0))
|
||||
_, err = h.walletSvc.AddToWallet(
|
||||
c.Context(), newWallet.ID, domain.ToCurrency(100.0), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
|
||||
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to update wallet for user", "userID", newUser.ID, "error", err)
|
||||
|
|
@ -413,6 +414,7 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
|
|||
return nil
|
||||
}
|
||||
companyID := c.Locals("company_id").(domain.ValidInt64)
|
||||
|
||||
users, err := h.userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString, req.Role, companyID)
|
||||
if err != nil {
|
||||
h.logger.Error("SearchUserByNameOrPhone failed", "error", err)
|
||||
|
|
|
|||
|
|
@ -87,6 +87,22 @@ func (a *App) CompanyOnly(c *fiber.Ctx) error {
|
|||
return c.Next()
|
||||
}
|
||||
|
||||
func (a *App) OnlyAdminAndAbove(c *fiber.Ctx) error {
|
||||
userRole := c.Locals("role").(domain.Role)
|
||||
if userRole != domain.RoleSuperAdmin && userRole != domain.RoleAdmin {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Invalid access token")
|
||||
}
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func (a *App) OnlyBranchManagerAndAbove(c *fiber.Ctx) error {
|
||||
userRole := c.Locals("role").(domain.Role)
|
||||
if userRole != domain.RoleSuperAdmin && userRole != domain.RoleAdmin && userRole != domain.RoleBranchManager {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Invalid access token")
|
||||
}
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func (a *App) WebsocketAuthMiddleware(c *fiber.Ctx) error {
|
||||
tokenStr := c.Query("token")
|
||||
if tokenStr == "" {
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ func (a *App) initAppRoutes() {
|
|||
a.fiber.Get("/wallet/:id", h.GetWalletByID)
|
||||
a.fiber.Put("/wallet/:id", h.UpdateWalletActive)
|
||||
a.fiber.Get("/branchWallet", a.authMiddleware, h.GetAllBranchWallets)
|
||||
a.fiber.Get("/cashierWallet", a.authMiddleware, h.GetWalletForCashier)
|
||||
a.fiber.Get("/cashierWallet", a.authMiddleware, h.GetWalletForCashier)
|
||||
|
||||
// Transfer
|
||||
// /transfer/wallet - transfer from one wallet to another wallet
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
time=2025-06-16T02:21:34.859+03:00 level=INFO msg="Authenticated WebSocket connection" service_info.env=development userID=3
|
||||
time=2025-06-16T02:23:59.721+03:00 level=INFO msg="Starting server" service_info.env=development port=8080
|
||||
Loading…
Reference in New Issue
Block a user