direct depost and veli games fix
This commit is contained in:
parent
fe7d9ad3b3
commit
1c7e076be5
12
cmd/main.go
12
cmd/main.go
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa"
|
||||||
"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"
|
||||||
|
directdeposit "github.com/SamuelTariku/FortuneBet-Backend/internal/services/direct_deposit"
|
||||||
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
|
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
||||||
|
|
@ -182,7 +183,7 @@ func main() {
|
||||||
walletSvc := wallet.NewService(
|
walletSvc := wallet.NewService(
|
||||||
repository.NewWalletStore(store),
|
repository.NewWalletStore(store),
|
||||||
repository.NewTransferStore(store),
|
repository.NewTransferStore(store),
|
||||||
repository.NewDirectDepositStore(store),
|
// repository.NewDirectDepositStore(store),
|
||||||
notificationSvc,
|
notificationSvc,
|
||||||
userSvc,
|
userSvc,
|
||||||
domain.MongoDBLogger,
|
domain.MongoDBLogger,
|
||||||
|
|
@ -326,6 +327,14 @@ func main() {
|
||||||
|
|
||||||
// Start cron jobs for automated reporting
|
// Start cron jobs for automated reporting
|
||||||
|
|
||||||
|
directdeposit := directdeposit.NewService(
|
||||||
|
*walletSvc,
|
||||||
|
repository.NewTransferStore(store),
|
||||||
|
repository.NewDirectDepositRepository(store),
|
||||||
|
notificationSvc,
|
||||||
|
userSvc,
|
||||||
|
)
|
||||||
|
|
||||||
enetPulseSvc := enetpulse.New(
|
enetPulseSvc := enetpulse.New(
|
||||||
*cfg,
|
*cfg,
|
||||||
store,
|
store,
|
||||||
|
|
@ -373,6 +382,7 @@ func main() {
|
||||||
|
|
||||||
// Initialize and start HTTP server
|
// Initialize and start HTTP server
|
||||||
app := httpserver.NewApp(
|
app := httpserver.NewApp(
|
||||||
|
directdeposit,
|
||||||
enetPulseSvc,
|
enetPulseSvc,
|
||||||
atlasVirtualGameService,
|
atlasVirtualGameService,
|
||||||
veliVirtualGameService,
|
veliVirtualGameService,
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,7 @@ CREATE TABLE IF NOT EXISTS virtual_game_providers (
|
||||||
provider_id VARCHAR(100) UNIQUE NOT NULL,
|
provider_id VARCHAR(100) UNIQUE NOT NULL,
|
||||||
-- providerId from Veli Games
|
-- providerId from Veli Games
|
||||||
provider_name VARCHAR(255) NOT NULL,
|
provider_name VARCHAR(255) NOT NULL,
|
||||||
-- providerName
|
|
||||||
logo_dark TEXT,
|
logo_dark TEXT,
|
||||||
-- logoForDark (URL)
|
|
||||||
logo_light TEXT,
|
logo_light TEXT,
|
||||||
-- logoForLight (URL)
|
-- logoForLight (URL)
|
||||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
|
@ -618,20 +616,23 @@ CREATE TABLE flags (
|
||||||
);
|
);
|
||||||
CREATE TABLE direct_deposits (
|
CREATE TABLE direct_deposits (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
customer_id BIGINT NOT NULL REFERENCES users (id),
|
customer_id BIGINT REFERENCES users(id),
|
||||||
wallet_id BIGINT NOT NULL REFERENCES wallets (id),
|
wallet_id BIGINT REFERENCES wallets(id),
|
||||||
amount NUMERIC(15, 2) NOT NULL,
|
bank_name TEXT,
|
||||||
bank_reference TEXT NOT NULL,
|
account_number TEXT,
|
||||||
sender_account TEXT NOT NULL,
|
account_holder TEXT,
|
||||||
status TEXT NOT NULL CHECK (status IN ('pending', 'completed', 'rejected')),
|
amount NUMERIC(18,2),
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
reference_number TEXT,
|
||||||
verified_by BIGINT REFERENCES users (id),
|
transfer_screenshot TEXT,
|
||||||
verification_notes TEXT,
|
status TEXT CHECK(status IN ('PENDING', 'APPROVED', 'REJECTED')),
|
||||||
verified_at TIMESTAMP
|
created_at TIMESTAMPTZ,
|
||||||
|
approved_by BIGINT NULL REFERENCES users(id),
|
||||||
|
approved_at TIMESTAMPTZ NULL,
|
||||||
|
rejection_reason TEXT NULL
|
||||||
);
|
);
|
||||||
CREATE INDEX idx_direct_deposits_status ON direct_deposits (status);
|
-- CREATE INDEX idx_direct_deposits_status ON direct_deposits (status);
|
||||||
CREATE INDEX idx_direct_deposits_customer ON direct_deposits (customer_id);
|
-- CREATE INDEX idx_direct_deposits_customer ON direct_deposits (customer_id);
|
||||||
CREATE INDEX idx_direct_deposits_reference ON direct_deposits (bank_reference);
|
-- CREATE INDEX idx_direct_deposits_reference ON direct_deposits (bank_reference);
|
||||||
CREATE TABLE IF NOT EXISTS raffles (
|
CREATE TABLE IF NOT EXISTS raffles (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
company_id INT NOT NULL,
|
company_id INT NOT NULL,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ CREATE TABLE virtual_game_sessions (
|
||||||
|
|
||||||
CREATE TABLE virtual_game_transactions (
|
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,
|
company_id BIGINT,
|
||||||
provider VARCHAR(100),
|
provider VARCHAR(100),
|
||||||
|
|
@ -26,7 +26,7 @@ CREATE TABLE virtual_game_transactions (
|
||||||
|
|
||||||
CREATE TABLE virtual_game_histories (
|
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,
|
company_id BIGINT,
|
||||||
provider VARCHAR(100),
|
provider VARCHAR(100),
|
||||||
|
|
@ -56,7 +56,7 @@ CREATE INDEX idx_virtual_game_game_id ON virtual_game_histories(game_id);
|
||||||
CREATE INDEX idx_virtual_game_external_transaction_id ON virtual_game_histories(external_transaction_id);
|
CREATE INDEX idx_virtual_game_external_transaction_id ON virtual_game_histories(external_transaction_id);
|
||||||
|
|
||||||
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
|
ALTER TABLE favorite_games
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,64 @@
|
||||||
-- name: CreateDirectDeposit :one
|
-- name: CreateDirectDeposit :one
|
||||||
INSERT INTO direct_deposits (
|
INSERT INTO direct_deposits (
|
||||||
customer_id,
|
customer_id, wallet_id, bank_name, account_number,
|
||||||
wallet_id,
|
account_holder, amount, reference_number,
|
||||||
amount,
|
transfer_screenshot, status
|
||||||
bank_reference,
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'PENDING')
|
||||||
sender_account,
|
|
||||||
status
|
|
||||||
) VALUES (
|
|
||||||
$1, $2, $3, $4, $5, $6
|
|
||||||
) RETURNING *;
|
|
||||||
|
|
||||||
-- name: GetDirectDeposit :one
|
|
||||||
SELECT * FROM direct_deposits WHERE id = $1;
|
|
||||||
|
|
||||||
-- name: UpdateDirectDeposit :one
|
|
||||||
UPDATE direct_deposits
|
|
||||||
SET
|
|
||||||
status = $2,
|
|
||||||
verified_by = $3,
|
|
||||||
verification_notes = $4,
|
|
||||||
verified_at = $5
|
|
||||||
WHERE id = $1
|
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
-- name: GetDirectDepositsByStatus :many
|
-- name: GetDirectDepositByID :one
|
||||||
SELECT * FROM direct_deposits WHERE status = $1 ORDER BY created_at DESC;
|
SELECT *
|
||||||
|
FROM direct_deposits
|
||||||
|
WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteDirectDeposit :exec
|
||||||
|
DELETE FROM direct_deposits
|
||||||
|
WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: GetDirectDepositsByStatus :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
customer_id,
|
||||||
|
wallet_id,
|
||||||
|
bank_name,
|
||||||
|
account_number,
|
||||||
|
account_holder,
|
||||||
|
amount,
|
||||||
|
reference_number,
|
||||||
|
transfer_screenshot,
|
||||||
|
status,
|
||||||
|
created_at,
|
||||||
|
approved_by,
|
||||||
|
approved_at,
|
||||||
|
rejection_reason
|
||||||
|
FROM direct_deposits
|
||||||
|
WHERE status = $1
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT $2 OFFSET $3;
|
||||||
|
|
||||||
|
-- name: CountDirectDepositsByStatus :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM direct_deposits
|
||||||
|
WHERE status = $1;
|
||||||
|
|
||||||
|
-- name: ApproveDirectDeposit :exec
|
||||||
|
UPDATE direct_deposits
|
||||||
|
SET
|
||||||
|
status = 'APPROVED',
|
||||||
|
approved_by = $2,
|
||||||
|
approved_at = NOW()
|
||||||
|
WHERE
|
||||||
|
id = $1
|
||||||
|
AND status = 'PENDING';
|
||||||
|
|
||||||
|
-- name: RejectDirectDeposit :exec
|
||||||
|
UPDATE direct_deposits
|
||||||
|
SET
|
||||||
|
status = 'REJECTED',
|
||||||
|
approved_by = $2, -- still track the admin who took final action
|
||||||
|
approved_at = NOW(),
|
||||||
|
rejection_reason = $3
|
||||||
|
WHERE
|
||||||
|
id = $1
|
||||||
|
AND status = 'PENDING';
|
||||||
|
|
||||||
-- name: GetCustomerDirectDeposits :many
|
|
||||||
SELECT * FROM direct_deposits WHERE customer_id = $1 ORDER BY created_at DESC;
|
|
||||||
|
|
@ -97,7 +97,7 @@ WHERE session_token = $1;
|
||||||
|
|
||||||
-- name: CreateVirtualGameTransaction :one
|
-- name: CreateVirtualGameTransaction :one
|
||||||
INSERT INTO virtual_game_transactions (
|
INSERT INTO virtual_game_transactions (
|
||||||
session_id,
|
-- session_id,
|
||||||
user_id,
|
user_id,
|
||||||
company_id,
|
company_id,
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -108,9 +108,9 @@ INSERT INTO virtual_game_transactions (
|
||||||
external_transaction_id,
|
external_transaction_id,
|
||||||
status
|
status
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
RETURNING id,
|
RETURNING id,
|
||||||
session_id,
|
-- session_id,
|
||||||
user_id,
|
user_id,
|
||||||
company_id,
|
company_id,
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -124,7 +124,7 @@ RETURNING id,
|
||||||
updated_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,
|
company_id,
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -148,11 +148,11 @@ VALUES (
|
||||||
$8,
|
$8,
|
||||||
$9,
|
$9,
|
||||||
$10,
|
$10,
|
||||||
$11,
|
$11
|
||||||
$12
|
-- $12
|
||||||
)
|
)
|
||||||
RETURNING id,
|
RETURNING id,
|
||||||
session_id,
|
-- session_id,
|
||||||
user_id,
|
user_id,
|
||||||
company_id,
|
company_id,
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -169,7 +169,7 @@ RETURNING id,
|
||||||
|
|
||||||
-- name: GetVirtualGameTransactionByExternalID :one
|
-- name: GetVirtualGameTransactionByExternalID :one
|
||||||
SELECT id,
|
SELECT id,
|
||||||
session_id,
|
-- session_id,
|
||||||
user_id,
|
user_id,
|
||||||
wallet_id,
|
wallet_id,
|
||||||
transaction_type,
|
transaction_type,
|
||||||
|
|
@ -193,7 +193,7 @@ SELECT c.name AS company_name,
|
||||||
COUNT(vgt.id) AS number_of_bets,
|
COUNT(vgt.id) AS number_of_bets,
|
||||||
COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum
|
COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum
|
||||||
FROM virtual_game_transactions vgt
|
FROM virtual_game_transactions vgt
|
||||||
JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
|
-- JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
|
||||||
JOIN virtual_games vg ON vgs.game_id = vg.id
|
JOIN virtual_games vg ON vgs.game_id = vg.id
|
||||||
JOIN companies c ON vgt.company_id = c.id
|
JOIN companies c ON vgt.company_id = c.id
|
||||||
WHERE vgt.transaction_type = 'BET'
|
WHERE vgt.transaction_type = 'BET'
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ services:
|
||||||
image: mongo:7.0.11
|
image: mongo:7.0.11
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "27020:27017"
|
- "27021:27017"
|
||||||
environment:
|
environment:
|
||||||
MONGO_INITDB_ROOT_USERNAME: root
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
MONGO_INITDB_ROOT_PASSWORD: secret
|
MONGO_INITDB_ROOT_PASSWORD: secret
|
||||||
|
|
|
||||||
1028
docs/docs.go
1028
docs/docs.go
File diff suppressed because it is too large
Load Diff
1028
docs/swagger.json
1028
docs/swagger.json
File diff suppressed because it is too large
Load Diff
|
|
@ -174,6 +174,8 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
addedTime:
|
addedTime:
|
||||||
$ref: '#/definitions/domain.ValidInt'
|
$ref: '#/definitions/domain.ValidInt'
|
||||||
|
avgBetAmount:
|
||||||
|
type: integer
|
||||||
awayTeam:
|
awayTeam:
|
||||||
type: string
|
type: string
|
||||||
awayTeamID:
|
awayTeamID:
|
||||||
|
|
@ -212,6 +214,8 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
matchPeriod:
|
matchPeriod:
|
||||||
$ref: '#/definitions/domain.ValidInt'
|
$ref: '#/definitions/domain.ValidInt'
|
||||||
|
numberOfBets:
|
||||||
|
type: integer
|
||||||
score:
|
score:
|
||||||
$ref: '#/definitions/domain.ValidString'
|
$ref: '#/definitions/domain.ValidString'
|
||||||
source:
|
source:
|
||||||
|
|
@ -226,8 +230,12 @@ definitions:
|
||||||
$ref: '#/definitions/domain.EventStatus'
|
$ref: '#/definitions/domain.EventStatus'
|
||||||
timerStatus:
|
timerStatus:
|
||||||
$ref: '#/definitions/domain.ValidString'
|
$ref: '#/definitions/domain.ValidString'
|
||||||
|
totalAmount:
|
||||||
|
type: integer
|
||||||
totalOddOutcomes:
|
totalOddOutcomes:
|
||||||
type: integer
|
type: integer
|
||||||
|
totalPotentialWinnings:
|
||||||
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
domain.BaseLeague:
|
domain.BaseLeague:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -349,6 +357,11 @@ definitions:
|
||||||
company_id:
|
company_id:
|
||||||
example: 1
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
|
company_name:
|
||||||
|
example: fortune
|
||||||
|
type: string
|
||||||
|
deducted_stake:
|
||||||
|
type: number
|
||||||
id:
|
id:
|
||||||
example: 1
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
|
|
@ -373,9 +386,25 @@ definitions:
|
||||||
name:
|
name:
|
||||||
example: 4-kilo Branch
|
example: 4-kilo Branch
|
||||||
type: string
|
type: string
|
||||||
|
number_of_unsettled:
|
||||||
|
type: integer
|
||||||
profit_percentage:
|
profit_percentage:
|
||||||
example: 0.1
|
example: 0.1
|
||||||
type: number
|
type: number
|
||||||
|
stats_updated_at:
|
||||||
|
type: string
|
||||||
|
total_bets:
|
||||||
|
type: integer
|
||||||
|
total_cash_backs:
|
||||||
|
type: number
|
||||||
|
total_cash_out:
|
||||||
|
type: number
|
||||||
|
total_cashiers:
|
||||||
|
type: integer
|
||||||
|
total_stake:
|
||||||
|
type: number
|
||||||
|
total_unsettled_amount:
|
||||||
|
type: number
|
||||||
wallet_id:
|
wallet_id:
|
||||||
example: 1
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
|
|
@ -608,6 +637,19 @@ definitions:
|
||||||
- customerEmail
|
- customerEmail
|
||||||
- customerPhone
|
- customerPhone
|
||||||
type: object
|
type: object
|
||||||
|
domain.CompanyMarketSettings:
|
||||||
|
properties:
|
||||||
|
companyID:
|
||||||
|
type: integer
|
||||||
|
isActive:
|
||||||
|
$ref: '#/definitions/domain.ValidBool'
|
||||||
|
marketID:
|
||||||
|
type: integer
|
||||||
|
marketName:
|
||||||
|
type: string
|
||||||
|
updatedAt:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
domain.CompanyRes:
|
domain.CompanyRes:
|
||||||
properties:
|
properties:
|
||||||
admin_id:
|
admin_id:
|
||||||
|
|
@ -712,6 +754,17 @@ definitions:
|
||||||
- name
|
- name
|
||||||
- operations
|
- operations
|
||||||
type: object
|
type: object
|
||||||
|
domain.CreateCompanyMarketSettings:
|
||||||
|
properties:
|
||||||
|
companyID:
|
||||||
|
type: integer
|
||||||
|
isActive:
|
||||||
|
$ref: '#/definitions/domain.ValidBool'
|
||||||
|
marketID:
|
||||||
|
type: integer
|
||||||
|
marketName:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
domain.CreateCompanyReq:
|
domain.CreateCompanyReq:
|
||||||
properties:
|
properties:
|
||||||
admin_id:
|
admin_id:
|
||||||
|
|
@ -728,6 +781,25 @@ definitions:
|
||||||
slug:
|
slug:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
domain.CreateDirectDeposit:
|
||||||
|
properties:
|
||||||
|
accountHolder:
|
||||||
|
type: string
|
||||||
|
accountNumber:
|
||||||
|
type: string
|
||||||
|
amount:
|
||||||
|
type: number
|
||||||
|
bankName:
|
||||||
|
type: string
|
||||||
|
customerID:
|
||||||
|
type: integer
|
||||||
|
referenceNumber:
|
||||||
|
type: string
|
||||||
|
transferScreenshot:
|
||||||
|
type: string
|
||||||
|
walletID:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
domain.CreateSupportedOperationReq:
|
domain.CreateSupportedOperationReq:
|
||||||
properties:
|
properties:
|
||||||
description:
|
description:
|
||||||
|
|
@ -857,8 +929,6 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
brandId:
|
brandId:
|
||||||
type: string
|
type: string
|
||||||
country:
|
|
||||||
type: string
|
|
||||||
deviceType:
|
deviceType:
|
||||||
type: string
|
type: string
|
||||||
gameId:
|
gameId:
|
||||||
|
|
@ -867,26 +937,39 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
language:
|
language:
|
||||||
type: string
|
type: string
|
||||||
playerId:
|
|
||||||
type: string
|
|
||||||
providerId:
|
providerId:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
domain.DirectDepositRequest:
|
domain.DirectDeposit:
|
||||||
properties:
|
properties:
|
||||||
|
accountHolder:
|
||||||
|
type: string
|
||||||
|
accountNumber:
|
||||||
|
type: string
|
||||||
amount:
|
amount:
|
||||||
type: integer
|
type: number
|
||||||
bank_reference:
|
approvedAt:
|
||||||
type: string
|
type: string
|
||||||
customer_id:
|
approvedBy:
|
||||||
type: integer
|
type: integer
|
||||||
sender_account:
|
bankName:
|
||||||
type: string
|
type: string
|
||||||
required:
|
createdAt:
|
||||||
- amount
|
type: string
|
||||||
- bank_reference
|
customerID:
|
||||||
- customer_id
|
type: integer
|
||||||
- sender_account
|
id:
|
||||||
|
type: integer
|
||||||
|
referenceNumber:
|
||||||
|
type: string
|
||||||
|
rejectionReason:
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
transferScreenshot:
|
||||||
|
type: string
|
||||||
|
walletID:
|
||||||
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
domain.EnetpulseFixture:
|
domain.EnetpulseFixture:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -1272,6 +1355,8 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
added_time:
|
added_time:
|
||||||
type: integer
|
type: integer
|
||||||
|
average_bet_amount:
|
||||||
|
type: number
|
||||||
away_team:
|
away_team:
|
||||||
type: string
|
type: string
|
||||||
away_team_id:
|
away_team_id:
|
||||||
|
|
@ -1314,6 +1399,8 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
match_period:
|
match_period:
|
||||||
type: integer
|
type: integer
|
||||||
|
number_of_bets:
|
||||||
|
type: integer
|
||||||
score:
|
score:
|
||||||
type: string
|
type: string
|
||||||
source:
|
source:
|
||||||
|
|
@ -1328,8 +1415,12 @@ definitions:
|
||||||
$ref: '#/definitions/domain.EventStatus'
|
$ref: '#/definitions/domain.EventStatus'
|
||||||
timer_status:
|
timer_status:
|
||||||
type: string
|
type: string
|
||||||
|
total_amount:
|
||||||
|
type: number
|
||||||
total_odd_outcomes:
|
total_odd_outcomes:
|
||||||
type: integer
|
type: integer
|
||||||
|
total_potential_winnings:
|
||||||
|
type: number
|
||||||
updated_at:
|
updated_at:
|
||||||
type: string
|
type: string
|
||||||
winning_upper_limit:
|
winning_upper_limit:
|
||||||
|
|
@ -1422,8 +1513,6 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
brandId:
|
brandId:
|
||||||
type: string
|
type: string
|
||||||
cashierUrl:
|
|
||||||
type: string
|
|
||||||
country:
|
country:
|
||||||
type: string
|
type: string
|
||||||
currency:
|
currency:
|
||||||
|
|
@ -1436,18 +1525,12 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
language:
|
language:
|
||||||
type: string
|
type: string
|
||||||
lobbyUrl:
|
|
||||||
type: string
|
|
||||||
playerId:
|
playerId:
|
||||||
type: string
|
type: string
|
||||||
playerName:
|
|
||||||
type: string
|
|
||||||
providerId:
|
providerId:
|
||||||
type: string
|
type: string
|
||||||
sessionId:
|
sessionId:
|
||||||
type: string
|
type: string
|
||||||
userAgent:
|
|
||||||
type: string
|
|
||||||
type: object
|
type: object
|
||||||
domain.GameStartResponse:
|
domain.GameStartResponse:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -1569,6 +1652,8 @@ definitions:
|
||||||
deducted_percentage:
|
deducted_percentage:
|
||||||
example: 0.1
|
example: 0.1
|
||||||
type: number
|
type: number
|
||||||
|
deducted_stake:
|
||||||
|
type: number
|
||||||
id:
|
id:
|
||||||
example: 1
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
|
|
@ -1581,9 +1666,35 @@ definitions:
|
||||||
name:
|
name:
|
||||||
example: CompanyName
|
example: CompanyName
|
||||||
type: string
|
type: string
|
||||||
|
number_of_unsettled:
|
||||||
|
type: integer
|
||||||
slug:
|
slug:
|
||||||
example: slug
|
example: slug
|
||||||
type: string
|
type: string
|
||||||
|
stats_updated_at:
|
||||||
|
type: string
|
||||||
|
total_admins:
|
||||||
|
type: integer
|
||||||
|
total_approvers:
|
||||||
|
type: integer
|
||||||
|
total_bets:
|
||||||
|
type: integer
|
||||||
|
total_branches:
|
||||||
|
type: integer
|
||||||
|
total_cash_backs:
|
||||||
|
type: number
|
||||||
|
total_cash_out:
|
||||||
|
type: number
|
||||||
|
total_cashiers:
|
||||||
|
type: integer
|
||||||
|
total_customers:
|
||||||
|
type: integer
|
||||||
|
total_managers:
|
||||||
|
type: integer
|
||||||
|
total_stake:
|
||||||
|
type: number
|
||||||
|
total_unsettled_amount:
|
||||||
|
type: number
|
||||||
wallet_id:
|
wallet_id:
|
||||||
example: 1
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
|
|
@ -1730,6 +1841,17 @@ definitions:
|
||||||
pagination:
|
pagination:
|
||||||
$ref: '#/definitions/domain.Pagination'
|
$ref: '#/definitions/domain.Pagination'
|
||||||
type: object
|
type: object
|
||||||
|
domain.MarketSettings:
|
||||||
|
properties:
|
||||||
|
isActive:
|
||||||
|
type: boolean
|
||||||
|
marketID:
|
||||||
|
type: integer
|
||||||
|
marketName:
|
||||||
|
type: string
|
||||||
|
updatedAt:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
domain.OddMarketFilter:
|
domain.OddMarketFilter:
|
||||||
properties:
|
properties:
|
||||||
limit:
|
limit:
|
||||||
|
|
@ -1758,7 +1880,7 @@ definitions:
|
||||||
- 5
|
- 5
|
||||||
type: integer
|
type: integer
|
||||||
x-enum-comments:
|
x-enum-comments:
|
||||||
OUTCOME_STATUS_ERROR: Half Win and Half Given Back
|
OUTCOME_STATUS_ERROR: Error (Unsettled Bet)
|
||||||
OUTCOME_STATUS_HALF: Half Win and Half Given Back
|
OUTCOME_STATUS_HALF: Half Win and Half Given Back
|
||||||
OUTCOME_STATUS_VOID: Give Back
|
OUTCOME_STATUS_VOID: Give Back
|
||||||
x-enum-varnames:
|
x-enum-varnames:
|
||||||
|
|
@ -1776,6 +1898,7 @@ definitions:
|
||||||
type: array
|
type: array
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
|
metadata: {}
|
||||||
pagination:
|
pagination:
|
||||||
$ref: '#/definitions/domain.Pagination'
|
$ref: '#/definitions/domain.Pagination'
|
||||||
status_code:
|
status_code:
|
||||||
|
|
@ -1946,6 +2069,7 @@ definitions:
|
||||||
data: {}
|
data: {}
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
|
metadata: {}
|
||||||
status_code:
|
status_code:
|
||||||
type: integer
|
type: integer
|
||||||
success:
|
success:
|
||||||
|
|
@ -2497,18 +2621,6 @@ definitions:
|
||||||
value:
|
value:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
domain.VerifyDirectDepositRequest:
|
|
||||||
properties:
|
|
||||||
deposit_id:
|
|
||||||
type: integer
|
|
||||||
is_verified:
|
|
||||||
type: boolean
|
|
||||||
notes:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- deposit_id
|
|
||||||
- is_verified
|
|
||||||
type: object
|
|
||||||
domain.VirtualGameProvider:
|
domain.VirtualGameProvider:
|
||||||
properties:
|
properties:
|
||||||
created_at:
|
created_at:
|
||||||
|
|
@ -2795,6 +2907,14 @@ definitions:
|
||||||
last_name:
|
last_name:
|
||||||
example: Smith
|
example: Smith
|
||||||
type: string
|
type: string
|
||||||
|
number_of_deposits:
|
||||||
|
type: integer
|
||||||
|
number_of_transactions:
|
||||||
|
type: integer
|
||||||
|
number_of_transfers:
|
||||||
|
type: integer
|
||||||
|
number_of_withdraws:
|
||||||
|
type: integer
|
||||||
phone_number:
|
phone_number:
|
||||||
example: "0911111111"
|
example: "0911111111"
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -2820,6 +2940,16 @@ definitions:
|
||||||
type: boolean
|
type: boolean
|
||||||
static_updated_at:
|
static_updated_at:
|
||||||
type: string
|
type: string
|
||||||
|
total_deposits_amount:
|
||||||
|
type: number
|
||||||
|
total_transactions:
|
||||||
|
type: number
|
||||||
|
total_transfers_amount:
|
||||||
|
type: number
|
||||||
|
total_withdraws_amount:
|
||||||
|
type: number
|
||||||
|
updated_at:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
handlers.CustomersRes:
|
handlers.CustomersRes:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -3568,6 +3698,120 @@ paths:
|
||||||
summary: Set the league to active
|
summary: Set the league to active
|
||||||
tags:
|
tags:
|
||||||
- leagues
|
- leagues
|
||||||
|
/api/v1/{tenant_slug}/market-settings:
|
||||||
|
delete:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Remove all overridden market settings for a specific tenant
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Delete all market settings for a tenant
|
||||||
|
tags:
|
||||||
|
- market_settings
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Get all market settings overridden for a specific tenant
|
||||||
|
parameters:
|
||||||
|
- description: Number of results to return (default 10)
|
||||||
|
in: query
|
||||||
|
name: limit
|
||||||
|
type: integer
|
||||||
|
- description: Number of results to skip (default 0)
|
||||||
|
in: query
|
||||||
|
name: offset
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.CompanyMarketSettings'
|
||||||
|
type: array
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Retrieve all market settings for a tenant
|
||||||
|
tags:
|
||||||
|
- market_settings
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Insert new market settings for a specific tenant/company
|
||||||
|
parameters:
|
||||||
|
- description: Market Settings
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.CreateCompanyMarketSettings'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Insert company-specific market settings
|
||||||
|
tags:
|
||||||
|
- market_settings
|
||||||
|
/api/v1/{tenant_slug}/market-settings/{id}:
|
||||||
|
delete:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Remove a specific overridden market setting for a tenant
|
||||||
|
parameters:
|
||||||
|
- description: Market ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Delete a specific market setting for a tenant
|
||||||
|
tags:
|
||||||
|
- market_settings
|
||||||
/api/v1/{tenant_slug}/odds:
|
/api/v1/{tenant_slug}/odds:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
@ -6385,87 +6629,235 @@ paths:
|
||||||
summary: Get all customer wallets
|
summary: Get all customer wallets
|
||||||
tags:
|
tags:
|
||||||
- wallet
|
- wallet
|
||||||
/api/v1/direct_deposit:
|
/api/v1/direct-deposits:
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Customer initiates a direct deposit from mobile banking
|
|
||||||
parameters:
|
|
||||||
- description: Deposit details
|
|
||||||
in: body
|
|
||||||
name: request
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.DirectDepositRequest'
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"201":
|
|
||||||
description: Created
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.Response'
|
|
||||||
"400":
|
|
||||||
description: Bad Request
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
|
||||||
"500":
|
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
|
||||||
summary: Initiate a direct deposit
|
|
||||||
tags:
|
|
||||||
- Direct Deposits
|
|
||||||
/api/v1/direct_deposit/pending:
|
|
||||||
get:
|
get:
|
||||||
description: Get list of direct deposits needing verification
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.Response'
|
|
||||||
"500":
|
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
|
||||||
summary: Get pending direct deposits
|
|
||||||
tags:
|
|
||||||
- Direct Deposits
|
|
||||||
/api/v1/direct_deposit/verify:
|
|
||||||
post:
|
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Cashier verifies a direct deposit transaction
|
description: Fetches direct deposits filtered by status with pagination
|
||||||
parameters:
|
parameters:
|
||||||
- description: Verification details
|
- description: Deposit status (e.g., PENDING, APPROVED, REJECTED)
|
||||||
in: body
|
in: query
|
||||||
name: request
|
name: status
|
||||||
required: true
|
required: true
|
||||||
schema:
|
type: string
|
||||||
$ref: '#/definitions/domain.VerifyDirectDepositRequest'
|
- description: Page number
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: Page size
|
||||||
|
in: query
|
||||||
|
name: pageSize
|
||||||
|
type: integer
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/domain.Response'
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.DirectDeposit'
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
"401":
|
"502":
|
||||||
description: Unauthorized
|
description: Bad Gateway
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
"500":
|
summary: Get direct deposits by status
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
|
||||||
summary: Verify a direct deposit
|
|
||||||
tags:
|
tags:
|
||||||
- Direct Deposits
|
- DirectDeposit
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Creates a direct deposit for a customer and notifies both the customer
|
||||||
|
and admins
|
||||||
|
parameters:
|
||||||
|
- description: Direct deposit details
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.CreateDirectDeposit'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/domain.DirectDeposit'
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"502":
|
||||||
|
description: Bad Gateway
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Create a new direct deposit
|
||||||
|
tags:
|
||||||
|
- DirectDeposit
|
||||||
|
/api/v1/direct-deposits/{depositID}:
|
||||||
|
delete:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Deletes a direct deposit by its ID
|
||||||
|
parameters:
|
||||||
|
- description: Deposit ID
|
||||||
|
in: path
|
||||||
|
name: depositID
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"502":
|
||||||
|
description: Bad Gateway
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Delete a direct deposit
|
||||||
|
tags:
|
||||||
|
- DirectDeposit
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Fetches a single direct deposit by its ID
|
||||||
|
parameters:
|
||||||
|
- description: Deposit ID
|
||||||
|
in: path
|
||||||
|
name: depositID
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/domain.DirectDeposit'
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"502":
|
||||||
|
description: Bad Gateway
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Get a direct deposit by ID
|
||||||
|
tags:
|
||||||
|
- DirectDeposit
|
||||||
|
/api/v1/direct-deposits/{depositID}/approve:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Approves a direct deposit by admin and credits customer wallet
|
||||||
|
parameters:
|
||||||
|
- description: Deposit ID
|
||||||
|
in: path
|
||||||
|
name: depositID
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Admin ID performing the approval
|
||||||
|
in: query
|
||||||
|
name: adminID
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"502":
|
||||||
|
description: Bad Gateway
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Approve a direct deposit
|
||||||
|
tags:
|
||||||
|
- DirectDeposit
|
||||||
|
/api/v1/direct-deposits/{depositID}/reject:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Rejects a direct deposit by admin and notifies the customer
|
||||||
|
parameters:
|
||||||
|
- description: Deposit ID
|
||||||
|
in: path
|
||||||
|
name: depositID
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Admin ID performing the rejection
|
||||||
|
in: query
|
||||||
|
name: adminID
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Reason for rejection
|
||||||
|
in: query
|
||||||
|
name: reason
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"502":
|
||||||
|
description: Bad Gateway
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Reject a direct deposit
|
||||||
|
tags:
|
||||||
|
- DirectDeposit
|
||||||
/api/v1/enetpulse/betting-offers:
|
/api/v1/enetpulse/betting-offers:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
@ -7303,6 +7695,40 @@ paths:
|
||||||
summary: Update Managers
|
summary: Update Managers
|
||||||
tags:
|
tags:
|
||||||
- manager
|
- manager
|
||||||
|
/api/v1/market-settings:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Get all market settings that apply globally
|
||||||
|
parameters:
|
||||||
|
- description: Number of results to return (default 10)
|
||||||
|
in: query
|
||||||
|
name: limit
|
||||||
|
type: integer
|
||||||
|
- description: Number of results to skip (default 0)
|
||||||
|
in: query
|
||||||
|
name: offset
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.MarketSettings'
|
||||||
|
type: array
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Retrieve all global market settings
|
||||||
|
tags:
|
||||||
|
- market_settings
|
||||||
/api/v1/odds:
|
/api/v1/odds:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
@ -9643,37 +10069,6 @@ paths:
|
||||||
summary: Process Alea Play game callback
|
summary: Process Alea Play game callback
|
||||||
tags:
|
tags:
|
||||||
- Alea Virtual Games
|
- Alea Virtual Games
|
||||||
/api/v1/win:
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Processes win callbacks from either Veli or PopOK providers by
|
|
||||||
auto-detecting the format
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Win processing result
|
|
||||||
schema: {}
|
|
||||||
"400":
|
|
||||||
description: Invalid request format
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
|
||||||
"401":
|
|
||||||
description: Authentication failed
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
|
||||||
"409":
|
|
||||||
description: Duplicate transaction
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
|
||||||
"500":
|
|
||||||
description: Internal server error
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.ErrorResponse'
|
|
||||||
summary: Handle win callback (Veli or PopOK)
|
|
||||||
tags:
|
|
||||||
- Wins
|
|
||||||
/betwin:
|
/betwin:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
@ -9942,6 +10337,37 @@ paths:
|
||||||
summary: Launch a PopOK virtual game
|
summary: Launch a PopOK virtual game
|
||||||
tags:
|
tags:
|
||||||
- Virtual Games - PopOK
|
- Virtual Games - PopOK
|
||||||
|
/win:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Processes win callbacks from either Veli or PopOK providers by
|
||||||
|
auto-detecting the format
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Win processing result
|
||||||
|
schema: {}
|
||||||
|
"400":
|
||||||
|
description: Invalid request format
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"401":
|
||||||
|
description: Authentication failed
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"409":
|
||||||
|
description: Duplicate transaction
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal server error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Handle win callback (Veli or PopOK)
|
||||||
|
tags:
|
||||||
|
- Wins
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
in: header
|
in: header
|
||||||
|
|
|
||||||
|
|
@ -11,119 +11,159 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const ApproveDirectDeposit = `-- name: ApproveDirectDeposit :exec
|
||||||
|
UPDATE direct_deposits
|
||||||
|
SET
|
||||||
|
status = 'APPROVED',
|
||||||
|
approved_by = $2,
|
||||||
|
approved_at = NOW()
|
||||||
|
WHERE
|
||||||
|
id = $1
|
||||||
|
AND status = 'PENDING'
|
||||||
|
`
|
||||||
|
|
||||||
|
type ApproveDirectDepositParams struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
ApprovedBy pgtype.Int8 `json:"approved_by"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ApproveDirectDeposit(ctx context.Context, arg ApproveDirectDepositParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, ApproveDirectDeposit, arg.ID, arg.ApprovedBy)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const CountDirectDepositsByStatus = `-- name: CountDirectDepositsByStatus :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM direct_deposits
|
||||||
|
WHERE status = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountDirectDepositsByStatus(ctx context.Context, status pgtype.Text) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CountDirectDepositsByStatus, status)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
const CreateDirectDeposit = `-- name: CreateDirectDeposit :one
|
const CreateDirectDeposit = `-- name: CreateDirectDeposit :one
|
||||||
INSERT INTO direct_deposits (
|
INSERT INTO direct_deposits (
|
||||||
customer_id,
|
customer_id, wallet_id, bank_name, account_number,
|
||||||
wallet_id,
|
account_holder, amount, reference_number,
|
||||||
amount,
|
transfer_screenshot, status
|
||||||
bank_reference,
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'PENDING')
|
||||||
sender_account,
|
RETURNING id, customer_id, wallet_id, bank_name, account_number, account_holder, amount, reference_number, transfer_screenshot, status, created_at, approved_by, approved_at, rejection_reason
|
||||||
status
|
|
||||||
) VALUES (
|
|
||||||
$1, $2, $3, $4, $5, $6
|
|
||||||
) RETURNING id, customer_id, wallet_id, amount, bank_reference, sender_account, status, created_at, verified_by, verification_notes, verified_at
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateDirectDepositParams struct {
|
type CreateDirectDepositParams struct {
|
||||||
CustomerID int64 `json:"customer_id"`
|
CustomerID pgtype.Int8 `json:"customer_id"`
|
||||||
WalletID int64 `json:"wallet_id"`
|
WalletID pgtype.Int8 `json:"wallet_id"`
|
||||||
|
BankName pgtype.Text `json:"bank_name"`
|
||||||
|
AccountNumber pgtype.Text `json:"account_number"`
|
||||||
|
AccountHolder pgtype.Text `json:"account_holder"`
|
||||||
Amount pgtype.Numeric `json:"amount"`
|
Amount pgtype.Numeric `json:"amount"`
|
||||||
BankReference string `json:"bank_reference"`
|
ReferenceNumber pgtype.Text `json:"reference_number"`
|
||||||
SenderAccount string `json:"sender_account"`
|
TransferScreenshot pgtype.Text `json:"transfer_screenshot"`
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateDirectDeposit(ctx context.Context, arg CreateDirectDepositParams) (DirectDeposit, error) {
|
func (q *Queries) CreateDirectDeposit(ctx context.Context, arg CreateDirectDepositParams) (DirectDeposit, error) {
|
||||||
row := q.db.QueryRow(ctx, CreateDirectDeposit,
|
row := q.db.QueryRow(ctx, CreateDirectDeposit,
|
||||||
arg.CustomerID,
|
arg.CustomerID,
|
||||||
arg.WalletID,
|
arg.WalletID,
|
||||||
|
arg.BankName,
|
||||||
|
arg.AccountNumber,
|
||||||
|
arg.AccountHolder,
|
||||||
arg.Amount,
|
arg.Amount,
|
||||||
arg.BankReference,
|
arg.ReferenceNumber,
|
||||||
arg.SenderAccount,
|
arg.TransferScreenshot,
|
||||||
arg.Status,
|
|
||||||
)
|
)
|
||||||
var i DirectDeposit
|
var i DirectDeposit
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.CustomerID,
|
&i.CustomerID,
|
||||||
&i.WalletID,
|
&i.WalletID,
|
||||||
|
&i.BankName,
|
||||||
|
&i.AccountNumber,
|
||||||
|
&i.AccountHolder,
|
||||||
&i.Amount,
|
&i.Amount,
|
||||||
&i.BankReference,
|
&i.ReferenceNumber,
|
||||||
&i.SenderAccount,
|
&i.TransferScreenshot,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.VerifiedBy,
|
&i.ApprovedBy,
|
||||||
&i.VerificationNotes,
|
&i.ApprovedAt,
|
||||||
&i.VerifiedAt,
|
&i.RejectionReason,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetCustomerDirectDeposits = `-- name: GetCustomerDirectDeposits :many
|
const DeleteDirectDeposit = `-- name: DeleteDirectDeposit :exec
|
||||||
SELECT id, customer_id, wallet_id, amount, bank_reference, sender_account, status, created_at, verified_by, verification_notes, verified_at FROM direct_deposits WHERE customer_id = $1 ORDER BY created_at DESC
|
DELETE FROM direct_deposits
|
||||||
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetCustomerDirectDeposits(ctx context.Context, customerID int64) ([]DirectDeposit, error) {
|
func (q *Queries) DeleteDirectDeposit(ctx context.Context, id int64) error {
|
||||||
rows, err := q.db.Query(ctx, GetCustomerDirectDeposits, customerID)
|
_, err := q.db.Exec(ctx, DeleteDirectDeposit, id)
|
||||||
if err != nil {
|
return err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var items []DirectDeposit
|
|
||||||
for rows.Next() {
|
|
||||||
var i DirectDeposit
|
|
||||||
if err := rows.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.CustomerID,
|
|
||||||
&i.WalletID,
|
|
||||||
&i.Amount,
|
|
||||||
&i.BankReference,
|
|
||||||
&i.SenderAccount,
|
|
||||||
&i.Status,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.VerifiedBy,
|
|
||||||
&i.VerificationNotes,
|
|
||||||
&i.VerifiedAt,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
items = append(items, i)
|
|
||||||
}
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return items, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetDirectDeposit = `-- name: GetDirectDeposit :one
|
const GetDirectDepositByID = `-- name: GetDirectDepositByID :one
|
||||||
SELECT id, customer_id, wallet_id, amount, bank_reference, sender_account, status, created_at, verified_by, verification_notes, verified_at FROM direct_deposits WHERE id = $1
|
SELECT id, customer_id, wallet_id, bank_name, account_number, account_holder, amount, reference_number, transfer_screenshot, status, created_at, approved_by, approved_at, rejection_reason
|
||||||
|
FROM direct_deposits
|
||||||
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetDirectDeposit(ctx context.Context, id int64) (DirectDeposit, error) {
|
func (q *Queries) GetDirectDepositByID(ctx context.Context, id int64) (DirectDeposit, error) {
|
||||||
row := q.db.QueryRow(ctx, GetDirectDeposit, id)
|
row := q.db.QueryRow(ctx, GetDirectDepositByID, id)
|
||||||
var i DirectDeposit
|
var i DirectDeposit
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.CustomerID,
|
&i.CustomerID,
|
||||||
&i.WalletID,
|
&i.WalletID,
|
||||||
|
&i.BankName,
|
||||||
|
&i.AccountNumber,
|
||||||
|
&i.AccountHolder,
|
||||||
&i.Amount,
|
&i.Amount,
|
||||||
&i.BankReference,
|
&i.ReferenceNumber,
|
||||||
&i.SenderAccount,
|
&i.TransferScreenshot,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.VerifiedBy,
|
&i.ApprovedBy,
|
||||||
&i.VerificationNotes,
|
&i.ApprovedAt,
|
||||||
&i.VerifiedAt,
|
&i.RejectionReason,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetDirectDepositsByStatus = `-- name: GetDirectDepositsByStatus :many
|
const GetDirectDepositsByStatus = `-- name: GetDirectDepositsByStatus :many
|
||||||
SELECT id, customer_id, wallet_id, amount, bank_reference, sender_account, status, created_at, verified_by, verification_notes, verified_at FROM direct_deposits WHERE status = $1 ORDER BY created_at DESC
|
SELECT
|
||||||
|
id,
|
||||||
|
customer_id,
|
||||||
|
wallet_id,
|
||||||
|
bank_name,
|
||||||
|
account_number,
|
||||||
|
account_holder,
|
||||||
|
amount,
|
||||||
|
reference_number,
|
||||||
|
transfer_screenshot,
|
||||||
|
status,
|
||||||
|
created_at,
|
||||||
|
approved_by,
|
||||||
|
approved_at,
|
||||||
|
rejection_reason
|
||||||
|
FROM direct_deposits
|
||||||
|
WHERE status = $1
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT $2 OFFSET $3
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetDirectDepositsByStatus(ctx context.Context, status string) ([]DirectDeposit, error) {
|
type GetDirectDepositsByStatusParams struct {
|
||||||
rows, err := q.db.Query(ctx, GetDirectDepositsByStatus, status)
|
Status pgtype.Text `json:"status"`
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetDirectDepositsByStatus(ctx context.Context, arg GetDirectDepositsByStatusParams) ([]DirectDeposit, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetDirectDepositsByStatus, arg.Status, arg.Limit, arg.Offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -135,14 +175,17 @@ func (q *Queries) GetDirectDepositsByStatus(ctx context.Context, status string)
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.CustomerID,
|
&i.CustomerID,
|
||||||
&i.WalletID,
|
&i.WalletID,
|
||||||
|
&i.BankName,
|
||||||
|
&i.AccountNumber,
|
||||||
|
&i.AccountHolder,
|
||||||
&i.Amount,
|
&i.Amount,
|
||||||
&i.BankReference,
|
&i.ReferenceNumber,
|
||||||
&i.SenderAccount,
|
&i.TransferScreenshot,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.VerifiedBy,
|
&i.ApprovedBy,
|
||||||
&i.VerificationNotes,
|
&i.ApprovedAt,
|
||||||
&i.VerifiedAt,
|
&i.RejectionReason,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -154,46 +197,25 @@ func (q *Queries) GetDirectDepositsByStatus(ctx context.Context, status string)
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const UpdateDirectDeposit = `-- name: UpdateDirectDeposit :one
|
const RejectDirectDeposit = `-- name: RejectDirectDeposit :exec
|
||||||
UPDATE direct_deposits
|
UPDATE direct_deposits
|
||||||
SET
|
SET
|
||||||
status = $2,
|
status = 'REJECTED',
|
||||||
verified_by = $3,
|
approved_by = $2, -- still track the admin who took final action
|
||||||
verification_notes = $4,
|
approved_at = NOW(),
|
||||||
verified_at = $5
|
rejection_reason = $3
|
||||||
WHERE id = $1
|
WHERE
|
||||||
RETURNING id, customer_id, wallet_id, amount, bank_reference, sender_account, status, created_at, verified_by, verification_notes, verified_at
|
id = $1
|
||||||
|
AND status = 'PENDING'
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateDirectDepositParams struct {
|
type RejectDirectDepositParams struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Status string `json:"status"`
|
ApprovedBy pgtype.Int8 `json:"approved_by"`
|
||||||
VerifiedBy pgtype.Int8 `json:"verified_by"`
|
RejectionReason pgtype.Text `json:"rejection_reason"`
|
||||||
VerificationNotes pgtype.Text `json:"verification_notes"`
|
|
||||||
VerifiedAt pgtype.Timestamp `json:"verified_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpdateDirectDeposit(ctx context.Context, arg UpdateDirectDepositParams) (DirectDeposit, error) {
|
func (q *Queries) RejectDirectDeposit(ctx context.Context, arg RejectDirectDepositParams) error {
|
||||||
row := q.db.QueryRow(ctx, UpdateDirectDeposit,
|
_, err := q.db.Exec(ctx, RejectDirectDeposit, arg.ID, arg.ApprovedBy, arg.RejectionReason)
|
||||||
arg.ID,
|
return err
|
||||||
arg.Status,
|
|
||||||
arg.VerifiedBy,
|
|
||||||
arg.VerificationNotes,
|
|
||||||
arg.VerifiedAt,
|
|
||||||
)
|
|
||||||
var i DirectDeposit
|
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.CustomerID,
|
|
||||||
&i.WalletID,
|
|
||||||
&i.Amount,
|
|
||||||
&i.BankReference,
|
|
||||||
&i.SenderAccount,
|
|
||||||
&i.Status,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.VerifiedBy,
|
|
||||||
&i.VerificationNotes,
|
|
||||||
&i.VerifiedAt,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -313,16 +313,19 @@ type CustomerWalletDetail struct {
|
||||||
|
|
||||||
type DirectDeposit struct {
|
type DirectDeposit struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
CustomerID int64 `json:"customer_id"`
|
CustomerID pgtype.Int8 `json:"customer_id"`
|
||||||
WalletID int64 `json:"wallet_id"`
|
WalletID pgtype.Int8 `json:"wallet_id"`
|
||||||
|
BankName pgtype.Text `json:"bank_name"`
|
||||||
|
AccountNumber pgtype.Text `json:"account_number"`
|
||||||
|
AccountHolder pgtype.Text `json:"account_holder"`
|
||||||
Amount pgtype.Numeric `json:"amount"`
|
Amount pgtype.Numeric `json:"amount"`
|
||||||
BankReference string `json:"bank_reference"`
|
ReferenceNumber pgtype.Text `json:"reference_number"`
|
||||||
SenderAccount string `json:"sender_account"`
|
TransferScreenshot pgtype.Text `json:"transfer_screenshot"`
|
||||||
Status string `json:"status"`
|
Status pgtype.Text `json:"status"`
|
||||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
VerifiedBy pgtype.Int8 `json:"verified_by"`
|
ApprovedBy pgtype.Int8 `json:"approved_by"`
|
||||||
VerificationNotes pgtype.Text `json:"verification_notes"`
|
ApprovedAt pgtype.Timestamptz `json:"approved_at"`
|
||||||
VerifiedAt pgtype.Timestamp `json:"verified_at"`
|
RejectionReason pgtype.Text `json:"rejection_reason"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnetpulseFixture struct {
|
type EnetpulseFixture struct {
|
||||||
|
|
@ -1223,7 +1226,6 @@ type VirtualGameSession struct {
|
||||||
|
|
||||||
type VirtualGameTransaction struct {
|
type VirtualGameTransaction struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SessionID int64 `json:"session_id"`
|
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
CompanyID pgtype.Int8 `json:"company_id"`
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
Provider pgtype.Text `json:"provider"`
|
Provider pgtype.Text `json:"provider"`
|
||||||
|
|
|
||||||
|
|
@ -445,7 +445,7 @@ 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,
|
-- session_id,
|
||||||
user_id,
|
user_id,
|
||||||
company_id,
|
company_id,
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -456,9 +456,9 @@ INSERT INTO virtual_game_transactions (
|
||||||
external_transaction_id,
|
external_transaction_id,
|
||||||
status
|
status
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
RETURNING id,
|
RETURNING id,
|
||||||
session_id,
|
-- session_id,
|
||||||
user_id,
|
user_id,
|
||||||
company_id,
|
company_id,
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -473,7 +473,6 @@ RETURNING id,
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateVirtualGameTransactionParams struct {
|
type CreateVirtualGameTransactionParams struct {
|
||||||
SessionID int64 `json:"session_id"`
|
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
CompanyID pgtype.Int8 `json:"company_id"`
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
Provider pgtype.Text `json:"provider"`
|
Provider pgtype.Text `json:"provider"`
|
||||||
|
|
@ -487,7 +486,6 @@ type CreateVirtualGameTransactionParams struct {
|
||||||
|
|
||||||
type CreateVirtualGameTransactionRow struct {
|
type CreateVirtualGameTransactionRow struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SessionID int64 `json:"session_id"`
|
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
CompanyID pgtype.Int8 `json:"company_id"`
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
Provider pgtype.Text `json:"provider"`
|
Provider pgtype.Text `json:"provider"`
|
||||||
|
|
@ -503,7 +501,6 @@ type CreateVirtualGameTransactionRow struct {
|
||||||
|
|
||||||
func (q *Queries) CreateVirtualGameTransaction(ctx context.Context, arg CreateVirtualGameTransactionParams) (CreateVirtualGameTransactionRow, error) {
|
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.UserID,
|
arg.UserID,
|
||||||
arg.CompanyID,
|
arg.CompanyID,
|
||||||
arg.Provider,
|
arg.Provider,
|
||||||
|
|
@ -517,7 +514,6 @@ func (q *Queries) CreateVirtualGameTransaction(ctx context.Context, arg CreateVi
|
||||||
var i CreateVirtualGameTransactionRow
|
var i CreateVirtualGameTransactionRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.SessionID,
|
|
||||||
&i.UserID,
|
&i.UserID,
|
||||||
&i.CompanyID,
|
&i.CompanyID,
|
||||||
&i.Provider,
|
&i.Provider,
|
||||||
|
|
@ -785,7 +781,7 @@ SELECT c.name AS company_name,
|
||||||
COUNT(vgt.id) AS number_of_bets,
|
COUNT(vgt.id) AS number_of_bets,
|
||||||
COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum
|
COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum
|
||||||
FROM virtual_game_transactions vgt
|
FROM virtual_game_transactions vgt
|
||||||
JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
|
-- JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
|
||||||
JOIN virtual_games vg ON vgs.game_id = vg.id
|
JOIN virtual_games vg ON vgs.game_id = vg.id
|
||||||
JOIN companies c ON vgt.company_id = c.id
|
JOIN companies c ON vgt.company_id = c.id
|
||||||
WHERE vgt.transaction_type = 'BET'
|
WHERE vgt.transaction_type = 'BET'
|
||||||
|
|
@ -833,7 +829,7 @@ func (q *Queries) GetVirtualGameSummaryInRange(ctx context.Context, arg GetVirtu
|
||||||
|
|
||||||
const GetVirtualGameTransactionByExternalID = `-- name: GetVirtualGameTransactionByExternalID :one
|
const GetVirtualGameTransactionByExternalID = `-- name: GetVirtualGameTransactionByExternalID :one
|
||||||
SELECT id,
|
SELECT id,
|
||||||
session_id,
|
-- session_id,
|
||||||
user_id,
|
user_id,
|
||||||
wallet_id,
|
wallet_id,
|
||||||
transaction_type,
|
transaction_type,
|
||||||
|
|
@ -849,7 +845,6 @@ WHERE external_transaction_id = $1
|
||||||
|
|
||||||
type GetVirtualGameTransactionByExternalIDRow struct {
|
type GetVirtualGameTransactionByExternalIDRow struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SessionID int64 `json:"session_id"`
|
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
WalletID int64 `json:"wallet_id"`
|
WalletID int64 `json:"wallet_id"`
|
||||||
TransactionType string `json:"transaction_type"`
|
TransactionType string `json:"transaction_type"`
|
||||||
|
|
@ -866,7 +861,6 @@ func (q *Queries) GetVirtualGameTransactionByExternalID(ctx context.Context, ext
|
||||||
var i GetVirtualGameTransactionByExternalIDRow
|
var i GetVirtualGameTransactionByExternalIDRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.SessionID,
|
|
||||||
&i.UserID,
|
&i.UserID,
|
||||||
&i.WalletID,
|
&i.WalletID,
|
||||||
&i.TransactionType,
|
&i.TransactionType,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@ type Response struct {
|
||||||
Data interface{} `json:"data,omitempty"`
|
Data interface{} `json:"data,omitempty"`
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
StatusCode int `json:"status_code"`
|
StatusCode int `json:"status_code"`
|
||||||
|
MetaData interface{} `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallbackErrorResponse struct {
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateWinnings(amount Currency, totalOdds float32) Currency {
|
func CalculateWinnings(amount Currency, totalOdds float32) Currency {
|
||||||
|
|
|
||||||
|
|
@ -1,113 +1,39 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import "time"
|
||||||
"time"
|
|
||||||
"math/big"
|
|
||||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DirectDepositStatus string
|
type DirectDepositStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DepositStatusPending DirectDepositStatus = "pending"
|
DepositStatusPending DirectDepositStatus = "PENDING"
|
||||||
DepositStatusCompleted DirectDepositStatus = "completed"
|
DepositStatusCompleted DirectDepositStatus = "COMPLETED"
|
||||||
DepositStatusRejected DirectDepositStatus = "rejected"
|
DepositStatusRejected DirectDepositStatus = "REJECTED"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DirectDeposit struct {
|
type DirectDeposit struct {
|
||||||
ID int64 `json:"id"`
|
ID int
|
||||||
CustomerID int64 `json:"customer_id"`
|
CustomerID int
|
||||||
WalletID int64 `json:"wallet_id"`
|
WalletID int
|
||||||
Wallet Wallet `json:"wallet"`
|
BankName string
|
||||||
Amount Currency `json:"amount"`
|
AccountNumber string
|
||||||
BankReference string `json:"bank_reference"`
|
AccountHolder string
|
||||||
SenderAccount string `json:"sender_account"`
|
Amount float64
|
||||||
Status DirectDepositStatus `json:"status"`
|
ReferenceNumber string
|
||||||
CreatedAt time.Time `json:"created_at"`
|
TransferScreenshot string
|
||||||
VerifiedBy *int64 `json:"verified_by"`
|
Status string
|
||||||
VerificationNotes string `json:"verification_notes"`
|
CreatedAt time.Time
|
||||||
VerifiedAt *time.Time `json:"verified_at"`
|
ApprovedBy *int
|
||||||
|
ApprovedAt *time.Time
|
||||||
|
RejectionReason *string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateDirectDeposit struct {
|
type CreateDirectDeposit struct {
|
||||||
CustomerID int64
|
CustomerID int
|
||||||
WalletID int64
|
WalletID int
|
||||||
Amount Currency
|
BankName string
|
||||||
BankReference string
|
AccountNumber string
|
||||||
SenderAccount string
|
AccountHolder string
|
||||||
Status DirectDepositStatus
|
Amount float64
|
||||||
}
|
ReferenceNumber string
|
||||||
|
TransferScreenshot string
|
||||||
type UpdateDirectDeposit struct {
|
|
||||||
ID int64
|
|
||||||
Status DirectDepositStatus
|
|
||||||
VerifiedBy int64
|
|
||||||
VerificationNotes string
|
|
||||||
VerifiedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type DirectDepositRequest struct {
|
|
||||||
CustomerID int64 `json:"customer_id" binding:"required"`
|
|
||||||
Amount Currency `json:"amount" binding:"required,gt=0"`
|
|
||||||
BankReference string `json:"bank_reference" binding:"required"`
|
|
||||||
SenderAccount string `json:"sender_account" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type VerifyDirectDepositRequest struct {
|
|
||||||
DepositID int64 `json:"deposit_id" binding:"required"`
|
|
||||||
IsVerified bool `json:"is_verified" binding:"required"`
|
|
||||||
Notes string `json:"notes"`
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func ConvertDBDirectDeposit(deposit dbgen.DirectDeposit) DirectDeposit {
|
|
||||||
return DirectDeposit{
|
|
||||||
ID: deposit.ID,
|
|
||||||
CustomerID: deposit.CustomerID,
|
|
||||||
WalletID: deposit.WalletID,
|
|
||||||
Amount: Currency(deposit.Amount.Int.Int64()),
|
|
||||||
BankReference: deposit.BankReference,
|
|
||||||
SenderAccount: deposit.SenderAccount,
|
|
||||||
Status: DirectDepositStatus(deposit.Status),
|
|
||||||
CreatedAt: deposit.CreatedAt.Time,
|
|
||||||
VerifiedBy: convertPgInt64ToPtr(deposit.VerifiedBy),
|
|
||||||
VerificationNotes: deposit.VerificationNotes.String,
|
|
||||||
VerifiedAt: convertPgTimeToPtr(deposit.VerifiedAt),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ConvertCreateDirectDeposit(deposit CreateDirectDeposit) dbgen.CreateDirectDepositParams {
|
|
||||||
return dbgen.CreateDirectDepositParams{
|
|
||||||
CustomerID: deposit.CustomerID,
|
|
||||||
WalletID: deposit.WalletID,
|
|
||||||
Amount: pgtype.Numeric{Int: big.NewInt(int64(deposit.Amount)), Valid: true},
|
|
||||||
BankReference: deposit.BankReference,
|
|
||||||
SenderAccount: deposit.SenderAccount,
|
|
||||||
Status: string(deposit.Status),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ConvertUpdateDirectDeposit(deposit UpdateDirectDeposit) dbgen.UpdateDirectDepositParams {
|
|
||||||
return dbgen.UpdateDirectDepositParams{
|
|
||||||
ID: deposit.ID,
|
|
||||||
Status: string(deposit.Status),
|
|
||||||
VerifiedBy: pgtype.Int8{Int64: deposit.VerifiedBy, Valid: true},
|
|
||||||
VerificationNotes: pgtype.Text{String: deposit.VerificationNotes, Valid: deposit.VerificationNotes != ""},
|
|
||||||
VerifiedAt: pgtype.Timestamp{Time: deposit.VerifiedAt, Valid: true},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertPgInt64ToPtr(i pgtype.Int8) *int64 {
|
|
||||||
if i.Valid {
|
|
||||||
return &i.Int64
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertPgTimeToPtr(t pgtype.Timestamp) *time.Time {
|
|
||||||
if t.Valid {
|
|
||||||
return &t.Time
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
@ -47,13 +47,13 @@ type ApprovalStore interface {
|
||||||
GetPendingApprovals(ctx context.Context) ([]domain.TransferDetail, error)
|
GetPendingApprovals(ctx context.Context) ([]domain.TransferDetail, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DirectDepositStore interface {
|
// type DirectDepositStore interface {
|
||||||
CreateDirectDeposit(ctx context.Context, deposit domain.CreateDirectDeposit) (domain.DirectDeposit, error)
|
// CreateDirectDeposit(ctx context.Context, deposit domain.CreateDirectDeposit) (domain.DirectDeposit, error)
|
||||||
GetDirectDeposit(ctx context.Context, id int64) (domain.DirectDeposit, error)
|
// GetDirectDeposit(ctx context.Context, id int64) (domain.DirectDeposit, error)
|
||||||
UpdateDirectDeposit(ctx context.Context, deposit domain.UpdateDirectDeposit) (domain.DirectDeposit, error)
|
// UpdateDirectDeposit(ctx context.Context, deposit domain.UpdateDirectDeposit) (domain.DirectDeposit, error)
|
||||||
GetDirectDepositsByStatus(ctx context.Context, status domain.DirectDepositStatus) ([]domain.DirectDeposit, error)
|
// GetDirectDepositsByStatus(ctx context.Context, status domain.DirectDepositStatus) ([]domain.DirectDeposit, error)
|
||||||
GetCustomerDirectDeposits(ctx context.Context, customerID int64) ([]domain.DirectDeposit, error)
|
// GetCustomerDirectDeposits(ctx context.Context, customerID int64) ([]domain.DirectDeposit, error)
|
||||||
}
|
// }
|
||||||
|
|
||||||
type WalletStatStore interface {
|
type WalletStatStore interface {
|
||||||
UpdateWalletStats(ctx context.Context) error
|
UpdateWalletStats(ctx context.Context) error
|
||||||
|
|
|
||||||
|
|
@ -2,61 +2,195 @@ package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Interface for creating new wallet store
|
type DirectDepositRepository interface {
|
||||||
func NewDirectDepositStore(s *Store) ports.DirectDepositStore { return s }
|
CreateDirectDeposit(ctx context.Context, deposit *domain.DirectDeposit) error
|
||||||
|
GetDirectDepositsByStatus(ctx context.Context, status string, page int, pageSize int) ([]domain.DirectDeposit, int64, error)
|
||||||
|
ApproveDirectDeposit(ctx context.Context, depositID int, adminID int) error
|
||||||
|
RejectDirectDeposit(ctx context.Context, depositID int, adminID int, reason string) error
|
||||||
|
DeleteDirectDeposit(ctx context.Context, id int) error
|
||||||
|
GetDirectDepositByID(ctx context.Context, id int) (*domain.DirectDeposit, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DirectDepositRepo struct {
|
||||||
|
store *Store
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) CreateDirectDeposit(ctx context.Context, deposit domain.CreateDirectDeposit) (domain.DirectDeposit, error) {
|
func NewDirectDepositRepository(store *Store) DirectDepositRepository {
|
||||||
newDeposit, err := s.queries.CreateDirectDeposit(ctx, domain.ConvertCreateDirectDeposit(deposit))
|
return &DirectDepositRepo{store: store}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DirectDepositRepo) CreateDirectDeposit(ctx context.Context, deposit *domain.DirectDeposit) error {
|
||||||
|
params := dbgen.CreateDirectDepositParams{
|
||||||
|
CustomerID: pgtype.Int8{Int64: int64(deposit.CustomerID)},
|
||||||
|
WalletID: pgtype.Int8{Int64: int64(deposit.WalletID)},
|
||||||
|
BankName: pgtype.Text{String: deposit.BankName},
|
||||||
|
AccountNumber: pgtype.Text{String: deposit.AccountNumber},
|
||||||
|
AccountHolder: pgtype.Text{String: deposit.AccountHolder},
|
||||||
|
Amount: pgtype.Numeric{Exp: int32(deposit.Amount)},
|
||||||
|
ReferenceNumber: pgtype.Text{String: deposit.ReferenceNumber},
|
||||||
|
TransferScreenshot: pgtype.Text{String: deposit.TransferScreenshot},
|
||||||
|
}
|
||||||
|
|
||||||
|
dbDeposit, err := r.store.queries.CreateDirectDeposit(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.DirectDeposit{}, err
|
return err
|
||||||
}
|
|
||||||
return domain.ConvertDBDirectDeposit(newDeposit), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) GetDirectDeposit(ctx context.Context, id int64) (domain.DirectDeposit, error) {
|
// Map back to domain struct
|
||||||
deposit, err := s.queries.GetDirectDeposit(ctx, id)
|
deposit.ID = int(dbDeposit.ID)
|
||||||
|
deposit.Status = dbDeposit.Status.String
|
||||||
|
deposit.CreatedAt = dbDeposit.CreatedAt.Time
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DirectDepositRepo) GetDirectDepositsByStatus(
|
||||||
|
ctx context.Context,
|
||||||
|
status string,
|
||||||
|
page int,
|
||||||
|
pageSize int,
|
||||||
|
) ([]domain.DirectDeposit, int64, error) {
|
||||||
|
|
||||||
|
// Default pagination rules
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize < 1 || pageSize > 100 {
|
||||||
|
pageSize = 50
|
||||||
|
}
|
||||||
|
offset := (page - 1) * pageSize
|
||||||
|
|
||||||
|
params := dbgen.GetDirectDepositsByStatusParams{
|
||||||
|
Status: pgtype.Text{String: status},
|
||||||
|
Limit: int32(pageSize),
|
||||||
|
Offset: int32(offset),
|
||||||
|
}
|
||||||
|
|
||||||
|
dbItems, err := r.store.queries.GetDirectDepositsByStatus(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.DirectDeposit{}, err
|
return nil, 0, err
|
||||||
}
|
|
||||||
return domain.ConvertDBDirectDeposit(deposit), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) UpdateDirectDeposit(ctx context.Context, deposit domain.UpdateDirectDeposit) (domain.DirectDeposit, error) {
|
total, err := r.store.queries.CountDirectDepositsByStatus(ctx, pgtype.Text{String: status})
|
||||||
updatedDeposit, err := s.queries.UpdateDirectDeposit(ctx, domain.ConvertUpdateDirectDeposit(deposit))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.DirectDeposit{}, err
|
return nil, 0, err
|
||||||
}
|
|
||||||
return domain.ConvertDBDirectDeposit(updatedDeposit), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) GetDirectDepositsByStatus(ctx context.Context, status domain.DirectDepositStatus) ([]domain.DirectDeposit, error) {
|
deposits := make([]domain.DirectDeposit, len(dbItems))
|
||||||
deposits, err := s.queries.GetDirectDepositsByStatus(ctx, string(status))
|
for i, d := range dbItems {
|
||||||
|
deposits[i] = *mapDBDirectDepositToDomain(&d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return deposits, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DirectDepositRepo) ApproveDirectDeposit(
|
||||||
|
ctx context.Context,
|
||||||
|
depositID int,
|
||||||
|
adminID int,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
params := dbgen.ApproveDirectDepositParams{
|
||||||
|
ID: int64(depositID),
|
||||||
|
ApprovedBy: pgtype.Int8{Int64: int64(adminID)},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.store.queries.ApproveDirectDeposit(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DirectDepositRepo) GetDirectDepositByID(
|
||||||
|
ctx context.Context,
|
||||||
|
id int,
|
||||||
|
) (*domain.DirectDeposit, error) {
|
||||||
|
|
||||||
|
dbDeposit, err := r.store.queries.GetDirectDepositByID(ctx, int64(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]domain.DirectDeposit, 0, len(deposits))
|
deposit := mapDBDirectDepositToDomain(&dbDeposit)
|
||||||
for _, deposit := range deposits {
|
return deposit, nil
|
||||||
result = append(result, domain.ConvertDBDirectDeposit(deposit))
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) GetCustomerDirectDeposits(ctx context.Context, customerID int64) ([]domain.DirectDeposit, error) {
|
func (r *DirectDepositRepo) DeleteDirectDeposit(
|
||||||
deposits, err := s.queries.GetCustomerDirectDeposits(ctx, customerID)
|
ctx context.Context,
|
||||||
|
id int,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
err := r.store.queries.DeleteDirectDeposit(ctx, int64(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]domain.DirectDeposit, 0, len(deposits))
|
return nil
|
||||||
for _, deposit := range deposits {
|
}
|
||||||
result = append(result, domain.ConvertDBDirectDeposit(deposit))
|
|
||||||
|
func (r *DirectDepositRepo) RejectDirectDeposit(
|
||||||
|
ctx context.Context,
|
||||||
|
depositID int,
|
||||||
|
adminID int,
|
||||||
|
reason string,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
params := dbgen.RejectDirectDepositParams{
|
||||||
|
ID: int64(depositID),
|
||||||
|
ApprovedBy: pgtype.Int8{Int64: int64(adminID)},
|
||||||
|
RejectionReason: pgtype.Text{String: reason},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.store.queries.RejectDirectDeposit(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapDBDirectDepositToDomain(d *dbgen.DirectDeposit) *domain.DirectDeposit {
|
||||||
|
var approvedBy *int
|
||||||
|
if d.ApprovedBy.Valid {
|
||||||
|
v := int(d.ApprovedBy.Int64)
|
||||||
|
approvedBy = &v
|
||||||
|
}
|
||||||
|
|
||||||
|
var approvedAt *time.Time
|
||||||
|
if d.ApprovedAt.Valid {
|
||||||
|
t := d.ApprovedAt.Time
|
||||||
|
approvedAt = &t
|
||||||
|
}
|
||||||
|
|
||||||
|
var rejectionReason *string
|
||||||
|
if d.RejectionReason.Valid {
|
||||||
|
r := d.RejectionReason.String
|
||||||
|
rejectionReason = &r
|
||||||
|
}
|
||||||
|
|
||||||
|
return &domain.DirectDeposit{
|
||||||
|
ID: int(d.ID),
|
||||||
|
CustomerID: int(d.CustomerID.Int64),
|
||||||
|
WalletID: int(d.WalletID.Int64),
|
||||||
|
BankName: d.BankName.String,
|
||||||
|
AccountNumber: d.AccountNumber.String,
|
||||||
|
AccountHolder: d.AccountHolder.String,
|
||||||
|
Amount: float64(d.Amount.Exp),
|
||||||
|
ReferenceNumber: d.ReferenceNumber.String,
|
||||||
|
TransferScreenshot: d.TransferScreenshot.String,
|
||||||
|
Status: d.Status.String,
|
||||||
|
CreatedAt: d.CreatedAt.Time,
|
||||||
|
ApprovedBy: approvedBy,
|
||||||
|
ApprovedAt: approvedAt,
|
||||||
|
RejectionReason: rejectionReason,
|
||||||
}
|
}
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ func (r *VirtualGameRepo) GetVirtualGameSessionByToken(ctx context.Context, toke
|
||||||
|
|
||||||
func (r *VirtualGameRepo) CreateVirtualGameTransaction(ctx context.Context, tx *domain.VirtualGameTransaction) error {
|
func (r *VirtualGameRepo) CreateVirtualGameTransaction(ctx context.Context, tx *domain.VirtualGameTransaction) error {
|
||||||
params := dbgen.CreateVirtualGameTransactionParams{
|
params := dbgen.CreateVirtualGameTransactionParams{
|
||||||
SessionID: tx.SessionID,
|
// SessionID: tx.SessionID,
|
||||||
UserID: tx.UserID,
|
UserID: tx.UserID,
|
||||||
WalletID: tx.WalletID,
|
WalletID: tx.WalletID,
|
||||||
TransactionType: tx.TransactionType,
|
TransactionType: tx.TransactionType,
|
||||||
|
|
@ -239,7 +239,7 @@ func (r *VirtualGameRepo) CreateVirtualGameTransaction(ctx context.Context, tx *
|
||||||
|
|
||||||
func (r *VirtualGameRepo) CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error {
|
func (r *VirtualGameRepo) CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error {
|
||||||
params := dbgen.CreateVirtualGameHistoryParams{
|
params := dbgen.CreateVirtualGameHistoryParams{
|
||||||
SessionID: pgtype.Text{String: his.SessionID, Valid: true},
|
// SessionID: pgtype.Text{String: his.SessionID, Valid: true},
|
||||||
UserID: his.UserID,
|
UserID: his.UserID,
|
||||||
// WalletID: pgtype.Int8{Int64: *his.WalletID, Valid: true},
|
// WalletID: pgtype.Int8{Int64: *his.WalletID, Valid: true},
|
||||||
TransactionType: his.TransactionType,
|
TransactionType: his.TransactionType,
|
||||||
|
|
@ -262,7 +262,7 @@ func (r *VirtualGameRepo) GetVirtualGameTransactionByExternalID(ctx context.Cont
|
||||||
}
|
}
|
||||||
return &domain.VirtualGameTransaction{
|
return &domain.VirtualGameTransaction{
|
||||||
ID: dbTx.ID,
|
ID: dbTx.ID,
|
||||||
SessionID: dbTx.SessionID,
|
// SessionID: dbTx.SessionID,
|
||||||
UserID: dbTx.UserID,
|
UserID: dbTx.UserID,
|
||||||
WalletID: dbTx.WalletID,
|
WalletID: dbTx.WalletID,
|
||||||
TransactionType: dbTx.TransactionType,
|
TransactionType: dbTx.TransactionType,
|
||||||
|
|
|
||||||
305
internal/services/direct_deposit/service.go
Normal file
305
internal/services/direct_deposit/service.go
Normal file
|
|
@ -0,0 +1,305 @@
|
||||||
|
package directdeposit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||||
|
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
walletSvc wallet.Service
|
||||||
|
transferStore ports.TransferStore
|
||||||
|
directDepositStore repository.DirectDepositRepository
|
||||||
|
notificationSvc *notificationservice.Service
|
||||||
|
userSvc *user.Service
|
||||||
|
// mongoLogger *zap.Logger
|
||||||
|
// logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(
|
||||||
|
walletSvc wallet.Service,
|
||||||
|
transferStore ports.TransferStore,
|
||||||
|
directDepositStore repository.DirectDepositRepository,
|
||||||
|
notificationSvc *notificationservice.Service,
|
||||||
|
userSvc *user.Service,
|
||||||
|
// mongoLogger *zap.Logger,
|
||||||
|
// logger *slog.Logger,
|
||||||
|
) *Service {
|
||||||
|
return &Service{
|
||||||
|
walletSvc: walletSvc,
|
||||||
|
transferStore: transferStore,
|
||||||
|
directDepositStore: directDepositStore,
|
||||||
|
notificationSvc: notificationSvc,
|
||||||
|
userSvc: userSvc,
|
||||||
|
// mongoLogger: mongoLogger,
|
||||||
|
// logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateDirectDeposit(
|
||||||
|
ctx context.Context,
|
||||||
|
req domain.CreateDirectDeposit,
|
||||||
|
) (domain.DirectDeposit, error) {
|
||||||
|
|
||||||
|
deposit := domain.DirectDeposit{
|
||||||
|
CustomerID: req.CustomerID,
|
||||||
|
WalletID: req.WalletID,
|
||||||
|
BankName: req.BankName,
|
||||||
|
AccountNumber: req.AccountNumber,
|
||||||
|
AccountHolder: req.AccountHolder,
|
||||||
|
Amount: req.Amount,
|
||||||
|
ReferenceNumber: req.ReferenceNumber,
|
||||||
|
TransferScreenshot: req.TransferScreenshot,
|
||||||
|
Status: "PENDING",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: create the deposit in DB
|
||||||
|
if err := s.directDepositStore.CreateDirectDeposit(ctx, &deposit); err != nil {
|
||||||
|
return domain.DirectDeposit{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: prepare common notification metadata
|
||||||
|
raw, _ := json.Marshal(map[string]any{
|
||||||
|
"deposit_id": deposit.ID,
|
||||||
|
"customer_id": deposit.CustomerID,
|
||||||
|
"amount": deposit.Amount,
|
||||||
|
"status": deposit.Status,
|
||||||
|
"timestamp": time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// -------------------------------
|
||||||
|
// Step 3a: notify the customer
|
||||||
|
customerNotification := &domain.Notification{
|
||||||
|
RecipientID: int64(deposit.CustomerID),
|
||||||
|
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||||
|
Reciever: domain.NotificationRecieverSideCustomer,
|
||||||
|
Type: domain.NOTIFICATION_TYPE_WALLET,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
IsRead: false,
|
||||||
|
Level: domain.NotificationLevelInfo,
|
||||||
|
Priority: 2,
|
||||||
|
Metadata: raw,
|
||||||
|
Payload: domain.NotificationPayload{
|
||||||
|
Headline: "Direct Deposit Created",
|
||||||
|
Message: fmt.Sprintf("Your direct deposit of %.2f is now pending approval.", deposit.Amount),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.notificationSvc.SendNotification(ctx, customerNotification); err != nil {
|
||||||
|
return domain.DirectDeposit{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------
|
||||||
|
// Step 3b: notify admins
|
||||||
|
adminNotification := &domain.Notification{
|
||||||
|
RecipientID: 0, // 0 or special ID for admin-wide notifications
|
||||||
|
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||||
|
Reciever: domain.NotificationRecieverSideAdmin,
|
||||||
|
Type: domain.NOTIFICATION_TYPE_APPROVAL_REQUIRED,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
IsRead: false,
|
||||||
|
Level: domain.NotificationLevelInfo,
|
||||||
|
Priority: 2,
|
||||||
|
Metadata: raw,
|
||||||
|
Payload: domain.NotificationPayload{
|
||||||
|
Headline: "New Direct Deposit Pending Approval",
|
||||||
|
Message: fmt.Sprintf("Customer #%d has created a direct deposit of %.2f that requires your approval.", deposit.CustomerID, deposit.Amount),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
|
||||||
|
return domain.DirectDeposit{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return deposit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetDirectDepositsByStatus(
|
||||||
|
ctx context.Context,
|
||||||
|
status string,
|
||||||
|
page int,
|
||||||
|
pageSize int,
|
||||||
|
) ([]domain.DirectDeposit, int64, error) {
|
||||||
|
|
||||||
|
deposits, total, err := s.directDepositStore.GetDirectDepositsByStatus(
|
||||||
|
ctx,
|
||||||
|
status,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return deposits, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ApproveDirectDeposit(
|
||||||
|
ctx context.Context,
|
||||||
|
depositID int,
|
||||||
|
adminID int,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
// Step 1: fetch deposit (ensure it exists)
|
||||||
|
deposit, err := s.directDepositStore.GetDirectDepositByID(ctx, depositID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: approve in DB
|
||||||
|
if err := s.directDepositStore.ApproveDirectDeposit(ctx, depositID, adminID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: credit wallet balance
|
||||||
|
wallet, err := s.walletSvc.GetCustomerWallet(ctx, int64(deposit.CustomerID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := s.walletSvc.AddToWallet(ctx,
|
||||||
|
wallet.RegularID,
|
||||||
|
domain.Currency(deposit.Amount),
|
||||||
|
domain.ValidInt64{},
|
||||||
|
domain.TRANSFER_DIRECT,
|
||||||
|
domain.PaymentDetails{},
|
||||||
|
"",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: record transfer
|
||||||
|
transfer := domain.CreateTransfer{
|
||||||
|
Amount: domain.Currency(deposit.Amount),
|
||||||
|
Verified: true,
|
||||||
|
Message: "Direct deposit approved and credited",
|
||||||
|
Type: domain.DEPOSIT,
|
||||||
|
PaymentMethod: domain.TRANSFER_DIRECT,
|
||||||
|
ReceiverWalletID: domain.ValidInt64{Valid: true, Value: wallet.RegularID},
|
||||||
|
ReferenceNumber: deposit.ReferenceNumber,
|
||||||
|
Status: string(domain.DepositStatusCompleted),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: send customer notification
|
||||||
|
raw, _ := json.Marshal(map[string]any{
|
||||||
|
"deposit_id": deposit.ID,
|
||||||
|
"amount": deposit.Amount,
|
||||||
|
"status": "APPROVED",
|
||||||
|
"timestamp": time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
notification := &domain.Notification{
|
||||||
|
RecipientID: int64(deposit.CustomerID),
|
||||||
|
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||||
|
Reciever: domain.NotificationRecieverSideCustomer,
|
||||||
|
Type: domain.NOTIFICATION_TYPE_TRANSFER_SUCCESS,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
IsRead: false,
|
||||||
|
Level: domain.NotificationLevelInfo,
|
||||||
|
Priority: 2,
|
||||||
|
Metadata: raw,
|
||||||
|
Payload: domain.NotificationPayload{
|
||||||
|
Headline: "Direct Deposit Approved",
|
||||||
|
Message: fmt.Sprintf("Your direct deposit of %.2f has been approved and credited to your wallet.", deposit.Amount),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) RejectDirectDeposit(
|
||||||
|
ctx context.Context,
|
||||||
|
depositID int,
|
||||||
|
adminID int,
|
||||||
|
reason string,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
// Step 1: fetch deposit to ensure it exists
|
||||||
|
deposit, err := s.directDepositStore.GetDirectDepositByID(ctx, depositID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: reject operation
|
||||||
|
if err := s.directDepositStore.RejectDirectDeposit(ctx, depositID, adminID, reason); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: send customer notification
|
||||||
|
raw, _ := json.Marshal(map[string]any{
|
||||||
|
"deposit_id": deposit.ID,
|
||||||
|
"amount": deposit.Amount,
|
||||||
|
"status": "REJECTED",
|
||||||
|
"reason": reason,
|
||||||
|
"timestamp": time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
notification := &domain.Notification{
|
||||||
|
RecipientID: int64(deposit.CustomerID),
|
||||||
|
DeliveryChannel: domain.DeliveryChannelInApp,
|
||||||
|
Reciever: domain.NotificationRecieverSideCustomer,
|
||||||
|
Type: domain.NOTIFICATION_TYPE_TRANSFER_REJECTED,
|
||||||
|
DeliveryStatus: domain.DeliveryStatusPending,
|
||||||
|
IsRead: false,
|
||||||
|
Level: domain.NotificationLevelWarning,
|
||||||
|
Priority: 2,
|
||||||
|
Metadata: raw,
|
||||||
|
Payload: domain.NotificationPayload{
|
||||||
|
Headline: "Direct Deposit Rejected",
|
||||||
|
Message: fmt.Sprintf("Your direct deposit of %.2f was rejected. Reason: %s", deposit.Amount, reason),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetDirectDepositByID(
|
||||||
|
ctx context.Context,
|
||||||
|
depositID int,
|
||||||
|
) (*domain.DirectDeposit, error) {
|
||||||
|
|
||||||
|
deposit, err := s.directDepositStore.GetDirectDepositByID(ctx, depositID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return deposit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DeleteDirectDeposit(
|
||||||
|
ctx context.Context,
|
||||||
|
depositID int,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
// Optional: fetch first to ensure deposit exists
|
||||||
|
_, err := s.directDepositStore.GetDirectDepositByID(ctx, depositID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform deletion
|
||||||
|
if err := s.directDepositStore.DeleteDirectDeposit(ctx, depositID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -71,7 +71,7 @@ func (s *service) GenerateGameLaunchURL(ctx context.Context, userID int64, gameI
|
||||||
|
|
||||||
// 3. Record virtual game history (optional but recommended)
|
// 3. Record virtual game history (optional but recommended)
|
||||||
history := &domain.VirtualGameHistory{
|
history := &domain.VirtualGameHistory{
|
||||||
SessionID: sessionID,
|
// SessionID: sessionID,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
CompanyID: user.CompanyID.Value,
|
CompanyID: user.CompanyID.Value,
|
||||||
Provider: string(domain.PROVIDER_POPOK),
|
Provider: string(domain.PROVIDER_POPOK),
|
||||||
|
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
package wallet
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InitiateDirectDeposit creates a pending deposit request
|
|
||||||
func (s *Service) InitiateDirectDeposit(
|
|
||||||
ctx context.Context,
|
|
||||||
customerID int64,
|
|
||||||
amount domain.Currency,
|
|
||||||
bankRef string, // Mobile banking transaction reference
|
|
||||||
senderAccount string, // Customer's account number
|
|
||||||
) (domain.DirectDeposit, error) {
|
|
||||||
// Get customer's betting wallet
|
|
||||||
customerWallet, err := s.GetCustomerWallet(ctx, customerID)
|
|
||||||
if err != nil {
|
|
||||||
return domain.DirectDeposit{}, fmt.Errorf("failed to get customer wallet: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create pending deposit record
|
|
||||||
deposit, err := s.directDepositStore.CreateDirectDeposit(ctx, domain.CreateDirectDeposit{
|
|
||||||
CustomerID: customerID,
|
|
||||||
WalletID: customerWallet.ID,
|
|
||||||
Amount: amount,
|
|
||||||
BankReference: bankRef,
|
|
||||||
SenderAccount: senderAccount,
|
|
||||||
Status: domain.DepositStatusPending,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return domain.DirectDeposit{}, fmt.Errorf("failed to create deposit record: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify cashiers for manual verification
|
|
||||||
go s.notifyCashiersForVerification(ctx, deposit.ID, customerID, amount)
|
|
||||||
|
|
||||||
return deposit, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyDirectDeposit verifies and processes the deposit
|
|
||||||
func (s *Service) VerifyDirectDeposit(
|
|
||||||
ctx context.Context,
|
|
||||||
depositID int64,
|
|
||||||
cashierID int64,
|
|
||||||
isVerified bool,
|
|
||||||
verificationNotes string,
|
|
||||||
) (domain.DirectDeposit, error) {
|
|
||||||
// Get the deposit record
|
|
||||||
deposit, err := s.directDepositStore.GetDirectDeposit(ctx, depositID)
|
|
||||||
if err != nil {
|
|
||||||
return domain.DirectDeposit{}, fmt.Errorf("failed to get deposit: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate deposit status
|
|
||||||
if deposit.Status != domain.DepositStatusPending {
|
|
||||||
return domain.DirectDeposit{}, errors.New("only pending deposits can be verified")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update based on verification result
|
|
||||||
if isVerified {
|
|
||||||
// Credit the wallet
|
|
||||||
err = s.walletStore.UpdateBalance(ctx, deposit.WalletID,
|
|
||||||
deposit.Wallet.Balance+deposit.Amount)
|
|
||||||
if err != nil {
|
|
||||||
return domain.DirectDeposit{}, fmt.Errorf("failed to update wallet balance: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish wallet update event
|
|
||||||
// go s.publishWalletUpdate(ctx, deposit.WalletID, deposit.Wallet.UserID,
|
|
||||||
// deposit.Wallet.Balance+deposit.Amount, "direct_deposit_verified")
|
|
||||||
|
|
||||||
// Update deposit status
|
|
||||||
deposit.Status = domain.DepositStatusCompleted
|
|
||||||
} else {
|
|
||||||
deposit.Status = domain.DepositStatusRejected
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update deposit record
|
|
||||||
updatedDeposit, err := s.directDepositStore.UpdateDirectDeposit(ctx, domain.UpdateDirectDeposit{
|
|
||||||
ID: depositID,
|
|
||||||
Status: deposit.Status,
|
|
||||||
VerifiedBy: cashierID,
|
|
||||||
VerificationNotes: verificationNotes,
|
|
||||||
VerifiedAt: time.Now(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return domain.DirectDeposit{}, fmt.Errorf("failed to update deposit: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify customer of verification result
|
|
||||||
go s.notifyCustomerVerificationResult(ctx, updatedDeposit)
|
|
||||||
|
|
||||||
return updatedDeposit, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPendingDirectDeposits returns deposits needing verification
|
|
||||||
func (s *Service) GetPendingDirectDeposits(ctx context.Context) ([]domain.DirectDeposit, error) {
|
|
||||||
return s.directDepositStore.GetDirectDepositsByStatus(ctx, domain.DepositStatusPending)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
func (s *Service) notifyCashiersForVerification(ctx context.Context, depositID, customerID int64, amount domain.Currency) {
|
|
||||||
cashiers, _, err := s.userSvc.GetAllCashiers(ctx, domain.UserFilter{Role: string(domain.RoleCashier)})
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("failed to get cashiers for notification",
|
|
||||||
"error", err,
|
|
||||||
"deposit_id", depositID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
customer, err := s.userSvc.GetUserByID(ctx, customerID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("failed to get customer details",
|
|
||||||
"error", err,
|
|
||||||
"customer_id", customerID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cashier := range cashiers {
|
|
||||||
metadataMap := map[string]interface{}{
|
|
||||||
"deposit_id": depositID,
|
|
||||||
"customer_id": customerID,
|
|
||||||
"amount": amount.Float32(),
|
|
||||||
}
|
|
||||||
metadataJSON, err := json.Marshal(metadataMap)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("failed to marshal notification metadata",
|
|
||||||
"error", err,
|
|
||||||
"deposit_id", depositID)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
notification := &domain.Notification{
|
|
||||||
RecipientID: cashier.ID,
|
|
||||||
Type: domain.NotificationTypeDepositVerification,
|
|
||||||
Level: domain.NotificationLevelInfo,
|
|
||||||
DeliveryChannel: domain.DeliveryChannelInApp,
|
|
||||||
Payload: domain.NotificationPayload{
|
|
||||||
Headline: "Direct Deposit Requires Verification",
|
|
||||||
Message: fmt.Sprintf("Customer %s deposited %.2f - please verify", customer.FirstName+" "+customer.LastName, amount.Float32()),
|
|
||||||
},
|
|
||||||
Metadata: metadataJSON,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
|
|
||||||
s.logger.Error("failed to send verification notification",
|
|
||||||
"cashier_id", cashier.ID,
|
|
||||||
"error", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) notifyCustomerVerificationResult(ctx context.Context, deposit domain.DirectDeposit) {
|
|
||||||
var (
|
|
||||||
headline string
|
|
||||||
message string
|
|
||||||
level domain.NotificationLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
if deposit.Status == domain.DepositStatusCompleted {
|
|
||||||
headline = "Deposit Verified"
|
|
||||||
message = fmt.Sprintf("Your deposit of %.2f has been credited to your wallet", deposit.Amount.Float32())
|
|
||||||
level = domain.NotificationLevelSuccess
|
|
||||||
} else {
|
|
||||||
headline = "Deposit Rejected"
|
|
||||||
message = fmt.Sprintf("Your deposit of %.2f was not verified. Reason: %s",
|
|
||||||
deposit.Amount.Float32(), deposit.VerificationNotes)
|
|
||||||
level = domain.NotificationLevelError
|
|
||||||
}
|
|
||||||
|
|
||||||
metadataMap := map[string]interface{}{
|
|
||||||
"deposit_id": deposit.ID,
|
|
||||||
"amount": deposit.Amount.Float32(),
|
|
||||||
"status": string(deposit.Status),
|
|
||||||
}
|
|
||||||
metadataJSON, err := json.Marshal(metadataMap)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("failed to marshal notification metadata",
|
|
||||||
"error", err,
|
|
||||||
"deposit_id", deposit.ID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
notification := &domain.Notification{
|
|
||||||
RecipientID: deposit.CustomerID,
|
|
||||||
Type: domain.NotificationTypeDepositResult,
|
|
||||||
Level: level,
|
|
||||||
DeliveryChannel: domain.DeliveryChannelInApp,
|
|
||||||
Payload: domain.NotificationPayload{
|
|
||||||
Headline: headline,
|
|
||||||
Message: message,
|
|
||||||
},
|
|
||||||
Metadata: metadataJSON,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
|
|
||||||
s.logger.Error("failed to send deposit result notification",
|
|
||||||
"customer_id", deposit.CustomerID,
|
|
||||||
"error", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (s *Service) publishWalletUpdate(ctx context.Context, walletID, userID int64, newBalance domain.Currency, trigger string) {
|
|
||||||
// s.kafkaProducer.Publish(ctx, fmt.Sprint(walletID), event.WalletEvent{
|
|
||||||
// EventType: event.WalletBalanceUpdated,
|
|
||||||
// WalletID: walletID,
|
|
||||||
// UserID: userID,
|
|
||||||
// Balance: newBalance,
|
|
||||||
// Trigger: trigger,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
@ -14,7 +14,7 @@ type Service struct {
|
||||||
// approvalStore ApprovalStore
|
// approvalStore ApprovalStore
|
||||||
walletStore ports.WalletStore
|
walletStore ports.WalletStore
|
||||||
transferStore ports.TransferStore
|
transferStore ports.TransferStore
|
||||||
directDepositStore ports.DirectDepositStore
|
// directDepositStore ports.DirectDepositStore
|
||||||
notificationSvc *notificationservice.Service
|
notificationSvc *notificationservice.Service
|
||||||
userSvc *user.Service
|
userSvc *user.Service
|
||||||
mongoLogger *zap.Logger
|
mongoLogger *zap.Logger
|
||||||
|
|
@ -24,7 +24,7 @@ type Service struct {
|
||||||
func NewService(
|
func NewService(
|
||||||
walletStore ports.WalletStore,
|
walletStore ports.WalletStore,
|
||||||
transferStore ports.TransferStore,
|
transferStore ports.TransferStore,
|
||||||
directDepositStore ports.DirectDepositStore,
|
// directDepositStore ports.DirectDepositStore,
|
||||||
notificationSvc *notificationservice.Service,
|
notificationSvc *notificationservice.Service,
|
||||||
userSvc *user.Service,
|
userSvc *user.Service,
|
||||||
mongoLogger *zap.Logger,
|
mongoLogger *zap.Logger,
|
||||||
|
|
@ -33,7 +33,7 @@ func NewService(
|
||||||
return &Service{
|
return &Service{
|
||||||
walletStore: walletStore,
|
walletStore: walletStore,
|
||||||
transferStore: transferStore,
|
transferStore: transferStore,
|
||||||
directDepositStore: directDepositStore,
|
// directDepositStore: directDepositStore,
|
||||||
// approvalStore: approvalStore,
|
// approvalStore: approvalStore,
|
||||||
notificationSvc: notificationSvc,
|
notificationSvc: notificationSvc,
|
||||||
userSvc: userSvc,
|
userSvc: userSvc,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa"
|
||||||
"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"
|
||||||
|
directdeposit "github.com/SamuelTariku/FortuneBet-Backend/internal/services/direct_deposit"
|
||||||
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
|
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
||||||
|
|
@ -48,6 +49,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
|
directDepositSvc *directdeposit.Service
|
||||||
enetPulseSvc *enetpulse.Service
|
enetPulseSvc *enetpulse.Service
|
||||||
atlasVirtualGameService atlas.AtlasVirtualGameService
|
atlasVirtualGameService atlas.AtlasVirtualGameService
|
||||||
veliVirtualGameService *veli.Service
|
veliVirtualGameService *veli.Service
|
||||||
|
|
@ -92,6 +94,7 @@ type App struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApp(
|
func NewApp(
|
||||||
|
directDepositSvc *directdeposit.Service,
|
||||||
enetPulseSvc *enetpulse.Service,
|
enetPulseSvc *enetpulse.Service,
|
||||||
atlasVirtualGameService atlas.AtlasVirtualGameService,
|
atlasVirtualGameService atlas.AtlasVirtualGameService,
|
||||||
veliVirtualGameService *veli.Service,
|
veliVirtualGameService *veli.Service,
|
||||||
|
|
@ -149,6 +152,7 @@ func NewApp(
|
||||||
app.Static("/static", "./static")
|
app.Static("/static", "./static")
|
||||||
|
|
||||||
s := &App{
|
s := &App{
|
||||||
|
directDepositSvc: directDepositSvc,
|
||||||
enetPulseSvc: enetPulseSvc,
|
enetPulseSvc: enetPulseSvc,
|
||||||
atlasVirtualGameService: atlasVirtualGameService,
|
atlasVirtualGameService: atlasVirtualGameService,
|
||||||
veliVirtualGameService: veliVirtualGameService,
|
veliVirtualGameService: veliVirtualGameService,
|
||||||
|
|
|
||||||
|
|
@ -1,124 +1,267 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitiateDirectDeposit godoc
|
// CreateDirectDeposit godoc
|
||||||
// @Summary Initiate a direct deposit
|
// @Summary Create a new direct deposit
|
||||||
// @Description Customer initiates a direct deposit from mobile banking
|
// @Description Creates a direct deposit for a customer and notifies both the customer and admins
|
||||||
// @Tags Direct Deposits
|
// @Tags DirectDeposit
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param request body domain.DirectDepositRequest true "Deposit details"
|
// @Param body body domain.CreateDirectDeposit true "Direct deposit details"
|
||||||
// @Success 201 {object} domain.Response
|
// @Success 200 {object} domain.Response{data=domain.DirectDeposit}
|
||||||
// @Failure 400 {object} domain.ErrorResponse
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
// @Failure 500 {object} domain.ErrorResponse
|
// @Failure 502 {object} domain.ErrorResponse
|
||||||
// @Router /api/v1/direct_deposit [post]
|
// @Router /api/v1/direct-deposits [post]
|
||||||
func (h *Handler) InitiateDirectDeposit(c *fiber.Ctx) error {
|
func (h *Handler) CreateDirectDeposit(c *fiber.Ctx) error {
|
||||||
var req domain.DirectDepositRequest
|
var req domain.CreateDirectDeposit
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
Error: err.Error(),
|
|
||||||
Message: "Invalid request payload",
|
Message: "Invalid request payload",
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
deposit, err := h.walletSvc.InitiateDirectDeposit(
|
|
||||||
c.Context(),
|
|
||||||
req.CustomerID,
|
|
||||||
req.Amount,
|
|
||||||
req.BankReference,
|
|
||||||
req.SenderAccount,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
||||||
Error: err.Error(),
|
Error: err.Error(),
|
||||||
Message: "Failed to initiate direct deposit",
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusCreated).JSON(domain.Response{
|
// Call service
|
||||||
Message: "Direct deposit initiated successfully",
|
deposit, err := h.directDepositSvc.CreateDirectDeposit(c.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("CreateDirectDeposit error", err.Error())
|
||||||
|
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to create direct deposit",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: "Direct deposit created successfully",
|
||||||
Data: deposit,
|
Data: deposit,
|
||||||
|
StatusCode: fiber.StatusOK,
|
||||||
Success: true,
|
Success: true,
|
||||||
StatusCode: fiber.StatusCreated,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyDirectDeposit godoc
|
// GetDirectDepositsByStatus godoc
|
||||||
// @Summary Verify a direct deposit
|
// @Summary Get direct deposits by status
|
||||||
// @Description Cashier verifies a direct deposit transaction
|
// @Description Fetches direct deposits filtered by status with pagination
|
||||||
// @Tags Direct Deposits
|
// @Tags DirectDeposit
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param request body domain.VerifyDirectDepositRequest true "Verification details"
|
// @Param status query string true "Deposit status (e.g., PENDING, APPROVED, REJECTED)"
|
||||||
// @Success 200 {object} domain.Response
|
// @Param page query int false "Page number"
|
||||||
|
// @Param pageSize query int false "Page size"
|
||||||
|
// @Success 200 {object} domain.Response{data=[]domain.DirectDeposit}
|
||||||
// @Failure 400 {object} domain.ErrorResponse
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
// @Failure 401 {object} domain.ErrorResponse
|
// @Failure 502 {object} domain.ErrorResponse
|
||||||
// @Failure 500 {object} domain.ErrorResponse
|
// @Router /api/v1/direct-deposits [get]
|
||||||
// @Router /api/v1/direct_deposit/verify [post]
|
func (h *Handler) GetDirectDepositsByStatus(c *fiber.Ctx) error {
|
||||||
func (h *Handler) VerifyDirectDeposit(c *fiber.Ctx) error {
|
status := c.Query("status")
|
||||||
var req domain.VerifyDirectDepositRequest
|
if status == "" {
|
||||||
if err := c.BodyParser(&req); err != nil {
|
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
Error: err.Error(),
|
Message: "status query parameter is required",
|
||||||
Message: "Invalid verification request",
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
cashierID := c.Locals("user_id")
|
page, _ := strconv.Atoi(c.Query("page", "1"))
|
||||||
if cashierID == nil {
|
pageSize, _ := strconv.Atoi(c.Query("pageSize", "50"))
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
|
|
||||||
Error: "missing user_id in context",
|
|
||||||
Message: "Unauthorized access",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
deposit, err := h.walletSvc.VerifyDirectDeposit(
|
deposits, total, err := h.directDepositSvc.GetDirectDepositsByStatus(c.Context(), status, page, pageSize)
|
||||||
c.Context(),
|
|
||||||
req.DepositID,
|
|
||||||
cashierID.(int64),
|
|
||||||
req.IsVerified,
|
|
||||||
req.Notes,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
h.logger.Error("GetDirectDepositsByStatus error", err)
|
||||||
|
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to fetch direct deposits",
|
||||||
Error: err.Error(),
|
Error: err.Error(),
|
||||||
Message: "Failed to verify deposit",
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
Message: "Deposit verification processed successfully",
|
Message: fmt.Sprintf("Direct deposits with status '%s' fetched successfully", status),
|
||||||
Data: deposit,
|
|
||||||
Success: true,
|
|
||||||
StatusCode: fiber.StatusOK,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPendingDeposits godoc
|
|
||||||
// @Summary Get pending direct deposits
|
|
||||||
// @Description Get list of direct deposits needing verification
|
|
||||||
// @Tags Direct Deposits
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200 {object} domain.Response
|
|
||||||
// @Failure 500 {object} domain.ErrorResponse
|
|
||||||
// @Router /api/v1/direct_deposit/pending [get]
|
|
||||||
func (h *Handler) GetPendingDirectDeposits(c *fiber.Ctx) error {
|
|
||||||
deposits, err := h.walletSvc.GetPendingDirectDeposits(c.Context())
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
||||||
Error: err.Error(),
|
|
||||||
Message: "Failed to retrieve pending deposits",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
||||||
Message: "Pending deposits retrieved successfully",
|
|
||||||
Data: deposits,
|
Data: deposits,
|
||||||
Success: true,
|
|
||||||
StatusCode: fiber.StatusOK,
|
StatusCode: fiber.StatusOK,
|
||||||
|
Success: true,
|
||||||
|
// Optional: include pagination info
|
||||||
|
MetaData: map[string]any{
|
||||||
|
"page": page,
|
||||||
|
"pageSize": pageSize,
|
||||||
|
"total": total,
|
||||||
|
"totalPage": int(math.Ceil(float64(total) / float64(pageSize))),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApproveDirectDeposit godoc
|
||||||
|
// @Summary Approve a direct deposit
|
||||||
|
// @Description Approves a direct deposit by admin and credits customer wallet
|
||||||
|
// @Tags DirectDeposit
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param depositID path int true "Deposit ID"
|
||||||
|
// @Param adminID query int true "Admin ID performing the approval"
|
||||||
|
// @Success 200 {object} domain.Response{data=string}
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 502 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/direct-deposits/{depositID}/approve [post]
|
||||||
|
func (h *Handler) ApproveDirectDeposit(c *fiber.Ctx) error {
|
||||||
|
depositID, err := strconv.Atoi(c.Params("depositID"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid deposit ID",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
adminID, err := strconv.Atoi(c.Query("adminID"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid admin ID",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.directDepositSvc.ApproveDirectDeposit(c.Context(), depositID, adminID); err != nil {
|
||||||
|
h.logger.Error("ApproveDirectDeposit error", err)
|
||||||
|
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to approve direct deposit",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: fmt.Sprintf("Direct deposit #%d approved successfully", depositID),
|
||||||
|
Data: fmt.Sprintf("Deposit #%d approved", depositID),
|
||||||
|
StatusCode: fiber.StatusOK,
|
||||||
|
Success: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RejectDirectDeposit godoc
|
||||||
|
// @Summary Reject a direct deposit
|
||||||
|
// @Description Rejects a direct deposit by admin and notifies the customer
|
||||||
|
// @Tags DirectDeposit
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param depositID path int true "Deposit ID"
|
||||||
|
// @Param adminID query int true "Admin ID performing the rejection"
|
||||||
|
// @Param reason query string true "Reason for rejection"
|
||||||
|
// @Success 200 {object} domain.Response{data=string}
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 502 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/direct-deposits/{depositID}/reject [post]
|
||||||
|
func (h *Handler) RejectDirectDeposit(c *fiber.Ctx) error {
|
||||||
|
depositID, err := strconv.Atoi(c.Params("depositID"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid deposit ID",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
adminID, err := strconv.Atoi(c.Query("adminID"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid admin ID",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reason := c.Query("reason")
|
||||||
|
if reason == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Rejection reason is required",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.directDepositSvc.RejectDirectDeposit(c.Context(), depositID, adminID, reason); err != nil {
|
||||||
|
h.logger.Error("RejectDirectDeposit error", err)
|
||||||
|
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to reject direct deposit",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: fmt.Sprintf("Direct deposit #%d rejected successfully", depositID),
|
||||||
|
Data: fmt.Sprintf("Deposit #%d rejected", depositID),
|
||||||
|
StatusCode: fiber.StatusOK,
|
||||||
|
Success: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDirectDepositByID godoc
|
||||||
|
// @Summary Get a direct deposit by ID
|
||||||
|
// @Description Fetches a single direct deposit by its ID
|
||||||
|
// @Tags DirectDeposit
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param depositID path int true "Deposit ID"
|
||||||
|
// @Success 200 {object} domain.Response{data=domain.DirectDeposit}
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 502 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/direct-deposits/{depositID} [get]
|
||||||
|
func (h *Handler) GetDirectDepositByID(c *fiber.Ctx) error {
|
||||||
|
depositID, err := strconv.Atoi(c.Params("depositID"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid deposit ID",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deposit, err := h.directDepositSvc.GetDirectDepositByID(c.Context(), depositID)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("GetDirectDepositByID error", err)
|
||||||
|
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to fetch direct deposit",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: fmt.Sprintf("Direct deposit #%d fetched successfully", depositID),
|
||||||
|
Data: deposit,
|
||||||
|
StatusCode: fiber.StatusOK,
|
||||||
|
Success: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDirectDeposit godoc
|
||||||
|
// @Summary Delete a direct deposit
|
||||||
|
// @Description Deletes a direct deposit by its ID
|
||||||
|
// @Tags DirectDeposit
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param depositID path int true "Deposit ID"
|
||||||
|
// @Success 200 {object} domain.Response{data=string}
|
||||||
|
// @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// @Failure 502 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/direct-deposits/{depositID} [delete]
|
||||||
|
func (h *Handler) DeleteDirectDeposit(c *fiber.Ctx) error {
|
||||||
|
depositID, err := strconv.Atoi(c.Params("depositID"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Invalid deposit ID",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.directDepositSvc.DeleteDirectDeposit(c.Context(), depositID); err != nil {
|
||||||
|
h.logger.Error("DeleteDirectDeposit error", err)
|
||||||
|
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to delete direct deposit",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
|
Message: fmt.Sprintf("Direct deposit #%d deleted successfully", depositID),
|
||||||
|
Data: fmt.Sprintf("Deposit #%d deleted", depositID),
|
||||||
|
StatusCode: fiber.StatusOK,
|
||||||
|
Success: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa"
|
||||||
"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"
|
||||||
|
directdeposit "github.com/SamuelTariku/FortuneBet-Backend/internal/services/direct_deposit"
|
||||||
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
|
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
|
||||||
|
|
@ -43,6 +44,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
|
directDepositSvc *directdeposit.Service
|
||||||
orchestrationSvc *orchestration.Service
|
orchestrationSvc *orchestration.Service
|
||||||
enetPulseSvc *enetpulse.Service
|
enetPulseSvc *enetpulse.Service
|
||||||
telebirrSvc *telebirr.TelebirrService
|
telebirrSvc *telebirr.TelebirrService
|
||||||
|
|
@ -84,6 +86,7 @@ type Handler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
|
directDepositSvc *directdeposit.Service,
|
||||||
orchestrationSvc *orchestration.Service,
|
orchestrationSvc *orchestration.Service,
|
||||||
enetPulseSvc *enetpulse.Service,
|
enetPulseSvc *enetpulse.Service,
|
||||||
telebirrSvc *telebirr.TelebirrService,
|
telebirrSvc *telebirr.TelebirrService,
|
||||||
|
|
@ -124,6 +127,7 @@ func New(
|
||||||
mongoLoggerSvc *zap.Logger,
|
mongoLoggerSvc *zap.Logger,
|
||||||
) *Handler {
|
) *Handler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
|
directDepositSvc: directDepositSvc,
|
||||||
orchestrationSvc: orchestrationSvc,
|
orchestrationSvc: orchestrationSvc,
|
||||||
enetPulseSvc: enetPulseSvc,
|
enetPulseSvc: enetPulseSvc,
|
||||||
telebirrSvc: telebirrSvc,
|
telebirrSvc: telebirrSvc,
|
||||||
|
|
|
||||||
|
|
@ -346,17 +346,17 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), req)
|
res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), veli.ErrDuplicateTransaction.Error()) {
|
if strings.Contains(err.Error(), veli.ErrDuplicateTransaction.Error()) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.CallbackErrorResponse{
|
||||||
// Message: "Duplicate transaction",
|
// Message: "Duplicate transaction",
|
||||||
Error: veli.ErrDuplicateTransaction.Error(),
|
Error: veli.ErrDuplicateTransaction.Error(),
|
||||||
})
|
})
|
||||||
} else if strings.Contains(err.Error(), veli.ErrInsufficientBalance.Error()) {
|
} else if strings.Contains(err.Error(), veli.ErrInsufficientBalance.Error()) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.CallbackErrorResponse{
|
||||||
// Message: "Wallet balance is insufficient",
|
// Message: "Wallet balance is insufficient",
|
||||||
Error: veli.ErrInsufficientBalance.Error(),
|
Error: veli.ErrInsufficientBalance.Error(),
|
||||||
})
|
})
|
||||||
} else if strings.Contains(err.Error(), veli.ErrPlayerNotFound.Error()) {
|
} else if strings.Contains(err.Error(), veli.ErrPlayerNotFound.Error()) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.CallbackErrorResponse{
|
||||||
// Message: "User not found",
|
// Message: "User not found",
|
||||||
Error: veli.ErrPlayerNotFound.Error(),
|
Error: veli.ErrPlayerNotFound.Error(),
|
||||||
})
|
})
|
||||||
|
|
@ -473,12 +473,12 @@ func (h *Handler) HandleWin(c *fiber.Ctx) error {
|
||||||
res, err := h.veliVirtualGameSvc.ProcessWin(c.Context(), req)
|
res, err := h.veliVirtualGameSvc.ProcessWin(c.Context(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, veli.ErrDuplicateTransaction) {
|
if errors.Is(err, veli.ErrDuplicateTransaction) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.CallbackErrorResponse{
|
||||||
// Message: "Duplicate transaction",
|
// Message: "Duplicate transaction",
|
||||||
Error: veli.ErrDuplicateTransaction.Error(),
|
Error: veli.ErrDuplicateTransaction.Error(),
|
||||||
})
|
})
|
||||||
} else if errors.Is(err, veli.ErrPlayerNotFound) {
|
} else if errors.Is(err, veli.ErrPlayerNotFound) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.CallbackErrorResponse{
|
||||||
// Message: "Duplicate transaction",
|
// Message: "Duplicate transaction",
|
||||||
Error: veli.ErrPlayerNotFound.Error(),
|
Error: veli.ErrPlayerNotFound.Error(),
|
||||||
})
|
})
|
||||||
|
|
@ -550,12 +550,12 @@ func (h *Handler) HandleCancel(c *fiber.Ctx) error {
|
||||||
res, err := h.veliVirtualGameSvc.ProcessCancel(c.Context(), req)
|
res, err := h.veliVirtualGameSvc.ProcessCancel(c.Context(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), veli.ErrDuplicateTransaction.Error()) {
|
if strings.Contains(err.Error(), veli.ErrDuplicateTransaction.Error()) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.CallbackErrorResponse{
|
||||||
// Message: "Duplicate transaction",
|
// Message: "Duplicate transaction",
|
||||||
Error: veli.ErrDuplicateTransaction.Error(),
|
Error: veli.ErrDuplicateTransaction.Error(),
|
||||||
})
|
})
|
||||||
} else if strings.Contains(err.Error(), veli.ErrPlayerNotFound.Error()) {
|
} else if strings.Contains(err.Error(), veli.ErrPlayerNotFound.Error()) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.CallbackErrorResponse{
|
||||||
// Message: "User not found",
|
// Message: "User not found",
|
||||||
Error: veli.ErrPlayerNotFound.Error(),
|
Error: veli.ErrPlayerNotFound.Error(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
|
|
||||||
func (a *App) initAppRoutes() {
|
func (a *App) initAppRoutes() {
|
||||||
h := handlers.New(
|
h := handlers.New(
|
||||||
|
a.directDepositSvc,
|
||||||
a.orchestrationSvc,
|
a.orchestrationSvc,
|
||||||
a.enetPulseSvc,
|
a.enetPulseSvc,
|
||||||
a.telebirrSvc,
|
a.telebirrSvc,
|
||||||
|
|
@ -103,9 +104,12 @@ func (a *App) initAppRoutes() {
|
||||||
groupV1.Get("/tenant", a.authMiddleware, h.GetTenantSlugByToken)
|
groupV1.Get("/tenant", a.authMiddleware, h.GetTenantSlugByToken)
|
||||||
|
|
||||||
//Direct_deposit
|
//Direct_deposit
|
||||||
groupV1.Post("/direct_deposit", a.authMiddleware, h.InitiateDirectDeposit)
|
groupV1.Post("/direct-deposits", a.authMiddleware, h.CreateDirectDeposit)
|
||||||
groupV1.Post("/direct_deposit/verify", a.authMiddleware, h.VerifyDirectDeposit)
|
groupV1.Post("/direct-deposits/:depositID/approve", a.authMiddleware, h.ApproveDirectDeposit)
|
||||||
groupV1.Get("/direct_deposit/pending", a.authMiddleware, h.GetPendingDirectDeposits)
|
groupV1.Post("/direct-deposits/:depositID/reject", a.authMiddleware, h.RejectDirectDeposit)
|
||||||
|
groupV1.Get("/direct-deposits", a.authMiddleware, h.GetDirectDepositsByStatus)
|
||||||
|
groupV1.Get("/direct-deposits/:depositID", a.authMiddleware, h.GetDirectDepositByID)
|
||||||
|
groupV1.Delete("/direct-deposits/:depositID", a.authMiddleware, h.DeleteDirectDeposit)
|
||||||
|
|
||||||
// Swagger
|
// Swagger
|
||||||
a.fiber.Get("/swagger/*", fiberSwagger.FiberWrapHandler())
|
a.fiber.Get("/swagger/*", fiberSwagger.FiberWrapHandler())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user