wallets and transaction routes

This commit is contained in:
Samuel Tariku 2025-04-04 21:51:15 +03:00
parent 7405023336
commit 675597c093
36 changed files with 2431 additions and 205 deletions

View File

@ -14,7 +14,9 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
httpserver "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server"
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
@ -57,6 +59,8 @@ func main() {
userSvc := user.NewService(store, store, mockSms, mockemail)
ticketSvc := ticket.NewService(store)
betSvc := bet.NewService(store)
walletSvc := wallet.NewService(store)
transactionSvc := transaction.NewService(store)
notificationRepo := repository.NewNotificationRepository(store)
notificationSvc := notificationservice.New(notificationRepo, logger)
@ -64,7 +68,7 @@ func main() {
app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{
JwtAccessKey: cfg.JwtKey,
JwtAccessExpiry: cfg.AccessExpiry,
}, userSvc, ticketSvc, betSvc, notificationSvc,
}, userSvc, ticketSvc, betSvc, walletSvc, transactionSvc, notificationSvc,
)
logger.Info("Starting server", "port", cfg.Port)

View File

@ -77,6 +77,7 @@ DROP TYPE IF EXISTS ua_registaration_type;
DROP TABLE IF EXISTS tickets;
DROP TABLE IF EXISTS bets;
DROP TABLE IF EXISTS wallets;
DROP TABLE IF EXISTS wallet_transfer;
DROP TABLE IF EXISTS transactions;
DROP TABLE IF EXISTS customer_wallets;

View File

