Merge branch 'main' into ticket-bet
This commit is contained in:
commit
d6abaac828
29
cmd/main.go
29
cmd/main.go
|
|
@ -35,6 +35,8 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
||||||
|
issuereporting "github.com/SamuelTariku/FortuneBet-Backend/internal/services/issue_reporting"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
|
||||||
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
|
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
|
|
@ -164,6 +166,8 @@ func main() {
|
||||||
|
|
||||||
go httpserver.SetupReportCronJobs(context.Background(), reportSvc)
|
go httpserver.SetupReportCronJobs(context.Background(), reportSvc)
|
||||||
|
|
||||||
|
bankRepository := repository.NewBankRepository(store)
|
||||||
|
instSvc := institutions.New(bankRepository)
|
||||||
// Initialize report worker with CSV exporter
|
// Initialize report worker with CSV exporter
|
||||||
// csvExporter := infrastructure.CSVExporter{
|
// csvExporter := infrastructure.CSVExporter{
|
||||||
// ExportPath: cfg.ReportExportPath, // Make sure to add this to your config
|
// ExportPath: cfg.ReportExportPath, // Make sure to add this to your config
|
||||||
|
|
@ -198,10 +202,35 @@ func main() {
|
||||||
|
|
||||||
httpserver.StartDataFetchingCrons(eventSvc, *oddsSvc, resultSvc)
|
httpserver.StartDataFetchingCrons(eventSvc, *oddsSvc, resultSvc)
|
||||||
httpserver.StartTicketCrons(*ticketSvc)
|
httpserver.StartTicketCrons(*ticketSvc)
|
||||||
|
|
||||||
|
// Fetch companies and branches for live wallet metrics update
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
companies := []domain.GetCompany{
|
||||||
|
{ID: 1, Name: "Company A", WalletBalance: 1000.0},
|
||||||
|
}
|
||||||
|
|
||||||
|
branches := []domain.BranchWallet{
|
||||||
|
{ID: 10, Name: "Branch Z", CompanyID: 1, Balance: 500.0},
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationSvc.UpdateLiveWalletMetrics(ctx, companies, branches)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to update live metrics:", err)
|
||||||
|
} else {
|
||||||
|
log.Println("Live metrics broadcasted successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
issueReportingRepo := repository.NewReportedIssueRepository(store)
|
||||||
|
|
||||||
|
issueReportingSvc := issuereporting.New(issueReportingRepo)
|
||||||
|
|
||||||
// go httpserver.SetupReportCronJob(reportWorker)
|
// go httpserver.SetupReportCronJob(reportWorker)
|
||||||
|
|
||||||
// Initialize and start HTTP server
|
// Initialize and start HTTP server
|
||||||
app := httpserver.NewApp(
|
app := httpserver.NewApp(
|
||||||
|
issueReportingSvc,
|
||||||
|
instSvc,
|
||||||
currSvc,
|
currSvc,
|
||||||
cfg.Port,
|
cfg.Port,
|
||||||
v,
|
v,
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,23 @@ CREATE TABLE IF NOT EXISTS ticket_outcomes (
|
||||||
status INT NOT NULL DEFAULT 0,
|
status INT NOT NULL DEFAULT 0,
|
||||||
expires TIMESTAMP NOT NULL
|
expires TIMESTAMP NOT NULL
|
||||||
);
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS banks (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
slug VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
swift VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
acct_length INT NOT NULL,
|
||||||
|
country_id INT NOT NULL,
|
||||||
|
is_mobilemoney INT, -- nullable integer (0 or 1)
|
||||||
|
is_active INT NOT NULL, -- 0 or 1
|
||||||
|
is_rtgs INT NOT NULL, -- 0 or 1
|
||||||
|
active INT NOT NULL, -- 0 or 1
|
||||||
|
is_24hrs INT, -- nullable integer (0 or 1)
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
currency VARCHAR(10) NOT NULL,
|
||||||
|
bank_logo TEXT -- URL or base64 string
|
||||||
|
);
|
||||||
CREATE TABLE IF NOT EXISTS wallets (
|
CREATE TABLE IF NOT EXISTS wallets (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
balance BIGINT NOT NULL DEFAULT 0,
|
balance BIGINT NOT NULL DEFAULT 0,
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ CREATE TABLE virtual_game_transactions (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
session_id BIGINT NOT NULL REFERENCES virtual_game_sessions(id),
|
session_id BIGINT NOT NULL REFERENCES virtual_game_sessions(id),
|
||||||
user_id BIGINT NOT NULL REFERENCES users(id),
|
user_id BIGINT NOT NULL REFERENCES users(id),
|
||||||
|
company_id BIGINT,
|
||||||
|
provider VARCHAR(100),
|
||||||
|
game_id VARCHAR(100),
|
||||||
wallet_id BIGINT NOT NULL REFERENCES wallets(id),
|
wallet_id BIGINT NOT NULL REFERENCES wallets(id),
|
||||||
transaction_type VARCHAR(20) NOT NULL,
|
transaction_type VARCHAR(20) NOT NULL,
|
||||||
amount BIGINT NOT NULL,
|
amount BIGINT NOT NULL,
|
||||||
|
|
@ -44,6 +47,8 @@ CREATE TABLE virtual_game_histories (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
session_id VARCHAR(100), -- nullable
|
session_id VARCHAR(100), -- nullable
|
||||||
user_id BIGINT NOT NULL,
|
user_id BIGINT NOT NULL,
|
||||||
|
company_id BIGINT,
|
||||||
|
provider VARCHAR(100),
|
||||||
wallet_id BIGINT, -- nullable
|
wallet_id BIGINT, -- nullable
|
||||||
game_id BIGINT, -- nullable
|
game_id BIGINT, -- nullable
|
||||||
transaction_type VARCHAR(20) NOT NULL, -- e.g., BET, WIN, CANCEL
|
transaction_type VARCHAR(20) NOT NULL, -- e.g., BET, WIN, CANCEL
|
||||||
|
|
@ -56,6 +61,13 @@ CREATE TABLE virtual_game_histories (
|
||||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
|
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS favorite_games (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id BIGINT NOT NULL,
|
||||||
|
game_id BIGINT NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
-- Optional: Indexes for performance
|
-- Optional: Indexes for performance
|
||||||
CREATE INDEX idx_virtual_game_user_id ON virtual_game_histories(user_id);
|
CREATE INDEX idx_virtual_game_user_id ON virtual_game_histories(user_id);
|
||||||
CREATE INDEX idx_virtual_game_transaction_type ON virtual_game_histories(transaction_type);
|
CREATE INDEX idx_virtual_game_transaction_type ON virtual_game_histories(transaction_type);
|
||||||
|
|
@ -65,3 +77,7 @@ CREATE INDEX idx_virtual_game_external_transaction_id ON virtual_game_histories(
|
||||||
CREATE INDEX idx_virtual_game_sessions_user_id ON virtual_game_sessions(user_id);
|
CREATE INDEX idx_virtual_game_sessions_user_id ON virtual_game_sessions(user_id);
|
||||||
CREATE INDEX idx_virtual_game_transactions_session_id ON virtual_game_transactions(session_id);
|
CREATE INDEX idx_virtual_game_transactions_session_id ON virtual_game_transactions(session_id);
|
||||||
CREATE INDEX idx_virtual_game_transactions_user_id ON virtual_game_transactions(user_id);
|
CREATE INDEX idx_virtual_game_transactions_user_id ON virtual_game_transactions(user_id);
|
||||||
|
|
||||||
|
ALTER TABLE favorite_games
|
||||||
|
ADD CONSTRAINT unique_user_game_favorite UNIQUE (user_id, game_id);
|
||||||
|
|
||||||
|
|
|
||||||
2
db/migrations/000008_issue_reporting.down.sql
Normal file
2
db/migrations/000008_issue_reporting.down.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
DROP TABLE IF EXISTS reported_issues;
|
||||||
|
|
||||||
12
db/migrations/000008_issue_reporting.up.sql
Normal file
12
db/migrations/000008_issue_reporting.up.sql
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS reported_issues (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
customer_id BIGINT NOT NULL,
|
||||||
|
subject TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
issue_type TEXT NOT NULL, -- e.g., "deposit", "withdrawal", "bet", "technical"
|
||||||
|
status TEXT NOT NULL DEFAULT 'pending', -- pending, in_progress, resolved, rejected
|
||||||
|
metadata JSONB,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
60
db/query/institutions.sql
Normal file
60
db/query/institutions.sql
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
-- name: CreateBank :one
|
||||||
|
INSERT INTO banks (
|
||||||
|
slug,
|
||||||
|
swift,
|
||||||
|
name,
|
||||||
|
acct_length,
|
||||||
|
country_id,
|
||||||
|
is_mobilemoney,
|
||||||
|
is_active,
|
||||||
|
is_rtgs,
|
||||||
|
active,
|
||||||
|
is_24hrs,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
currency,
|
||||||
|
bank_logo
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, $11, $12
|
||||||
|
)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetBankByID :one
|
||||||
|
SELECT *
|
||||||
|
FROM banks
|
||||||
|
WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: GetAllBanks :many
|
||||||
|
SELECT *
|
||||||
|
FROM banks
|
||||||
|
WHERE (
|
||||||
|
country_id = sqlc.narg('country_id')
|
||||||
|
OR sqlc.narg('country_id') IS NULL
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
is_active = sqlc.narg('is_active')
|
||||||
|
OR sqlc.narg('is_active') IS NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: UpdateBank :one
|
||||||
|
UPDATE banks
|
||||||
|
SET slug = COALESCE(sqlc.narg(slug), slug),
|
||||||
|
swift = COALESCE(sqlc.narg(swift), swift),
|
||||||
|
name = COALESCE(sqlc.narg(name), name),
|
||||||
|
acct_length = COALESCE(sqlc.narg(acct_length), acct_length),
|
||||||
|
country_id = COALESCE(sqlc.narg(country_id), country_id),
|
||||||
|
is_mobilemoney = COALESCE(sqlc.narg(is_mobilemoney), is_mobilemoney),
|
||||||
|
is_active = COALESCE(sqlc.narg(is_active), is_active),
|
||||||
|
is_rtgs = COALESCE(sqlc.narg(is_rtgs), is_rtgs),
|
||||||
|
active = COALESCE(sqlc.narg(active), active),
|
||||||
|
is_24hrs = COALESCE(sqlc.narg(is_24hrs), is_24hrs),
|
||||||
|
updated_at = CURRENT_TIMESTAMP,
|
||||||
|
currency = COALESCE(sqlc.narg(currency), currency),
|
||||||
|
bank_logo = COALESCE(sqlc.narg(bank_logo), bank_logo)
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: DeleteBank :exec
|
||||||
|
DELETE FROM banks
|
||||||
|
WHERE id = $1;
|
||||||
32
db/query/issue_reporting.sql
Normal file
32
db/query/issue_reporting.sql
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
-- name: CreateReportedIssue :one
|
||||||
|
INSERT INTO reported_issues (
|
||||||
|
customer_id, subject, description, issue_type, metadata
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5
|
||||||
|
)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: ListReportedIssues :many
|
||||||
|
SELECT * FROM reported_issues
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
|
-- name: ListReportedIssuesByCustomer :many
|
||||||
|
SELECT * FROM reported_issues
|
||||||
|
WHERE customer_id = $1
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT $2 OFFSET $3;
|
||||||
|
|
||||||
|
-- name: CountReportedIssues :one
|
||||||
|
SELECT COUNT(*) FROM reported_issues;
|
||||||
|
|
||||||
|
-- name: CountReportedIssuesByCustomer :one
|
||||||
|
SELECT COUNT(*) FROM reported_issues WHERE customer_id = $1;
|
||||||
|
|
||||||
|
-- name: UpdateReportedIssueStatus :exec
|
||||||
|
UPDATE reported_issues
|
||||||
|
SET status = $2, updated_at = NOW()
|
||||||
|
WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteReportedIssue :exec
|
||||||
|
DELETE FROM reported_issues WHERE id = $1;
|
||||||
|
|
@ -1,34 +1,44 @@
|
||||||
-- name: GetTotalBetsMadeInRange :one
|
-- name: GetTotalBetsMadeInRange :one
|
||||||
SELECT COUNT(*) AS total_bets
|
SELECT COUNT(*) AS total_bets
|
||||||
FROM bets
|
FROM bets
|
||||||
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to');
|
||||||
AND (
|
|
||||||
company_id = sqlc.narg('company_id')
|
|
||||||
OR sqlc.narg('company_id') IS NULL
|
|
||||||
);
|
|
||||||
-- name: GetTotalCashMadeInRange :one
|
-- name: GetTotalCashMadeInRange :one
|
||||||
SELECT COALESCE(SUM(amount), 0) AS total_cash_made
|
SELECT COALESCE(SUM(amount), 0) AS total_cash_made
|
||||||
FROM bets
|
FROM bets
|
||||||
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to');
|
||||||
AND (
|
|
||||||
company_id = sqlc.narg('company_id')
|
|
||||||
OR sqlc.narg('company_id') IS NULL
|
|
||||||
);
|
|
||||||
-- name: GetTotalCashOutInRange :one
|
-- name: GetTotalCashOutInRange :one
|
||||||
SELECT COALESCE(SUM(amount), 0) AS total_cash_out
|
SELECT COALESCE(SUM(amount), 0) AS total_cash_out
|
||||||
FROM bets
|
FROM bets
|
||||||
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
||||||
AND cashed_out = true
|
AND cashed_out = true;
|
||||||
AND (
|
|
||||||
company_id = sqlc.narg('company_id')
|
|
||||||
OR sqlc.narg('company_id') IS NULL
|
|
||||||
);
|
|
||||||
-- name: GetTotalCashBacksInRange :one
|
-- name: GetTotalCashBacksInRange :one
|
||||||
SELECT COALESCE(SUM(amount), 0) AS total_cash_backs
|
SELECT COALESCE(SUM(amount), 0) AS total_cash_backs
|
||||||
FROM bets
|
FROM bets
|
||||||
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
||||||
AND status = 5
|
AND status = 5;
|
||||||
AND (
|
-- name: GetCompanyWiseReport :many
|
||||||
company_id = sqlc.narg('company_id')
|
SELECT
|
||||||
OR sqlc.narg('company_id') IS NULL
|
b.company_id,
|
||||||
);
|
c.name AS company_name,
|
||||||
|
COUNT(*) AS total_bets,
|
||||||
|
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||||
|
COALESCE(SUM(CASE WHEN b.cashed_out THEN b.amount ELSE 0 END), 0) AS total_cash_out,
|
||||||
|
COALESCE(SUM(CASE WHEN b.status = 5 THEN b.amount ELSE 0 END), 0) AS total_cash_backs
|
||||||
|
FROM bets b
|
||||||
|
JOIN companies c ON b.company_id = c.id
|
||||||
|
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
||||||
|
GROUP BY b.company_id, c.name;
|
||||||
|
-- name: GetBranchWiseReport :many
|
||||||
|
SELECT
|
||||||
|
b.branch_id,
|
||||||
|
br.name AS branch_name,
|
||||||
|
br.company_id,
|
||||||
|
COUNT(*) AS total_bets,
|
||||||
|
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||||
|
COALESCE(SUM(CASE WHEN b.cashed_out THEN b.amount ELSE 0 END), 0) AS total_cash_out,
|
||||||
|
COALESCE(SUM(CASE WHEN b.status = 5 THEN b.amount ELSE 0 END), 0) AS total_cash_backs
|
||||||
|
FROM bets b
|
||||||
|
JOIN branches br ON b.branch_id = br.id
|
||||||
|
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
||||||
|
GROUP BY b.branch_id, br.name, br.company_id;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,28 +4,26 @@ INSERT INTO virtual_game_sessions (
|
||||||
) VALUES (
|
) VALUES (
|
||||||
$1, $2, $3, $4, $5, $6
|
$1, $2, $3, $4, $5, $6
|
||||||
) RETURNING id, user_id, game_id, session_token, currency, status, created_at, updated_at, expires_at;
|
) RETURNING id, user_id, game_id, session_token, currency, status, created_at, updated_at, expires_at;
|
||||||
|
|
||||||
-- name: GetVirtualGameSessionByToken :one
|
-- name: GetVirtualGameSessionByToken :one
|
||||||
SELECT id, user_id, game_id, session_token, currency, status, created_at, updated_at, expires_at
|
SELECT id, user_id, game_id, session_token, currency, status, created_at, updated_at, expires_at
|
||||||
FROM virtual_game_sessions
|
FROM virtual_game_sessions
|
||||||
WHERE session_token = $1;
|
WHERE session_token = $1;
|
||||||
|
|
||||||
-- name: UpdateVirtualGameSessionStatus :exec
|
-- name: UpdateVirtualGameSessionStatus :exec
|
||||||
UPDATE virtual_game_sessions
|
UPDATE virtual_game_sessions
|
||||||
SET status = $2, updated_at = CURRENT_TIMESTAMP
|
SET status = $2, updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
-- name: CreateVirtualGameTransaction :one
|
-- name: CreateVirtualGameTransaction :one
|
||||||
INSERT INTO virtual_game_transactions (
|
INSERT INTO virtual_game_transactions (
|
||||||
session_id, user_id, wallet_id, transaction_type, amount, currency, external_transaction_id, status
|
session_id, user_id, company_id, provider, wallet_id, transaction_type, amount, currency, external_transaction_id, status
|
||||||
) VALUES (
|
) VALUES (
|
||||||
$1, $2, $3, $4, $5, $6, $7, $8
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
||||||
) RETURNING id, session_id, user_id, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at;
|
) RETURNING id, session_id, user_id, company_id, provider, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at;
|
||||||
|
|
||||||
-- name: CreateVirtualGameHistory :one
|
-- name: CreateVirtualGameHistory :one
|
||||||
INSERT INTO virtual_game_histories (
|
INSERT INTO virtual_game_histories (
|
||||||
session_id,
|
session_id,
|
||||||
user_id,
|
user_id,
|
||||||
|
company_id,
|
||||||
|
provider,
|
||||||
wallet_id,
|
wallet_id,
|
||||||
game_id,
|
game_id,
|
||||||
transaction_type,
|
transaction_type,
|
||||||
|
|
@ -35,11 +33,13 @@ INSERT INTO virtual_game_histories (
|
||||||
reference_transaction_id,
|
reference_transaction_id,
|
||||||
status
|
status
|
||||||
) VALUES (
|
) VALUES (
|
||||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
|
||||||
) RETURNING
|
) RETURNING
|
||||||
id,
|
id,
|
||||||
session_id,
|
session_id,
|
||||||
user_id,
|
user_id,
|
||||||
|
company_id,
|
||||||
|
provider,
|
||||||
wallet_id,
|
wallet_id,
|
||||||
game_id,
|
game_id,
|
||||||
transaction_type,
|
transaction_type,
|
||||||
|
|
@ -50,25 +50,39 @@ INSERT INTO virtual_game_histories (
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at;
|
updated_at;
|
||||||
|
|
||||||
|
|
||||||
-- name: GetVirtualGameTransactionByExternalID :one
|
-- name: GetVirtualGameTransactionByExternalID :one
|
||||||
SELECT id, session_id, user_id, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at
|
SELECT id, session_id, user_id, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at
|
||||||
FROM virtual_game_transactions
|
FROM virtual_game_transactions
|
||||||
WHERE external_transaction_id = $1;
|
WHERE external_transaction_id = $1;
|
||||||
|
|
||||||
-- name: UpdateVirtualGameTransactionStatus :exec
|
-- name: UpdateVirtualGameTransactionStatus :exec
|
||||||
UPDATE virtual_game_transactions
|
UPDATE virtual_game_transactions
|
||||||
SET status = $2, updated_at = CURRENT_TIMESTAMP
|
SET status = $2, updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
-- name: GetVirtualGameSummaryInRange :many
|
-- name: GetVirtualGameSummaryInRange :many
|
||||||
SELECT
|
SELECT
|
||||||
|
c.name AS company_name,
|
||||||
vg.name AS game_name,
|
vg.name AS game_name,
|
||||||
COUNT(vgh.id) AS number_of_bets,
|
COUNT(vgt.id) AS number_of_bets,
|
||||||
COALESCE(SUM(vgh.amount), 0) AS total_transaction_sum
|
COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum
|
||||||
FROM virtual_game_histories vgh
|
FROM virtual_game_transactions vgt
|
||||||
JOIN virtual_games vg ON vgh.game_id = vg.id
|
JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
|
||||||
WHERE vgh.transaction_type = 'BET'
|
JOIN virtual_games vg ON vgs.game_id = vg.id
|
||||||
AND vgh.created_at BETWEEN $1 AND $2
|
JOIN companies c ON vgt.company_id = c.id
|
||||||
GROUP BY vg.name;
|
WHERE vgt.transaction_type = 'BET'
|
||||||
|
AND vgt.created_at BETWEEN $1 AND $2
|
||||||
|
GROUP BY c.name, vg.name;
|
||||||
|
-- name: AddFavoriteGame :exec
|
||||||
|
INSERT INTO favorite_games (
|
||||||
|
user_id,
|
||||||
|
game_id,
|
||||||
|
created_at
|
||||||
|
) VALUES ($1, $2, NOW())
|
||||||
|
ON CONFLICT (user_id, game_id) DO NOTHING;
|
||||||
|
-- name: RemoveFavoriteGame :exec
|
||||||
|
DELETE FROM favorite_games
|
||||||
|
WHERE user_id = $1 AND game_id = $2;
|
||||||
|
-- name: ListFavoriteGames :many
|
||||||
|
SELECT game_id
|
||||||
|
FROM favorite_games
|
||||||
|
WHERE user_id = $1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,3 +56,13 @@ UPDATE wallets
|
||||||
SET is_active = $1,
|
SET is_active = $1,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = $2;
|
WHERE id = $2;
|
||||||
|
-- name: GetCompanyByWalletID :one
|
||||||
|
SELECT id, name, admin_id, wallet_id
|
||||||
|
FROM companies
|
||||||
|
WHERE wallet_id = $1
|
||||||
|
LIMIT 1;
|
||||||
|
-- name: GetBranchByWalletID :one
|
||||||
|
SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at
|
||||||
|
FROM branches
|
||||||
|
WHERE wallet_id = $1
|
||||||
|
LIMIT 1;
|
||||||
1362
docs/docs.go
1362
docs/docs.go
File diff suppressed because it is too large
Load Diff
1362
docs/swagger.json
1362
docs/swagger.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
251
gen/db/institutions.sql.go
Normal file
251
gen/db/institutions.sql.go
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.29.0
|
||||||
|
// source: institutions.sql
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CreateBank = `-- name: CreateBank :one
|
||||||
|
INSERT INTO banks (
|
||||||
|
slug,
|
||||||
|
swift,
|
||||||
|
name,
|
||||||
|
acct_length,
|
||||||
|
country_id,
|
||||||
|
is_mobilemoney,
|
||||||
|
is_active,
|
||||||
|
is_rtgs,
|
||||||
|
active,
|
||||||
|
is_24hrs,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
currency,
|
||||||
|
bank_logo
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, $11, $12
|
||||||
|
)
|
||||||
|
RETURNING id, slug, swift, name, acct_length, country_id, is_mobilemoney, is_active, is_rtgs, active, is_24hrs, created_at, updated_at, currency, bank_logo
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateBankParams struct {
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Swift string `json:"swift"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
AcctLength int32 `json:"acct_length"`
|
||||||
|
CountryID int32 `json:"country_id"`
|
||||||
|
IsMobilemoney pgtype.Int4 `json:"is_mobilemoney"`
|
||||||
|
IsActive int32 `json:"is_active"`
|
||||||
|
IsRtgs int32 `json:"is_rtgs"`
|
||||||
|
Active int32 `json:"active"`
|
||||||
|
Is24hrs pgtype.Int4 `json:"is_24hrs"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
BankLogo pgtype.Text `json:"bank_logo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateBank(ctx context.Context, arg CreateBankParams) (Bank, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CreateBank,
|
||||||
|
arg.Slug,
|
||||||
|
arg.Swift,
|
||||||
|
arg.Name,
|
||||||
|
arg.AcctLength,
|
||||||
|
arg.CountryID,
|
||||||
|
arg.IsMobilemoney,
|
||||||
|
arg.IsActive,
|
||||||
|
arg.IsRtgs,
|
||||||
|
arg.Active,
|
||||||
|
arg.Is24hrs,
|
||||||
|
arg.Currency,
|
||||||
|
arg.BankLogo,
|
||||||
|
)
|
||||||
|
var i Bank
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Slug,
|
||||||
|
&i.Swift,
|
||||||
|
&i.Name,
|
||||||
|
&i.AcctLength,
|
||||||
|
&i.CountryID,
|
||||||
|
&i.IsMobilemoney,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.IsRtgs,
|
||||||
|
&i.Active,
|
||||||
|
&i.Is24hrs,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.Currency,
|
||||||
|
&i.BankLogo,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeleteBank = `-- name: DeleteBank :exec
|
||||||
|
DELETE FROM banks
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteBank(ctx context.Context, id int64) error {
|
||||||
|
_, err := q.db.Exec(ctx, DeleteBank, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetAllBanks = `-- name: GetAllBanks :many
|
||||||
|
SELECT id, slug, swift, name, acct_length, country_id, is_mobilemoney, is_active, is_rtgs, active, is_24hrs, created_at, updated_at, currency, bank_logo
|
||||||
|
FROM banks
|
||||||
|
WHERE (
|
||||||
|
country_id = $1
|
||||||
|
OR $1 IS NULL
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
is_active = $2
|
||||||
|
OR $2 IS NULL
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetAllBanksParams struct {
|
||||||
|
CountryID pgtype.Int4 `json:"country_id"`
|
||||||
|
IsActive pgtype.Int4 `json:"is_active"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetAllBanks(ctx context.Context, arg GetAllBanksParams) ([]Bank, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetAllBanks, arg.CountryID, arg.IsActive)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Bank
|
||||||
|
for rows.Next() {
|
||||||
|
var i Bank
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Slug,
|
||||||
|
&i.Swift,
|
||||||
|
&i.Name,
|
||||||
|
&i.AcctLength,
|
||||||
|
&i.CountryID,
|
||||||
|
&i.IsMobilemoney,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.IsRtgs,
|
||||||
|
&i.Active,
|
||||||
|
&i.Is24hrs,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.Currency,
|
||||||
|
&i.BankLogo,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetBankByID = `-- name: GetBankByID :one
|
||||||
|
SELECT id, slug, swift, name, acct_length, country_id, is_mobilemoney, is_active, is_rtgs, active, is_24hrs, created_at, updated_at, currency, bank_logo
|
||||||
|
FROM banks
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetBankByID(ctx context.Context, id int64) (Bank, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetBankByID, id)
|
||||||
|
var i Bank
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Slug,
|
||||||
|
&i.Swift,
|
||||||
|
&i.Name,
|
||||||
|
&i.AcctLength,
|
||||||
|
&i.CountryID,
|
||||||
|
&i.IsMobilemoney,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.IsRtgs,
|
||||||
|
&i.Active,
|
||||||
|
&i.Is24hrs,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.Currency,
|
||||||
|
&i.BankLogo,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateBank = `-- name: UpdateBank :one
|
||||||
|
UPDATE banks
|
||||||
|
SET slug = COALESCE($2, slug),
|
||||||
|
swift = COALESCE($3, swift),
|
||||||
|
name = COALESCE($4, name),
|
||||||
|
acct_length = COALESCE($5, acct_length),
|
||||||
|
country_id = COALESCE($6, country_id),
|
||||||
|
is_mobilemoney = COALESCE($7, is_mobilemoney),
|
||||||
|
is_active = COALESCE($8, is_active),
|
||||||
|
is_rtgs = COALESCE($9, is_rtgs),
|
||||||
|
active = COALESCE($10, active),
|
||||||
|
is_24hrs = COALESCE($11, is_24hrs),
|
||||||
|
updated_at = CURRENT_TIMESTAMP,
|
||||||
|
currency = COALESCE($12, currency),
|
||||||
|
bank_logo = COALESCE($13, bank_logo)
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, slug, swift, name, acct_length, country_id, is_mobilemoney, is_active, is_rtgs, active, is_24hrs, created_at, updated_at, currency, bank_logo
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateBankParams struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Slug pgtype.Text `json:"slug"`
|
||||||
|
Swift pgtype.Text `json:"swift"`
|
||||||
|
Name pgtype.Text `json:"name"`
|
||||||
|
AcctLength pgtype.Int4 `json:"acct_length"`
|
||||||
|
CountryID pgtype.Int4 `json:"country_id"`
|
||||||
|
IsMobilemoney pgtype.Int4 `json:"is_mobilemoney"`
|
||||||
|
IsActive pgtype.Int4 `json:"is_active"`
|
||||||
|
IsRtgs pgtype.Int4 `json:"is_rtgs"`
|
||||||
|
Active pgtype.Int4 `json:"active"`
|
||||||
|
Is24hrs pgtype.Int4 `json:"is_24hrs"`
|
||||||
|
Currency pgtype.Text `json:"currency"`
|
||||||
|
BankLogo pgtype.Text `json:"bank_logo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateBank(ctx context.Context, arg UpdateBankParams) (Bank, error) {
|
||||||
|
row := q.db.QueryRow(ctx, UpdateBank,
|
||||||
|
arg.ID,
|
||||||
|
arg.Slug,
|
||||||
|
arg.Swift,
|
||||||
|
arg.Name,
|
||||||
|
arg.AcctLength,
|
||||||
|
arg.CountryID,
|
||||||
|
arg.IsMobilemoney,
|
||||||
|
arg.IsActive,
|
||||||
|
arg.IsRtgs,
|
||||||
|
arg.Active,
|
||||||
|
arg.Is24hrs,
|
||||||
|
arg.Currency,
|
||||||
|
arg.BankLogo,
|
||||||
|
)
|
||||||
|
var i Bank
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Slug,
|
||||||
|
&i.Swift,
|
||||||
|
&i.Name,
|
||||||
|
&i.AcctLength,
|
||||||
|
&i.CountryID,
|
||||||
|
&i.IsMobilemoney,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.IsRtgs,
|
||||||
|
&i.Active,
|
||||||
|
&i.Is24hrs,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.Currency,
|
||||||
|
&i.BankLogo,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
181
gen/db/issue_reporting.sql.go
Normal file
181
gen/db/issue_reporting.sql.go
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.29.0
|
||||||
|
// source: issue_reporting.sql
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CountReportedIssues = `-- name: CountReportedIssues :one
|
||||||
|
SELECT COUNT(*) FROM reported_issues
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountReportedIssues(ctx context.Context) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CountReportedIssues)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const CountReportedIssuesByCustomer = `-- name: CountReportedIssuesByCustomer :one
|
||||||
|
SELECT COUNT(*) FROM reported_issues WHERE customer_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountReportedIssuesByCustomer(ctx context.Context, customerID int64) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CountReportedIssuesByCustomer, customerID)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateReportedIssue = `-- name: CreateReportedIssue :one
|
||||||
|
INSERT INTO reported_issues (
|
||||||
|
customer_id, subject, description, issue_type, metadata
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5
|
||||||
|
)
|
||||||
|
RETURNING id, customer_id, subject, description, issue_type, status, metadata, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateReportedIssueParams struct {
|
||||||
|
CustomerID int64 `json:"customer_id"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IssueType string `json:"issue_type"`
|
||||||
|
Metadata []byte `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateReportedIssue(ctx context.Context, arg CreateReportedIssueParams) (ReportedIssue, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CreateReportedIssue,
|
||||||
|
arg.CustomerID,
|
||||||
|
arg.Subject,
|
||||||
|
arg.Description,
|
||||||
|
arg.IssueType,
|
||||||
|
arg.Metadata,
|
||||||
|
)
|
||||||
|
var i ReportedIssue
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.CustomerID,
|
||||||
|
&i.Subject,
|
||||||
|
&i.Description,
|
||||||
|
&i.IssueType,
|
||||||
|
&i.Status,
|
||||||
|
&i.Metadata,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeleteReportedIssue = `-- name: DeleteReportedIssue :exec
|
||||||
|
DELETE FROM reported_issues WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteReportedIssue(ctx context.Context, id int64) error {
|
||||||
|
_, err := q.db.Exec(ctx, DeleteReportedIssue, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListReportedIssues = `-- name: ListReportedIssues :many
|
||||||
|
SELECT id, customer_id, subject, description, issue_type, status, metadata, created_at, updated_at FROM reported_issues
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT $1 OFFSET $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListReportedIssuesParams struct {
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListReportedIssues(ctx context.Context, arg ListReportedIssuesParams) ([]ReportedIssue, error) {
|
||||||
|
rows, err := q.db.Query(ctx, ListReportedIssues, arg.Limit, arg.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ReportedIssue
|
||||||
|
for rows.Next() {
|
||||||
|
var i ReportedIssue
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.CustomerID,
|
||||||
|
&i.Subject,
|
||||||
|
&i.Description,
|
||||||
|
&i.IssueType,
|
||||||
|
&i.Status,
|
||||||
|
&i.Metadata,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListReportedIssuesByCustomer = `-- name: ListReportedIssuesByCustomer :many
|
||||||
|
SELECT id, customer_id, subject, description, issue_type, status, metadata, created_at, updated_at FROM reported_issues
|
||||||
|
WHERE customer_id = $1
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT $2 OFFSET $3
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListReportedIssuesByCustomerParams struct {
|
||||||
|
CustomerID int64 `json:"customer_id"`
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListReportedIssuesByCustomer(ctx context.Context, arg ListReportedIssuesByCustomerParams) ([]ReportedIssue, error) {
|
||||||
|
rows, err := q.db.Query(ctx, ListReportedIssuesByCustomer, arg.CustomerID, arg.Limit, arg.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ReportedIssue
|
||||||
|
for rows.Next() {
|
||||||
|
var i ReportedIssue
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.CustomerID,
|
||||||
|
&i.Subject,
|
||||||
|
&i.Description,
|
||||||
|
&i.IssueType,
|
||||||
|
&i.Status,
|
||||||
|
&i.Metadata,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateReportedIssueStatus = `-- name: UpdateReportedIssueStatus :exec
|
||||||
|
UPDATE reported_issues
|
||||||
|
SET status = $2, updated_at = NOW()
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateReportedIssueStatusParams struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateReportedIssueStatus(ctx context.Context, arg UpdateReportedIssueStatusParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, UpdateReportedIssueStatus, arg.ID, arg.Status)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -55,6 +55,24 @@ func (ns NullReferralstatus) Value() (driver.Value, error) {
|
||||||
return string(ns.Referralstatus), nil
|
return string(ns.Referralstatus), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Bank struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Swift string `json:"swift"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
AcctLength int32 `json:"acct_length"`
|
||||||
|
CountryID int32 `json:"country_id"`
|
||||||
|
IsMobilemoney pgtype.Int4 `json:"is_mobilemoney"`
|
||||||
|
IsActive int32 `json:"is_active"`
|
||||||
|
IsRtgs int32 `json:"is_rtgs"`
|
||||||
|
Active int32 `json:"active"`
|
||||||
|
Is24hrs pgtype.Int4 `json:"is_24hrs"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
BankLogo pgtype.Text `json:"bank_logo"`
|
||||||
|
}
|
||||||
|
|
||||||
type Bet struct {
|
type Bet struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Amount int64 `json:"amount"`
|
Amount int64 `json:"amount"`
|
||||||
|
|
@ -233,6 +251,13 @@ type ExchangeRate struct {
|
||||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FavoriteGame struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
GameID int64 `json:"game_id"`
|
||||||
|
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type League struct {
|
type League struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -327,6 +352,18 @@ type RefreshToken struct {
|
||||||
Revoked bool `json:"revoked"`
|
Revoked bool `json:"revoked"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReportedIssue struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
CustomerID int64 `json:"customer_id"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IssueType string `json:"issue_type"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Metadata []byte `json:"metadata"`
|
||||||
|
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
BetOutcomeID int64 `json:"bet_outcome_id"`
|
BetOutcomeID int64 `json:"bet_outcome_id"`
|
||||||
|
|
@ -476,6 +513,8 @@ type VirtualGameHistory struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SessionID pgtype.Text `json:"session_id"`
|
SessionID pgtype.Text `json:"session_id"`
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
|
Provider pgtype.Text `json:"provider"`
|
||||||
WalletID pgtype.Int8 `json:"wallet_id"`
|
WalletID pgtype.Int8 `json:"wallet_id"`
|
||||||
GameID pgtype.Int8 `json:"game_id"`
|
GameID pgtype.Int8 `json:"game_id"`
|
||||||
TransactionType string `json:"transaction_type"`
|
TransactionType string `json:"transaction_type"`
|
||||||
|
|
@ -504,6 +543,9 @@ type VirtualGameTransaction struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SessionID int64 `json:"session_id"`
|
SessionID int64 `json:"session_id"`
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
|
Provider pgtype.Text `json:"provider"`
|
||||||
|
GameID pgtype.Text `json:"game_id"`
|
||||||
WalletID int64 `json:"wallet_id"`
|
WalletID int64 `json:"wallet_id"`
|
||||||
TransactionType string `json:"transaction_type"`
|
TransactionType string `json:"transaction_type"`
|
||||||
Amount int64 `json:"amount"`
|
Amount int64 `json:"amount"`
|
||||||
|
|
|
||||||
|
|
@ -11,24 +11,132 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const GetBranchWiseReport = `-- name: GetBranchWiseReport :many
|
||||||
|
SELECT
|
||||||
|
b.branch_id,
|
||||||
|
br.name AS branch_name,
|
||||||
|
br.company_id,
|
||||||
|
COUNT(*) AS total_bets,
|
||||||
|
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||||
|
COALESCE(SUM(CASE WHEN b.cashed_out THEN b.amount ELSE 0 END), 0) AS total_cash_out,
|
||||||
|
COALESCE(SUM(CASE WHEN b.status = 5 THEN b.amount ELSE 0 END), 0) AS total_cash_backs
|
||||||
|
FROM bets b
|
||||||
|
JOIN branches br ON b.branch_id = br.id
|
||||||
|
WHERE b.created_at BETWEEN $1 AND $2
|
||||||
|
GROUP BY b.branch_id, br.name, br.company_id
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetBranchWiseReportParams struct {
|
||||||
|
From pgtype.Timestamp `json:"from"`
|
||||||
|
To pgtype.Timestamp `json:"to"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetBranchWiseReportRow struct {
|
||||||
|
BranchID pgtype.Int8 `json:"branch_id"`
|
||||||
|
BranchName string `json:"branch_name"`
|
||||||
|
CompanyID int64 `json:"company_id"`
|
||||||
|
TotalBets int64 `json:"total_bets"`
|
||||||
|
TotalCashMade interface{} `json:"total_cash_made"`
|
||||||
|
TotalCashOut interface{} `json:"total_cash_out"`
|
||||||
|
TotalCashBacks interface{} `json:"total_cash_backs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetBranchWiseReport(ctx context.Context, arg GetBranchWiseReportParams) ([]GetBranchWiseReportRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetBranchWiseReport, arg.From, arg.To)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetBranchWiseReportRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetBranchWiseReportRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.BranchID,
|
||||||
|
&i.BranchName,
|
||||||
|
&i.CompanyID,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalCashMade,
|
||||||
|
&i.TotalCashOut,
|
||||||
|
&i.TotalCashBacks,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetCompanyWiseReport = `-- name: GetCompanyWiseReport :many
|
||||||
|
SELECT
|
||||||
|
b.company_id,
|
||||||
|
c.name AS company_name,
|
||||||
|
COUNT(*) AS total_bets,
|
||||||
|
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||||
|
COALESCE(SUM(CASE WHEN b.cashed_out THEN b.amount ELSE 0 END), 0) AS total_cash_out,
|
||||||
|
COALESCE(SUM(CASE WHEN b.status = 5 THEN b.amount ELSE 0 END), 0) AS total_cash_backs
|
||||||
|
FROM bets b
|
||||||
|
JOIN companies c ON b.company_id = c.id
|
||||||
|
WHERE b.created_at BETWEEN $1 AND $2
|
||||||
|
GROUP BY b.company_id, c.name
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetCompanyWiseReportParams struct {
|
||||||
|
From pgtype.Timestamp `json:"from"`
|
||||||
|
To pgtype.Timestamp `json:"to"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetCompanyWiseReportRow struct {
|
||||||
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
|
CompanyName string `json:"company_name"`
|
||||||
|
TotalBets int64 `json:"total_bets"`
|
||||||
|
TotalCashMade interface{} `json:"total_cash_made"`
|
||||||
|
TotalCashOut interface{} `json:"total_cash_out"`
|
||||||
|
TotalCashBacks interface{} `json:"total_cash_backs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetCompanyWiseReport(ctx context.Context, arg GetCompanyWiseReportParams) ([]GetCompanyWiseReportRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetCompanyWiseReport, arg.From, arg.To)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetCompanyWiseReportRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetCompanyWiseReportRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.CompanyID,
|
||||||
|
&i.CompanyName,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalCashMade,
|
||||||
|
&i.TotalCashOut,
|
||||||
|
&i.TotalCashBacks,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const GetTotalBetsMadeInRange = `-- name: GetTotalBetsMadeInRange :one
|
const GetTotalBetsMadeInRange = `-- name: GetTotalBetsMadeInRange :one
|
||||||
SELECT COUNT(*) AS total_bets
|
SELECT COUNT(*) AS total_bets
|
||||||
FROM bets
|
FROM bets
|
||||||
WHERE created_at BETWEEN $1 AND $2
|
WHERE created_at BETWEEN $1 AND $2
|
||||||
AND (
|
|
||||||
company_id = $3
|
|
||||||
OR $3 IS NULL
|
|
||||||
)
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetTotalBetsMadeInRangeParams struct {
|
type GetTotalBetsMadeInRangeParams struct {
|
||||||
From pgtype.Timestamp `json:"from"`
|
From pgtype.Timestamp `json:"from"`
|
||||||
To pgtype.Timestamp `json:"to"`
|
To pgtype.Timestamp `json:"to"`
|
||||||
CompanyID pgtype.Int8 `json:"company_id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetTotalBetsMadeInRange(ctx context.Context, arg GetTotalBetsMadeInRangeParams) (int64, error) {
|
func (q *Queries) GetTotalBetsMadeInRange(ctx context.Context, arg GetTotalBetsMadeInRangeParams) (int64, error) {
|
||||||
row := q.db.QueryRow(ctx, GetTotalBetsMadeInRange, arg.From, arg.To, arg.CompanyID)
|
row := q.db.QueryRow(ctx, GetTotalBetsMadeInRange, arg.From, arg.To)
|
||||||
var total_bets int64
|
var total_bets int64
|
||||||
err := row.Scan(&total_bets)
|
err := row.Scan(&total_bets)
|
||||||
return total_bets, err
|
return total_bets, err
|
||||||
|
|
@ -39,20 +147,15 @@ SELECT COALESCE(SUM(amount), 0) AS total_cash_backs
|
||||||
FROM bets
|
FROM bets
|
||||||
WHERE created_at BETWEEN $1 AND $2
|
WHERE created_at BETWEEN $1 AND $2
|
||||||
AND status = 5
|
AND status = 5
|
||||||
AND (
|
|
||||||
company_id = $3
|
|
||||||
OR $3 IS NULL
|
|
||||||
)
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetTotalCashBacksInRangeParams struct {
|
type GetTotalCashBacksInRangeParams struct {
|
||||||
From pgtype.Timestamp `json:"from"`
|
From pgtype.Timestamp `json:"from"`
|
||||||
To pgtype.Timestamp `json:"to"`
|
To pgtype.Timestamp `json:"to"`
|
||||||
CompanyID pgtype.Int8 `json:"company_id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetTotalCashBacksInRange(ctx context.Context, arg GetTotalCashBacksInRangeParams) (interface{}, error) {
|
func (q *Queries) GetTotalCashBacksInRange(ctx context.Context, arg GetTotalCashBacksInRangeParams) (interface{}, error) {
|
||||||
row := q.db.QueryRow(ctx, GetTotalCashBacksInRange, arg.From, arg.To, arg.CompanyID)
|
row := q.db.QueryRow(ctx, GetTotalCashBacksInRange, arg.From, arg.To)
|
||||||
var total_cash_backs interface{}
|
var total_cash_backs interface{}
|
||||||
err := row.Scan(&total_cash_backs)
|
err := row.Scan(&total_cash_backs)
|
||||||
return total_cash_backs, err
|
return total_cash_backs, err
|
||||||
|
|
@ -62,20 +165,15 @@ const GetTotalCashMadeInRange = `-- name: GetTotalCashMadeInRange :one
|
||||||
SELECT COALESCE(SUM(amount), 0) AS total_cash_made
|
SELECT COALESCE(SUM(amount), 0) AS total_cash_made
|
||||||
FROM bets
|
FROM bets
|
||||||
WHERE created_at BETWEEN $1 AND $2
|
WHERE created_at BETWEEN $1 AND $2
|
||||||
AND (
|
|
||||||
company_id = $3
|
|
||||||
OR $3 IS NULL
|
|
||||||
)
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetTotalCashMadeInRangeParams struct {
|
type GetTotalCashMadeInRangeParams struct {
|
||||||
From pgtype.Timestamp `json:"from"`
|
From pgtype.Timestamp `json:"from"`
|
||||||
To pgtype.Timestamp `json:"to"`
|
To pgtype.Timestamp `json:"to"`
|
||||||
CompanyID pgtype.Int8 `json:"company_id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetTotalCashMadeInRange(ctx context.Context, arg GetTotalCashMadeInRangeParams) (interface{}, error) {
|
func (q *Queries) GetTotalCashMadeInRange(ctx context.Context, arg GetTotalCashMadeInRangeParams) (interface{}, error) {
|
||||||
row := q.db.QueryRow(ctx, GetTotalCashMadeInRange, arg.From, arg.To, arg.CompanyID)
|
row := q.db.QueryRow(ctx, GetTotalCashMadeInRange, arg.From, arg.To)
|
||||||
var total_cash_made interface{}
|
var total_cash_made interface{}
|
||||||
err := row.Scan(&total_cash_made)
|
err := row.Scan(&total_cash_made)
|
||||||
return total_cash_made, err
|
return total_cash_made, err
|
||||||
|
|
@ -86,20 +184,15 @@ SELECT COALESCE(SUM(amount), 0) AS total_cash_out
|
||||||
FROM bets
|
FROM bets
|
||||||
WHERE created_at BETWEEN $1 AND $2
|
WHERE created_at BETWEEN $1 AND $2
|
||||||
AND cashed_out = true
|
AND cashed_out = true
|
||||||
AND (
|
|
||||||
company_id = $3
|
|
||||||
OR $3 IS NULL
|
|
||||||
)
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetTotalCashOutInRangeParams struct {
|
type GetTotalCashOutInRangeParams struct {
|
||||||
From pgtype.Timestamp `json:"from"`
|
From pgtype.Timestamp `json:"from"`
|
||||||
To pgtype.Timestamp `json:"to"`
|
To pgtype.Timestamp `json:"to"`
|
||||||
CompanyID pgtype.Int8 `json:"company_id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetTotalCashOutInRange(ctx context.Context, arg GetTotalCashOutInRangeParams) (interface{}, error) {
|
func (q *Queries) GetTotalCashOutInRange(ctx context.Context, arg GetTotalCashOutInRangeParams) (interface{}, error) {
|
||||||
row := q.db.QueryRow(ctx, GetTotalCashOutInRange, arg.From, arg.To, arg.CompanyID)
|
row := q.db.QueryRow(ctx, GetTotalCashOutInRange, arg.From, arg.To)
|
||||||
var total_cash_out interface{}
|
var total_cash_out interface{}
|
||||||
err := row.Scan(&total_cash_out)
|
err := row.Scan(&total_cash_out)
|
||||||
return total_cash_out, err
|
return total_cash_out, err
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,31 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const AddFavoriteGame = `-- name: AddFavoriteGame :exec
|
||||||
|
INSERT INTO favorite_games (
|
||||||
|
user_id,
|
||||||
|
game_id,
|
||||||
|
created_at
|
||||||
|
) VALUES ($1, $2, NOW())
|
||||||
|
ON CONFLICT (user_id, game_id) DO NOTHING
|
||||||
|
`
|
||||||
|
|
||||||
|
type AddFavoriteGameParams struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
GameID int64 `json:"game_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) AddFavoriteGame(ctx context.Context, arg AddFavoriteGameParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, AddFavoriteGame, arg.UserID, arg.GameID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const CreateVirtualGameHistory = `-- name: CreateVirtualGameHistory :one
|
const CreateVirtualGameHistory = `-- name: CreateVirtualGameHistory :one
|
||||||
INSERT INTO virtual_game_histories (
|
INSERT INTO virtual_game_histories (
|
||||||
session_id,
|
session_id,
|
||||||
user_id,
|
user_id,
|
||||||
|
company_id,
|
||||||
|
provider,
|
||||||
wallet_id,
|
wallet_id,
|
||||||
game_id,
|
game_id,
|
||||||
transaction_type,
|
transaction_type,
|
||||||
|
|
@ -24,11 +45,13 @@ INSERT INTO virtual_game_histories (
|
||||||
reference_transaction_id,
|
reference_transaction_id,
|
||||||
status
|
status
|
||||||
) VALUES (
|
) VALUES (
|
||||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
|
||||||
) RETURNING
|
) RETURNING
|
||||||
id,
|
id,
|
||||||
session_id,
|
session_id,
|
||||||
user_id,
|
user_id,
|
||||||
|
company_id,
|
||||||
|
provider,
|
||||||
wallet_id,
|
wallet_id,
|
||||||
game_id,
|
game_id,
|
||||||
transaction_type,
|
transaction_type,
|
||||||
|
|
@ -44,6 +67,8 @@ INSERT INTO virtual_game_histories (
|
||||||
type CreateVirtualGameHistoryParams struct {
|
type CreateVirtualGameHistoryParams struct {
|
||||||
SessionID pgtype.Text `json:"session_id"`
|
SessionID pgtype.Text `json:"session_id"`
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
|
Provider pgtype.Text `json:"provider"`
|
||||||
WalletID pgtype.Int8 `json:"wallet_id"`
|
WalletID pgtype.Int8 `json:"wallet_id"`
|
||||||
GameID pgtype.Int8 `json:"game_id"`
|
GameID pgtype.Int8 `json:"game_id"`
|
||||||
TransactionType string `json:"transaction_type"`
|
TransactionType string `json:"transaction_type"`
|
||||||
|
|
@ -58,6 +83,8 @@ func (q *Queries) CreateVirtualGameHistory(ctx context.Context, arg CreateVirtua
|
||||||
row := q.db.QueryRow(ctx, CreateVirtualGameHistory,
|
row := q.db.QueryRow(ctx, CreateVirtualGameHistory,
|
||||||
arg.SessionID,
|
arg.SessionID,
|
||||||
arg.UserID,
|
arg.UserID,
|
||||||
|
arg.CompanyID,
|
||||||
|
arg.Provider,
|
||||||
arg.WalletID,
|
arg.WalletID,
|
||||||
arg.GameID,
|
arg.GameID,
|
||||||
arg.TransactionType,
|
arg.TransactionType,
|
||||||
|
|
@ -72,6 +99,8 @@ func (q *Queries) CreateVirtualGameHistory(ctx context.Context, arg CreateVirtua
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.SessionID,
|
&i.SessionID,
|
||||||
&i.UserID,
|
&i.UserID,
|
||||||
|
&i.CompanyID,
|
||||||
|
&i.Provider,
|
||||||
&i.WalletID,
|
&i.WalletID,
|
||||||
&i.GameID,
|
&i.GameID,
|
||||||
&i.TransactionType,
|
&i.TransactionType,
|
||||||
|
|
@ -129,15 +158,17 @@ func (q *Queries) CreateVirtualGameSession(ctx context.Context, arg CreateVirtua
|
||||||
|
|
||||||
const CreateVirtualGameTransaction = `-- name: CreateVirtualGameTransaction :one
|
const CreateVirtualGameTransaction = `-- name: CreateVirtualGameTransaction :one
|
||||||
INSERT INTO virtual_game_transactions (
|
INSERT INTO virtual_game_transactions (
|
||||||
session_id, user_id, wallet_id, transaction_type, amount, currency, external_transaction_id, status
|
session_id, user_id, company_id, provider, wallet_id, transaction_type, amount, currency, external_transaction_id, status
|
||||||
) VALUES (
|
) VALUES (
|
||||||
$1, $2, $3, $4, $5, $6, $7, $8
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
||||||
) RETURNING id, session_id, user_id, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at
|
) RETURNING id, session_id, user_id, company_id, provider, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateVirtualGameTransactionParams struct {
|
type CreateVirtualGameTransactionParams struct {
|
||||||
SessionID int64 `json:"session_id"`
|
SessionID int64 `json:"session_id"`
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
|
Provider pgtype.Text `json:"provider"`
|
||||||
WalletID int64 `json:"wallet_id"`
|
WalletID int64 `json:"wallet_id"`
|
||||||
TransactionType string `json:"transaction_type"`
|
TransactionType string `json:"transaction_type"`
|
||||||
Amount int64 `json:"amount"`
|
Amount int64 `json:"amount"`
|
||||||
|
|
@ -146,10 +177,28 @@ type CreateVirtualGameTransactionParams struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateVirtualGameTransaction(ctx context.Context, arg CreateVirtualGameTransactionParams) (VirtualGameTransaction, error) {
|
type CreateVirtualGameTransactionRow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
SessionID int64 `json:"session_id"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
|
Provider pgtype.Text `json:"provider"`
|
||||||
|
WalletID int64 `json:"wallet_id"`
|
||||||
|
TransactionType string `json:"transaction_type"`
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
ExternalTransactionID string `json:"external_transaction_id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateVirtualGameTransaction(ctx context.Context, arg CreateVirtualGameTransactionParams) (CreateVirtualGameTransactionRow, error) {
|
||||||
row := q.db.QueryRow(ctx, CreateVirtualGameTransaction,
|
row := q.db.QueryRow(ctx, CreateVirtualGameTransaction,
|
||||||
arg.SessionID,
|
arg.SessionID,
|
||||||
arg.UserID,
|
arg.UserID,
|
||||||
|
arg.CompanyID,
|
||||||
|
arg.Provider,
|
||||||
arg.WalletID,
|
arg.WalletID,
|
||||||
arg.TransactionType,
|
arg.TransactionType,
|
||||||
arg.Amount,
|
arg.Amount,
|
||||||
|
|
@ -157,11 +206,13 @@ func (q *Queries) CreateVirtualGameTransaction(ctx context.Context, arg CreateVi
|
||||||
arg.ExternalTransactionID,
|
arg.ExternalTransactionID,
|
||||||
arg.Status,
|
arg.Status,
|
||||||
)
|
)
|
||||||
var i VirtualGameTransaction
|
var i CreateVirtualGameTransactionRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.SessionID,
|
&i.SessionID,
|
||||||
&i.UserID,
|
&i.UserID,
|
||||||
|
&i.CompanyID,
|
||||||
|
&i.Provider,
|
||||||
&i.WalletID,
|
&i.WalletID,
|
||||||
&i.TransactionType,
|
&i.TransactionType,
|
||||||
&i.Amount,
|
&i.Amount,
|
||||||
|
|
@ -199,22 +250,26 @@ func (q *Queries) GetVirtualGameSessionByToken(ctx context.Context, sessionToken
|
||||||
|
|
||||||
const GetVirtualGameSummaryInRange = `-- name: GetVirtualGameSummaryInRange :many
|
const GetVirtualGameSummaryInRange = `-- name: GetVirtualGameSummaryInRange :many
|
||||||
SELECT
|
SELECT
|
||||||
|
c.name AS company_name,
|
||||||
vg.name AS game_name,
|
vg.name AS game_name,
|
||||||
COUNT(vgh.id) AS number_of_bets,
|
COUNT(vgt.id) AS number_of_bets,
|
||||||
COALESCE(SUM(vgh.amount), 0) AS total_transaction_sum
|
COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum
|
||||||
FROM virtual_game_histories vgh
|
FROM virtual_game_transactions vgt
|
||||||
JOIN virtual_games vg ON vgh.game_id = vg.id
|
JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
|
||||||
WHERE vgh.transaction_type = 'BET'
|
JOIN virtual_games vg ON vgs.game_id = vg.id
|
||||||
AND vgh.created_at BETWEEN $1 AND $2
|
JOIN companies c ON vgt.company_id = c.id
|
||||||
GROUP BY vg.name
|
WHERE vgt.transaction_type = 'BET'
|
||||||
|
AND vgt.created_at BETWEEN $1 AND $2
|
||||||
|
GROUP BY c.name, vg.name
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetVirtualGameSummaryInRangeParams struct {
|
type GetVirtualGameSummaryInRangeParams struct {
|
||||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
CreatedAt_2 pgtype.Timestamp `json:"created_at_2"`
|
CreatedAt_2 pgtype.Timestamptz `json:"created_at_2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetVirtualGameSummaryInRangeRow struct {
|
type GetVirtualGameSummaryInRangeRow struct {
|
||||||
|
CompanyName string `json:"company_name"`
|
||||||
GameName string `json:"game_name"`
|
GameName string `json:"game_name"`
|
||||||
NumberOfBets int64 `json:"number_of_bets"`
|
NumberOfBets int64 `json:"number_of_bets"`
|
||||||
TotalTransactionSum interface{} `json:"total_transaction_sum"`
|
TotalTransactionSum interface{} `json:"total_transaction_sum"`
|
||||||
|
|
@ -229,7 +284,12 @@ func (q *Queries) GetVirtualGameSummaryInRange(ctx context.Context, arg GetVirtu
|
||||||
var items []GetVirtualGameSummaryInRangeRow
|
var items []GetVirtualGameSummaryInRangeRow
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i GetVirtualGameSummaryInRangeRow
|
var i GetVirtualGameSummaryInRangeRow
|
||||||
if err := rows.Scan(&i.GameName, &i.NumberOfBets, &i.TotalTransactionSum); err != nil {
|
if err := rows.Scan(
|
||||||
|
&i.CompanyName,
|
||||||
|
&i.GameName,
|
||||||
|
&i.NumberOfBets,
|
||||||
|
&i.TotalTransactionSum,
|
||||||
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
items = append(items, i)
|
items = append(items, i)
|
||||||
|
|
@ -246,9 +306,23 @@ FROM virtual_game_transactions
|
||||||
WHERE external_transaction_id = $1
|
WHERE external_transaction_id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetVirtualGameTransactionByExternalID(ctx context.Context, externalTransactionID string) (VirtualGameTransaction, error) {
|
type GetVirtualGameTransactionByExternalIDRow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
SessionID int64 `json:"session_id"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
WalletID int64 `json:"wallet_id"`
|
||||||
|
TransactionType string `json:"transaction_type"`
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
ExternalTransactionID string `json:"external_transaction_id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetVirtualGameTransactionByExternalID(ctx context.Context, externalTransactionID string) (GetVirtualGameTransactionByExternalIDRow, error) {
|
||||||
row := q.db.QueryRow(ctx, GetVirtualGameTransactionByExternalID, externalTransactionID)
|
row := q.db.QueryRow(ctx, GetVirtualGameTransactionByExternalID, externalTransactionID)
|
||||||
var i VirtualGameTransaction
|
var i GetVirtualGameTransactionByExternalIDRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.SessionID,
|
&i.SessionID,
|
||||||
|
|
@ -265,6 +339,47 @@ func (q *Queries) GetVirtualGameTransactionByExternalID(ctx context.Context, ext
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ListFavoriteGames = `-- name: ListFavoriteGames :many
|
||||||
|
SELECT game_id
|
||||||
|
FROM favorite_games
|
||||||
|
WHERE user_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) ListFavoriteGames(ctx context.Context, userID int64) ([]int64, error) {
|
||||||
|
rows, err := q.db.Query(ctx, ListFavoriteGames, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []int64
|
||||||
|
for rows.Next() {
|
||||||
|
var game_id int64
|
||||||
|
if err := rows.Scan(&game_id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, game_id)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const RemoveFavoriteGame = `-- name: RemoveFavoriteGame :exec
|
||||||
|
DELETE FROM favorite_games
|
||||||
|
WHERE user_id = $1 AND game_id = $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type RemoveFavoriteGameParams struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
GameID int64 `json:"game_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) RemoveFavoriteGame(ctx context.Context, arg RemoveFavoriteGameParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, RemoveFavoriteGame, arg.UserID, arg.GameID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const UpdateVirtualGameSessionStatus = `-- name: UpdateVirtualGameSessionStatus :exec
|
const UpdateVirtualGameSessionStatus = `-- name: UpdateVirtualGameSessionStatus :exec
|
||||||
UPDATE virtual_game_sessions
|
UPDATE virtual_game_sessions
|
||||||
SET status = $2, updated_at = CURRENT_TIMESTAMP
|
SET status = $2, updated_at = CURRENT_TIMESTAMP
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,50 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GetBranchByWalletID = `-- name: GetBranchByWalletID :one
|
||||||
|
SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at
|
||||||
|
FROM branches
|
||||||
|
WHERE wallet_id = $1
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetBranchByWalletID(ctx context.Context, walletID int64) (Branch, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetBranchByWalletID, walletID)
|
||||||
|
var i Branch
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.Location,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.WalletID,
|
||||||
|
&i.BranchManagerID,
|
||||||
|
&i.CompanyID,
|
||||||
|
&i.IsSelfOwned,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetCompanyByWalletID = `-- name: GetCompanyByWalletID :one
|
||||||
|
SELECT id, name, admin_id, wallet_id
|
||||||
|
FROM companies
|
||||||
|
WHERE wallet_id = $1
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetCompanyByWalletID(ctx context.Context, walletID int64) (Company, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetCompanyByWalletID, walletID)
|
||||||
|
var i Company
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.AdminID,
|
||||||
|
&i.WalletID,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const GetCustomerWallet = `-- name: GetCustomerWallet :one
|
const GetCustomerWallet = `-- name: GetCustomerWallet :one
|
||||||
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number
|
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number
|
||||||
FROM customer_wallet_details
|
FROM customer_wallet_details
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type PaymentStatus string
|
||||||
type WithdrawalStatus string
|
type WithdrawalStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
WithdrawalStatusSuccessful WithdrawalStatus = "success"
|
||||||
WithdrawalStatusPending WithdrawalStatus = "pending"
|
WithdrawalStatusPending WithdrawalStatus = "pending"
|
||||||
WithdrawalStatusProcessing WithdrawalStatus = "processing"
|
WithdrawalStatusProcessing WithdrawalStatus = "processing"
|
||||||
WithdrawalStatusCompleted WithdrawalStatus = "completed"
|
WithdrawalStatusCompleted WithdrawalStatus = "completed"
|
||||||
|
|
@ -23,6 +24,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
PaymentStatusSuccessful PaymentStatus = "success"
|
||||||
PaymentStatusPending PaymentStatus = "pending"
|
PaymentStatusPending PaymentStatus = "pending"
|
||||||
PaymentStatusCompleted PaymentStatus = "completed"
|
PaymentStatusCompleted PaymentStatus = "completed"
|
||||||
PaymentStatusFailed PaymentStatus = "failed"
|
PaymentStatusFailed PaymentStatus = "failed"
|
||||||
|
|
@ -70,22 +72,23 @@ type ChapaVerificationResponse struct {
|
||||||
TxRef string `json:"tx_ref"`
|
TxRef string `json:"tx_ref"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Bank struct {
|
// type Bank struct {
|
||||||
ID int `json:"id"`
|
// ID int `json:"id"`
|
||||||
Slug string `json:"slug"`
|
// Slug string `json:"slug"`
|
||||||
Swift string `json:"swift"`
|
// Swift string `json:"swift"`
|
||||||
Name string `json:"name"`
|
// Name string `json:"name"`
|
||||||
AcctLength int `json:"acct_length"`
|
// AcctLength int `json:"acct_length"`
|
||||||
CountryID int `json:"country_id"`
|
// CountryID int `json:"country_id"`
|
||||||
IsMobileMoney int `json:"is_mobilemoney"` // nullable
|
// IsMobileMoney int `json:"is_mobilemoney"` // nullable
|
||||||
IsActive int `json:"is_active"`
|
// IsActive int `json:"is_active"`
|
||||||
IsRTGS int `json:"is_rtgs"`
|
// IsRTGS int `json:"is_rtgs"`
|
||||||
Active int `json:"active"`
|
// Active int `json:"active"`
|
||||||
Is24Hrs int `json:"is_24hrs"` // nullable
|
// Is24Hrs int `json:"is_24hrs"` // nullable
|
||||||
CreatedAt time.Time `json:"created_at"`
|
// CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
// UpdatedAt time.Time `json:"updated_at"`
|
||||||
Currency string `json:"currency"`
|
// Currency string `json:"currency"`
|
||||||
}
|
// BankLogo string `json:"bank_logo"` // URL or base64
|
||||||
|
// }
|
||||||
|
|
||||||
type BankResponse struct {
|
type BankResponse struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
|
@ -142,11 +145,9 @@ type ChapaWithdrawalRequest struct {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
type ChapaWithdrawalResponse struct {
|
type ChapaWithdrawalResponse struct {
|
||||||
Status string `json:"status"`
|
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Data struct {
|
Status string `json:"status"`
|
||||||
Reference string `json:"reference"`
|
Data string `json:"data"` // Accepts string instead of struct
|
||||||
} `json:"data"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChapaTransactionType struct {
|
type ChapaTransactionType struct {
|
||||||
|
|
|
||||||
21
internal/domain/institutions.go
Normal file
21
internal/domain/institutions.go
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Bank struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Swift string `json:"swift"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
AcctLength int `json:"acct_length"`
|
||||||
|
CountryID int `json:"country_id"`
|
||||||
|
IsMobileMoney int `json:"is_mobilemoney"` // nullable
|
||||||
|
IsActive int `json:"is_active"`
|
||||||
|
IsRTGS int `json:"is_rtgs"`
|
||||||
|
Active int `json:"active"`
|
||||||
|
Is24Hrs int `json:"is_24hrs"` // nullable
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
BankLogo string `json:"bank_logo"` // URL or base64
|
||||||
|
}
|
||||||
15
internal/domain/issue_reporting.go
Normal file
15
internal/domain/issue_reporting.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ReportedIssue struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
CustomerID int64 `json:"customer_id"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IssueType string `json:"issue_type"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
@ -97,15 +97,16 @@ func FromJSON(data []byte) (*Notification, error) {
|
||||||
|
|
||||||
func ReceiverFromRole(role Role) NotificationRecieverSide {
|
func ReceiverFromRole(role Role) NotificationRecieverSide {
|
||||||
|
|
||||||
if role == RoleAdmin {
|
switch role {
|
||||||
|
case RoleAdmin:
|
||||||
return NotificationRecieverSideAdmin
|
return NotificationRecieverSideAdmin
|
||||||
} else if role == RoleCashier {
|
case RoleCashier:
|
||||||
return NotificationRecieverSideCashier
|
return NotificationRecieverSideCashier
|
||||||
} else if role == RoleBranchManager {
|
case RoleBranchManager:
|
||||||
return NotificationRecieverSideBranchManager
|
return NotificationRecieverSideBranchManager
|
||||||
} else if role == RoleCustomer {
|
case RoleCustomer:
|
||||||
return NotificationRecieverSideCustomer
|
return NotificationRecieverSideCustomer
|
||||||
} else {
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ type ReportData struct {
|
||||||
Deposits float64
|
Deposits float64
|
||||||
TotalTickets int64
|
TotalTickets int64
|
||||||
VirtualGameStats []VirtualGameStat
|
VirtualGameStats []VirtualGameStat
|
||||||
|
CompanyReports []CompanyReport
|
||||||
|
BranchReports []BranchReport
|
||||||
}
|
}
|
||||||
|
|
||||||
type VirtualGameStat struct {
|
type VirtualGameStat struct {
|
||||||
|
|
@ -366,3 +368,41 @@ type CashierPerformance struct {
|
||||||
LastActivity time.Time `json:"last_activity"`
|
LastActivity time.Time `json:"last_activity"`
|
||||||
ActiveDays int `json:"active_days"`
|
ActiveDays int `json:"active_days"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CompanyWalletBalance struct {
|
||||||
|
CompanyID int64 `json:"company_id"`
|
||||||
|
CompanyName string `json:"company_name"`
|
||||||
|
Balance float64 `json:"balance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BranchWalletBalance struct {
|
||||||
|
BranchID int64 `json:"branch_id"`
|
||||||
|
BranchName string `json:"branch_name"`
|
||||||
|
CompanyID int64 `json:"company_id"`
|
||||||
|
Balance float64 `json:"balance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LiveWalletMetrics struct {
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
CompanyBalances []CompanyWalletBalance `json:"company_balances"`
|
||||||
|
BranchBalances []BranchWalletBalance `json:"branch_balances"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompanyReport struct {
|
||||||
|
CompanyID int64
|
||||||
|
CompanyName string
|
||||||
|
TotalBets int64
|
||||||
|
TotalCashIn float64
|
||||||
|
TotalCashOut float64
|
||||||
|
TotalCashBacks float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type BranchReport struct {
|
||||||
|
BranchID int64
|
||||||
|
BranchName string
|
||||||
|
CompanyID int64
|
||||||
|
TotalBets int64
|
||||||
|
TotalCashIn float64
|
||||||
|
TotalCashOut float64
|
||||||
|
TotalCashBacks float64
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,30 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Provider string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PROVIDER_POPOK Provider = "PopOk"
|
||||||
|
PROVIDER_ALEA_PLAY Provider = "AleaPlay"
|
||||||
|
PROVIDER_VELI_GAMES Provider = "VeliGames"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FavoriteGame struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
GameID int64 `json:"game_id"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FavoriteGameRequest struct {
|
||||||
|
GameID int64 `json:"game_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FavoriteGameResponse struct {
|
||||||
|
GameID int64 `json:"game_id"`
|
||||||
|
GameName string `json:"game_name"`
|
||||||
|
}
|
||||||
|
|
||||||
type VirtualGame struct {
|
type VirtualGame struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -42,6 +66,8 @@ type VirtualGameHistory struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SessionID string `json:"session_id,omitempty"` // Optional, if session tracking is used
|
SessionID string `json:"session_id,omitempty"` // Optional, if session tracking is used
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
|
CompanyID int64 `json:"company_id"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
WalletID *int64 `json:"wallet_id,omitempty"` // Optional if wallet detail is needed
|
WalletID *int64 `json:"wallet_id,omitempty"` // Optional if wallet detail is needed
|
||||||
GameID *int64 `json:"game_id,omitempty"` // Optional for game-level analysis
|
GameID *int64 `json:"game_id,omitempty"` // Optional for game-level analysis
|
||||||
TransactionType string `json:"transaction_type"` // BET, WIN, CANCEL, etc.
|
TransactionType string `json:"transaction_type"` // BET, WIN, CANCEL, etc.
|
||||||
|
|
@ -58,6 +84,9 @@ type VirtualGameTransaction struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SessionID int64 `json:"session_id"`
|
SessionID int64 `json:"session_id"`
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
|
CompanyID int64 `json:"company_id"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
GameID string `json:"game_id"`
|
||||||
WalletID int64 `json:"wallet_id"`
|
WalletID int64 `json:"wallet_id"`
|
||||||
TransactionType string `json:"transaction_type"` // BET, WIN, REFUND, CASHOUT, etc.
|
TransactionType string `json:"transaction_type"` // BET, WIN, REFUND, CASHOUT, etc.
|
||||||
Amount int64 `json:"amount"` // Always in cents
|
Amount int64 `json:"amount"` // Always in cents
|
||||||
|
|
@ -159,6 +188,11 @@ type PopOKWinResponse struct {
|
||||||
Balance float64 `json:"balance"`
|
Balance float64 `json:"balance"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PopOKGenerateTokenRequest struct {
|
||||||
|
GameID string `json:"newGameId"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
type PopOKCancelRequest struct {
|
type PopOKCancelRequest struct {
|
||||||
ExternalToken string `json:"externalToken"`
|
ExternalToken string `json:"externalToken"`
|
||||||
PlayerID string `json:"playerId"`
|
PlayerID string `json:"playerId"`
|
||||||
|
|
@ -172,6 +206,10 @@ type PopOKCancelResponse struct {
|
||||||
Balance float64 `json:"balance"`
|
Balance float64 `json:"balance"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PopOKGenerateTokenResponse struct {
|
||||||
|
NewToken string `json:"newToken"`
|
||||||
|
}
|
||||||
|
|
||||||
type AleaPlayCallback struct {
|
type AleaPlayCallback struct {
|
||||||
EventID string `json:"event_id"`
|
EventID string `json:"event_id"`
|
||||||
TransactionID string `json:"transaction_id"`
|
TransactionID string `json:"transaction_id"`
|
||||||
|
|
|
||||||
139
internal/repository/institutions.go
Normal file
139
internal/repository/institutions.go
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BankRepository interface {
|
||||||
|
CreateBank(ctx context.Context, bank *domain.Bank) error
|
||||||
|
GetBankByID(ctx context.Context, id int) (*domain.Bank, error)
|
||||||
|
GetAllBanks(ctx context.Context, countryID *int, isActive *int) ([]domain.Bank, error)
|
||||||
|
UpdateBank(ctx context.Context, bank *domain.Bank) error
|
||||||
|
DeleteBank(ctx context.Context, id int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type BankRepo struct {
|
||||||
|
store *Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBankRepository(store *Store) BankRepository {
|
||||||
|
return &BankRepo{store: store}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BankRepo) CreateBank(ctx context.Context, bank *domain.Bank) error {
|
||||||
|
params := dbgen.CreateBankParams{
|
||||||
|
Slug: bank.Slug,
|
||||||
|
Swift: bank.Swift,
|
||||||
|
Name: bank.Name,
|
||||||
|
AcctLength: int32(bank.AcctLength),
|
||||||
|
CountryID: int32(bank.CountryID),
|
||||||
|
IsMobilemoney: pgtype.Int4{Int32: int32(bank.IsMobileMoney), Valid: true},
|
||||||
|
IsActive: int32(bank.IsActive),
|
||||||
|
IsRtgs: int32(bank.IsRTGS),
|
||||||
|
Active: int32(bank.Active),
|
||||||
|
Is24hrs: pgtype.Int4{Int32: int32(bank.Is24Hrs), Valid: true},
|
||||||
|
Currency: bank.Currency,
|
||||||
|
BankLogo: pgtype.Text{String: bank.BankLogo, Valid: true},
|
||||||
|
}
|
||||||
|
createdBank, err := r.store.queries.CreateBank(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Update the ID and timestamps on the passed struct
|
||||||
|
bank.ID = int(createdBank.ID)
|
||||||
|
bank.CreatedAt = createdBank.CreatedAt.Time
|
||||||
|
bank.UpdatedAt = createdBank.UpdatedAt.Time
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BankRepo) GetBankByID(ctx context.Context, id int) (*domain.Bank, error) {
|
||||||
|
dbBank, err := r.store.queries.GetBankByID(ctx, int64(id))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mapDBBankToDomain(&dbBank), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BankRepo) GetAllBanks(ctx context.Context, countryID *int, isActive *int) ([]domain.Bank, error) {
|
||||||
|
params := dbgen.GetAllBanksParams{
|
||||||
|
CountryID: pgtype.Int4{},
|
||||||
|
IsActive: pgtype.Int4{},
|
||||||
|
}
|
||||||
|
if countryID != nil {
|
||||||
|
params.CountryID = pgtype.Int4{Int32: int32(*countryID), Valid: true}
|
||||||
|
}
|
||||||
|
if isActive != nil {
|
||||||
|
params.IsActive = pgtype.Int4{Int32: int32(*isActive), Valid: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbBanks, err := r.store.queries.GetAllBanks(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
banks := make([]domain.Bank, len(dbBanks))
|
||||||
|
for i, b := range dbBanks {
|
||||||
|
banks[i] = *mapDBBankToDomain(&b)
|
||||||
|
}
|
||||||
|
return banks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BankRepo) UpdateBank(ctx context.Context, bank *domain.Bank) error {
|
||||||
|
params := dbgen.UpdateBankParams{
|
||||||
|
ID: int64(bank.ID),
|
||||||
|
Slug: pgtype.Text{String: bank.Slug, Valid: true},
|
||||||
|
Swift: pgtype.Text{String: bank.Swift, Valid: true},
|
||||||
|
Name: pgtype.Text{String: bank.Name, Valid: true},
|
||||||
|
AcctLength: pgtype.Int4{Int32: int32(bank.AcctLength), Valid: true},
|
||||||
|
CountryID: pgtype.Int4{Int32: int32(bank.CountryID), Valid: true},
|
||||||
|
IsMobilemoney: pgtype.Int4{Int32: int32(bank.IsMobileMoney), Valid: true},
|
||||||
|
IsActive: pgtype.Int4{Int32: int32(bank.IsActive), Valid: true},
|
||||||
|
IsRtgs: pgtype.Int4{Int32: int32(bank.IsRTGS), Valid: true},
|
||||||
|
Active: pgtype.Int4{Int32: int32(bank.Active), Valid: true},
|
||||||
|
Is24hrs: pgtype.Int4{Int32: int32(bank.Is24Hrs), Valid: true},
|
||||||
|
Currency: pgtype.Text{String: bank.Currency, Valid: true},
|
||||||
|
BankLogo: pgtype.Text{String: bank.BankLogo, Valid: true},
|
||||||
|
}
|
||||||
|
updatedBank, err := r.store.queries.UpdateBank(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update timestamps in domain struct
|
||||||
|
bank.UpdatedAt = updatedBank.UpdatedAt.Time
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BankRepo) DeleteBank(ctx context.Context, id int) error {
|
||||||
|
return r.store.queries.DeleteBank(ctx, int64(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to map DB struct to domain
|
||||||
|
func mapDBBankToDomain(dbBank *dbgen.Bank) *domain.Bank {
|
||||||
|
return &domain.Bank{
|
||||||
|
ID: int(dbBank.ID),
|
||||||
|
Slug: dbBank.Slug,
|
||||||
|
Swift: dbBank.Swift,
|
||||||
|
Name: dbBank.Name,
|
||||||
|
AcctLength: int(dbBank.AcctLength),
|
||||||
|
CountryID: int(dbBank.CountryID),
|
||||||
|
IsMobileMoney: int(dbBank.IsMobilemoney.Int32),
|
||||||
|
IsActive: int(dbBank.IsActive),
|
||||||
|
IsRTGS: int(dbBank.IsRtgs),
|
||||||
|
Active: int(dbBank.Active),
|
||||||
|
Is24Hrs: int(dbBank.Is24hrs.Int32),
|
||||||
|
CreatedAt: dbBank.CreatedAt.Time,
|
||||||
|
UpdatedAt: dbBank.UpdatedAt.Time,
|
||||||
|
Currency: dbBank.Currency,
|
||||||
|
BankLogo: dbBank.BankLogo.String,
|
||||||
|
}
|
||||||
|
}
|
||||||
65
internal/repository/issue_reporting.go
Normal file
65
internal/repository/issue_reporting.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReportedIssueRepository interface {
|
||||||
|
CreateReportedIssue(ctx context.Context, arg dbgen.CreateReportedIssueParams) (dbgen.ReportedIssue, error)
|
||||||
|
ListReportedIssues(ctx context.Context, limit, offset int32) ([]dbgen.ReportedIssue, error)
|
||||||
|
ListReportedIssuesByCustomer(ctx context.Context, customerID int64, limit, offset int32) ([]dbgen.ReportedIssue, error)
|
||||||
|
CountReportedIssues(ctx context.Context) (int64, error)
|
||||||
|
CountReportedIssuesByCustomer(ctx context.Context, customerID int64) (int64, error)
|
||||||
|
UpdateReportedIssueStatus(ctx context.Context, id int64, status string) error
|
||||||
|
DeleteReportedIssue(ctx context.Context, id int64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReportedIssueRepo struct {
|
||||||
|
store *Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReportedIssueRepository(store *Store) ReportedIssueRepository {
|
||||||
|
return &ReportedIssueRepo{store: store}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReportedIssueRepo) CreateReportedIssue(ctx context.Context, arg dbgen.CreateReportedIssueParams) (dbgen.ReportedIssue, error) {
|
||||||
|
return s.store.queries.CreateReportedIssue(ctx, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReportedIssueRepo) ListReportedIssues(ctx context.Context, limit, offset int32) ([]dbgen.ReportedIssue, error) {
|
||||||
|
params := dbgen.ListReportedIssuesParams{
|
||||||
|
Limit: limit,
|
||||||
|
Offset: offset,
|
||||||
|
}
|
||||||
|
return s.store.queries.ListReportedIssues(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReportedIssueRepo) ListReportedIssuesByCustomer(ctx context.Context, customerID int64, limit, offset int32) ([]dbgen.ReportedIssue, error) {
|
||||||
|
params := dbgen.ListReportedIssuesByCustomerParams{
|
||||||
|
CustomerID: customerID,
|
||||||
|
Limit: limit,
|
||||||
|
Offset: offset,
|
||||||
|
}
|
||||||
|
return s.store.queries.ListReportedIssuesByCustomer(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReportedIssueRepo) CountReportedIssues(ctx context.Context) (int64, error) {
|
||||||
|
return s.store.queries.CountReportedIssues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReportedIssueRepo) CountReportedIssuesByCustomer(ctx context.Context, customerID int64) (int64, error) {
|
||||||
|
return s.store.queries.CountReportedIssuesByCustomer(ctx, customerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReportedIssueRepo) UpdateReportedIssueStatus(ctx context.Context, id int64, status string) error {
|
||||||
|
return s.store.queries.UpdateReportedIssueStatus(ctx, dbgen.UpdateReportedIssueStatusParams{
|
||||||
|
ID: id,
|
||||||
|
Status: status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReportedIssueRepo) DeleteReportedIssue(ctx context.Context, id int64) error {
|
||||||
|
return s.store.queries.DeleteReportedIssue(ctx, id)
|
||||||
|
}
|
||||||
|
|
@ -317,6 +317,40 @@ func (s *Store) CountUnreadNotifications(ctx context.Context, userID int64) (int
|
||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetCompanyByWalletID(ctx context.Context, walletID int64) (domain.Company, error) {
|
||||||
|
dbCompany, err := s.queries.GetCompanyByWalletID(ctx, walletID)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Company{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.Company{
|
||||||
|
ID: dbCompany.ID,
|
||||||
|
Name: dbCompany.Name,
|
||||||
|
AdminID: dbCompany.AdminID,
|
||||||
|
WalletID: dbCompany.WalletID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetBranchByWalletID(ctx context.Context, walletID int64) (domain.Branch, error) {
|
||||||
|
dbBranch, err := s.queries.GetBranchByWalletID(ctx, walletID)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Branch{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.Branch{
|
||||||
|
ID: dbBranch.ID,
|
||||||
|
Name: dbBranch.Name,
|
||||||
|
Location: dbBranch.Location,
|
||||||
|
IsSuspended: dbBranch.IsActive,
|
||||||
|
WalletID: dbBranch.WalletID,
|
||||||
|
BranchManagerID: dbBranch.BranchManagerID,
|
||||||
|
CompanyID: dbBranch.CompanyID,
|
||||||
|
IsSelfOwned: dbBranch.IsSelfOwned,
|
||||||
|
// Creat: dbBranch.CreatedAt.Time,
|
||||||
|
// UpdatedAt: dbBranch.UpdatedAt.Time,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// func (s *Store) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) {
|
// func (s *Store) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) {
|
||||||
// dbNotifications, err := s.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{
|
// dbNotifications, err := s.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{
|
||||||
// Limit: int32(limit),
|
// Limit: int32(limit),
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,15 @@ type ReportRepository interface {
|
||||||
SaveReport(report *domain.Report) error
|
SaveReport(report *domain.Report) error
|
||||||
FindReportsByTimeFrame(timeFrame domain.TimeFrame, limit int) ([]*domain.Report, error)
|
FindReportsByTimeFrame(timeFrame domain.TimeFrame, limit int) ([]*domain.Report, error)
|
||||||
|
|
||||||
GetTotalCashOutInRange(ctx context.Context, from, to time.Time, companyID int64) (float64, error)
|
GetTotalCashOutInRange(ctx context.Context, from, to time.Time) (float64, error)
|
||||||
GetTotalCashMadeInRange(ctx context.Context, from, to time.Time, companyID int64) (float64, error)
|
GetTotalCashMadeInRange(ctx context.Context, from, to time.Time) (float64, error)
|
||||||
GetTotalCashBacksInRange(ctx context.Context, from, to time.Time, companyID int64) (float64, error)
|
GetTotalCashBacksInRange(ctx context.Context, from, to time.Time) (float64, error)
|
||||||
GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time, companyID int64) (int64, error)
|
GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time) (int64, error)
|
||||||
GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error)
|
GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error)
|
||||||
GetAllTicketsInRange(ctx context.Context, from, to time.Time) (dbgen.GetAllTicketsInRangeRow, error)
|
GetAllTicketsInRange(ctx context.Context, from, to time.Time) (dbgen.GetAllTicketsInRangeRow, error)
|
||||||
GetWalletTransactionsInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetWalletTransactionsInRangeRow, error)
|
GetWalletTransactionsInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetWalletTransactionsInRangeRow, error)
|
||||||
|
GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error)
|
||||||
|
GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReportRepo struct {
|
type ReportRepo struct {
|
||||||
|
|
@ -117,20 +119,18 @@ func (r *ReportRepo) FindReportsByTimeFrame(timeFrame domain.TimeFrame, limit in
|
||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReportRepo) GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time, companyID int64) (int64, error) {
|
func (r *ReportRepo) GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time) (int64, error) {
|
||||||
params := dbgen.GetTotalBetsMadeInRangeParams{
|
params := dbgen.GetTotalBetsMadeInRangeParams{
|
||||||
From: ToPgTimestamp(from),
|
From: ToPgTimestamp(from),
|
||||||
To: ToPgTimestamp(to),
|
To: ToPgTimestamp(to),
|
||||||
CompanyID: ToPgInt8(companyID),
|
|
||||||
}
|
}
|
||||||
return r.store.queries.GetTotalBetsMadeInRange(ctx, params)
|
return r.store.queries.GetTotalBetsMadeInRange(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReportRepo) GetTotalCashBacksInRange(ctx context.Context, from, to time.Time, companyID int64) (float64, error) {
|
func (r *ReportRepo) GetTotalCashBacksInRange(ctx context.Context, from, to time.Time) (float64, error) {
|
||||||
params := dbgen.GetTotalCashBacksInRangeParams{
|
params := dbgen.GetTotalCashBacksInRangeParams{
|
||||||
From: ToPgTimestamp(from),
|
From: ToPgTimestamp(from),
|
||||||
To: ToPgTimestamp(to),
|
To: ToPgTimestamp(to),
|
||||||
CompanyID: ToPgInt8(companyID),
|
|
||||||
}
|
}
|
||||||
value, err := r.store.queries.GetTotalCashBacksInRange(ctx, params)
|
value, err := r.store.queries.GetTotalCashBacksInRange(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -139,11 +139,10 @@ func (r *ReportRepo) GetTotalCashBacksInRange(ctx context.Context, from, to time
|
||||||
return parseFloat(value)
|
return parseFloat(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReportRepo) GetTotalCashMadeInRange(ctx context.Context, from, to time.Time, companyID int64) (float64, error) {
|
func (r *ReportRepo) GetTotalCashMadeInRange(ctx context.Context, from, to time.Time) (float64, error) {
|
||||||
params := dbgen.GetTotalCashMadeInRangeParams{
|
params := dbgen.GetTotalCashMadeInRangeParams{
|
||||||
From: ToPgTimestamp(from),
|
From: ToPgTimestamp(from),
|
||||||
To: ToPgTimestamp(to),
|
To: ToPgTimestamp(to),
|
||||||
CompanyID: ToPgInt8(companyID),
|
|
||||||
}
|
}
|
||||||
value, err := r.store.queries.GetTotalCashMadeInRange(ctx, params)
|
value, err := r.store.queries.GetTotalCashMadeInRange(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -152,11 +151,10 @@ func (r *ReportRepo) GetTotalCashMadeInRange(ctx context.Context, from, to time.
|
||||||
return parseFloat(value)
|
return parseFloat(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReportRepo) GetTotalCashOutInRange(ctx context.Context, from, to time.Time, companyID int64) (float64, error) {
|
func (r *ReportRepo) GetTotalCashOutInRange(ctx context.Context, from, to time.Time) (float64, error) {
|
||||||
params := dbgen.GetTotalCashOutInRangeParams{
|
params := dbgen.GetTotalCashOutInRangeParams{
|
||||||
From: ToPgTimestamp(from),
|
From: ToPgTimestamp(from),
|
||||||
To: ToPgTimestamp(to),
|
To: ToPgTimestamp(to),
|
||||||
CompanyID: ToPgInt8(companyID),
|
|
||||||
}
|
}
|
||||||
value, err := r.store.queries.GetTotalCashOutInRange(ctx, params)
|
value, err := r.store.queries.GetTotalCashOutInRange(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -183,8 +181,8 @@ func (r *ReportRepo) GetAllTicketsInRange(ctx context.Context, from, to time.Tim
|
||||||
|
|
||||||
func (r *ReportRepo) GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error) {
|
func (r *ReportRepo) GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error) {
|
||||||
params := dbgen.GetVirtualGameSummaryInRangeParams{
|
params := dbgen.GetVirtualGameSummaryInRangeParams{
|
||||||
CreatedAt: ToPgTimestamp(from),
|
CreatedAt: ToPgTimestamptz(from),
|
||||||
CreatedAt_2: ToPgTimestamp(to),
|
CreatedAt_2: ToPgTimestamptz(to),
|
||||||
}
|
}
|
||||||
return r.store.queries.GetVirtualGameSummaryInRange(ctx, params)
|
return r.store.queries.GetVirtualGameSummaryInRange(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
@ -193,8 +191,8 @@ func ToPgTimestamp(t time.Time) pgtype.Timestamp {
|
||||||
return pgtype.Timestamp{Time: t, Valid: true}
|
return pgtype.Timestamp{Time: t, Valid: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToPgInt8(i int64) pgtype.Int8 {
|
func ToPgTimestamptz(t time.Time) pgtype.Timestamptz {
|
||||||
return pgtype.Int8{Int64: i, Valid: true}
|
return pgtype.Timestamptz{Time: t, Valid: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFloat(value interface{}) (float64, error) {
|
func parseFloat(value interface{}) (float64, error) {
|
||||||
|
|
@ -218,3 +216,19 @@ func parseFloat(value interface{}) (float64, error) {
|
||||||
return 0, fmt.Errorf("unexpected type %T for value: %+v", v, v)
|
return 0, fmt.Errorf("unexpected type %T for value: %+v", v, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ReportRepo) GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error) {
|
||||||
|
params := dbgen.GetCompanyWiseReportParams{
|
||||||
|
From: ToPgTimestamp(from),
|
||||||
|
To: ToPgTimestamp(to),
|
||||||
|
}
|
||||||
|
return r.store.queries.GetCompanyWiseReport(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ReportRepo) GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error) {
|
||||||
|
params := dbgen.GetBranchWiseReportParams{
|
||||||
|
From: ToPgTimestamp(from),
|
||||||
|
To: ToPgTimestamp(to),
|
||||||
|
}
|
||||||
|
return r.store.queries.GetBranchWiseReport(ctx, params)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ type VirtualGameRepository interface {
|
||||||
GetVirtualGameTransactionByExternalID(ctx context.Context, externalID string) (*domain.VirtualGameTransaction, error)
|
GetVirtualGameTransactionByExternalID(ctx context.Context, externalID string) (*domain.VirtualGameTransaction, error)
|
||||||
UpdateVirtualGameTransactionStatus(ctx context.Context, id int64, status string) error
|
UpdateVirtualGameTransactionStatus(ctx context.Context, id int64, status string) error
|
||||||
// WithTransaction(ctx context.Context, fn func(ctx context.Context) error) error
|
// WithTransaction(ctx context.Context, fn func(ctx context.Context) error) error
|
||||||
|
AddFavoriteGame(ctx context.Context, userID, gameID int64) error
|
||||||
|
RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error
|
||||||
|
ListFavoriteGames(ctx context.Context, userID int64) ([]int64, error)
|
||||||
|
|
||||||
GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
|
GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
|
||||||
GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error)
|
GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error)
|
||||||
|
|
@ -38,6 +41,26 @@ func NewVirtualGameRepository(store *Store) VirtualGameRepository {
|
||||||
return &VirtualGameRepo{store: store}
|
return &VirtualGameRepo{store: store}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *VirtualGameRepo) AddFavoriteGame(ctx context.Context, userID, gameID int64) error {
|
||||||
|
params := dbgen.AddFavoriteGameParams{
|
||||||
|
UserID: userID,
|
||||||
|
GameID: gameID,
|
||||||
|
}
|
||||||
|
return r.store.queries.AddFavoriteGame(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VirtualGameRepo) RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error {
|
||||||
|
params := dbgen.RemoveFavoriteGameParams{
|
||||||
|
UserID: userID,
|
||||||
|
GameID: gameID,
|
||||||
|
}
|
||||||
|
return r.store.queries.RemoveFavoriteGame(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VirtualGameRepo) ListFavoriteGames(ctx context.Context, userID int64) ([]int64, error) {
|
||||||
|
return r.store.queries.ListFavoriteGames(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *VirtualGameRepo) CreateVirtualGameSession(ctx context.Context, session *domain.VirtualGameSession) error {
|
func (r *VirtualGameRepo) CreateVirtualGameSession(ctx context.Context, session *domain.VirtualGameSession) error {
|
||||||
params := dbgen.CreateVirtualGameSessionParams{
|
params := dbgen.CreateVirtualGameSessionParams{
|
||||||
UserID: session.UserID,
|
UserID: session.UserID,
|
||||||
|
|
|
||||||
|
|
@ -275,3 +275,4 @@ func (s *Store) GetTotalWallets(ctx context.Context, filter domain.ReportFilter)
|
||||||
|
|
||||||
return total, nil
|
return total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,51 @@ func (c *Client) ManualVerifyPayment(ctx context.Context, txRef string) (*domain
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) ManualVerifyTransfer(ctx context.Context, txRef string) (*domain.ChapaVerificationResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/transfers/verify/%s", c.baseURL, txRef)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", "Bearer "+c.secretKey)
|
||||||
|
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var status domain.PaymentStatus
|
||||||
|
switch response.Status {
|
||||||
|
case "success":
|
||||||
|
status = domain.PaymentStatusCompleted
|
||||||
|
default:
|
||||||
|
status = domain.PaymentStatusFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
return &domain.ChapaVerificationResponse{
|
||||||
|
Status: string(status),
|
||||||
|
Amount: response.Amount,
|
||||||
|
Currency: response.Currency,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) FetchSupportedBanks(ctx context.Context) ([]domain.Bank, error) {
|
func (c *Client) FetchSupportedBanks(ctx context.Context) ([]domain.Bank, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/banks", nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/banks", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -223,10 +268,6 @@ func (c *Client) FetchSupportedBanks(ctx context.Context) ([]domain.Bank, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) InitiateTransfer(ctx context.Context, req domain.ChapaWithdrawalRequest) (bool, error) {
|
func (c *Client) InitiateTransfer(ctx context.Context, req domain.ChapaWithdrawalRequest) (bool, error) {
|
||||||
// base, err := url.Parse(c.baseURL)
|
|
||||||
// if err != nil {
|
|
||||||
// return false, fmt.Errorf("invalid base URL: %w", err)
|
|
||||||
// }
|
|
||||||
endpoint := c.baseURL + "/transfers"
|
endpoint := c.baseURL + "/transfers"
|
||||||
fmt.Printf("\n\nChapa withdrawal URL is %v\n\n", endpoint)
|
fmt.Printf("\n\nChapa withdrawal URL is %v\n\n", endpoint)
|
||||||
|
|
||||||
|
|
@ -240,7 +281,9 @@ func (c *Client) InitiateTransfer(ctx context.Context, req domain.ChapaWithdrawa
|
||||||
return false, fmt.Errorf("failed to create request: %w", err)
|
return false, fmt.Errorf("failed to create request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setHeaders(httpReq)
|
// Set headers here
|
||||||
|
httpReq.Header.Set("Authorization", "Bearer "+c.secretKey)
|
||||||
|
httpReq.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
resp, err := c.httpClient.Do(httpReq)
|
resp, err := c.httpClient.Do(httpReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -249,7 +292,8 @@ func (c *Client) InitiateTransfer(ctx context.Context, req domain.ChapaWithdrawa
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return false, fmt.Errorf("chapa api returned status: %d", resp.StatusCode)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
return false, fmt.Errorf("chapa api returned status: %d - %s", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
var response domain.ChapaWithdrawalResponse
|
var response domain.ChapaWithdrawalResponse
|
||||||
|
|
@ -257,7 +301,7 @@ func (c *Client) InitiateTransfer(ctx context.Context, req domain.ChapaWithdrawa
|
||||||
return false, fmt.Errorf("failed to decode response: %w", err)
|
return false, fmt.Errorf("failed to decode response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Status == string(domain.WithdrawalStatusProcessing), nil
|
return response.Status == string(domain.WithdrawalStatusSuccessful), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) VerifyTransfer(ctx context.Context, reference string) (*domain.ChapaVerificationResponse, error) {
|
func (c *Client) VerifyTransfer(ctx context.Context, reference string) (*domain.ChapaVerificationResponse, error) {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
|
@ -60,7 +61,9 @@ func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount doma
|
||||||
var senderWallet domain.Wallet
|
var senderWallet domain.Wallet
|
||||||
|
|
||||||
// Generate unique reference
|
// Generate unique reference
|
||||||
reference := uuid.New().String()
|
// reference := uuid.New().String()
|
||||||
|
reference := fmt.Sprintf("chapa-deposit-%d-%s", userID, uuid.New().String())
|
||||||
|
|
||||||
senderWallets, err := s.walletStore.GetWalletsByUser(ctx, userID)
|
senderWallets, err := s.walletStore.GetWalletsByUser(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get sender wallets: %w", err)
|
return "", fmt.Errorf("failed to get sender wallets: %w", err)
|
||||||
|
|
@ -92,8 +95,7 @@ func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount doma
|
||||||
Verified: false,
|
Verified: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize payment with Chapa
|
payload := domain.ChapaDepositRequest{
|
||||||
response, err := s.chapaClient.InitializePayment(ctx, domain.ChapaDepositRequest{
|
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
Currency: "ETB",
|
Currency: "ETB",
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
|
|
@ -102,7 +104,12 @@ func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount doma
|
||||||
TxRef: reference,
|
TxRef: reference,
|
||||||
CallbackURL: s.cfg.CHAPA_CALLBACK_URL,
|
CallbackURL: s.cfg.CHAPA_CALLBACK_URL,
|
||||||
ReturnURL: s.cfg.CHAPA_RETURN_URL,
|
ReturnURL: s.cfg.CHAPA_RETURN_URL,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Initialize payment with Chapa
|
||||||
|
response, err := s.chapaClient.InitializePayment(ctx, payload)
|
||||||
|
|
||||||
|
fmt.Printf("\n\nChapa payload is: %+v\n\n", payload)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Update payment status to failed
|
// Update payment status to failed
|
||||||
|
|
@ -110,10 +117,14 @@ func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount doma
|
||||||
return "", fmt.Errorf("failed to initialize payment: %w", err)
|
return "", fmt.Errorf("failed to initialize payment: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil {
|
tempTransfer, err := s.transferStore.CreateTransfer(ctx, transfer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to save payment: %w", err)
|
return "", fmt.Errorf("failed to save payment: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n\nTemp transfer is: %v\n\n", tempTransfer)
|
||||||
|
|
||||||
return response.CheckoutURL, nil
|
return response.CheckoutURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,12 +196,16 @@ func (s *Service) InitiateWithdrawal(ctx context.Context, userID int64, req doma
|
||||||
}
|
}
|
||||||
|
|
||||||
success, err := s.chapaClient.InitiateTransfer(ctx, transferReq)
|
success, err := s.chapaClient.InitiateTransfer(ctx, transferReq)
|
||||||
if err != nil || !success {
|
if err != nil {
|
||||||
// Update withdrawal status to failed
|
|
||||||
_ = s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusFailed))
|
_ = s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusFailed))
|
||||||
return nil, fmt.Errorf("failed to initiate transfer: %w", err)
|
return nil, fmt.Errorf("failed to initiate transfer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !success {
|
||||||
|
_ = s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusFailed))
|
||||||
|
return nil, errors.New("chapa rejected the transfer request")
|
||||||
|
}
|
||||||
|
|
||||||
// Update withdrawal status to processing
|
// Update withdrawal status to processing
|
||||||
if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusProcessing)); err != nil {
|
if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusProcessing)); err != nil {
|
||||||
return nil, fmt.Errorf("failed to update withdrawal status: %w", err)
|
return nil, fmt.Errorf("failed to update withdrawal status: %w", err)
|
||||||
|
|
@ -212,50 +227,68 @@ func (s *Service) GetSupportedBanks(ctx context.Context) ([]domain.Bank, error)
|
||||||
return banks, nil
|
return banks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) ManualVerifTransaction(ctx context.Context, txRef string) (*domain.ChapaVerificationResponse, error) {
|
func (s *Service) ManuallyVerify(ctx context.Context, txRef string) (*domain.ChapaVerificationResponse, error) {
|
||||||
// First check if we already have a verified record
|
// Lookup transfer by reference
|
||||||
transfer, err := s.transferStore.GetTransferByReference(ctx, txRef)
|
transfer, err := s.transferStore.GetTransferByReference(ctx, txRef)
|
||||||
if err == nil && transfer.Verified {
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("transfer not found for reference %s: %w", txRef, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if transfer.Verified {
|
||||||
return &domain.ChapaVerificationResponse{
|
return &domain.ChapaVerificationResponse{
|
||||||
Status: string(domain.PaymentStatusCompleted),
|
Status: string(domain.PaymentStatusCompleted),
|
||||||
Amount: float64(transfer.Amount) / 100, // Convert from cents/kobo
|
Amount: float64(transfer.Amount) / 100,
|
||||||
Currency: "ETB",
|
Currency: "ETB",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\n\nSender wallet ID is:%v\n\n", transfer.SenderWalletID.Value)
|
// Validate sender wallet
|
||||||
fmt.Printf("\n\nTransfer is:%v\n\n", transfer)
|
|
||||||
|
|
||||||
// just making sure that the sender id is valid
|
|
||||||
if !transfer.SenderWalletID.Valid {
|
if !transfer.SenderWalletID.Valid {
|
||||||
return nil, fmt.Errorf("sender wallet id is invalid: %v \n", transfer.SenderWalletID)
|
return nil, fmt.Errorf("invalid sender wallet ID: %v", transfer.SenderWalletID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not verified or not found, verify with Chapa
|
var verification *domain.ChapaVerificationResponse
|
||||||
verification, err := s.chapaClient.VerifyPayment(ctx, txRef)
|
|
||||||
|
// Decide verification method based on type
|
||||||
|
switch strings.ToLower(string(transfer.Type)) {
|
||||||
|
case "deposit":
|
||||||
|
// Use Chapa Payment Verification
|
||||||
|
verification, err = s.chapaClient.ManualVerifyPayment(ctx, txRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to verify payment: %w", err)
|
return nil, fmt.Errorf("failed to verify deposit with Chapa: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our records if payment is successful
|
if verification.Status == string(domain.PaymentStatusSuccessful) {
|
||||||
if verification.Status == domain.PaymentStatusCompleted {
|
// Mark verified
|
||||||
err = s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true)
|
if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to mark deposit transfer as verified: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credit wallet
|
||||||
|
if _, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{}, domain.TRANSFER_CHAPA, domain.PaymentDetails{}); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to credit wallet: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "withdraw":
|
||||||
|
// Use Chapa Transfer Verification
|
||||||
|
verification, err = s.chapaClient.ManualVerifyTransfer(ctx, txRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to update verification status: %w", err)
|
return nil, fmt.Errorf("failed to verify withdrawal with Chapa: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Credit user's wallet
|
if verification.Status == string(domain.PaymentStatusSuccessful) {
|
||||||
err = s.walletStore.UpdateBalance(ctx, transfer.SenderWalletID.Value, transfer.Amount)
|
// Mark verified (withdraw doesn't affect balance)
|
||||||
if err != nil {
|
if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil {
|
||||||
return nil, fmt.Errorf("failed to update wallet balance: %w", err)
|
return nil, fmt.Errorf("failed to mark withdrawal transfer as verified: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &domain.ChapaVerificationResponse{
|
default:
|
||||||
Status: string(verification.Status),
|
return nil, fmt.Errorf("unsupported transfer type: %s", transfer.Type)
|
||||||
Amount: float64(verification.Amount),
|
}
|
||||||
Currency: verification.Currency,
|
|
||||||
}, nil
|
return verification, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) HandleVerifyDepositWebhook(ctx context.Context, transfer domain.ChapaWebHookTransfer) error {
|
func (s *Service) HandleVerifyDepositWebhook(ctx context.Context, transfer domain.ChapaWebHookTransfer) error {
|
||||||
|
|
@ -281,12 +314,13 @@ func (s *Service) HandleVerifyDepositWebhook(ctx context.Context, transfer domai
|
||||||
// verified = true
|
// verified = true
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// If payment is completed, credit user's wallet
|
||||||
|
if transfer.Status == string(domain.PaymentStatusSuccessful) {
|
||||||
|
|
||||||
if err := s.transferStore.UpdateTransferVerification(ctx, payment.ID, true); err != nil {
|
if err := s.transferStore.UpdateTransferVerification(ctx, payment.ID, true); err != nil {
|
||||||
return fmt.Errorf("failed to update payment status: %w", err)
|
return fmt.Errorf("failed to update payment status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If payment is completed, credit user's wallet
|
|
||||||
if transfer.Status == string(domain.PaymentStatusCompleted) {
|
|
||||||
if _, err := s.walletStore.AddToWallet(ctx, payment.SenderWalletID.Value, payment.Amount, domain.ValidInt64{}, domain.TRANSFER_CHAPA, domain.PaymentDetails{
|
if _, err := s.walletStore.AddToWallet(ctx, payment.SenderWalletID.Value, payment.Amount, domain.ValidInt64{}, domain.TRANSFER_CHAPA, domain.PaymentDetails{
|
||||||
ReferenceNumber: domain.ValidString{
|
ReferenceNumber: domain.ValidString{
|
||||||
Value: transfer.Reference,
|
Value: transfer.Reference,
|
||||||
|
|
@ -322,12 +356,11 @@ func (s *Service) HandleVerifyWithdrawWebhook(ctx context.Context, payment domai
|
||||||
// verified = true
|
// verified = true
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
if payment.Status == string(domain.PaymentStatusSuccessful) {
|
||||||
if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil {
|
if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil {
|
||||||
return fmt.Errorf("failed to update payment status: %w", err)
|
return fmt.Errorf("failed to update payment status: %w", err)
|
||||||
}
|
} // If payment is completed, credit user's walle
|
||||||
|
} else {
|
||||||
// If payment is completed, credit user's wallet
|
|
||||||
if payment.Status == string(domain.PaymentStatusFailed) {
|
|
||||||
if _, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil {
|
if _, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil {
|
||||||
return fmt.Errorf("failed to credit user wallet: %w", err)
|
return fmt.Errorf("failed to credit user wallet: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
internal/services/institutions/port.go
Normal file
1
internal/services/institutions/port.go
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
package institutions
|
||||||
44
internal/services/institutions/service.go
Normal file
44
internal/services/institutions/service.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package institutions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
repo repository.BankRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(repo repository.BankRepository) *Service {
|
||||||
|
return &Service{repo: repo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Create(ctx context.Context, bank *domain.Bank) error {
|
||||||
|
return s.repo.CreateBank(ctx, bank)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Update(ctx context.Context, bank *domain.Bank) error {
|
||||||
|
return s.repo.UpdateBank(ctx, bank)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetByID(ctx context.Context, id int64) (*domain.Bank, error) {
|
||||||
|
return s.repo.GetBankByID(ctx, int(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Delete(ctx context.Context, id int64) error {
|
||||||
|
return s.repo.DeleteBank(ctx, int(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) List(ctx context.Context) ([]*domain.Bank, error) {
|
||||||
|
banks, err := s.repo.GetAllBanks(ctx, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]*domain.Bank, len(banks))
|
||||||
|
for i := range banks {
|
||||||
|
result[i] = &banks[i]
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
83
internal/services/issue_reporting/service.go
Normal file
83
internal/services/issue_reporting/service.go
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
package issuereporting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
repo repository.ReportedIssueRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(repo repository.ReportedIssueRepository) *Service {
|
||||||
|
return &Service{repo: repo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateReportedIssue(ctx context.Context, issue domain.ReportedIssue) (domain.ReportedIssue, error) {
|
||||||
|
params := dbgen.CreateReportedIssueParams{
|
||||||
|
// Map fields from domain.ReportedIssue to dbgen.CreateReportedIssueParams here.
|
||||||
|
// Example:
|
||||||
|
// Title: issue.Title,
|
||||||
|
// Description: issue.Description,
|
||||||
|
// CustomerID: issue.CustomerID,
|
||||||
|
// Status: issue.Status,
|
||||||
|
// Add other fields as necessary.
|
||||||
|
}
|
||||||
|
dbIssue, err := s.repo.CreateReportedIssue(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return domain.ReportedIssue{}, err
|
||||||
|
}
|
||||||
|
// Map dbgen.ReportedIssue to domain.ReportedIssue
|
||||||
|
reportedIssue := domain.ReportedIssue{
|
||||||
|
ID: dbIssue.ID,
|
||||||
|
Subject: dbIssue.Subject,
|
||||||
|
Description: dbIssue.Description,
|
||||||
|
CustomerID: dbIssue.CustomerID,
|
||||||
|
Status: dbIssue.Status,
|
||||||
|
CreatedAt: dbIssue.CreatedAt.Time,
|
||||||
|
UpdatedAt: dbIssue.UpdatedAt.Time,
|
||||||
|
// Add other fields as necessary
|
||||||
|
}
|
||||||
|
return reportedIssue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetIssuesForCustomer(ctx context.Context, customerID int64, limit, offset int) ([]domain.ReportedIssue, error) {
|
||||||
|
dbIssues, err := s.repo.ListReportedIssuesByCustomer(ctx, customerID, int32(limit), int32(offset))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reportedIssues := make([]domain.ReportedIssue, len(dbIssues))
|
||||||
|
for i, dbIssue := range dbIssues {
|
||||||
|
reportedIssues[i] = domain.ReportedIssue{
|
||||||
|
ID: dbIssue.ID,
|
||||||
|
Subject: dbIssue.Subject,
|
||||||
|
Description: dbIssue.Description,
|
||||||
|
CustomerID: dbIssue.CustomerID,
|
||||||
|
Status: dbIssue.Status,
|
||||||
|
CreatedAt: dbIssue.CreatedAt.Time,
|
||||||
|
UpdatedAt: dbIssue.UpdatedAt.Time,
|
||||||
|
// Add other fields as necessary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reportedIssues, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetAllIssues(ctx context.Context, limit, offset int) ([]dbgen.ReportedIssue, error) {
|
||||||
|
return s.repo.ListReportedIssues(ctx, int32(limit), int32(offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpdateIssueStatus(ctx context.Context, issueID int64, status string) error {
|
||||||
|
validStatuses := map[string]bool{"pending": true, "in_progress": true, "resolved": true, "rejected": true}
|
||||||
|
if !validStatuses[status] {
|
||||||
|
return errors.New("invalid status")
|
||||||
|
}
|
||||||
|
return s.repo.UpdateReportedIssueStatus(ctx, issueID, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DeleteIssue(ctx context.Context, issueID int64) error {
|
||||||
|
return s.repo.DeleteReportedIssue(ctx, issueID)
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotificationStore interface {
|
type NotificationStore interface {
|
||||||
|
GetCompanyByWalletID(ctx context.Context, walletID int64) (domain.Company, error)
|
||||||
|
GetBranchByWalletID(ctx context.Context, walletID int64) (domain.Branch, error)
|
||||||
SendNotification(ctx context.Context, notification *domain.Notification) error
|
SendNotification(ctx context.Context, notification *domain.Notification) error
|
||||||
MarkAsRead(ctx context.Context, notificationID string, recipientID int64) error
|
MarkAsRead(ctx context.Context, notificationID string, recipientID int64) error
|
||||||
ListNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, error)
|
ListNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, error)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||||
|
|
||||||
|
// "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws"
|
||||||
afro "github.com/amanuelabay/afrosms-go"
|
afro "github.com/amanuelabay/afrosms-go"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
|
@ -21,6 +23,7 @@ import (
|
||||||
type Service struct {
|
type Service struct {
|
||||||
repo repository.NotificationRepository
|
repo repository.NotificationRepository
|
||||||
Hub *ws.NotificationHub
|
Hub *ws.NotificationHub
|
||||||
|
notificationStore NotificationStore
|
||||||
connections sync.Map
|
connections sync.Map
|
||||||
notificationCh chan *domain.Notification
|
notificationCh chan *domain.Notification
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
|
|
@ -32,7 +35,7 @@ type Service struct {
|
||||||
func New(repo repository.NotificationRepository, logger *slog.Logger, cfg *config.Config) *Service {
|
func New(repo repository.NotificationRepository, logger *slog.Logger, cfg *config.Config) *Service {
|
||||||
hub := ws.NewNotificationHub()
|
hub := ws.NewNotificationHub()
|
||||||
rdb := redis.NewClient(&redis.Options{
|
rdb := redis.NewClient(&redis.Options{
|
||||||
Addr: cfg.RedisAddr, // e.g., “redis:6379”
|
Addr: cfg.RedisAddr, // e.g., "redis:6379"
|
||||||
})
|
})
|
||||||
|
|
||||||
svc := &Service{
|
svc := &Service{
|
||||||
|
|
@ -264,7 +267,8 @@ func (s *Service) retryFailedNotifications() {
|
||||||
go func(notification *domain.Notification) {
|
go func(notification *domain.Notification) {
|
||||||
for attempt := 0; attempt < 3; attempt++ {
|
for attempt := 0; attempt < 3; attempt++ {
|
||||||
time.Sleep(time.Duration(attempt) * time.Second)
|
time.Sleep(time.Duration(attempt) * time.Second)
|
||||||
if notification.DeliveryChannel == domain.DeliveryChannelSMS {
|
switch notification.DeliveryChannel {
|
||||||
|
case domain.DeliveryChannelSMS:
|
||||||
if err := s.SendSMS(ctx, notification.RecipientID, notification.Payload.Message); err == nil {
|
if err := s.SendSMS(ctx, notification.RecipientID, notification.Payload.Message); err == nil {
|
||||||
notification.DeliveryStatus = domain.DeliveryStatusSent
|
notification.DeliveryStatus = domain.DeliveryStatusSent
|
||||||
if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
|
if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
|
||||||
|
|
@ -273,7 +277,7 @@ func (s *Service) retryFailedNotifications() {
|
||||||
s.logger.Info("[NotificationSvc.RetryFailedNotifications] Successfully retried notification", "id", notification.ID)
|
s.logger.Info("[NotificationSvc.RetryFailedNotifications] Successfully retried notification", "id", notification.ID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if notification.DeliveryChannel == domain.DeliveryChannelEmail {
|
case domain.DeliveryChannelEmail:
|
||||||
if err := s.SendEmail(ctx, notification.RecipientID, notification.Payload.Headline, notification.Payload.Message); err == nil {
|
if err := s.SendEmail(ctx, notification.RecipientID, notification.Payload.Headline, notification.Payload.Message); err == nil {
|
||||||
notification.DeliveryStatus = domain.DeliveryStatusSent
|
notification.DeliveryStatus = domain.DeliveryStatusSent
|
||||||
if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
|
if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
|
||||||
|
|
@ -303,52 +307,60 @@ func (s *Service) RunRedisSubscriber(ctx context.Context) {
|
||||||
|
|
||||||
ch := pubsub.Channel()
|
ch := pubsub.Channel()
|
||||||
for msg := range ch {
|
for msg := range ch {
|
||||||
var payload domain.LiveMetric
|
var parsed map[string]interface{}
|
||||||
if err := json.Unmarshal([]byte(msg.Payload), &payload); err != nil {
|
if err := json.Unmarshal([]byte(msg.Payload), &parsed); err != nil {
|
||||||
s.logger.Error("[NotificationSvc.runRedisSubscriber] failed unmarshal metric", "error", err)
|
s.logger.Error("invalid Redis message format", "payload", msg.Payload, "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Broadcast via WebSocket Hub
|
|
||||||
s.Hub.Broadcast <- map[string]interface{}{
|
eventType, _ := parsed["type"].(string)
|
||||||
"type": "LIVE_METRIC_UPDATE",
|
payload := parsed["payload"]
|
||||||
|
recipientID, hasRecipient := parsed["recipient_id"]
|
||||||
|
recipientType, _ := parsed["recipient_type"].(string)
|
||||||
|
|
||||||
|
message := map[string]interface{}{
|
||||||
|
"type": eventType,
|
||||||
"payload": payload,
|
"payload": payload,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hasRecipient {
|
||||||
|
message["recipient_id"] = recipientID
|
||||||
|
message["recipient_type"] = recipientType
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Hub.Broadcast <- message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) UpdateLiveMetrics(ctx context.Context, updates domain.MetricUpdates) error {
|
func (s *Service) UpdateLiveWalletMetrics(ctx context.Context, companies []domain.GetCompany, branches []domain.BranchWallet) error {
|
||||||
const key = "live_metrics"
|
const key = "live_metrics"
|
||||||
|
|
||||||
val, err := s.redisClient.Get(ctx, key).Result()
|
companyBalances := make([]domain.CompanyWalletBalance, 0, len(companies))
|
||||||
var metric domain.LiveMetric
|
for _, c := range companies {
|
||||||
if err == redis.Nil {
|
companyBalances = append(companyBalances, domain.CompanyWalletBalance{
|
||||||
metric = domain.LiveMetric{}
|
CompanyID: c.ID,
|
||||||
} else if err != nil {
|
CompanyName: c.Name,
|
||||||
return err
|
Balance: float64(c.WalletBalance.Float32()),
|
||||||
} else {
|
})
|
||||||
if err := json.Unmarshal([]byte(val), &metric); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply increments if provided
|
branchBalances := make([]domain.BranchWalletBalance, 0, len(branches))
|
||||||
if updates.TotalCashSportsbookDelta != nil {
|
for _, b := range branches {
|
||||||
metric.TotalCashSportsbook += *updates.TotalCashSportsbookDelta
|
branchBalances = append(branchBalances, domain.BranchWalletBalance{
|
||||||
}
|
BranchID: b.ID,
|
||||||
if updates.TotalCashSportGamesDelta != nil {
|
BranchName: b.Name,
|
||||||
metric.TotalCashSportGames += *updates.TotalCashSportGamesDelta
|
CompanyID: b.CompanyID,
|
||||||
}
|
Balance: float64(b.Balance.Float32()),
|
||||||
if updates.TotalLiveTicketsDelta != nil {
|
})
|
||||||
metric.TotalLiveTickets += *updates.TotalLiveTicketsDelta
|
|
||||||
}
|
|
||||||
if updates.TotalUnsettledCashDelta != nil {
|
|
||||||
metric.TotalUnsettledCash += *updates.TotalUnsettledCashDelta
|
|
||||||
}
|
|
||||||
if updates.TotalGamesDelta != nil {
|
|
||||||
metric.TotalGames += *updates.TotalGamesDelta
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedData, err := json.Marshal(metric)
|
payload := domain.LiveWalletMetrics{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
CompanyBalances: companyBalances,
|
||||||
|
BranchBalances: branchBalances,
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedData, err := json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -357,11 +369,9 @@ func (s *Service) UpdateLiveMetrics(ctx context.Context, updates domain.MetricUp
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.redisClient.Publish(ctx, "live_metrics", updatedData).Err(); err != nil {
|
if err := s.redisClient.Publish(ctx, key, updatedData).Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("[NotificationSvc.UpdateLiveMetrics] Live metrics updated and broadcasted")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,3 +393,83 @@ func (s *Service) GetLiveMetrics(ctx context.Context) (domain.LiveMetric, error)
|
||||||
|
|
||||||
return metric, nil
|
return metric, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpdateLiveWalletMetricForWallet(ctx context.Context, wallet domain.Wallet) {
|
||||||
|
var (
|
||||||
|
payload domain.LiveWalletMetrics
|
||||||
|
event map[string]interface{}
|
||||||
|
key = "live_metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Try company first
|
||||||
|
company, companyErr := s.notificationStore.GetCompanyByWalletID(ctx, wallet.ID)
|
||||||
|
if companyErr == nil {
|
||||||
|
payload = domain.LiveWalletMetrics{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
CompanyBalances: []domain.CompanyWalletBalance{{
|
||||||
|
CompanyID: company.ID,
|
||||||
|
CompanyName: company.Name,
|
||||||
|
Balance: float64(wallet.Balance),
|
||||||
|
}},
|
||||||
|
BranchBalances: []domain.BranchWalletBalance{},
|
||||||
|
}
|
||||||
|
|
||||||
|
event = map[string]interface{}{
|
||||||
|
"type": "LIVE_WALLET_METRICS_UPDATE",
|
||||||
|
"recipient_id": company.ID,
|
||||||
|
"recipient_type": "company",
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Try branch next
|
||||||
|
branch, branchErr := s.notificationStore.GetBranchByWalletID(ctx, wallet.ID)
|
||||||
|
if branchErr == nil {
|
||||||
|
payload = domain.LiveWalletMetrics{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
CompanyBalances: []domain.CompanyWalletBalance{},
|
||||||
|
BranchBalances: []domain.BranchWalletBalance{{
|
||||||
|
BranchID: branch.ID,
|
||||||
|
BranchName: branch.Name,
|
||||||
|
CompanyID: branch.CompanyID,
|
||||||
|
Balance: float64(wallet.Balance),
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
event = map[string]interface{}{
|
||||||
|
"type": "LIVE_WALLET_METRICS_UPDATE",
|
||||||
|
"recipient_id": branch.ID,
|
||||||
|
"recipient_type": "branch",
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Neither company nor branch matched this wallet
|
||||||
|
s.logger.Warn("wallet not linked to any company or branch", "walletID", wallet.ID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save latest metric to Redis
|
||||||
|
if jsonBytes, err := json.Marshal(payload); err == nil {
|
||||||
|
s.redisClient.Set(ctx, key, jsonBytes, 0)
|
||||||
|
} else {
|
||||||
|
s.logger.Error("failed to marshal wallet metrics payload", "walletID", wallet.ID, "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish via Redis
|
||||||
|
if jsonEvent, err := json.Marshal(event); err == nil {
|
||||||
|
s.redisClient.Publish(ctx, key, jsonEvent)
|
||||||
|
} else {
|
||||||
|
s.logger.Error("failed to marshal event payload", "walletID", wallet.ID, "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast over WebSocket
|
||||||
|
s.Hub.Broadcast <- event
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetCompanyByWalletID(ctx context.Context, walletID int64) (domain.Company, error) {
|
||||||
|
return s.notificationStore.GetCompanyByWalletID(ctx, walletID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetBranchByWalletID(ctx context.Context, walletID int64) (domain.Branch, error) {
|
||||||
|
return s.notificationStore.GetBranchByWalletID(ctx, walletID)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -476,6 +476,7 @@ func (s *Service) GenerateReport(ctx context.Context, period string) error {
|
||||||
defer writer.Flush()
|
defer writer.Flush()
|
||||||
|
|
||||||
// Summary section
|
// Summary section
|
||||||
|
writer.Write([]string{"Sports Betting Reports (Periodic)"})
|
||||||
writer.Write([]string{"Period", "Total Bets", "Total Cash Made", "Total Cash Out", "Total Cash Backs", "Total Deposits", "Total Withdrawals", "Total Tickets"})
|
writer.Write([]string{"Period", "Total Bets", "Total Cash Made", "Total Cash Out", "Total Cash Backs", "Total Deposits", "Total Withdrawals", "Total Tickets"})
|
||||||
writer.Write([]string{
|
writer.Write([]string{
|
||||||
period,
|
period,
|
||||||
|
|
@ -491,6 +492,7 @@ func (s *Service) GenerateReport(ctx context.Context, period string) error {
|
||||||
writer.Write([]string{}) // Empty line for spacing
|
writer.Write([]string{}) // Empty line for spacing
|
||||||
|
|
||||||
// Virtual Game Summary section
|
// Virtual Game Summary section
|
||||||
|
writer.Write([]string{"Virtual Game Reports (Periodic)"})
|
||||||
writer.Write([]string{"Game Name", "Number of Bets", "Total Transaction Sum"})
|
writer.Write([]string{"Game Name", "Number of Bets", "Total Transaction Sum"})
|
||||||
for _, row := range data.VirtualGameStats {
|
for _, row := range data.VirtualGameStats {
|
||||||
writer.Write([]string{
|
writer.Write([]string{
|
||||||
|
|
@ -500,18 +502,66 @@ func (s *Service) GenerateReport(ctx context.Context, period string) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writer.Write([]string{}) // Empty line
|
||||||
|
writer.Write([]string{"Company Reports (Periodic)"})
|
||||||
|
writer.Write([]string{"Company ID", "Company Name", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
||||||
|
for _, cr := range data.CompanyReports {
|
||||||
|
writer.Write([]string{
|
||||||
|
fmt.Sprintf("%d", cr.CompanyID),
|
||||||
|
cr.CompanyName,
|
||||||
|
fmt.Sprintf("%d", cr.TotalBets),
|
||||||
|
fmt.Sprintf("%.2f", cr.TotalCashIn),
|
||||||
|
fmt.Sprintf("%.2f", cr.TotalCashOut),
|
||||||
|
fmt.Sprintf("%.2f", cr.TotalCashBacks),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write([]string{}) // Empty line
|
||||||
|
writer.Write([]string{"Branch Reports (Periodic)"})
|
||||||
|
writer.Write([]string{"Branch ID", "Branch Name", "Company ID", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
||||||
|
for _, br := range data.BranchReports {
|
||||||
|
writer.Write([]string{
|
||||||
|
fmt.Sprintf("%d", br.BranchID),
|
||||||
|
br.BranchName,
|
||||||
|
fmt.Sprintf("%d", br.CompanyID),
|
||||||
|
fmt.Sprintf("%d", br.TotalBets),
|
||||||
|
fmt.Sprintf("%.2f", br.TotalCashIn),
|
||||||
|
fmt.Sprintf("%.2f", br.TotalCashOut),
|
||||||
|
fmt.Sprintf("%.2f", br.TotalCashBacks),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalBets int64
|
||||||
|
var totalCashIn, totalCashOut, totalCashBacks float64
|
||||||
|
for _, cr := range data.CompanyReports {
|
||||||
|
totalBets += cr.TotalBets
|
||||||
|
totalCashIn += cr.TotalCashIn
|
||||||
|
totalCashOut += cr.TotalCashOut
|
||||||
|
totalCashBacks += cr.TotalCashBacks
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write([]string{})
|
||||||
|
writer.Write([]string{"Total Summary"})
|
||||||
|
writer.Write([]string{"Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
||||||
|
writer.Write([]string{
|
||||||
|
fmt.Sprintf("%d", totalBets),
|
||||||
|
fmt.Sprintf("%.2f", totalCashIn),
|
||||||
|
fmt.Sprintf("%.2f", totalCashOut),
|
||||||
|
fmt.Sprintf("%.2f", totalCashBacks),
|
||||||
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) fetchReportData(ctx context.Context, period string) (domain.ReportData, error) {
|
func (s *Service) fetchReportData(ctx context.Context, period string) (domain.ReportData, error) {
|
||||||
from, to := getTimeRange(period)
|
from, to := getTimeRange(period)
|
||||||
companyID := int64(0)
|
// companyID := int64(0)
|
||||||
|
|
||||||
// Basic metrics
|
// Basic metrics
|
||||||
totalBets, _ := s.repo.GetTotalBetsMadeInRange(ctx, from, to, companyID)
|
totalBets, _ := s.repo.GetTotalBetsMadeInRange(ctx, from, to)
|
||||||
cashIn, _ := s.repo.GetTotalCashMadeInRange(ctx, from, to, companyID)
|
cashIn, _ := s.repo.GetTotalCashMadeInRange(ctx, from, to)
|
||||||
cashOut, _ := s.repo.GetTotalCashOutInRange(ctx, from, to, companyID)
|
cashOut, _ := s.repo.GetTotalCashOutInRange(ctx, from, to)
|
||||||
cashBacks, _ := s.repo.GetTotalCashBacksInRange(ctx, from, to, companyID)
|
cashBacks, _ := s.repo.GetTotalCashBacksInRange(ctx, from, to)
|
||||||
|
|
||||||
// Wallet Transactions
|
// Wallet Transactions
|
||||||
transactions, _ := s.repo.GetWalletTransactionsInRange(ctx, from, to)
|
transactions, _ := s.repo.GetWalletTransactionsInRange(ctx, from, to)
|
||||||
|
|
@ -555,6 +605,113 @@ func (s *Service) fetchReportData(ctx context.Context, period string) (domain.Re
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companyRows, _ := s.repo.GetCompanyWiseReport(ctx, from, to)
|
||||||
|
var companyReports []domain.CompanyReport
|
||||||
|
for _, row := range companyRows {
|
||||||
|
var totalCashIn, totalCashOut, totalCashBacks float64
|
||||||
|
switch v := row.TotalCashMade.(type) {
|
||||||
|
case string:
|
||||||
|
val, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err == nil {
|
||||||
|
totalCashIn = val
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
totalCashIn = v
|
||||||
|
case int:
|
||||||
|
totalCashIn = float64(v)
|
||||||
|
default:
|
||||||
|
totalCashIn = 0
|
||||||
|
}
|
||||||
|
switch v := row.TotalCashOut.(type) {
|
||||||
|
case string:
|
||||||
|
val, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err == nil {
|
||||||
|
totalCashOut = val
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
totalCashOut = v
|
||||||
|
case int:
|
||||||
|
totalCashOut = float64(v)
|
||||||
|
default:
|
||||||
|
totalCashOut = 0
|
||||||
|
}
|
||||||
|
switch v := row.TotalCashBacks.(type) {
|
||||||
|
case string:
|
||||||
|
val, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err == nil {
|
||||||
|
totalCashBacks = val
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
totalCashBacks = v
|
||||||
|
case int:
|
||||||
|
totalCashBacks = float64(v)
|
||||||
|
default:
|
||||||
|
totalCashBacks = 0
|
||||||
|
}
|
||||||
|
companyReports = append(companyReports, domain.CompanyReport{
|
||||||
|
CompanyID: row.CompanyID.Int64,
|
||||||
|
CompanyName: row.CompanyName,
|
||||||
|
TotalBets: row.TotalBets,
|
||||||
|
TotalCashIn: totalCashIn,
|
||||||
|
TotalCashOut: totalCashOut,
|
||||||
|
TotalCashBacks: totalCashBacks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
branchRows, _ := s.repo.GetBranchWiseReport(ctx, from, to)
|
||||||
|
var branchReports []domain.BranchReport
|
||||||
|
for _, row := range branchRows {
|
||||||
|
var totalCashIn, totalCashOut, totalCashBacks float64
|
||||||
|
switch v := row.TotalCashMade.(type) {
|
||||||
|
case string:
|
||||||
|
val, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err == nil {
|
||||||
|
totalCashIn = val
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
totalCashIn = v
|
||||||
|
case int:
|
||||||
|
totalCashIn = float64(v)
|
||||||
|
default:
|
||||||
|
totalCashIn = 0
|
||||||
|
}
|
||||||
|
switch v := row.TotalCashOut.(type) {
|
||||||
|
case string:
|
||||||
|
val, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err == nil {
|
||||||
|
totalCashOut = val
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
totalCashOut = v
|
||||||
|
case int:
|
||||||
|
totalCashOut = float64(v)
|
||||||
|
default:
|
||||||
|
totalCashOut = 0
|
||||||
|
}
|
||||||
|
switch v := row.TotalCashBacks.(type) {
|
||||||
|
case string:
|
||||||
|
val, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err == nil {
|
||||||
|
totalCashBacks = val
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
totalCashBacks = v
|
||||||
|
case int:
|
||||||
|
totalCashBacks = float64(v)
|
||||||
|
default:
|
||||||
|
totalCashBacks = 0
|
||||||
|
}
|
||||||
|
branchReports = append(branchReports, domain.BranchReport{
|
||||||
|
BranchID: row.BranchID.Int64,
|
||||||
|
BranchName: row.BranchName,
|
||||||
|
CompanyID: row.CompanyID,
|
||||||
|
TotalBets: row.TotalBets,
|
||||||
|
TotalCashIn: totalCashIn,
|
||||||
|
TotalCashOut: totalCashOut,
|
||||||
|
TotalCashBacks: totalCashBacks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return domain.ReportData{
|
return domain.ReportData{
|
||||||
TotalBets: totalBets,
|
TotalBets: totalBets,
|
||||||
TotalCashIn: cashIn,
|
TotalCashIn: cashIn,
|
||||||
|
|
@ -564,6 +721,8 @@ func (s *Service) fetchReportData(ctx context.Context, period string) (domain.Re
|
||||||
Withdrawals: totalWithdrawals,
|
Withdrawals: totalWithdrawals,
|
||||||
TotalTickets: totalTickets.TotalTickets,
|
TotalTickets: totalTickets.TotalTickets,
|
||||||
VirtualGameStats: virtualGameStatsDomain,
|
VirtualGameStats: virtualGameStatsDomain,
|
||||||
|
CompanyReports: companyReports,
|
||||||
|
BranchReports: branchReports,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -595,8 +754,6 @@ func getTimeRange(period string) (time.Time, time.Time) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// func (s *Service) GetCompanyPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CompanyPerformance, error) {
|
// func (s *Service) GetCompanyPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CompanyPerformance, error) {
|
||||||
// // Get company bet activity
|
// // Get company bet activity
|
||||||
// companyBets, err := s.betStore.GetCompanyBetActivity(ctx, filter)
|
// companyBets, err := s.betStore.GetCompanyBetActivity(ctx, filter)
|
||||||
|
|
|
||||||
|
|
@ -236,13 +236,13 @@ func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq,
|
||||||
return domain.Ticket{}, rows, err
|
return domain.Ticket{}, rows, err
|
||||||
}
|
}
|
||||||
|
|
||||||
updates := domain.MetricUpdates{
|
// updates := domain.MetricUpdates{
|
||||||
TotalLiveTicketsDelta: domain.PtrInt64(1),
|
// TotalLiveTicketsDelta: domain.PtrInt64(1),
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := s.notificationSvc.UpdateLiveMetrics(ctx, updates); err != nil {
|
// if err := s.notificationSvc.UpdateLiveMetrics(ctx, updates); err != nil {
|
||||||
// handle error
|
// // handle error
|
||||||
}
|
// }
|
||||||
|
|
||||||
return ticket, rows, nil
|
return ticket, rows, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,13 @@ type VirtualGameService interface {
|
||||||
GetPlayerInfo(ctx context.Context, req *domain.PopOKPlayerInfoRequest) (*domain.PopOKPlayerInfoResponse, error)
|
GetPlayerInfo(ctx context.Context, req *domain.PopOKPlayerInfoRequest) (*domain.PopOKPlayerInfoResponse, error)
|
||||||
ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error)
|
ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error)
|
||||||
ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequest) (*domain.PopOKCancelResponse, error)
|
ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequest) (*domain.PopOKCancelResponse, error)
|
||||||
|
ProcessTournamentWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error)
|
||||||
|
ProcessPromoWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error)
|
||||||
|
|
||||||
GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
|
GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
|
||||||
ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error)
|
ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error)
|
||||||
RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error)
|
RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error)
|
||||||
|
AddFavoriteGame(ctx context.Context, userID, gameID int64) error
|
||||||
|
RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error
|
||||||
|
ListFavoriteGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ func (s *service) GenerateGameLaunchURL(ctx context.Context, userID int64, gameI
|
||||||
sessionId := fmt.Sprintf("%d-%s-%d", userID, gameID, time.Now().UnixNano())
|
sessionId := fmt.Sprintf("%d-%s-%d", userID, gameID, time.Now().UnixNano())
|
||||||
token, err := jwtutil.CreatePopOKJwt(
|
token, err := jwtutil.CreatePopOKJwt(
|
||||||
userID,
|
userID,
|
||||||
|
user.CompanyID,
|
||||||
user.FirstName,
|
user.FirstName,
|
||||||
currency,
|
currency,
|
||||||
"en",
|
"en",
|
||||||
|
|
@ -69,6 +70,8 @@ func (s *service) GenerateGameLaunchURL(ctx context.Context, userID int64, gameI
|
||||||
tx := &domain.VirtualGameHistory{
|
tx := &domain.VirtualGameHistory{
|
||||||
SessionID: sessionId, // Optional: populate if session tracking is implemented
|
SessionID: sessionId, // Optional: populate if session tracking is implemented
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
|
CompanyID: user.CompanyID.Value,
|
||||||
|
Provider: string(domain.PROVIDER_POPOK),
|
||||||
GameID: toInt64Ptr(gameID),
|
GameID: toInt64Ptr(gameID),
|
||||||
TransactionType: "LAUNCH",
|
TransactionType: "LAUNCH",
|
||||||
Amount: 0,
|
Amount: 0,
|
||||||
|
|
@ -211,8 +214,11 @@ func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (
|
||||||
// Create transaction record
|
// Create transaction record
|
||||||
tx := &domain.VirtualGameTransaction{
|
tx := &domain.VirtualGameTransaction{
|
||||||
UserID: claims.UserID,
|
UserID: claims.UserID,
|
||||||
|
CompanyID: claims.CompanyID.Value,
|
||||||
|
Provider: string(domain.PROVIDER_POPOK),
|
||||||
|
GameID: req.GameID,
|
||||||
TransactionType: "BET",
|
TransactionType: "BET",
|
||||||
Amount: -amountCents, // Negative for bets
|
Amount: amountCents, // Negative for bets
|
||||||
Currency: req.Currency,
|
Currency: req.Currency,
|
||||||
ExternalTransactionID: req.TransactionID,
|
ExternalTransactionID: req.TransactionID,
|
||||||
Status: "COMPLETED",
|
Status: "COMPLETED",
|
||||||
|
|
@ -279,6 +285,9 @@ func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (
|
||||||
// 5. Create transaction record
|
// 5. Create transaction record
|
||||||
tx := &domain.VirtualGameTransaction{
|
tx := &domain.VirtualGameTransaction{
|
||||||
UserID: claims.UserID,
|
UserID: claims.UserID,
|
||||||
|
CompanyID: claims.CompanyID.Value,
|
||||||
|
Provider: string(domain.PROVIDER_POPOK),
|
||||||
|
GameID: req.GameID,
|
||||||
TransactionType: "WIN",
|
TransactionType: "WIN",
|
||||||
Amount: amountCents,
|
Amount: amountCents,
|
||||||
Currency: req.Currency,
|
Currency: req.Currency,
|
||||||
|
|
@ -299,6 +308,167 @@ func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) ProcessTournamentWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) {
|
||||||
|
// 1. Validate token and get user ID
|
||||||
|
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Invalid token in tournament win request", "error", err)
|
||||||
|
return nil, fmt.Errorf("invalid token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check for duplicate tournament win transaction
|
||||||
|
existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to check existing tournament transaction", "error", err)
|
||||||
|
return nil, fmt.Errorf("transaction check failed")
|
||||||
|
}
|
||||||
|
if existingTx != nil && existingTx.TransactionType == "TOURNAMENT_WIN" {
|
||||||
|
s.logger.Warn("Duplicate tournament win", "transactionID", req.TransactionID)
|
||||||
|
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
|
balance := 0.0
|
||||||
|
if len(wallets) > 0 {
|
||||||
|
balance = float64(wallets[0].Balance) / 100
|
||||||
|
}
|
||||||
|
return &domain.PopOKWinResponse{
|
||||||
|
TransactionID: req.TransactionID,
|
||||||
|
ExternalTrxID: fmt.Sprintf("%v", existingTx.ID),
|
||||||
|
Balance: balance,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Convert amount to cents
|
||||||
|
amountCents := int64(req.Amount * 100)
|
||||||
|
|
||||||
|
// 4. Credit user wallet
|
||||||
|
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 for tournament", "userID", claims.UserID, "error", err)
|
||||||
|
return nil, fmt.Errorf("wallet credit failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Log tournament win transaction
|
||||||
|
tx := &domain.VirtualGameTransaction{
|
||||||
|
UserID: claims.UserID,
|
||||||
|
TransactionType: "TOURNAMENT_WIN",
|
||||||
|
Amount: amountCents,
|
||||||
|
Currency: req.Currency,
|
||||||
|
ExternalTransactionID: req.TransactionID,
|
||||||
|
Status: "COMPLETED",
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.repo.CreateVirtualGameTransaction(ctx, tx); err != nil {
|
||||||
|
s.logger.Error("Failed to record tournament win transaction", "error", err)
|
||||||
|
return nil, fmt.Errorf("transaction recording failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Fetch updated balance
|
||||||
|
wallets, err := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to get wallet balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &domain.PopOKWinResponse{
|
||||||
|
TransactionID: req.TransactionID,
|
||||||
|
ExternalTrxID: fmt.Sprintf("%v", tx.ID),
|
||||||
|
Balance: float64(wallets[0].Balance) / 100,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) ProcessPromoWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) {
|
||||||
|
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Invalid token in promo win request", "error", err)
|
||||||
|
return nil, fmt.Errorf("invalid token")
|
||||||
|
}
|
||||||
|
|
||||||
|
existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to check existing promo transaction", "error", err)
|
||||||
|
return nil, fmt.Errorf("transaction check failed")
|
||||||
|
}
|
||||||
|
if existingTx != nil && existingTx.TransactionType == "PROMO_WIN" {
|
||||||
|
s.logger.Warn("Duplicate promo win", "transactionID", req.TransactionID)
|
||||||
|
wallets, _ := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
|
balance := 0.0
|
||||||
|
if len(wallets) > 0 {
|
||||||
|
balance = float64(wallets[0].Balance) / 100
|
||||||
|
}
|
||||||
|
return &domain.PopOKWinResponse{
|
||||||
|
TransactionID: req.TransactionID,
|
||||||
|
ExternalTrxID: fmt.Sprintf("%v", existingTx.ID),
|
||||||
|
Balance: balance,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
amountCents := int64(req.Amount * 100)
|
||||||
|
|
||||||
|
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 for promo", "userID", claims.UserID, "error", err)
|
||||||
|
return nil, fmt.Errorf("wallet credit failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := &domain.VirtualGameTransaction{
|
||||||
|
UserID: claims.UserID,
|
||||||
|
TransactionType: "PROMO_WIN",
|
||||||
|
Amount: amountCents,
|
||||||
|
Currency: req.Currency,
|
||||||
|
ExternalTransactionID: req.TransactionID,
|
||||||
|
Status: "COMPLETED",
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.repo.CreateVirtualGameTransaction(ctx, tx); err != nil {
|
||||||
|
s.logger.Error("Failed to create promo win transaction", "error", err)
|
||||||
|
return nil, fmt.Errorf("transaction recording failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
wallets, err := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read wallets")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &domain.PopOKWinResponse{
|
||||||
|
TransactionID: req.TransactionID,
|
||||||
|
ExternalTrxID: fmt.Sprintf("%v", tx.ID),
|
||||||
|
Balance: float64(wallets[0].Balance) / 100,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (s *service) GenerateNewToken(ctx context.Context, req *domain.PopOKGenerateTokenRequest) (*domain.PopOKGenerateTokenResponse, error) {
|
||||||
|
// userID, err := strconv.ParseInt(req.PlayerID, 10, 64)
|
||||||
|
// if err != nil {
|
||||||
|
// s.logger.Error("Invalid player ID", "playerID", req.PlayerID, "error", err)
|
||||||
|
// return nil, fmt.Errorf("invalid player ID")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// user, err := s.store.GetUserByID(ctx, userID)
|
||||||
|
// if err != nil {
|
||||||
|
// s.logger.Error("Failed to find user for token refresh", "userID", userID, "error", err)
|
||||||
|
// return nil, fmt.Errorf("user not found")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// newSessionID := fmt.Sprintf("%d-%s-%d", userID, req.GameID, time.Now().UnixNano())
|
||||||
|
|
||||||
|
// token, err := jwtutil.CreatePopOKJwt(
|
||||||
|
// userID,
|
||||||
|
// user.FirstName,
|
||||||
|
// req.Currency,
|
||||||
|
// "en",
|
||||||
|
// req.Mode,
|
||||||
|
// newSessionID,
|
||||||
|
// s.config.PopOK.SecretKey,
|
||||||
|
// 24*time.Hour,
|
||||||
|
// )
|
||||||
|
// if err != nil {
|
||||||
|
// s.logger.Error("Failed to generate new token", "userID", userID, "error", err)
|
||||||
|
// return nil, fmt.Errorf("token generation failed")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return &domain.PopOKGenerateTokenResponse{
|
||||||
|
// NewToken: token,
|
||||||
|
// }, nil
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequest) (*domain.PopOKCancelResponse, error) {
|
func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequest) (*domain.PopOKCancelResponse, error) {
|
||||||
// 1. Validate token and get user ID
|
// 1. Validate token and get user ID
|
||||||
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
||||||
|
|
@ -480,7 +650,7 @@ func (s *service) ListGames(ctx context.Context, currency string) ([]domain.PopO
|
||||||
|
|
||||||
func (s *service) RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error) {
|
func (s *service) RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error) {
|
||||||
// Fetch all available games
|
// Fetch all available games
|
||||||
games, err := s.ListGames(ctx, "ETB") // currency can be dynamic
|
games, err := s.ListGames(ctx, "ETB")
|
||||||
if err != nil || len(games) == 0 {
|
if err != nil || len(games) == 0 {
|
||||||
return nil, fmt.Errorf("could not fetch games")
|
return nil, fmt.Errorf("could not fetch games")
|
||||||
}
|
}
|
||||||
|
|
@ -544,3 +714,48 @@ func toInt64Ptr(s string) *int64 {
|
||||||
}
|
}
|
||||||
return &id
|
return &id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) AddFavoriteGame(ctx context.Context, userID, gameID int64) error {
|
||||||
|
return s.repo.AddFavoriteGame(ctx, userID, gameID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error {
|
||||||
|
return s.repo.RemoveFavoriteGame(ctx, userID, gameID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) ListFavoriteGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error) {
|
||||||
|
gameIDs, err := s.repo.ListFavoriteGames(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to list favorite games", "userID", userID, "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(gameIDs) == 0 {
|
||||||
|
return []domain.GameRecommendation{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
allGames, err := s.ListGames(ctx, "ETB") // You can use dynamic currency if needed
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var favorites []domain.GameRecommendation
|
||||||
|
idMap := make(map[int64]bool)
|
||||||
|
for _, id := range gameIDs {
|
||||||
|
idMap[id] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, g := range allGames {
|
||||||
|
if idMap[int64(g.ID)] {
|
||||||
|
favorites = append(favorites, domain.GameRecommendation{
|
||||||
|
GameID: g.ID,
|
||||||
|
GameName: g.GameName,
|
||||||
|
Thumbnail: g.Thumbnail,
|
||||||
|
Bets: g.Bets,
|
||||||
|
Reason: "Marked as favorite",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return favorites, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type WalletStore interface {
|
type WalletStore interface {
|
||||||
|
// GetCompanyByWalletID(ctx context.Context, walletID int64) (domain.Company, error)
|
||||||
|
// GetBranchByWalletID(ctx context.Context, walletID int64) (domain.Branch, error)
|
||||||
CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error)
|
CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error)
|
||||||
CreateCustomerWallet(ctx context.Context, customerWallet domain.CreateCustomerWallet) (domain.CustomerWallet, error)
|
CreateCustomerWallet(ctx context.Context, customerWallet domain.CreateCustomerWallet) (domain.CustomerWallet, error)
|
||||||
GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error)
|
GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ type Service struct {
|
||||||
walletStore WalletStore
|
walletStore WalletStore
|
||||||
transferStore TransferStore
|
transferStore TransferStore
|
||||||
notificationStore notificationservice.NotificationStore
|
notificationStore notificationservice.NotificationStore
|
||||||
|
notificationSvc *notificationservice.Service
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,18 @@ func (s *Service) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWalle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error {
|
func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error {
|
||||||
return s.walletStore.UpdateBalance(ctx, id, balance)
|
err := s.walletStore.UpdateBalance(ctx, id, balance)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wallet, err := s.GetWalletByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.notificationSvc.UpdateLiveWalletMetricForWallet(ctx, wallet)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) AddToWallet(
|
func (s *Service) AddToWallet(
|
||||||
|
|
@ -84,6 +95,8 @@ func (s *Service) AddToWallet(
|
||||||
return domain.Transfer{}, err
|
return domain.Transfer{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
|
|
@ -121,6 +134,8 @@ 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)
|
||||||
|
|
||||||
// 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{
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
||||||
|
issuereporting "github.com/SamuelTariku/FortuneBet-Backend/internal/services/issue_reporting"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/recommendation"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/recommendation"
|
||||||
|
|
@ -37,6 +39,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
|
issueReportingSvc *issuereporting.Service
|
||||||
|
instSvc *institutions.Service
|
||||||
currSvc *currency.Service
|
currSvc *currency.Service
|
||||||
fiber *fiber.App
|
fiber *fiber.App
|
||||||
aleaVirtualGameService alea.AleaVirtualGameService
|
aleaVirtualGameService alea.AleaVirtualGameService
|
||||||
|
|
@ -70,6 +74,8 @@ type App struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApp(
|
func NewApp(
|
||||||
|
issueReportingSvc *issuereporting.Service,
|
||||||
|
instSvc *institutions.Service,
|
||||||
currSvc *currency.Service,
|
currSvc *currency.Service,
|
||||||
port int, validator *customvalidator.CustomValidator,
|
port int, validator *customvalidator.CustomValidator,
|
||||||
settingSvc *settings.Service,
|
settingSvc *settings.Service,
|
||||||
|
|
@ -113,6 +119,8 @@ func NewApp(
|
||||||
}))
|
}))
|
||||||
|
|
||||||
s := &App{
|
s := &App{
|
||||||
|
issueReportingSvc: issueReportingSvc,
|
||||||
|
instSvc: instSvc,
|
||||||
currSvc: currSvc,
|
currSvc: currSvc,
|
||||||
fiber: app,
|
fiber: app,
|
||||||
port: port,
|
port: port,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import (
|
||||||
// @Success 200 {object} domain.BetRes
|
// @Success 200 {object} domain.BetRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /bet [post]
|
// @Router /sport/bet [post]
|
||||||
func (h *Handler) CreateBet(c *fiber.Ctx) error {
|
func (h *Handler) CreateBet(c *fiber.Ctx) error {
|
||||||
userID := c.Locals("user_id").(int64)
|
userID := c.Locals("user_id").(int64)
|
||||||
role := c.Locals("role").(domain.Role)
|
role := c.Locals("role").(domain.Role)
|
||||||
|
|
@ -82,7 +82,7 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
|
||||||
// @Success 200 {object} domain.BetRes
|
// @Success 200 {object} domain.BetRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /random/bet [post]
|
// @Router /sport/random/bet [post]
|
||||||
func (h *Handler) RandomBet(c *fiber.Ctx) error {
|
func (h *Handler) RandomBet(c *fiber.Ctx) error {
|
||||||
userID := c.Locals("user_id").(int64)
|
userID := c.Locals("user_id").(int64)
|
||||||
|
|
||||||
|
|
@ -207,7 +207,7 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error {
|
||||||
// @Success 200 {array} domain.BetRes
|
// @Success 200 {array} domain.BetRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /bet [get]
|
// @Router /sport/bet [get]
|
||||||
func (h *Handler) GetAllBet(c *fiber.Ctx) error {
|
func (h *Handler) GetAllBet(c *fiber.Ctx) error {
|
||||||
role := c.Locals("role").(domain.Role)
|
role := c.Locals("role").(domain.Role)
|
||||||
companyID := c.Locals("company_id").(domain.ValidInt64)
|
companyID := c.Locals("company_id").(domain.ValidInt64)
|
||||||
|
|
@ -305,7 +305,7 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
|
||||||
// @Success 200 {object} domain.BetRes
|
// @Success 200 {object} domain.BetRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /bet/{id} [get]
|
// @Router /sport/bet/{id} [get]
|
||||||
func (h *Handler) GetBetByID(c *fiber.Ctx) error {
|
func (h *Handler) GetBetByID(c *fiber.Ctx) error {
|
||||||
betID := c.Params("id")
|
betID := c.Params("id")
|
||||||
id, err := strconv.ParseInt(betID, 10, 64)
|
id, err := strconv.ParseInt(betID, 10, 64)
|
||||||
|
|
@ -351,7 +351,7 @@ func (h *Handler) GetBetByID(c *fiber.Ctx) error {
|
||||||
// @Success 200 {object} domain.BetRes
|
// @Success 200 {object} domain.BetRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /bet/cashout/{id} [get]
|
// @Router /sport/bet/cashout/{id} [get]
|
||||||
func (h *Handler) GetBetByCashoutID(c *fiber.Ctx) error {
|
func (h *Handler) GetBetByCashoutID(c *fiber.Ctx) error {
|
||||||
cashoutID := c.Params("id")
|
cashoutID := c.Params("id")
|
||||||
|
|
||||||
|
|
@ -392,7 +392,7 @@ type UpdateCashOutReq struct {
|
||||||
// @Success 200 {object} response.APIResponse
|
// @Success 200 {object} response.APIResponse
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /bet/{id} [patch]
|
// @Router /sport/bet/{id} [patch]
|
||||||
func (h *Handler) UpdateCashOut(c *fiber.Ctx) error {
|
func (h *Handler) UpdateCashOut(c *fiber.Ctx) error {
|
||||||
type UpdateCashOutReq struct {
|
type UpdateCashOutReq struct {
|
||||||
CashedOut bool `json:"cashed_out" validate:"required" example:"true"`
|
CashedOut bool `json:"cashed_out" validate:"required" example:"true"`
|
||||||
|
|
@ -455,7 +455,7 @@ func (h *Handler) UpdateCashOut(c *fiber.Ctx) error {
|
||||||
// @Success 200 {object} response.APIResponse
|
// @Success 200 {object} response.APIResponse
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /bet/{id} [delete]
|
// @Router /sport/bet/{id} [delete]
|
||||||
func (h *Handler) DeleteBet(c *fiber.Ctx) error {
|
func (h *Handler) DeleteBet(c *fiber.Ctx) error {
|
||||||
betID := c.Params("id")
|
betID := c.Params("id")
|
||||||
id, err := strconv.ParseInt(betID, 10, 64)
|
id, err := strconv.ParseInt(betID, 10, 64)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func (h *Handler) InitiateDeposit(c *fiber.Ctx) error {
|
||||||
var req domain.ChapaDepositRequestPayload
|
var req domain.ChapaDepositRequestPayload
|
||||||
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
fmt.Sprintln("We first first are here init Chapa payment")
|
// fmt.Println("We first first are here init Chapa payment")
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
Error: err.Error(),
|
Error: err.Error(),
|
||||||
Message: "Failed to parse request body",
|
Message: "Failed to parse request body",
|
||||||
|
|
@ -41,7 +41,7 @@ func (h *Handler) InitiateDeposit(c *fiber.Ctx) error {
|
||||||
|
|
||||||
amount := domain.Currency(req.Amount * 100)
|
amount := domain.Currency(req.Amount * 100)
|
||||||
|
|
||||||
fmt.Sprintln("We are here init Chapa payment")
|
fmt.Println("We are here init Chapa payment")
|
||||||
|
|
||||||
checkoutURL, err := h.chapaSvc.InitiateDeposit(c.Context(), userID, amount)
|
checkoutURL, err := h.chapaSvc.InitiateDeposit(c.Context(), userID, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -79,7 +79,7 @@ func (h *Handler) WebhookCallback(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch chapaTransactionType.Type {
|
switch chapaTransactionType.Type {
|
||||||
case h.Cfg.CHAPA_TRANSFER_TYPE:
|
case h.Cfg.CHAPA_PAYMENT_TYPE:
|
||||||
chapaTransferVerificationRequest := new(domain.ChapaWebHookTransfer)
|
chapaTransferVerificationRequest := new(domain.ChapaWebHookTransfer)
|
||||||
|
|
||||||
if err := c.BodyParser(chapaTransferVerificationRequest); err != nil {
|
if err := c.BodyParser(chapaTransferVerificationRequest); err != nil {
|
||||||
|
|
@ -100,7 +100,7 @@ func (h *Handler) WebhookCallback(c *fiber.Ctx) error {
|
||||||
Data: chapaTransferVerificationRequest,
|
Data: chapaTransferVerificationRequest,
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
case h.Cfg.CHAPA_PAYMENT_TYPE:
|
case h.Cfg.CHAPA_TRANSFER_TYPE:
|
||||||
chapaPaymentVerificationRequest := new(domain.ChapaWebHookPayment)
|
chapaPaymentVerificationRequest := new(domain.ChapaWebHookPayment)
|
||||||
if err := c.BodyParser(chapaPaymentVerificationRequest); err != nil {
|
if err := c.BodyParser(chapaPaymentVerificationRequest); err != nil {
|
||||||
return domain.UnProcessableEntityResponse(c)
|
return domain.UnProcessableEntityResponse(c)
|
||||||
|
|
@ -147,7 +147,7 @@ func (h *Handler) ManualVerifyTransaction(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
verification, err := h.chapaSvc.ManualVerifTransaction(c.Context(), txRef)
|
verification, err := h.chapaSvc.ManuallyVerify(c.Context(), txRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
Message: "Failed to verify Chapa transaction",
|
Message: "Failed to verify Chapa transaction",
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
||||||
|
issuereporting "github.com/SamuelTariku/FortuneBet-Backend/internal/services/issue_reporting"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
|
||||||
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
|
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
|
|
@ -31,6 +33,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
|
issueReportingSvc *issuereporting.Service
|
||||||
|
instSvc *institutions.Service
|
||||||
currSvc *currency.Service
|
currSvc *currency.Service
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
settingSvc *settings.Service
|
settingSvc *settings.Service
|
||||||
|
|
@ -61,6 +65,8 @@ type Handler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
|
issueReportingSvc *issuereporting.Service,
|
||||||
|
instSvc *institutions.Service,
|
||||||
currSvc *currency.Service,
|
currSvc *currency.Service,
|
||||||
logger *slog.Logger,
|
logger *slog.Logger,
|
||||||
settingSvc *settings.Service,
|
settingSvc *settings.Service,
|
||||||
|
|
@ -90,6 +96,8 @@ func New(
|
||||||
mongoLoggerSvc *zap.Logger,
|
mongoLoggerSvc *zap.Logger,
|
||||||
) *Handler {
|
) *Handler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
|
issueReportingSvc: issueReportingSvc,
|
||||||
|
instSvc: instSvc,
|
||||||
currSvc: currSvc,
|
currSvc: currSvc,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
settingSvc: settingSvc,
|
settingSvc: settingSvc,
|
||||||
|
|
|
||||||
135
internal/web_server/handlers/institutions.go
Normal file
135
internal/web_server/handlers/institutions.go
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Summary Create a new bank
|
||||||
|
// @Tags Institutions - Banks
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param bank body domain.Bank true "Bank Info"
|
||||||
|
// @Success 201 {object} domain.Bank
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/banks [post]
|
||||||
|
func (h *Handler) CreateBank(c *fiber.Ctx) error {
|
||||||
|
var bank domain.Bank
|
||||||
|
if err := c.BodyParser(&bank); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.instSvc.Create(c.Context(), &bank)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusCreated).JSON(bank)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Get a bank by ID
|
||||||
|
// @Tags Institutions - Banks
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "Bank ID"
|
||||||
|
// @Success 200 {object} domain.Bank
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 404 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/banks/{id} [get]
|
||||||
|
func (h *Handler) GetBankByID(c *fiber.Ctx) error {
|
||||||
|
id, err := c.ParamsInt("id")
|
||||||
|
if err != nil || id <= 0 {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
bank, err := h.instSvc.GetByID(c.Context(), int64(id))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "bank not found"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(bank)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Update a bank
|
||||||
|
// @Tags Institutions - Banks
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "Bank ID"
|
||||||
|
// @Param bank body domain.Bank true "Bank Info"
|
||||||
|
// @Success 200 {object} domain.Bank
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 404 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/banks/{id} [put]
|
||||||
|
func (h *Handler) UpdateBank(c *fiber.Ctx) error {
|
||||||
|
id, err := c.ParamsInt("id")
|
||||||
|
if err != nil || id <= 0 {
|
||||||
|
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to update bank",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var bank domain.Bank
|
||||||
|
if err := c.BodyParser(&bank); err != nil {
|
||||||
|
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to update bank",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
bank.ID = id
|
||||||
|
|
||||||
|
err = h.instSvc.Update(c.Context(), &bank)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to update bank",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: "Bank updated successfully",
|
||||||
|
StatusCode: fiber.StatusOK,
|
||||||
|
Success: true,
|
||||||
|
Data: bank,
|
||||||
|
})
|
||||||
|
// return c.JSON(bank)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Delete a bank
|
||||||
|
// @Tags Institutions - Banks
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "Bank ID"
|
||||||
|
// @Success 204 {string} string "Deleted successfully"
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 404 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/banks/{id} [delete]
|
||||||
|
func (h *Handler) DeleteBank(c *fiber.Ctx) error {
|
||||||
|
id, err := c.ParamsInt("id")
|
||||||
|
if err != nil || id <= 0 {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.instSvc.Delete(c.Context(), int64(id))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary List all banks
|
||||||
|
// @Tags Institutions - Banks
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} domain.Bank
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/banks [get]
|
||||||
|
func (h *Handler) ListBanks(c *fiber.Ctx) error {
|
||||||
|
banks, err := h.instSvc.List(c.Context())
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
}
|
||||||
|
return c.JSON(banks)
|
||||||
|
}
|
||||||
147
internal/web_server/handlers/issue_reporting.go
Normal file
147
internal/web_server/handlers/issue_reporting.go
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateIssue godoc
|
||||||
|
// @Summary Report an issue
|
||||||
|
// @Description Allows a customer to report a new issue related to the betting platform
|
||||||
|
// @Tags Issues
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param issue body domain.ReportedIssue true "Issue to report"
|
||||||
|
// @Success 201 {object} domain.ReportedIssue
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/issues [post]
|
||||||
|
func (h *Handler) CreateIssue(c *fiber.Ctx) error {
|
||||||
|
var req domain.ReportedIssue
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
|
}
|
||||||
|
|
||||||
|
created, err := h.issueReportingSvc.CreateReportedIssue(c.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusCreated).JSON(created)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomerIssues godoc
|
||||||
|
// @Summary Get reported issues by a customer
|
||||||
|
// @Description Returns all issues reported by a specific customer
|
||||||
|
// @Tags Issues
|
||||||
|
// @Produce json
|
||||||
|
// @Param customer_id path int true "Customer ID"
|
||||||
|
// @Param limit query int false "Limit"
|
||||||
|
// @Param offset query int false "Offset"
|
||||||
|
// @Success 200 {array} domain.ReportedIssue
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/issues/customer/{customer_id} [get]
|
||||||
|
func (h *Handler) GetCustomerIssues(c *fiber.Ctx) error {
|
||||||
|
customerID, err := strconv.ParseInt(c.Params("customer_id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid customer ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
limit, offset := getPaginationParams(c)
|
||||||
|
|
||||||
|
issues, err := h.issueReportingSvc.GetIssuesForCustomer(c.Context(), customerID, limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(issues)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllIssues godoc
|
||||||
|
// @Summary Get all reported issues
|
||||||
|
// @Description Admin endpoint to list all reported issues with pagination
|
||||||
|
// @Tags Issues
|
||||||
|
// @Produce json
|
||||||
|
// @Param limit query int false "Limit"
|
||||||
|
// @Param offset query int false "Offset"
|
||||||
|
// @Success 200 {array} domain.ReportedIssue
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/issues [get]
|
||||||
|
func (h *Handler) GetAllIssues(c *fiber.Ctx) error {
|
||||||
|
limit, offset := getPaginationParams(c)
|
||||||
|
|
||||||
|
issues, err := h.issueReportingSvc.GetAllIssues(c.Context(), limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(issues)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateIssueStatus godoc
|
||||||
|
// @Summary Update issue status
|
||||||
|
// @Description Admin endpoint to update the status of a reported issue
|
||||||
|
// @Tags Issues
|
||||||
|
// @Accept json
|
||||||
|
// @Param issue_id path int true "Issue ID"
|
||||||
|
// @Param status body object{status=string} true "New issue status (pending, in_progress, resolved, rejected)"
|
||||||
|
// @Success 204
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/issues/{issue_id}/status [patch]
|
||||||
|
func (h *Handler) UpdateIssueStatus(c *fiber.Ctx) error {
|
||||||
|
issueID, err := strconv.ParseInt(c.Params("issue_id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid issue ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
var body struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
if err := c.BodyParser(&body); err != nil || body.Status == "" {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid status payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.issueReportingSvc.UpdateIssueStatus(c.Context(), issueID, body.Status); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIssue godoc
|
||||||
|
// @Summary Delete a reported issue
|
||||||
|
// @Description Admin endpoint to delete a reported issue
|
||||||
|
// @Tags Issues
|
||||||
|
// @Param issue_id path int true "Issue ID"
|
||||||
|
// @Success 204
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/issues/{issue_id} [delete]
|
||||||
|
func (h *Handler) DeleteIssue(c *fiber.Ctx) error {
|
||||||
|
issueID, err := strconv.ParseInt(c.Params("issue_id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid issue ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.issueReportingSvc.DeleteIssue(c.Context(), issueID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPaginationParams(c *fiber.Ctx) (limit, offset int) {
|
||||||
|
limit = 20
|
||||||
|
offset = 0
|
||||||
|
|
||||||
|
if l, err := strconv.Atoi(c.Query("limit")); err == nil && l > 0 {
|
||||||
|
limit = l
|
||||||
|
}
|
||||||
|
if o, err := strconv.Atoi(c.Query("offset")); err == nil && o >= 0 {
|
||||||
|
offset = o
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// @Summary Get virtual game recommendations
|
// @Summary Get virtual game recommendations
|
||||||
// @Description Returns a list of recommended virtual games for a specific user
|
// @Description Returns a list of recommended virtual games for a specific user
|
||||||
// @Tags Recommendations
|
// @Tags Recommendations
|
||||||
|
|
@ -13,14 +9,15 @@ import (
|
||||||
// @Success 200 {object} domain.RecommendationSuccessfulResponse "Recommended games fetched successfully"
|
// @Success 200 {object} domain.RecommendationSuccessfulResponse "Recommended games fetched successfully"
|
||||||
// @Failure 500 {object} domain.RecommendationErrorResponse "Failed to fetch recommendations"
|
// @Failure 500 {object} domain.RecommendationErrorResponse "Failed to fetch recommendations"
|
||||||
// @Router /api/v1/virtual-games/recommendations/{userID} [get]
|
// @Router /api/v1/virtual-games/recommendations/{userID} [get]
|
||||||
func (h *Handler) GetRecommendations(c *fiber.Ctx) error {
|
|
||||||
userID := c.Params("userID") // or from JWT
|
// func (h *Handler) GetRecommendations(c *fiber.Ctx) error {
|
||||||
recommendations, err := h.recommendationSvc.GetRecommendations(c.Context(), userID)
|
// userID := c.Params("userID") // or from JWT
|
||||||
if err != nil {
|
// recommendations, err := h.recommendationSvc.GetRecommendations(c.Context(), userID)
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch recommendations")
|
// if err != nil {
|
||||||
}
|
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch recommendations")
|
||||||
return c.JSON(fiber.Map{
|
// }
|
||||||
"message": "Recommended games fetched successfully",
|
// return c.JSON(fiber.Map{
|
||||||
"recommended_games": recommendations,
|
// "message": "Recommended games fetched successfully",
|
||||||
})
|
// "recommended_games": recommendations,
|
||||||
}
|
// })
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type TransferWalletRes struct {
|
type TransferWalletRes struct {
|
||||||
ID int64 `json:"id" example:"1"`
|
ID int64 `json:"id"`
|
||||||
Amount float32 `json:"amount" example:"100.0"`
|
Amount float32 `json:"amount"`
|
||||||
Verified bool `json:"verified" example:"true"`
|
Verified bool `json:"verified"`
|
||||||
Type string `json:"type" example:"transfer"`
|
Type string `json:"type"`
|
||||||
PaymentMethod string `json:"payment_method" example:"bank"`
|
PaymentMethod string `json:"payment_method"`
|
||||||
ReceiverWalletID *int64 `json:"receiver_wallet_id" example:"1"`
|
ReceiverWalletID *int64 `json:"receiver_wallet_id,omitempty"`
|
||||||
SenderWalletID *int64 `json:"sender_wallet_id" example:"1"`
|
SenderWalletID *int64 `json:"sender_wallet_id,omitempty"`
|
||||||
CashierID *int64 `json:"cashier_id" example:"789"`
|
CashierID *int64 `json:"cashier_id,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
|
ReferenceNumber string `json:"reference_number"` // ← Add this
|
||||||
UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RefillRes struct {
|
type RefillRes struct {
|
||||||
ID int64 `json:"id" example:"1"`
|
ID int64 `json:"id" example:"1"`
|
||||||
Amount float32 `json:"amount" example:"100.0"`
|
Amount float32 `json:"amount" example:"100.0"`
|
||||||
|
|
@ -35,33 +37,34 @@ type RefillRes struct {
|
||||||
UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"`
|
UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertTransfer(transfer domain.Transfer) TransferWalletRes {
|
func convertTransfer(t domain.Transfer) TransferWalletRes {
|
||||||
|
var receiverID *int64
|
||||||
|
if t.ReceiverWalletID.Valid {
|
||||||
|
receiverID = &t.ReceiverWalletID.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var senderID *int64
|
||||||
|
if t.SenderWalletID.Valid {
|
||||||
|
senderID = &t.SenderWalletID.Value
|
||||||
|
}
|
||||||
|
|
||||||
var cashierID *int64
|
var cashierID *int64
|
||||||
if transfer.CashierID.Valid {
|
if t.CashierID.Valid {
|
||||||
cashierID = &transfer.CashierID.Value
|
cashierID = &t.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{
|
return TransferWalletRes{
|
||||||
ID: transfer.ID,
|
ID: t.ID,
|
||||||
Amount: transfer.Amount.Float32(),
|
Amount: float32(t.Amount),
|
||||||
Verified: transfer.Verified,
|
Verified: t.Verified,
|
||||||
Type: string(transfer.Type),
|
Type: string(t.Type),
|
||||||
PaymentMethod: string(transfer.PaymentMethod),
|
PaymentMethod: string(t.PaymentMethod),
|
||||||
ReceiverWalletID: receiverID,
|
ReceiverWalletID: receiverID,
|
||||||
SenderWalletID: senderId,
|
SenderWalletID: senderID,
|
||||||
CashierID: cashierID,
|
CashierID: cashierID,
|
||||||
CreatedAt: transfer.CreatedAt,
|
ReferenceNumber: t.ReferenceNumber,
|
||||||
UpdatedAt: transfer.UpdatedAt,
|
CreatedAt: t.CreatedAt,
|
||||||
|
UpdatedAt: t.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,10 +145,11 @@ func (h *Handler) TransferToWallet(c *fiber.Ctx) error {
|
||||||
var senderID int64
|
var senderID int64
|
||||||
|
|
||||||
//TODO: check to make sure that the cashiers aren't transferring TO branch wallet
|
//TODO: check to make sure that the cashiers aren't transferring TO branch wallet
|
||||||
if role == domain.RoleCustomer {
|
switch role {
|
||||||
|
case domain.RoleCustomer:
|
||||||
h.logger.Error("Unauthorized access", "userID", userID, "role", role)
|
h.logger.Error("Unauthorized access", "userID", userID, "role", role)
|
||||||
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
|
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
|
||||||
} else if role == domain.RoleBranchManager || role == domain.RoleAdmin || role == domain.RoleSuperAdmin {
|
case domain.RoleBranchManager, domain.RoleAdmin, domain.RoleSuperAdmin:
|
||||||
company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value)
|
company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
|
@ -156,7 +160,7 @@ func (h *Handler) TransferToWallet(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
senderID = company.WalletID
|
senderID = company.WalletID
|
||||||
h.logger.Error("Will", "userID", userID, "role", role)
|
h.logger.Error("Will", "userID", userID, "role", role)
|
||||||
} else {
|
default:
|
||||||
cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
|
cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error("Failed to get branch", "user ID", userID, "error", err)
|
h.logger.Error("Failed to get branch", "user ID", userID, "error", err)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
|
@ -25,9 +27,9 @@ type launchVirtualGameRes struct {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param launch body launchVirtualGameReq true "Game launch details"
|
// @Param launch body launchVirtualGameReq true "Game launch details"
|
||||||
// @Success 200 {object} launchVirtualGameRes
|
// @Success 200 {object} launchVirtualGameRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
// @Failure 401 {object} response.APIResponse
|
// @Failure 401 {object} domain.ErrorResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
// @Router /virtual-game/launch [post]
|
// @Router /virtual-game/launch [post]
|
||||||
func (h *Handler) LaunchVirtualGame(c *fiber.Ctx) error {
|
func (h *Handler) LaunchVirtualGame(c *fiber.Ctx) error {
|
||||||
|
|
||||||
|
|
@ -37,6 +39,12 @@ func (h *Handler) LaunchVirtualGame(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Invalid user identification")
|
return fiber.NewError(fiber.StatusUnauthorized, "Invalid user identification")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// companyID, ok := c.Locals("company_id").(int64)
|
||||||
|
// if !ok || companyID == 0 {
|
||||||
|
// h.logger.Error("Invalid company ID in context")
|
||||||
|
// return fiber.NewError(fiber.StatusUnauthorized, "Invalid company identification")
|
||||||
|
// }
|
||||||
|
|
||||||
var req launchVirtualGameReq
|
var req launchVirtualGameReq
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
h.logger.Error("Failed to parse LaunchVirtualGame request", "error", err)
|
h.logger.Error("Failed to parse LaunchVirtualGame request", "error", err)
|
||||||
|
|
@ -64,9 +72,9 @@ func (h *Handler) LaunchVirtualGame(c *fiber.Ctx) error {
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param callback body domain.PopOKCallback true "Callback data"
|
// @Param callback body domain.PopOKCallback true "Callback data"
|
||||||
// @Success 200 {object} response.APIResponse
|
// @Success 200 {object} domain.ErrorResponse
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
// @Router /virtual-game/callback [post]
|
// @Router /virtual-game/callback [post]
|
||||||
func (h *Handler) HandleVirtualGameCallback(c *fiber.Ctx) error {
|
func (h *Handler) HandleVirtualGameCallback(c *fiber.Ctx) error {
|
||||||
var callback domain.PopOKCallback
|
var callback domain.PopOKCallback
|
||||||
|
|
@ -199,3 +207,119 @@ func (h *Handler) RecommendGames(c *fiber.Ctx) error {
|
||||||
|
|
||||||
return c.JSON(recommendations)
|
return c.JSON(recommendations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) HandleTournamentWin(c *fiber.Ctx) error {
|
||||||
|
var req domain.PopOKWinRequest
|
||||||
|
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
h.logger.Error("Invalid tournament win request body", "error", err)
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"error": "Invalid request body",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := h.virtualGameSvc.ProcessTournamentWin(c.Context(), &req)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("Failed to process tournament win", "error", err)
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) HandlePromoWin(c *fiber.Ctx) error {
|
||||||
|
var req domain.PopOKWinRequest
|
||||||
|
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
h.logger.Error("Invalid promo win request body", "error", err)
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"error": "Invalid request body",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := h.virtualGameSvc.ProcessPromoWin(c.Context(), &req)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("Failed to process promo win", "error", err)
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFavoriteGame godoc
|
||||||
|
// @Summary Add game to favorites
|
||||||
|
// @Description Adds a game to the user's favorite games list
|
||||||
|
// @Tags VirtualGames - Favourites
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body domain.FavoriteGameRequest true "Game ID to add"
|
||||||
|
// @Success 201 {string} domain.Response "created"
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/virtual-game/favorites [post]
|
||||||
|
func (h *Handler) AddFavorite(c *fiber.Ctx) error {
|
||||||
|
userID := c.Locals("user_id").(int64)
|
||||||
|
|
||||||
|
var req domain.FavoriteGameRequest
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.virtualGameSvc.AddFavoriteGame(c.Context(), userID, req.GameID)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Could not add favorite",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
// return fiber.NewError(fiber.StatusInternalServerError, "Could not add favorite")
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusCreated).JSON(domain.Response{
|
||||||
|
Message: "Game added to favorites",
|
||||||
|
StatusCode: fiber.StatusCreated,
|
||||||
|
Success: true,
|
||||||
|
})
|
||||||
|
// return c.SendStatus(fiber.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFavoriteGame godoc
|
||||||
|
// @Summary Remove game from favorites
|
||||||
|
// @Description Removes a game from the user's favorites
|
||||||
|
// @Tags VirtualGames - Favourites
|
||||||
|
// @Produce json
|
||||||
|
// @Param gameID path int64 true "Game ID to remove"
|
||||||
|
// @Success 200 {string} domain.Response "removed"
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/virtual-game/favorites/{gameID} [delete]
|
||||||
|
func (h *Handler) RemoveFavorite(c *fiber.Ctx) error {
|
||||||
|
userID := c.Locals("user_id").(int64)
|
||||||
|
gameID, _ := strconv.ParseInt(c.Params("gameID"), 10, 64)
|
||||||
|
|
||||||
|
err := h.virtualGameSvc.RemoveFavoriteGame(c.Context(), userID, gameID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Could not remove favorite")
|
||||||
|
}
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFavoriteGames godoc
|
||||||
|
// @Summary Get user's favorite games
|
||||||
|
// @Description Lists the games that the user marked as favorite
|
||||||
|
// @Tags VirtualGames - Favourites
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} domain.GameRecommendation
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/virtual-game/favorites [get]
|
||||||
|
func (h *Handler) ListFavorites(c *fiber.Ctx) error {
|
||||||
|
userID := c.Locals("user_id").(int64)
|
||||||
|
|
||||||
|
games, err := h.virtualGameSvc.ListFavoriteGames(c.Context(), userID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Could not fetch favorites")
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusOK).JSON(games)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ type PopOKClaim struct {
|
||||||
Lang string `json:"lang"`
|
Lang string `json:"lang"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
SessionID string `json:"session_id"`
|
SessionID string `json:"session_id"`
|
||||||
|
CompanyID domain.ValidInt64 `json:"company_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JwtConfig struct {
|
type JwtConfig struct {
|
||||||
|
|
@ -54,7 +55,7 @@ func CreateJwt(userId int64, Role domain.Role, CompanyID domain.ValidInt64, key
|
||||||
return jwtToken, err
|
return jwtToken, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePopOKJwt(userID int64, username, currency, lang, mode, sessionID, key string, expiry time.Duration) (string, error) {
|
func CreatePopOKJwt(userID int64, CompanyID domain.ValidInt64, username, currency, lang, mode, sessionID, key string, expiry time.Duration) (string, error) {
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, PopOKClaim{
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, PopOKClaim{
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
Issuer: "fortune-bet",
|
Issuer: "fortune-bet",
|
||||||
|
|
@ -69,6 +70,7 @@ func CreatePopOKJwt(userID int64, username, currency, lang, mode, sessionID, key
|
||||||
Lang: lang,
|
Lang: lang,
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
SessionID: sessionID,
|
SessionID: sessionID,
|
||||||
|
CompanyID: CompanyID,
|
||||||
})
|
})
|
||||||
return token.SignedString([]byte(key))
|
return token.SignedString([]byte(key))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import (
|
||||||
|
|
||||||
func (a *App) initAppRoutes() {
|
func (a *App) initAppRoutes() {
|
||||||
h := handlers.New(
|
h := handlers.New(
|
||||||
|
a.issueReportingSvc,
|
||||||
|
a.instSvc,
|
||||||
a.currSvc,
|
a.currSvc,
|
||||||
a.logger,
|
a.logger,
|
||||||
a.settingSvc,
|
a.settingSvc,
|
||||||
|
|
@ -182,14 +184,14 @@ func (a *App) initAppRoutes() {
|
||||||
a.fiber.Get("/ticket/:id", h.GetTicketByID)
|
a.fiber.Get("/ticket/:id", h.GetTicketByID)
|
||||||
|
|
||||||
// Bet Routes
|
// Bet Routes
|
||||||
a.fiber.Post("/bet", a.authMiddleware, h.CreateBet)
|
a.fiber.Post("/sport/bet", a.authMiddleware, h.CreateBet)
|
||||||
a.fiber.Get("/bet", a.authMiddleware, h.GetAllBet)
|
a.fiber.Get("/sport/bet", a.authMiddleware, h.GetAllBet)
|
||||||
a.fiber.Get("/bet/:id", h.GetBetByID)
|
a.fiber.Get("/sport/bet/:id", h.GetBetByID)
|
||||||
a.fiber.Get("/bet/cashout/:id", a.authMiddleware, h.GetBetByCashoutID)
|
a.fiber.Get("/sport/bet/cashout/:id", a.authMiddleware, h.GetBetByCashoutID)
|
||||||
a.fiber.Patch("/bet/:id", a.authMiddleware, h.UpdateCashOut)
|
a.fiber.Patch("/sport/bet/:id", a.authMiddleware, h.UpdateCashOut)
|
||||||
a.fiber.Delete("/bet/:id", a.authMiddleware, h.DeleteBet)
|
a.fiber.Delete("/sport/bet/:id", a.authMiddleware, h.DeleteBet)
|
||||||
|
|
||||||
a.fiber.Post("/random/bet", a.authMiddleware, h.RandomBet)
|
a.fiber.Post("/sport/random/bet", a.authMiddleware, h.RandomBet)
|
||||||
|
|
||||||
// Wallet
|
// Wallet
|
||||||
a.fiber.Get("/wallet", h.GetAllWallets)
|
a.fiber.Get("/wallet", h.GetAllWallets)
|
||||||
|
|
@ -277,9 +279,20 @@ func (a *App) initAppRoutes() {
|
||||||
a.fiber.Post("/bet", h.HandleBet)
|
a.fiber.Post("/bet", h.HandleBet)
|
||||||
a.fiber.Post("/win", h.HandleWin)
|
a.fiber.Post("/win", h.HandleWin)
|
||||||
a.fiber.Post("/cancel", h.HandleCancel)
|
a.fiber.Post("/cancel", h.HandleCancel)
|
||||||
|
a.fiber.Post("/promoWin ", h.HandlePromoWin)
|
||||||
|
a.fiber.Post("/tournamentWin ", h.HandleTournamentWin)
|
||||||
a.fiber.Get("/popok/games", h.GetGameList)
|
a.fiber.Get("/popok/games", h.GetGameList)
|
||||||
a.fiber.Get("/popok/games/recommend", a.authMiddleware, h.RecommendGames)
|
a.fiber.Get("/popok/games/recommend", a.authMiddleware, h.RecommendGames)
|
||||||
|
group.Post("/virtual-game/favorites", a.authMiddleware, h.AddFavorite)
|
||||||
|
group.Delete("/virtual-game/favorites/:gameID", a.authMiddleware, h.RemoveFavorite)
|
||||||
|
group.Get("/virtual-game/favorites", a.authMiddleware, h.ListFavorites)
|
||||||
|
|
||||||
|
//Issue Reporting Routes
|
||||||
|
group.Post("/issues", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateIssue)
|
||||||
|
group.Get("/issues/customer/:customer_id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetCustomerIssues)
|
||||||
|
group.Get("/issues", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllIssues)
|
||||||
|
group.Patch("/issues/:issue_id/status", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateIssueStatus)
|
||||||
|
group.Delete("/issues/:issue_id", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteIssue)
|
||||||
}
|
}
|
||||||
|
|
||||||
///user/profile get
|
///user/profile get
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user