@ -75,7 +75,7 @@ CREATE TABLE IF NOT EXISTS tickets (
CREATE TABLE IF NOT EXISTS wallets (
id BIGSERIAL PRIMARY KEY,
balance BIGINT NOT NULL,
balance BIGINT NOT NULL DEFAULT 0,
is_withdraw BOOLEAN NOT NULL,
is_bettable BOOLEAN NOT NULL,
user_id BIGINT NOT NULL,
@ -96,11 +96,30 @@ CREATE TABLE IF NOT EXISTS customer_wallets (
UNIQUE (customer_id, company_id)
);
CREATE TABLE IF NOT EXISTS wallet_transfer (
id BIGSERIAL PRIMARY KEY,
amount BIGINT NOT NULL,
wallet_transfer VARCHAR(255) NOT NULL,
wallet_id BIGINT NOT NULL,
verified BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS transactions (
id BIGSERIAL PRIMARY KEY,
amount BIGINT NOT NULL,
transaction_type VARCHAR(255) NOT NULL,
wallet_id BIGINT NOT NULL,
branch_id BIGINT NOT NULL,
cashier_id BIGINT NOT NULL,
bet_id BIGINT NOT NULL,
payment_option BIGINT NOT NULL,
full_name VARCHAR(255) NOT NULL,
phone_number VARCHAR(255) NOT NULL,
bank_code VARCHAR(255) NOT NULL,
beneficiary_name VARCHAR(255) NOT NULL,
account_name VARCHAR(255) NOT NULL,
account_number VARCHAR(255) NOT NULL,
reference_number VARCHAR(255) NOT NULL,
verified BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

View File

@ -1,14 +1,13 @@
-- name: CreateTransaction :one
INSERT INTO transactions (amount, transaction_type, wallet_id) VALUES ($1, $2, $3) RETURNING *;
INSERT INTO transactions (amount, branch_id, cashier_id, bet_id, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING *;
-- name: GetAllTransactions :many
SELECT * FROM transactions;
-- name: GetTransactionsByWallet :many
SELECT * FROM transactions WHERE wallet_id = $1;
-- name: GetTransactionByID :one
SELECT * FROM transactions WHERE id = $1;
-- name: UpdateTransferVerification :exec
UPDATE transactions SET verified = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2;
-- name: UpdateTransactionVerified :exec
UPDATE transactions SET verified = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1;

14
db/query/transfer.sql Normal file
View File

@ -0,0 +1,14 @@
-- name: CreateTransfer :one
INSERT INTO wallet_transfer (amount, wallet_transfer, wallet_id) VALUES ($1, $2, $3) RETURNING *;
-- name: GetAllTransfers :many
SELECT * FROM wallet_transfer;
-- name: GetTransfersByWallet :many
SELECT * FROM wallet_transfer WHERE wallet_id = $1;
-- name: GetTransferByID :one
SELECT * FROM wallet_transfer WHERE id = $1;
-- name: UpdateTransferVerification :exec
UPDATE wallet_transfer SET verified = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2;

View File

@ -1,5 +1,5 @@
-- name: CreateWallet :one
INSERT INTO wallets (balance, is_withdraw, is_bettable, user_id) VALUES ($1, $2, $3, $4) RETURNING *;
INSERT INTO wallets (is_withdraw, is_bettable, user_id) VALUES ($1, $2, $3) RETURNING *;
-- name: CreateCustomerWallet :one
INSERT INTO customer_wallets (customer_id, company_id, regular_wallet_id, static_wallet_id) VALUES ($1, $2, $3, $4) RETURNING *;
@ -10,6 +10,9 @@ SELECT * FROM wallets;
-- name: GetWalletByID :one
SELECT * FROM wallets WHERE id = $1;
-- name: GetWalletByUserID :many
SELECT * FROM wallets WHERE user_id = $1;
-- name: GetCustomerWallet :one
SELECT
cw.id,
@ -18,7 +21,10 @@ SELECT
rw.id AS regular_id,
rw.balance AS regular_balance,
sw.id AS static_id,
sw.balance AS static_balance
sw.balance AS static_balance,
rw.updated_at as regular_updated_at,
sw.updated_at as static_updated_at,
cw.created_at
FROM customer_wallets cw
JOIN wallets rw ON cw.regular_wallet_id = rw.id
JOIN wallets sw ON cw.static_wallet_id = sw.id

View File

@ -525,6 +525,183 @@ const docTemplate = `{
}
}
},
"/transaction": {
"get": {
"description": "Gets all the transactions",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transaction"
],
"summary": "Gets all transactions",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.TransactionRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": {
"description": "Creates a transaction",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transaction"
],
"summary": "Create a transaction",
"parameters": [
{
"description": "Creates transaction",
"name": "createBet",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateTransactionReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.TransactionRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/transaction/{id}": {
"get": {
"description": "Gets a single transaction by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transaction"
],
"summary": "Gets transaction by id",
"parameters": [
{
"type": "integer",
"description": "Transaction ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.TransactionRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"patch": {
"description": "Updates the cashed out field",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transaction"
],
"summary": "Updates the cashed out field",
"parameters": [
{
"type": "integer",
"description": "Transaction ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Updates Transaction Verification",
"name": "updateCashOut",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.UpdateTransactionVerifiedReq"
}
}
],
"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"
}
}
}
}
},
"/user/checkPhoneEmailExist": {
"post": {
"description": "Check if phone number or email exist",
@ -794,6 +971,139 @@ const docTemplate = `{
}
}
}
},
"/wallet": {
"get": {
"description": "Retrieve all wallets",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"wallet"
],
"summary": "Get all wallets",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.WalletRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/wallet/{id}": {
"get": {
"description": "Retrieve wallet details by wallet ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"wallet"
],
"summary": "Get wallet by ID",
"parameters": [
{
"type": "integer",
"description": "Wallet ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.WalletRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"patch": {
"description": "Can activate and deactivate wallet",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"wallet"
],
"summary": "Activate and Deactivate Wallet",
"parameters": [
{
"type": "integer",
"description": "Wallet ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update Wallet Active",
"name": "updateCashOut",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.UpdateWalletActiveReq"
}
}
],
"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"
}
}
}
}
}
},
"definitions": {
@ -815,20 +1125,35 @@ const docTemplate = `{
"domain.Outcome": {
"type": "object"
},
"domain.PaymentOption": {
"type": "integer",
"enum": [
0,
1,
2,
3
],
"x-enum-varnames": [
"CASH_TRANSACTION",
"TELEBIRR_TRANSACTION",
"ARIFPAY_TRANSACTION",
"BANK"
]
},
"domain.Role": {
"type": "string",
"enum": [
"admin",
"customer",
"super_admin",
"admin",
"branch_manager",
"customer",
"cashier"
],
"x-enum-varnames": [
"RoleAdmin",
"RoleCustomer",
"RoleSuperAdmin",
"RoleAdmin",
"RoleBranchManager",
"RoleCustomer",
"RoleCashier"
]
},
@ -974,6 +1299,59 @@ const docTemplate = `{
}
}
},
"handlers.CreateTransactionReq": {
"type": "object",
"properties": {
"account_name": {
"type": "string"
},
"account_number": {
"type": "string"
},
"amount": {
"type": "number",
"example": 100
},
"bank_code": {
"description": "Payment Details for bank",
"type": "string"
},
"beneficiary_name": {
"type": "string"
},
"bet_id": {
"type": "integer",
"example": 1
},
"branch_id": {
"type": "integer",
"example": 1
},
"cashier_id": {
"type": "integer",
"example": 1
},
"full_name": {
"type": "string",
"example": "John Smith"
},
"payment_option": {
"allOf": [
{
"$ref": "#/definitions/domain.PaymentOption"
}
],
"example": 1
},
"phone_number": {
"type": "string",
"example": "0911111111"
},
"reference_number": {
"type": "string"
}
}
},
"handlers.RegisterCodeReq": {
"type": "object",
"properties": {
@ -1074,6 +1452,66 @@ const docTemplate = `{
}
}
},
"handlers.TransactionRes": {
"type": "object",
"properties": {
"account_name": {
"type": "string"
},
"account_number": {
"type": "string"
},
"amount": {
"type": "number",
"example": 100
},
"bank_code": {
"type": "string"
},
"beneficiary_name": {
"type": "string"
},
"bet_id": {
"type": "integer",
"example": 1
},
"branch_id": {
"type": "integer",
"example": 1
},
"cashier_id": {
"type": "integer",
"example": 1
},
"full_name": {
"type": "string",
"example": "John Smith"
},
"id": {
"type": "integer",
"example": 1
},
"payment_option": {
"allOf": [
{
"$ref": "#/definitions/domain.PaymentOption"
}
],
"example": 1
},
"phone_number": {
"type": "string",
"example": "0911111111"
},
"reference_number": {
"type": "string"
},
"verified": {
"type": "boolean",
"example": true
}
}
},
"handlers.UpdateCashOutReq": {
"type": "object",
"properties": {
@ -1082,6 +1520,22 @@ const docTemplate = `{
}
}
},
"handlers.UpdateTransactionVerifiedReq": {
"type": "object",
"properties": {
"verified": {
"type": "boolean"
}
}
},
"handlers.UpdateWalletActiveReq": {
"type": "object",
"properties": {
"isActive": {
"type": "boolean"
}
}
},
"handlers.UserProfileRes": {
"type": "object",
"properties": {
@ -1123,6 +1577,41 @@ const docTemplate = `{
}
}
},
"handlers.WalletRes": {
"type": "object",
"properties": {
"amount": {
"type": "number",
"example": 100
},
"created_at": {
"type": "string"
},
"id": {
"type": "integer",
"example": 1
},
"is_active": {
"type": "boolean",
"example": true
},
"is_bettable": {
"type": "boolean",
"example": true
},
"is_withdraw": {
"type": "boolean",
"example": true
},
"updated_at": {
"type": "string"
},
"user_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.loginCustomerReq": {
"type": "object",
"properties": {

View File

@ -517,6 +517,183 @@
}
}
},
"/transaction": {
"get": {
"description": "Gets all the transactions",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transaction"
],
"summary": "Gets all transactions",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.TransactionRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": {
"description": "Creates a transaction",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transaction"
],
"summary": "Create a transaction",
"parameters": [
{
"description": "Creates transaction",
"name": "createBet",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateTransactionReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.TransactionRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/transaction/{id}": {
"get": {
"description": "Gets a single transaction by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transaction"
],
"summary": "Gets transaction by id",
"parameters": [
{
"type": "integer",
"description": "Transaction ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.TransactionRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"patch": {
"description": "Updates the cashed out field",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transaction"
],
"summary": "Updates the cashed out field",
"parameters": [
{
"type": "integer",
"description": "Transaction ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Updates Transaction Verification",
"name": "updateCashOut",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.UpdateTransactionVerifiedReq"
}
}
],
"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"
}
}
}
}
},
"/user/checkPhoneEmailExist": {
"post": {
"description": "Check if phone number or email exist",
@ -786,6 +963,139 @@
}
}
}
},
"/wallet": {
"get": {
"description": "Retrieve all wallets",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"wallet"
],
"summary": "Get all wallets",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.WalletRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/wallet/{id}": {
"get": {
"description": "Retrieve wallet details by wallet ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"wallet"
],
"summary": "Get wallet by ID",
"parameters": [
{
"type": "integer",
"description": "Wallet ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.WalletRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"patch": {
"description": "Can activate and deactivate wallet",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"wallet"
],
"summary": "Activate and Deactivate Wallet",
"parameters": [
{
"type": "integer",
"description": "Wallet ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update Wallet Active",
"name": "updateCashOut",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.UpdateWalletActiveReq"
}
}
],
"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"
}
}
}
}
}
},
"definitions": {
@ -807,20 +1117,35 @@
"domain.Outcome": {
"type": "object"
},
"domain.PaymentOption": {
"type": "integer",
"enum": [
0,
1,
2,
3
],
"x-enum-varnames": [
"CASH_TRANSACTION",
"TELEBIRR_TRANSACTION",
"ARIFPAY_TRANSACTION",
"BANK"
]
},
"domain.Role": {
"type": "string",
"enum": [
"admin",
"customer",
"super_admin",
"admin",
"branch_manager",
"customer",
"cashier"
],
"x-enum-varnames": [
"RoleAdmin",
"RoleCustomer",
"RoleSuperAdmin",
"RoleAdmin",
"RoleBranchManager",
"RoleCustomer",
"RoleCashier"
]
},
@ -966,6 +1291,59 @@
}
}
},
"handlers.CreateTransactionReq": {
"type": "object",
"properties": {
"account_name": {
"type": "string"
},
"account_number": {
"type": "string"
},
"amount": {
"type": "number",
"example": 100
},
"bank_code": {
"description": "Payment Details for bank",
"type": "string"
},
"beneficiary_name": {
"type": "string"
},
"bet_id": {
"type": "integer",
"example": 1
},
"branch_id": {
"type": "integer",
"example": 1
},
"cashier_id": {
"type": "integer",
"example": 1
},
"full_name": {
"type": "string",
"example": "John Smith"
},
"payment_option": {
"allOf": [
{
"$ref": "#/definitions/domain.PaymentOption"
}
],
"example": 1
},
"phone_number": {
"type": "string",
"example": "0911111111"
},
"reference_number": {
"type": "string"
}
}
},
"handlers.RegisterCodeReq": {
"type": "object",
"properties": {
@ -1066,6 +1444,66 @@
}
}
},
"handlers.TransactionRes": {
"type": "object",
"properties": {
"account_name": {
"type": "string"
},
"account_number": {
"type": "string"
},
"amount": {
"type": "number",
"example": 100
},
"bank_code": {
"type": "string"
},
"beneficiary_name": {
"type": "string"
},
"bet_id": {
"type": "integer",
"example": 1
},
"branch_id": {
"type": "integer",
"example": 1
},
"cashier_id": {
"type": "integer",
"example": 1
},
"full_name": {
"type": "string",
"example": "John Smith"
},
"id": {
"type": "integer",
"example": 1
},
"payment_option": {
"allOf": [
{
"$ref": "#/definitions/domain.PaymentOption"
}
],
"example": 1
},
"phone_number": {
"type": "string",
"example": "0911111111"
},
"reference_number": {
"type": "string"
},
"verified": {
"type": "boolean",
"example": true
}
}
},
"handlers.UpdateCashOutReq": {
"type": "object",
"properties": {
@ -1074,6 +1512,22 @@
}
}
},
"handlers.UpdateTransactionVerifiedReq": {
"type": "object",
"properties": {
"verified": {
"type": "boolean"
}
}
},
"handlers.UpdateWalletActiveReq": {
"type": "object",
"properties": {
"isActive": {
"type": "boolean"
}
}
},
"handlers.UserProfileRes": {
"type": "object",
"properties": {
@ -1115,6 +1569,41 @@
}
}
},
"handlers.WalletRes": {
"type": "object",
"properties": {
"amount": {
"type": "number",
"example": 100
},
"created_at": {
"type": "string"
},
"id": {
"type": "integer",
"example": 1
},
"is_active": {
"type": "boolean",
"example": true
},
"is_bettable": {
"type": "boolean",
"example": true
},
"is_withdraw": {
"type": "boolean",
"example": true
},
"updated_at": {
"type": "string"
},
"user_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.loginCustomerReq": {
"type": "object",
"properties": {

View File

@ -13,19 +13,31 @@ definitions:
- BET_STATUS_ERROR
domain.Outcome:
type: object
domain.PaymentOption:
enum:
- 0
- 1
- 2
- 3
type: integer
x-enum-varnames:
- CASH_TRANSACTION
- TELEBIRR_TRANSACTION
- ARIFPAY_TRANSACTION
- BANK
domain.Role:
enum:
- admin
- customer
- super_admin
- admin
- branch_manager
- customer
- cashier
type: string
x-enum-varnames:
- RoleAdmin
- RoleCustomer
- RoleSuperAdmin
- RoleAdmin
- RoleBranchManager
- RoleCustomer
- RoleCashier
handlers.BetRes:
properties:
@ -123,6 +135,42 @@ definitions:
example: 1234
type: integer
type: object
handlers.CreateTransactionReq:
properties:
account_name:
type: string
account_number:
type: string
amount:
example: 100
type: number
bank_code:
description: Payment Details for bank
type: string
beneficiary_name:
type: string
bet_id:
example: 1
type: integer
branch_id:
example: 1
type: integer
cashier_id:
example: 1
type: integer
full_name:
example: John Smith
type: string
payment_option:
allOf:
- $ref: '#/definitions/domain.PaymentOption'
example: 1
phone_number:
example: "0911111111"
type: string
reference_number:
type: string
type: object
handlers.RegisterCodeReq:
properties:
email:
@ -193,11 +241,62 @@ definitions:
example: 4.22
type: number
type: object
handlers.TransactionRes:
properties:
account_name:
type: string
account_number:
type: string
amount:
example: 100
type: number
bank_code:
type: string
beneficiary_name:
type: string
bet_id:
example: 1
type: integer
branch_id:
example: 1
type: integer
cashier_id:
example: 1
type: integer
full_name:
example: John Smith
type: string
id:
example: 1
type: integer
payment_option:
allOf:
- $ref: '#/definitions/domain.PaymentOption'
example: 1
phone_number:
example: "0911111111"
type: string
reference_number:
type: string
verified:
example: true
type: boolean
type: object
handlers.UpdateCashOutReq:
properties:
cashedOut:
type: boolean
type: object
handlers.UpdateTransactionVerifiedReq:
properties:
verified:
type: boolean
type: object
handlers.UpdateWalletActiveReq:
properties:
isActive:
type: boolean
type: object
handlers.UserProfileRes:
properties:
created_at:
@ -225,6 +324,31 @@ definitions:
updated_at:
type: string
type: object
handlers.WalletRes:
properties:
amount:
example: 100
type: number
created_at:
type: string
id:
example: 1
type: integer
is_active:
example: true
type: boolean
is_bettable:
example: true
type: boolean
is_withdraw:
example: true
type: boolean
updated_at:
type: string
user_id:
example: 1
type: integer
type: object
handlers.loginCustomerReq:
properties:
email:
@ -618,6 +742,123 @@ paths:
summary: Get ticket by ID
tags:
- ticket
/transaction:
get:
consumes:
- application/json
description: Gets all the transactions
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.TransactionRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets all transactions
tags:
- transaction
post:
consumes:
- application/json
description: Creates a transaction
parameters:
- description: Creates transaction
in: body
name: createBet
required: true
schema:
$ref: '#/definitions/handlers.CreateTransactionReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.TransactionRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Create a transaction
tags:
- transaction
/transaction/{id}:
get:
consumes:
- application/json
description: Gets a single transaction by id
parameters:
- description: Transaction ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.TransactionRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets transaction by id
tags:
- transaction
patch:
consumes:
- application/json
description: Updates the cashed out field
parameters:
- description: Transaction ID
in: path
name: id
required: true
type: integer
- description: Updates Transaction Verification
in: body
name: updateCashOut
required: true
schema:
$ref: '#/definitions/handlers.UpdateTransactionVerifiedReq'
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: Updates the cashed out field
tags:
- transaction
/user/checkPhoneEmailExist:
post:
consumes:
@ -793,6 +1034,94 @@ paths:
summary: Send reset code
tags:
- user
/wallet:
get:
consumes:
- application/json
description: Retrieve all wallets
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.WalletRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Get all wallets
tags:
- wallet
/wallet/{id}:
get:
consumes:
- application/json
description: Retrieve wallet details by wallet ID
parameters:
- description: Wallet ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.WalletRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Get wallet by ID
tags:
- wallet
patch:
consumes:
- application/json
description: Can activate and deactivate wallet
parameters:
- description: Wallet ID
in: path
name: id
required: true
type: integer
- description: Update Wallet Active
in: body
name: updateCashOut
required: true
schema:
$ref: '#/definitions/handlers.UpdateWalletActiveReq'
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: Activate and Deactivate Wallet
tags:
- wallet
securityDefinitions:
Bearer:
in: header

View File

@ -82,8 +82,17 @@ type Ticket struct {
type Transaction struct {
ID int64
Amount int64
TransactionType string
WalletID int64
BranchID int64
CashierID int64
BetID int64
PaymentOption int64
FullName string
PhoneNumber string
BankCode string
BeneficiaryName string
AccountName string
AccountNumber string
ReferenceNumber string
Verified bool
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
@ -115,3 +124,13 @@ type Wallet struct {
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type WalletTransfer struct {
ID int64
Amount int64
WalletTransfer string
WalletID int64
Verified bool
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}

View File

@ -10,23 +10,54 @@ import (
)
const CreateTransaction = `-- name: CreateTransaction :one
INSERT INTO transactions (amount, transaction_type, wallet_id) VALUES ($1, $2, $3) RETURNING id, amount, transaction_type, wallet_id, verified, created_at, updated_at
INSERT INTO transactions (amount, branch_id, cashier_id, bet_id, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING id, amount, branch_id, cashier_id, bet_id, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at
`
type CreateTransactionParams struct {
Amount int64
TransactionType string
WalletID int64
BranchID int64
CashierID int64
BetID int64
PaymentOption int64
FullName string
PhoneNumber string
BankCode string
BeneficiaryName string
AccountName string
AccountNumber string
ReferenceNumber string
}
func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionParams) (Transaction, error) {
row := q.db.QueryRow(ctx, CreateTransaction, arg.Amount, arg.TransactionType, arg.WalletID)
row := q.db.QueryRow(ctx, CreateTransaction,
arg.Amount,
arg.BranchID,
arg.CashierID,
arg.BetID,
arg.PaymentOption,
arg.FullName,
arg.PhoneNumber,
arg.BankCode,
arg.BeneficiaryName,
arg.AccountName,
arg.AccountNumber,
arg.ReferenceNumber,
)
var i Transaction
err := row.Scan(
&i.ID,
&i.Amount,
&i.TransactionType,
&i.WalletID,
&i.BranchID,
&i.CashierID,
&i.BetID,
&i.PaymentOption,
&i.FullName,
&i.PhoneNumber,
&i.BankCode,
&i.BeneficiaryName,
&i.AccountName,
&i.AccountNumber,
&i.ReferenceNumber,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
@ -35,7 +66,7 @@ func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionPa
}
const GetAllTransactions = `-- name: GetAllTransactions :many
SELECT id, amount, transaction_type, wallet_id, verified, created_at, updated_at FROM transactions
SELECT id, amount, branch_id, cashier_id, bet_id, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at FROM transactions
`
func (q *Queries) GetAllTransactions(ctx context.Context) ([]Transaction, error) {
@ -50,8 +81,17 @@ func (q *Queries) GetAllTransactions(ctx context.Context) ([]Transaction, error)
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.TransactionType,
&i.WalletID,
&i.BranchID,
&i.CashierID,
&i.BetID,
&i.PaymentOption,
&i.FullName,
&i.PhoneNumber,
&i.BankCode,
&i.BeneficiaryName,
&i.AccountName,
&i.AccountNumber,
&i.ReferenceNumber,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
@ -67,7 +107,7 @@ func (q *Queries) GetAllTransactions(ctx context.Context) ([]Transaction, error)
}
const GetTransactionByID = `-- name: GetTransactionByID :one
SELECT id, amount, transaction_type, wallet_id, verified, created_at, updated_at FROM transactions WHERE id = $1
SELECT id, amount, branch_id, cashier_id, bet_id, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at FROM transactions WHERE id = $1
`
func (q *Queries) GetTransactionByID(ctx context.Context, id int64) (Transaction, error) {
@ -76,8 +116,17 @@ func (q *Queries) GetTransactionByID(ctx context.Context, id int64) (Transaction
err := row.Scan(
&i.ID,
&i.Amount,
&i.TransactionType,
&i.WalletID,
&i.BranchID,
&i.CashierID,
&i.BetID,
&i.PaymentOption,
&i.FullName,
&i.PhoneNumber,
&i.BankCode,
&i.BeneficiaryName,
&i.AccountName,
&i.AccountNumber,
&i.ReferenceNumber,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
@ -85,48 +134,16 @@ func (q *Queries) GetTransactionByID(ctx context.Context, id int64) (Transaction
return i, err
}
const GetTransactionsByWallet = `-- name: GetTransactionsByWallet :many
SELECT id, amount, transaction_type, wallet_id, verified, created_at, updated_at FROM transactions WHERE wallet_id = $1
const UpdateTransactionVerified = `-- name: UpdateTransactionVerified :exec
UPDATE transactions SET verified = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1
`
func (q *Queries) GetTransactionsByWallet(ctx context.Context, walletID int64) ([]Transaction, error) {
rows, err := q.db.Query(ctx, GetTransactionsByWallet, walletID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Transaction
for rows.Next() {
var i Transaction
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.TransactionType,
&i.WalletID,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateTransferVerification = `-- name: UpdateTransferVerification :exec
UPDATE transactions SET verified = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2
`
type UpdateTransferVerificationParams struct {
Verified bool
type UpdateTransactionVerifiedParams struct {
ID int64
Verified bool
}
func (q *Queries) UpdateTransferVerification(ctx context.Context, arg UpdateTransferVerificationParams) error {
_, err := q.db.Exec(ctx, UpdateTransferVerification, arg.Verified, arg.ID)
func (q *Queries) UpdateTransactionVerified(ctx context.Context, arg UpdateTransactionVerifiedParams) error {
_, err := q.db.Exec(ctx, UpdateTransactionVerified, arg.ID, arg.Verified)
return err
}

132
gen/db/transfer.sql.go Normal file
View File

@ -0,0 +1,132 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
// source: transfer.sql
package dbgen
import (
"context"
)
const CreateTransfer = `-- name: CreateTransfer :one
INSERT INTO wallet_transfer (amount, wallet_transfer, wallet_id) VALUES ($1, $2, $3) RETURNING id, amount, wallet_transfer, wallet_id, verified, created_at, updated_at
`
type CreateTransferParams struct {
Amount int64
WalletTransfer string
WalletID int64
}
func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams) (WalletTransfer, error) {
row := q.db.QueryRow(ctx, CreateTransfer, arg.Amount, arg.WalletTransfer, arg.WalletID)
var i WalletTransfer
err := row.Scan(
&i.ID,
&i.Amount,
&i.WalletTransfer,
&i.WalletID,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const GetAllTransfers = `-- name: GetAllTransfers :many
SELECT id, amount, wallet_transfer, wallet_id, verified, created_at, updated_at FROM wallet_transfer
`
func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error) {
rows, err := q.db.Query(ctx, GetAllTransfers)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WalletTransfer
for rows.Next() {
var i WalletTransfer
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.WalletTransfer,
&i.WalletID,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetTransferByID = `-- name: GetTransferByID :one
SELECT id, amount, wallet_transfer, wallet_id, verified, created_at, updated_at FROM wallet_transfer WHERE id = $1
`
func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer, error) {
row := q.db.QueryRow(ctx, GetTransferByID, id)
var i WalletTransfer
err := row.Scan(
&i.ID,
&i.Amount,
&i.WalletTransfer,
&i.WalletID,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const GetTransfersByWallet = `-- name: GetTransfersByWallet :many
SELECT id, amount, wallet_transfer, wallet_id, verified, created_at, updated_at FROM wallet_transfer WHERE wallet_id = $1
`
func (q *Queries) GetTransfersByWallet(ctx context.Context, walletID int64) ([]WalletTransfer, error) {
rows, err := q.db.Query(ctx, GetTransfersByWallet, walletID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WalletTransfer
for rows.Next() {
var i WalletTransfer
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.WalletTransfer,
&i.WalletID,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateTransferVerification = `-- name: UpdateTransferVerification :exec
UPDATE wallet_transfer SET verified = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2
`
type UpdateTransferVerificationParams struct {
Verified bool
ID int64
}
func (q *Queries) UpdateTransferVerification(ctx context.Context, arg UpdateTransferVerificationParams) error {
_, err := q.db.Exec(ctx, UpdateTransferVerification, arg.Verified, arg.ID)
return err
}

View File

@ -7,6 +7,8 @@ package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateCustomerWallet = `-- name: CreateCustomerWallet :one
@ -41,23 +43,17 @@ func (q *Queries) CreateCustomerWallet(ctx context.Context, arg CreateCustomerWa
}
const CreateWallet = `-- name: CreateWallet :one
INSERT INTO wallets (balance, is_withdraw, is_bettable, user_id) VALUES ($1, $2, $3, $4) RETURNING id, balance, is_withdraw, is_bettable, user_id, is_active, created_at, updated_at
INSERT INTO wallets (is_withdraw, is_bettable, user_id) VALUES ($1, $2, $3) RETURNING id, balance, is_withdraw, is_bettable, user_id, is_active, created_at, updated_at
`
type CreateWalletParams struct {
Balance int64
IsWithdraw bool
IsBettable bool
UserID int64
}
func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wallet, error) {
row := q.db.QueryRow(ctx, CreateWallet,
arg.Balance,
arg.IsWithdraw,
arg.IsBettable,
arg.UserID,
)
row := q.db.QueryRow(ctx, CreateWallet, arg.IsWithdraw, arg.IsBettable, arg.UserID)
var i Wallet
err := row.Scan(
&i.ID,
@ -113,7 +109,10 @@ SELECT
rw.id AS regular_id,
rw.balance AS regular_balance,
sw.id AS static_id,
sw.balance AS static_balance
sw.balance AS static_balance,
rw.updated_at as regular_updated_at,
sw.updated_at as static_updated_at,
cw.created_at
FROM customer_wallets cw
JOIN wallets rw ON cw.regular_wallet_id = rw.id
JOIN wallets sw ON cw.static_wallet_id = sw.id
@ -133,6 +132,9 @@ type GetCustomerWalletRow struct {
RegularBalance int64
StaticID int64
StaticBalance int64
RegularUpdatedAt pgtype.Timestamp
StaticUpdatedAt pgtype.Timestamp
CreatedAt pgtype.Timestamp
}
func (q *Queries) GetCustomerWallet(ctx context.Context, arg GetCustomerWalletParams) (GetCustomerWalletRow, error) {
@ -146,6 +148,9 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, arg GetCustomerWalletPa
&i.RegularBalance,
&i.StaticID,
&i.StaticBalance,
&i.RegularUpdatedAt,
&i.StaticUpdatedAt,
&i.CreatedAt,
)
return i, err
}
@ -170,6 +175,39 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
return i, err
}
const GetWalletByUserID = `-- name: GetWalletByUserID :many
SELECT id, balance, is_withdraw, is_bettable, user_id, is_active, created_at, updated_at FROM wallets WHERE user_id = $1
`
func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet, error) {
rows, err := q.db.Query(ctx, GetWalletByUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Wallet
for rows.Next() {
var i Wallet
if err := rows.Scan(
&i.ID,
&i.Balance,
&i.IsWithdraw,
&i.IsBettable,
&i.UserID,
&i.IsActive,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateBalance = `-- name: UpdateBalance :exec
UPDATE wallets SET balance = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2
`

View File

@ -1,22 +1,44 @@
package domain
type TransactionType string
type PaymentOption int64
const (
DEPOSIT TransactionType = "deposit"
WITHDRAW TransactionType = "withdraw"
CASH_TRANSACTION PaymentOption = iota
TELEBIRR_TRANSACTION
ARIFPAY_TRANSACTION
BANK
)
type Transaction struct {
ID int64
Amount Currency
Type TransactionType
BranchID int64
CashierID int64
BetID int64
PaymentOption PaymentOption
FullName string
PhoneNumber string
// Payment Details for bank
BankCode string
BeneficiaryName string
AccountName string
AccountNumber string
ReferenceNumber string
Verified bool
WalletID int64
}
type CreateTransaction struct {
Amount Currency
Type TransactionType
WalletID int64
BranchID int64
CashierID int64
BetID int64
PaymentOption PaymentOption
FullName string
PhoneNumber string
// Payment Details for bank
BankCode string
BeneficiaryName string
AccountName string
AccountNumber string
ReferenceNumber string
}

View File

@ -0,0 +1,23 @@
package domain
type TransferType string
const (
DEPOSIT TransferType = "deposit"
WITHDRAW TransferType = "withdraw"
)
type Transfer struct {
ID int64
Amount Currency
Verified bool
WalletID int64
Type TransferType
}
type CreateTransfer struct {
Amount Currency
Verified bool
WalletID int64
Type TransferType
}

View File

@ -1,5 +1,7 @@
package domain
import "time"
type Wallet struct {
ID int64
Balance Currency
@ -7,6 +9,8 @@ type Wallet struct {
IsBettable bool
IsActive bool
UserID int64
UpdatedAt time.Time
CreatedAt time.Time
}
type CustomerWallet struct {
@ -24,10 +28,12 @@ type GetCustomerWallet struct {
StaticBalance Currency
CustomerID int64
CompanyID int64
RegularUpdatedAt time.Time
StaticUpdatedAt time.Time
CreatedAt time.Time
}
type CreateWallet struct {
Balance Currency
IsWithdraw bool
IsBettable bool
UserID int64

View File

@ -73,7 +73,7 @@ func (s *Store) GetAllBets(ctx context.Context) ([]domain.Bet, error) {
return nil, err
}
var result []domain.Bet
var result []domain.Bet = make([]domain.Bet, len(bets))
for _, bet := range bets {
result = append(result, convertDBBet(bet))
}

View File

@ -51,7 +51,7 @@ func (s *Store) GetAllTickets(ctx context.Context) ([]domain.Ticket, error) {
return nil, err
}
var result []domain.Ticket
var result []domain.Ticket = make([]domain.Ticket, len(tickets))
for _, ticket := range tickets {
result = append(result, convertDBTicket(ticket))
}

View File

@ -9,68 +9,74 @@ import (
func convertDBTransaction(transaction dbgen.Transaction) domain.Transaction {
return domain.Transaction{
ID: transaction.ID,
Amount: domain.Currency(transaction.Amount),
Type: domain.TransactionType(transaction.TransactionType),
Verified: transaction.Verified,
WalletID: transaction.WalletID,
BranchID: transaction.BranchID,
CashierID: transaction.CashierID,
BetID: transaction.BetID,
PaymentOption: domain.PaymentOption(transaction.PaymentOption),
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: transaction.BankCode,
BeneficiaryName: transaction.BeneficiaryName,
AccountName: transaction.AccountName,
AccountNumber: transaction.AccountNumber,
ReferenceNumber: transaction.ReferenceNumber,
}
}
func convertCreateTransaction(transaction domain.CreateTransaction) dbgen.CreateTransactionParams {
return dbgen.CreateTransactionParams{
Amount: int64(transaction.Amount),
TransactionType: string(transaction.Type),
WalletID: transaction.WalletID,
BranchID: transaction.BranchID,
CashierID: transaction.CashierID,
BetID: transaction.BetID,
PaymentOption: int64(transaction.PaymentOption),
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: transaction.BankCode,
BeneficiaryName: transaction.BeneficiaryName,
AccountName: transaction.AccountName,
AccountNumber: transaction.AccountNumber,
ReferenceNumber: transaction.ReferenceNumber,
}
}
func (s *Store) CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error) {
newTransaction, err := s.queries.CreateTransaction(ctx, convertCreateTransaction(transaction))
if err != nil {
return domain.Transaction{}, err
}
return convertDBTransaction(newTransaction), nil
return convertDBTransaction(newTransaction), err
}
func (s *Store) GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) {
transactions, err := s.queries.GetAllTransactions(ctx)
func (s *Store) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) {
transaction, err := s.queries.GetTransactionByID(ctx, id)
if err != nil {
return nil, err
}
var result []domain.Transaction
for _, transaction := range transactions {
result = append(result, convertDBTransaction(transaction))
}
return result, nil
}
func (s *Store) GetTransactionsByWallet(ctx context.Context, walletID int64) ([]domain.Transaction, error) {
transactions, err := s.queries.GetTransactionsByWallet(ctx, walletID)
if err != nil {
return nil, err
return domain.Transaction{}, err
}
var result []domain.Transaction
for _, transaction := range transactions {
result = append(result, convertDBTransaction(transaction))
}
return result, nil
}
func (s *Store) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error){
transaction, err := s.queries.GetTransactionByID(ctx, id);
if err != nil {
return domain.Transaction{}, nil
}
return convertDBTransaction(transaction), nil
}
func (s *Store) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error{
err := s.queries.UpdateTransferVerification(ctx, dbgen.UpdateTransferVerificationParams{
func (s *Store) GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) {
transaction, err := s.queries.GetAllTransactions(ctx)
if err != nil {
return nil, err
}
var result []domain.Transaction = make([]domain.Transaction, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBTransaction(ticket))
}
return result, nil
}
func (s *Store) UpdateTransactionVerified(ctx context.Context, id int64, verified bool) error {
err := s.queries.UpdateTransactionVerified(ctx, dbgen.UpdateTransactionVerifiedParams{
ID: id,
Verified: verified,
})
return err
}

View File

@ -0,0 +1,77 @@
package repository
// import (
// "context"
// dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
// )
// func convertDBTransaction(transaction dbgen.Transaction) domain.Transaction {
// return domain.Transaction{
// ID: transaction.ID,
// Amount: domain.Currency(transaction.Amount),
// Type: domain.TransactionType(transaction.TransactionType),
// Verified: transaction.Verified,
// WalletID: transaction.WalletID,
// }
// }
// func convertCreateTransaction(transaction domain.CreateTransaction) dbgen.CreateTransactionParams {
// return dbgen.CreateTransactionParams{
// Amount: int64(transaction.Amount),
// TransactionType: string(transaction.Type),
// WalletID: transaction.WalletID,
// }
// }
// func (s *Store) CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error) {
// newTransaction, err := s.queries.CreateTransaction(ctx, convertCreateTransaction(transaction))
// if err != nil {
// return domain.Transaction{}, err
// }
// return convertDBTransaction(newTransaction), nil
// }
// func (s *Store) GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) {
// transactions, err := s.queries.GetAllTransactions(ctx)
// if err != nil {
// return nil, err
// }
// var result []domain.Transaction = make([]domain.Transaction, len(transactions))
// for _, transaction := range transactions {
// result = append(result, convertDBTransaction(transaction))
// }
// return result, nil
// }
// func (s *Store) GetTransactionsByWallet(ctx context.Context, walletID int64) ([]domain.Transaction, error) {
// transactions, err := s.queries.GetTransactionsByWallet(ctx, walletID)
// if err != nil {
// return nil, err
// }
// var result []domain.Transaction = make([]domain.Transaction, len(transactions))
// for _, transaction := range transactions {
// result = append(result, convertDBTransaction(transaction))
// }
// return result, nil
// }
// func (s *Store) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) {
// transaction, err := s.queries.GetTransactionByID(ctx, id)
// if err != nil {
// return domain.Transaction{}, nil
// }
// return convertDBTransaction(transaction), nil
// }
// func (s *Store) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error {
// err := s.queries.UpdateTransferVerification(ctx, dbgen.UpdateTransferVerificationParams{
// ID: id,
// Verified: verified,
// })
// return err
// }

View File

@ -15,12 +15,13 @@ func convertDBWallet(wallet dbgen.Wallet) domain.Wallet {
IsBettable: wallet.IsBettable,
IsActive: wallet.IsActive,
UserID: wallet.UserID,
UpdatedAt: wallet.UpdatedAt.Time,
CreatedAt: wallet.CreatedAt.Time,
}
}
func convertCreateWallet(wallet domain.CreateWallet) dbgen.CreateWalletParams {
return dbgen.CreateWalletParams{
Balance: int64(wallet.Balance),
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
UserID: wallet.UserID,
@ -54,6 +55,9 @@ func convertDBGetCustomerWallet(customerWallet dbgen.GetCustomerWalletRow) domai
StaticBalance: domain.Currency(customerWallet.StaticBalance),
CustomerID: customerWallet.CustomerID,
CompanyID: customerWallet.CompanyID,
RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time,
StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time,
CreatedAt: customerWallet.CreatedAt.Time,
}
}
@ -66,6 +70,7 @@ func (s *Store) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (d
}
func (s *Store) CreateCustomerWallet(ctx context.Context, customerWallet domain.CreateCustomerWallet) (domain.CustomerWallet, error) {
newCustomerWallet, err := s.queries.CreateCustomerWallet(ctx, convertCreateCustomerWallet(customerWallet))
if err != nil {
return domain.CustomerWallet{}, err
@ -88,7 +93,22 @@ func (s *Store) GetAllWallets(ctx context.Context) ([]domain.Wallet, error) {
return nil, err
}
var result []domain.Wallet
var result []domain.Wallet = make([]domain.Wallet, len(wallets))
for _, wallet := range wallets {
result = append(result, convertDBWallet(wallet))
}
return result, nil
}
func (s *Store) GetWalletsByUser(ctx context.Context, userID int64) ([]domain.Wallet, error) {
wallets, err := s.queries.GetWalletByUserID(ctx, userID)
if err != nil {
return nil, err
}
var result []domain.Wallet = make([]domain.Wallet, len(wallets))
for _, wallet := range wallets {
result = append(result, convertDBWallet(wallet))
}

View File

@ -8,8 +8,7 @@ import (
type TransactionStore interface {
CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error)
GetAllTransactions(ctx context.Context) ([]domain.Transaction, error)
GetTransactionsByWallet(ctx context.Context, walletID int64) ([]domain.Transaction, error)
GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error)
UpdateTransferVerification(ctx context.Context, id int64, verified bool) error
GetAllTransactions(ctx context.Context) ([]domain.Transaction, error)
UpdateTransactionVerified(ctx context.Context, id int64, verified bool) error
}

View File

@ -19,15 +19,13 @@ func NewService(transactionStore TransactionStore) *Service {
func (s *Service) CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error) {
return s.transactionStore.CreateTransaction(ctx, transaction)
}
func (s *Service) GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) {
return s.transactionStore.GetAllTransactions(ctx)
}
func (s *Service) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) {
return s.transactionStore.GetTransactionByID(ctx, id)
}
func (s *Service) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error {
return s.transactionStore.UpdateTransferVerification(ctx, id, verified)
func (s *Service) GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) {
return s.transactionStore.GetAllTransactions(ctx)
}
func (s *Service) UpdateTransactionVerified(ctx context.Context, id int64, verified bool) error {
return s.transactionStore.UpdateTransactionVerified(ctx, id, verified)
}

View File

@ -0,0 +1 @@
package transfer

View File

@ -0,0 +1,15 @@
package transfer
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type TransferStore interface {
CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error)
GetAllTransfers(ctx context.Context) ([]domain.Transfer, error)
GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.Transfer, error)
GetTransferByID(ctx context.Context, id int64) (domain.Transfer, error)
UpdateTransferVerification(ctx context.Context, id int64, verified bool) error
}

View File

@ -0,0 +1,33 @@
package transfer
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type Service struct {
transferStore TransferStore
}
func NewService(transferStore TransferStore) *Service {
return &Service{
transferStore: transferStore,
}
}
func (s *Service) CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) {
return s.transferStore.CreateTransfer(ctx, transfer)
}
func (s *Service) GetAllTransfers(ctx context.Context) ([]domain.Transfer, error) {
return s.transferStore.GetAllTransfers(ctx)
}
func (s *Service) GetTransferByID(ctx context.Context, id int64) (domain.Transfer, error) {
return s.transferStore.GetTransferByID(ctx, id)
}
func (s *Service) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error {
return s.transferStore.UpdateTransferVerification(ctx, id, verified)
}

View File

@ -13,6 +13,7 @@ type Service struct {
otpStore OtpStore
smsGateway SmsGateway
emailGateway EmailGateway
}
func NewService(

View File

@ -11,6 +11,7 @@ type WalletStore interface {
CreateCustomerWallet(ctx context.Context, customerWallet domain.CreateCustomerWallet) (domain.CustomerWallet, error)
GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error)
GetAllWallets(ctx context.Context) ([]domain.Wallet, error)
GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error)
GetCustomerWallet(ctx context.Context, customerID int64, companyID int64) (domain.GetCustomerWallet, error)
UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error
UpdateWalletActive(ctx context.Context, id int64, isActive bool) error

View File

@ -2,6 +2,7 @@ package wallet
import (
"context"
"errors"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
@ -16,12 +17,42 @@ func NewService(walletStore WalletStore) *Service {
}
}
var (
ErrBalanceInsufficient = errors.New("wallet balance is insufficient")
)
func (s *Service) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) {
return s.walletStore.CreateWallet(ctx, wallet)
}
func (s *Service) CreateCustomerWallet(ctx context.Context, customerWallet domain.CreateCustomerWallet) (domain.CustomerWallet, error) {
return s.walletStore.CreateCustomerWallet(ctx, customerWallet)
func (s *Service) CreateCustomerWallet(ctx context.Context, customerID int64, companyID int64) (domain.CustomerWallet, error) {
regularWallet, err := s.CreateWallet(ctx, domain.CreateWallet{
IsWithdraw: true,
IsBettable: true,
UserID: customerID,
})
if err != nil {
return domain.CustomerWallet{}, err
}
staticWallet, err := s.CreateWallet(ctx, domain.CreateWallet{
IsWithdraw: false,
IsBettable: true,
UserID: customerID,
})
if err != nil {
return domain.CustomerWallet{}, err
}
return s.walletStore.CreateCustomerWallet(ctx, domain.CreateCustomerWallet{
CustomerID: customerID,
CompanyID: companyID,
RegularWalletID: regularWallet.ID,
StaticWalletID: staticWallet.ID,
})
}
func (s *Service) GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error) {
@ -32,6 +63,10 @@ func (s *Service) GetAllWallets(ctx context.Context) ([]domain.Wallet, error) {
return s.walletStore.GetAllWallets(ctx)
}
func (s *Service) GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error) {
return s.walletStore.GetWalletsByUser(ctx, id)
}
func (s *Service) GetCustomerWallet(ctx context.Context, customerID int64, companyID int64) (domain.GetCustomerWallet, error) {
return s.walletStore.GetCustomerWallet(ctx, customerID, companyID)
}
@ -40,6 +75,28 @@ func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Cu
return s.walletStore.UpdateBalance(ctx, id, balance)
}
func (s *Service) Add(ctx context.Context, id int64, amount domain.Currency) error {
wallet, err := s.GetWalletByID(ctx, id)
if err != nil {
return err
}
return s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount)
}
func (s *Service) Deduct(ctx context.Context, id int64, amount domain.Currency) error {
wallet, err := s.GetWalletByID(ctx, id)
if err != nil {
return err
}
if wallet.Balance < amount {
return ErrBalanceInsufficient
}
return s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount)
}
func (s *Service) UpdateWalletActive(ctx context.Context, id int64, isActive bool) error {
return s.walletStore.UpdateWalletActive(ctx, id, isActive)
}

View File

@ -7,7 +7,9 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
@ -25,6 +27,8 @@ type App struct {
userSvc *user.Service
ticketSvc *ticket.Service
betSvc *bet.Service
walletSvc *wallet.Service
transactionSvc *transaction.Service
validator *customvalidator.CustomValidator
JwtConfig jwtutil.JwtConfig
Logger *slog.Logger
@ -38,7 +42,10 @@ func NewApp(
userSvc *user.Service,
ticketSvc *ticket.Service,
betSvc *bet.Service,
notidicationStore notificationservice.NotificationStore) *App {
walletSvc *wallet.Service,
transactionSvc *transaction.Service,
notidicationStore notificationservice.NotificationStore,
) *App {
app := fiber.New(fiber.Config{
CaseSensitive: true,
DisableHeaderNormalizing: true,
@ -55,6 +62,8 @@ func NewApp(
userSvc: userSvc,
ticketSvc: ticketSvc,
betSvc: betSvc,
walletSvc: walletSvc,
transactionSvc: transactionSvc,
NotidicationStore: notidicationStore,
Logger: logger,
}

View File

@ -136,7 +136,7 @@ func GetAllBet(logger *slog.Logger, betSvc *bet.Service, validator *customvalida
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bets", err, nil)
}
var res []BetRes
var res []BetRes = make([]BetRes, len(bets))
for _, bet := range bets {
res = append(res, convertBet(bet))
}

View File

@ -136,7 +136,7 @@ func GetAllTickets(logger *slog.Logger, ticketSvc *ticket.Service,
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve tickets", err, nil)
}
var res []TicketRes
var res []TicketRes = make([]TicketRes, len(tickets))
for _, ticket := range tickets {
res = append(res, TicketRes{

View File

@ -0,0 +1,232 @@
package handlers
import (
"log/slog"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
"github.com/gofiber/fiber/v2"
)
type TransactionRes struct {
ID int64 `json:"id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
BranchID int64 `json:"branch_id" example:"1"`
CashierID int64 `json:"cashier_id" example:"1"`
BetID int64 `json:"bet_id" example:"1"`
PaymentOption domain.PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
BankCode string `json:"bank_code"`
BeneficiaryName string `json:"beneficiary_name"`
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
ReferenceNumber string `json:"reference_number"`
Verified bool `json:"verified" example:"true"`
}
type CreateTransactionReq struct {
Amount float32 `json:"amount" example:"100.0"`
BranchID int64 `json:"branch_id" example:"1"`
CashierID int64 `json:"cashier_id" example:"1"`
BetID int64 `json:"bet_id" example:"1"`
PaymentOption domain.PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
// Payment Details for bank
BankCode string `json:"bank_code"`
BeneficiaryName string `json:"beneficiary_name"`
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
ReferenceNumber string `json:"reference_number"`
}
func convertTransaction(transaction domain.Transaction) TransactionRes {
return TransactionRes{
ID: transaction.ID,
Amount: transaction.Amount.Float64(),
BranchID: transaction.BranchID,
CashierID: transaction.CashierID,
BetID: transaction.BetID,
PaymentOption: transaction.PaymentOption,
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: transaction.BankCode,
BeneficiaryName: transaction.BeneficiaryName,
AccountName: transaction.AccountName,
AccountNumber: transaction.AccountNumber,
ReferenceNumber: transaction.ReferenceNumber,
Verified: transaction.Verified,
}
}
// CreateTransaction godoc
// @Summary Create a transaction
// @Description Creates a transaction
// @Tags transaction
// @Accept json
// @Produce json
// @Param createBet body CreateTransactionReq true "Creates transaction"
// @Success 200 {object} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction [post]
func CreateTransaction(logger *slog.Logger, transactionSvc *transaction.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req CreateTransactionReq
if err := c.BodyParser(&req); err != nil {
logger.Error("CreateTransactionReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
transaction, err := transactionSvc.CreateTransaction(c.Context(), domain.CreateTransaction{
Amount: domain.Currency(req.Amount),
BranchID: req.BranchID,
CashierID: req.CashierID,
BetID: req.BetID,
PaymentOption: domain.PaymentOption(req.PaymentOption),
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
BankCode: req.BankCode,
BeneficiaryName: req.BeneficiaryName,
AccountName: req.AccountName,
AccountNumber: req.AccountNumber,
ReferenceNumber: req.ReferenceNumber,
})
if err != nil {
logger.Error("CreateTransactionReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
}
res := convertTransaction(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction Created", res, nil)
}
}
// GetAllTransactions godoc
// @Summary Gets all transactions
// @Description Gets all the transactions
// @Tags transaction
// @Accept json
// @Produce json
// @Success 200 {array} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction [get]
func GetAllTransactions(logger *slog.Logger, transactionSvc *transaction.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
transactions, err := transactionSvc.GetAllTransactions(c.Context())
if err != nil {
logger.Error("Failed to get transaction", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transaction", err, nil)
}
var res []TransactionRes = make([]TransactionRes, len(transactions))
for _, transaction := range transactions {
res = append(res, convertTransaction(transaction))
}
return response.WriteJSON(c, fiber.StatusOK, "All Transactions Retrieved", res, nil)
}
}
// GetTransactionByID godoc
// @Summary Gets transaction by id
// @Description Gets a single transaction by id
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Success 200 {object} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction/{id} [get]
func GetTransactionByID(logger *slog.Logger, transactionSvc *transaction.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
transactionID := c.Params("id")
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
logger.Error("Invalid transaction ID", "transactionID", transactionID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid bet ID", err, nil)
}
transaction, err := transactionSvc.GetTransactionByID(c.Context(), id)
if err != nil {
logger.Error("Failed to get transaction by ID", "transactionID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transaction", err, nil)
}
res := convertTransaction(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil)
}
}
type UpdateTransactionVerifiedReq struct {
Verified bool
}
// UpdateTransactionVerified godoc
// @Summary Updates the cashed out field
// @Description Updates the cashed out field
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Param updateCashOut body UpdateTransactionVerifiedReq true "Updates Transaction Verification"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction/{id} [patch]
func UpdateTransactionVerified(logger *slog.Logger, transactionSvc *transaction.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
transactionID := c.Params("id")
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
logger.Error("Invalid transaction ID", "transactionID", transactionID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid bet ID", err, nil)
}
var req UpdateTransactionVerifiedReq
if err := c.BodyParser(&req); err != nil {
logger.Error("UpdateTransactionVerifiedReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
err = transactionSvc.UpdateTransactionVerified(c.Context(), id, req.Verified)
if err != nil {
logger.Error("Failed to update transaction verification", "transactionID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update transaction verification", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Transaction updated successfully", nil, nil)
}
}

View File

@ -7,6 +7,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
@ -137,7 +138,7 @@ type RegisterUserReq struct {
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /user/register [post]
func RegisterUser(logger *slog.Logger, userSvc *user.Service,
func RegisterUser(logger *slog.Logger, userSvc *user.Service, walletSvc *wallet.Service,
validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req RegisterUserReq
@ -170,7 +171,8 @@ func RegisterUser(logger *slog.Logger, userSvc *user.Service,
})
}
user.OtpMedium = medium
if _, err := userSvc.RegisterUser(c.Context(), user); err != nil {
newUser, err := userSvc.RegisterUser(c.Context(), user)
if err != nil {
if errors.Is(err, domain.ErrOtpAlreadyUsed) {
return response.WriteJSON(c, fiber.StatusBadRequest, "Otp already used", nil, nil)
}
@ -188,6 +190,15 @@ func RegisterUser(logger *slog.Logger, userSvc *user.Service,
"error": "Internal server error",
})
}
// TODO: Integrate company when we move to multi-vendor system
_, err = walletSvc.CreateCustomerWallet(c.Context(), newUser.ID, 0)
if err != nil {
logger.Error("RegisterUser failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create customer wallet for user", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Registration successful", nil, nil)
}
}

View File

@ -0,0 +1,148 @@
package handlers
import (
"log/slog"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
"github.com/gofiber/fiber/v2"
)
type WalletRes struct {
ID int64 `json:"id" example:"1"`
Balance float32 `json:"amount" example:"100.0"`
IsWithdraw bool `json:"is_withdraw" example:"true"`
IsBettable bool `json:"is_bettable" example:"true"`
IsActive bool `json:"is_active" example:"true"`
UserID int64 `json:"user_id" example:"1"`
UpdatedAt time.Time `json:"updated_at"`
CreatedAt time.Time `json:"created_at"`
}
// GetWalletByID godoc
// @Summary Get wallet by ID
// @Description Retrieve wallet details by wallet ID
// @Tags wallet
// @Accept json
// @Produce json
// @Param id path int true "Wallet ID"
// @Success 200 {object} WalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /wallet/{id} [get]
func GetWalletByID(logger *slog.Logger, walletSvc *wallet.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
walletID := c.Params("id")
id, err := strconv.ParseInt(walletID, 10, 64)
if err != nil {
logger.Error("Invalid wallet ID", "walletID", walletID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
}
wallet, err := walletSvc.GetWalletByID(c.Context(), id)
if err != nil {
logger.Error("Failed to get wallet by ID", "walletID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve wallet", err, nil)
}
res := WalletRes{
ID: wallet.ID,
Balance: wallet.Balance.Float64(),
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
IsActive: wallet.IsActive,
UserID: wallet.UserID,
UpdatedAt: wallet.UpdatedAt,
CreatedAt: wallet.CreatedAt,
}
return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil)
}
}
// GetAllWallets godoc
// @Summary Get all wallets
// @Description Retrieve all wallets
// @Tags wallet
// @Accept json
// @Produce json
// @Success 200 {array} WalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /wallet [get]
func GetAllWallets(logger *slog.Logger, walletSvc *wallet.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
wallets, err := walletSvc.GetAllWallets(c.Context())
if err != nil {
logger.Error("Failed to get wallets", "error", err)
}
var res []WalletRes = make([]WalletRes, len(wallets))
for _, wallet := range wallets {
res = append(res, WalletRes{
ID: wallet.ID,
Balance: wallet.Balance.Float64(),
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
IsActive: wallet.IsActive,
UserID: wallet.UserID,
UpdatedAt: wallet.UpdatedAt,
CreatedAt: wallet.CreatedAt,
})
}
return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil)
}
}
type UpdateWalletActiveReq struct {
IsActive bool
}
// UpdateWalletActive godoc
// @Summary Activate and Deactivate Wallet
// @Description Can activate and deactivate wallet
// @Tags wallet
// @Accept json
// @Produce json
// @Param id path int true "Wallet ID"
// @Param updateCashOut body UpdateWalletActiveReq true "Update Wallet Active"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /wallet/{id} [patch]
func UpdateWalletActive(logger *slog.Logger, walletSvc *wallet.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
walletID := c.Params("id")
id, err := strconv.ParseInt(walletID, 10, 64)
if err != nil {
logger.Error("Invalid bet ID", "walletID", walletID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
}
var req UpdateWalletActiveReq
if err := c.BodyParser(&req); err != nil {
logger.Error("UpdateWalletActiveReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
err = walletSvc.UpdateWalletActive(c.Context(), id, req.IsActive)
if err != nil {
logger.Error("Failed to update", "walletID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update wallet", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Wallet successfully updated", nil, nil)
}
}

View File

@ -22,7 +22,7 @@ func (a *App) initAppRoutes() {
})
a.fiber.Post("/user/resetPassword", handlers.ResetPassword(a.logger, a.userSvc, a.validator))
a.fiber.Post("/user/sendResetCode", handlers.SendResetCode(a.logger, a.userSvc, a.validator))
a.fiber.Post("/user/register", handlers.RegisterUser(a.logger, a.userSvc, a.validator))
a.fiber.Post("/user/register", handlers.RegisterUser(a.logger, a.userSvc, a.walletSvc, a.validator))
a.fiber.Post("/user/sendRegisterCode", handlers.SendRegisterCode(a.logger, a.userSvc, a.validator))
a.fiber.Post("/user/checkPhoneEmailExist", handlers.CheckPhoneEmailExist(a.logger, a.userSvc, a.validator))
a.fiber.Get("/user/profile", a.authMiddleware, handlers.UserProfile(a.logger, a.userSvc))
@ -37,10 +37,21 @@ func (a *App) initAppRoutes() {
// Bet
a.fiber.Post("/bet", handlers.CreateBet(a.logger, a.betSvc, a.validator))
a.fiber.Get("/bet", handlers.GetAllBet(a.logger, a.betSvc, a.validator))
a.fiber.Get("/bet/:id", handlers.GetAllBet(a.logger, a.betSvc, a.validator))
a.fiber.Get("/bet/:id", handlers.GetBetByID(a.logger, a.betSvc, a.validator))
a.fiber.Patch("/bet/:id", handlers.UpdateCashOut(a.logger, a.betSvc, a.validator))
a.fiber.Delete("/bet/:id", handlers.DeleteBet(a.logger, a.betSvc, a.validator))
// Wallet
a.fiber.Get("/wallet", handlers.GetAllWallets(a.logger, a.walletSvc, a.validator))
a.fiber.Get("/wallet/:id", handlers.GetWalletByID(a.logger, a.walletSvc, a.validator))
a.fiber.Put("/wallet/:id", handlers.UpdateWalletActive(a.logger, a.walletSvc, a.validator))
// Transactions /transactions
a.fiber.Post("/transaction", handlers.CreateTransaction(a.logger, a.transactionSvc, a.validator))
a.fiber.Get("/transaction", handlers.GetAllTransactions(a.logger, a.transactionSvc, a.validator))
a.fiber.Get("/transaction/:id", handlers.GetTransactionByID(a.logger, a.transactionSvc, a.validator))
a.fiber.Patch("/transaction/:id", handlers.UpdateTransactionVerified(a.logger, a.transactionSvc, a.validator))
a.fiber.Get("/ws/:recipientID", handlers.ConnectSocket(*a.logger, a.NotidicationStore, a.validator))
}