transfer and branch service implementation

This commit is contained in:
Samuel Tariku 2025-04-08 23:18:53 +03:00
parent cf0e4cba8b
commit c9df0b3303
39 changed files with 2886 additions and 372 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
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"
@ -59,8 +60,9 @@ func main() {
userSvc := user.NewService(store, store, mockSms, mockemail)
ticketSvc := ticket.NewService(store)
betSvc := bet.NewService(store)
walletSvc := wallet.NewService(store)
walletSvc := wallet.NewService(store, store)
transactionSvc := transaction.NewService(store)
branchSvc := branch.NewService(store)
notificationRepo := repository.NewNotificationRepository(store)
notificationSvc := notificationservice.New(notificationRepo, logger, cfg)
@ -68,7 +70,7 @@ func main() {
app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{
JwtAccessKey: cfg.JwtKey,
JwtAccessExpiry: cfg.AccessExpiry,
}, userSvc, ticketSvc, betSvc, walletSvc, transactionSvc, notificationSvc,
}, userSvc, ticketSvc, betSvc, walletSvc, transactionSvc, branchSvc, notificationSvc,
)
logger.Info("Starting server", "port", cfg.Port)

View File

@ -80,4 +80,6 @@ DROP TABLE IF EXISTS wallets;
DROP TABLE IF EXISTS wallet_transfer;
DROP TABLE IF EXISTS transactions;
DROP TABLE IF EXISTS customer_wallets;
DROP TABLE IF EXISTS branches;

View File

@ -78,6 +78,7 @@ CREATE TABLE IF NOT EXISTS wallets (
balance BIGINT NOT NULL DEFAULT 0,
is_withdraw BOOLEAN NOT NULL,
is_bettable BOOLEAN NOT NULL,
is_transferable BOOLEAN NOT NULL,
user_id BIGINT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
@ -99,10 +100,12 @@ CREATE TABLE IF NOT EXISTS customer_wallets (
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,
type VARCHAR(255) NOT NULL,
receiver_wallet_id BIGINT NOT NULL,
sender_wallet_id BIGINT,
cashier_id BIGINT,
verified BOOLEAN NOT NULL DEFAULT false,
payment_method INT NOT NULL,
payment_method VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -4,9 +4,12 @@ INSERT INTO transactions (amount, branch_id, cashier_id, bet_id, payment_option,
-- name: GetAllTransactions :many
SELECT * FROM transactions;
-- name: GetTransactionByID :one
-- name: GetTransactionByID :one
SELECT * FROM transactions WHERE id = $1;
-- name: GetTransactionByBranch :many
SELECT * FROM transactions WHERE branch_id = $1;
-- name: UpdateTransactionVerified :exec
UPDATE transactions SET verified = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1;

View File

@ -1,11 +1,11 @@
-- name: CreateTransfer :one
INSERT INTO wallet_transfer (amount, wallet_transfer, wallet_id) VALUES ($1, $2, $3) RETURNING *;
INSERT INTO wallet_transfer (amount, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, payment_method) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *;
-- name: GetAllTransfers :many
SELECT * FROM wallet_transfer;
-- name: GetTransfersByWallet :many
SELECT * FROM wallet_transfer WHERE wallet_id = $1;
SELECT * FROM wallet_transfer WHERE receiver_wallet_id = $1 OR sender_wallet_id = $1;
-- name: GetTransferByID :one
SELECT * FROM wallet_transfer WHERE id = $1;

View File

@ -1,5 +1,5 @@
-- name: CreateWallet :one
INSERT INTO wallets (is_withdraw, is_bettable, user_id) VALUES ($1, $2, $3) RETURNING *;
INSERT INTO wallets (is_withdraw, is_bettable, is_transferable, user_id) VALUES ($1, $2, $3, $4) RETURNING *;
-- name: CreateCustomerWallet :one
INSERT INTO customer_wallets (customer_id, company_id, regular_wallet_id, static_wallet_id) VALUES ($1, $2, $3, $4) RETURNING *;

View File

@ -399,6 +399,509 @@ const docTemplate = `{
}
}
},
"/branch": {
"get": {
"description": "Gets all branches",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets all branches",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": {
"description": "Creates a branch",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Create a branch",
"parameters": [
{
"description": "Creates branch",
"name": "createBranch",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateBranchReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BranchRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/branch/{id}": {
"get": {
"description": "Gets a single branch by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branch by id",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"put": {
"description": "Updates a branch",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Updates a branch",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update Branch",
"name": "updateBranch",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateBranchReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BranchRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"delete": {
"description": "Delete the branch",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Delete the branch",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
}
],
"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"
}
}
}
}
},
"/branch/{id}/operation": {
"get": {
"description": "Gets branch operations",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branch operations",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchOperationRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/branch/{id}/operation/{opID}": {
"delete": {
"description": "Delete the branch operation",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Delete the branch operation",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Branch Operation ID",
"name": "opID",
"in": "path",
"required": true
}
],
"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"
}
}
}
}
},
"/company/{id}/branch": {
"get": {
"description": "Gets branches by company id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branches by company id",
"parameters": [
{
"type": "integer",
"description": "Company ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/manager/{id}/branch": {
"get": {
"description": "Gets a branches by manager id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branches by manager id",
"parameters": [
{
"type": "integer",
"description": "User ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/operation": {
"post": {
"description": "Creates a operation",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Create a operation",
"parameters": [
{
"description": "Creates operation",
"name": "createBranchOperation",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateBranchOperationReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BranchOperationRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/supportedOperation": {
"post": {
"description": "Creates a supported operation",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Create a supported operation",
"parameters": [
{
"description": "Creates supported operation",
"name": "createSupportedOperation",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateSupportedOperationReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.SupportedOperationRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/ticket": {
"get": {
"description": "Retrieve all tickets",
@ -702,6 +1205,52 @@ const docTemplate = `{
}
}
},
"/transfer/wallet": {
"post": {
"description": "Create a transfer to wallet",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transfer"
],
"summary": "Create a transfer to wallet",
"parameters": [
{
"description": "Create Transfer",
"name": "transferToWallet",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateTransferReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.TransferRes"
}
},
"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",
@ -1257,6 +1806,85 @@ const docTemplate = `{
}
}
},
"handlers.BranchDetailRes": {
"type": "object",
"properties": {
"branch_manager_id": {
"type": "integer",
"example": 1
},
"company_id": {
"type": "integer",
"example": 1
},
"is_self_owned": {
"type": "boolean",
"example": false
},
"location": {
"type": "string",
"example": "Addis Ababa"
},
"manager_name": {
"type": "string",
"example": "John Smith"
},
"manager_phone_number": {
"type": "string",
"example": "0911111111"
},
"name": {
"type": "string",
"example": "4-kilo Branch"
},
"wallet_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.BranchOperationRes": {
"type": "object",
"properties": {
"description": {
"type": "string",
"example": "Betting on sport events"
},
"name": {
"type": "string",
"example": "SportsBook"
}
}
},
"handlers.BranchRes": {
"type": "object",
"properties": {
"branch_manager_id": {
"type": "integer",
"example": 1
},
"company_id": {
"type": "integer",
"example": 1
},
"is_self_owned": {
"type": "boolean",
"example": false
},
"location": {
"type": "string",
"example": "Addis Ababa"
},
"name": {
"type": "string",
"example": "4-kilo Branch"
},
"wallet_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.CheckPhoneEmailExistReq": {
"type": "object",
"properties": {
@ -1320,6 +1948,57 @@ const docTemplate = `{
}
}
},
"handlers.CreateBranchOperationReq": {
"type": "object",
"properties": {
"branch_id": {
"type": "integer",
"example": 1
},
"operation_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.CreateBranchReq": {
"type": "object",
"properties": {
"branch_manager_id": {
"type": "integer",
"example": 1
},
"company_id": {
"type": "integer",
"example": 1
},
"is_self_owned": {
"type": "boolean",
"example": false
},
"location": {
"type": "string",
"example": "Addis Ababa"
},
"name": {
"type": "string",
"example": "4-kilo Branch"
}
}
},
"handlers.CreateSupportedOperationReq": {
"type": "object",
"properties": {
"description": {
"type": "string",
"example": "Betting on sport events"
},
"name": {
"type": "string",
"example": "SportsBook"
}
}
},
"handlers.CreateTicketReq": {
"type": "object",
"properties": {
@ -1401,6 +2080,9 @@ const docTemplate = `{
}
}
},
"handlers.CreateTransferReq": {
"type": "object"
},
"handlers.CustomerWalletRes": {
"type": "object",
"properties": {
@ -1520,6 +2202,23 @@ const docTemplate = `{
}
}
},
"handlers.SupportedOperationRes": {
"type": "object",
"properties": {
"description": {
"type": "string",
"example": "Betting on sport events"
},
"id": {
"type": "integer",
"example": 1
},
"name": {
"type": "string",
"example": "SportsBook"
}
}
},
"handlers.TicketRes": {
"type": "object",
"properties": {
@ -1603,6 +2302,51 @@ const docTemplate = `{
}
}
},
"handlers.TransferRes": {
"type": "object",
"properties": {
"amount": {
"type": "number",
"example": 100
},
"cashier_id": {
"type": "integer",
"example": 789
},
"created_at": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"id": {
"type": "integer",
"example": 1
},
"payment_method": {
"type": "string",
"example": "bank"
},
"receiver_wallet_id": {
"type": "integer",
"example": 1
},
"sender_wallet_id": {
"type": "integer",
"example": 1
},
"type": {
"type": "string",
"example": "transfer"
},
"updated_at": {
"type": "string",
"example": "2025-04-08T12:30:00Z"
},
"verified": {
"type": "boolean",
"example": true
}
}
},
"handlers.UpdateCashOutReq": {
"type": "object",
"properties": {
@ -1690,6 +2434,10 @@ const docTemplate = `{
"type": "boolean",
"example": true
},
"is_transferable": {
"type": "boolean",
"example": true
},
"is_withdraw": {
"type": "boolean",
"example": true

View File

@ -391,6 +391,509 @@
}
}
},
"/branch": {
"get": {
"description": "Gets all branches",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets all branches",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": {
"description": "Creates a branch",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Create a branch",
"parameters": [
{
"description": "Creates branch",
"name": "createBranch",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateBranchReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BranchRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/branch/{id}": {
"get": {
"description": "Gets a single branch by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branch by id",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"put": {
"description": "Updates a branch",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Updates a branch",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update Branch",
"name": "updateBranch",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateBranchReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BranchRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"delete": {
"description": "Delete the branch",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Delete the branch",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
}
],
"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"
}
}
}
}
},
"/branch/{id}/operation": {
"get": {
"description": "Gets branch operations",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branch operations",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchOperationRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/branch/{id}/operation/{opID}": {
"delete": {
"description": "Delete the branch operation",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Delete the branch operation",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Branch Operation ID",
"name": "opID",
"in": "path",
"required": true
}
],
"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"
}
}
}
}
},
"/company/{id}/branch": {
"get": {
"description": "Gets branches by company id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branches by company id",
"parameters": [
{
"type": "integer",
"description": "Company ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/manager/{id}/branch": {
"get": {
"description": "Gets a branches by manager id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branches by manager id",
"parameters": [
{
"type": "integer",
"description": "User ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/operation": {
"post": {
"description": "Creates a operation",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Create a operation",
"parameters": [
{
"description": "Creates operation",
"name": "createBranchOperation",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateBranchOperationReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BranchOperationRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/supportedOperation": {
"post": {
"description": "Creates a supported operation",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Create a supported operation",
"parameters": [
{
"description": "Creates supported operation",
"name": "createSupportedOperation",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateSupportedOperationReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.SupportedOperationRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/ticket": {
"get": {
"description": "Retrieve all tickets",
@ -694,6 +1197,52 @@
}
}
},
"/transfer/wallet": {
"post": {
"description": "Create a transfer to wallet",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"transfer"
],
"summary": "Create a transfer to wallet",
"parameters": [
{
"description": "Create Transfer",
"name": "transferToWallet",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateTransferReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.TransferRes"
}
},
"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",
@ -1249,6 +1798,85 @@
}
}
},
"handlers.BranchDetailRes": {
"type": "object",
"properties": {
"branch_manager_id": {
"type": "integer",
"example": 1
},
"company_id": {
"type": "integer",
"example": 1
},
"is_self_owned": {
"type": "boolean",
"example": false
},
"location": {
"type": "string",
"example": "Addis Ababa"
},
"manager_name": {
"type": "string",
"example": "John Smith"
},
"manager_phone_number": {
"type": "string",
"example": "0911111111"
},
"name": {
"type": "string",
"example": "4-kilo Branch"
},
"wallet_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.BranchOperationRes": {
"type": "object",
"properties": {
"description": {
"type": "string",
"example": "Betting on sport events"
},
"name": {
"type": "string",
"example": "SportsBook"
}
}
},
"handlers.BranchRes": {
"type": "object",
"properties": {
"branch_manager_id": {
"type": "integer",
"example": 1
},
"company_id": {
"type": "integer",
"example": 1
},
"is_self_owned": {
"type": "boolean",
"example": false
},
"location": {
"type": "string",
"example": "Addis Ababa"
},
"name": {
"type": "string",
"example": "4-kilo Branch"
},
"wallet_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.CheckPhoneEmailExistReq": {
"type": "object",
"properties": {
@ -1312,6 +1940,57 @@
}
}
},
"handlers.CreateBranchOperationReq": {
"type": "object",
"properties": {
"branch_id": {
"type": "integer",
"example": 1
},
"operation_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.CreateBranchReq": {
"type": "object",
"properties": {
"branch_manager_id": {
"type": "integer",
"example": 1
},
"company_id": {
"type": "integer",
"example": 1
},
"is_self_owned": {
"type": "boolean",
"example": false
},
"location": {
"type": "string",
"example": "Addis Ababa"
},
"name": {
"type": "string",
"example": "4-kilo Branch"
}
}
},
"handlers.CreateSupportedOperationReq": {
"type": "object",
"properties": {
"description": {
"type": "string",
"example": "Betting on sport events"
},
"name": {
"type": "string",
"example": "SportsBook"
}
}
},
"handlers.CreateTicketReq": {
"type": "object",
"properties": {
@ -1393,6 +2072,9 @@
}
}
},
"handlers.CreateTransferReq": {
"type": "object"
},
"handlers.CustomerWalletRes": {
"type": "object",
"properties": {
@ -1512,6 +2194,23 @@
}
}
},
"handlers.SupportedOperationRes": {
"type": "object",
"properties": {
"description": {
"type": "string",
"example": "Betting on sport events"
},
"id": {
"type": "integer",
"example": 1
},
"name": {
"type": "string",
"example": "SportsBook"
}
}
},
"handlers.TicketRes": {
"type": "object",
"properties": {
@ -1595,6 +2294,51 @@
}
}
},
"handlers.TransferRes": {
"type": "object",
"properties": {
"amount": {
"type": "number",
"example": 100
},
"cashier_id": {
"type": "integer",
"example": 789
},
"created_at": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"id": {
"type": "integer",
"example": 1
},
"payment_method": {
"type": "string",
"example": "bank"
},
"receiver_wallet_id": {
"type": "integer",
"example": 1
},
"sender_wallet_id": {
"type": "integer",
"example": 1
},
"type": {
"type": "string",
"example": "transfer"
},
"updated_at": {
"type": "string",
"example": "2025-04-08T12:30:00Z"
},
"verified": {
"type": "boolean",
"example": true
}
}
},
"handlers.UpdateCashOutReq": {
"type": "object",
"properties": {
@ -1682,6 +2426,10 @@
"type": "boolean",
"example": true
},
"is_transferable": {
"type": "boolean",
"example": true
},
"is_withdraw": {
"type": "boolean",
"example": true

View File

@ -74,6 +74,63 @@ definitions:
example: 2
type: integer
type: object
handlers.BranchDetailRes:
properties:
branch_manager_id:
example: 1
type: integer
company_id:
example: 1
type: integer
is_self_owned:
example: false
type: boolean
location:
example: Addis Ababa
type: string
manager_name:
example: John Smith
type: string
manager_phone_number:
example: "0911111111"
type: string
name:
example: 4-kilo Branch
type: string
wallet_id:
example: 1
type: integer
type: object
handlers.BranchOperationRes:
properties:
description:
example: Betting on sport events
type: string
name:
example: SportsBook
type: string
type: object
handlers.BranchRes:
properties:
branch_manager_id:
example: 1
type: integer
company_id:
example: 1
type: integer
is_self_owned:
example: false
type: boolean
location:
example: Addis Ababa
type: string
name:
example: 4-kilo Branch
type: string
wallet_id:
example: 1
type: integer
type: object
handlers.CheckPhoneEmailExistReq:
properties:
email:
@ -116,6 +173,42 @@ definitions:
example: 4.22
type: number
type: object
handlers.CreateBranchOperationReq:
properties:
branch_id:
example: 1
type: integer
operation_id:
example: 1
type: integer
type: object
handlers.CreateBranchReq:
properties:
branch_manager_id:
example: 1
type: integer
company_id:
example: 1
type: integer
is_self_owned:
example: false
type: boolean
location:
example: Addis Ababa
type: string
name:
example: 4-kilo Branch
type: string
type: object
handlers.CreateSupportedOperationReq:
properties:
description:
example: Betting on sport events
type: string
name:
example: SportsBook
type: string
type: object
handlers.CreateTicketReq:
properties:
amount:
@ -171,6 +264,8 @@ definitions:
reference_number:
type: string
type: object
handlers.CreateTransferReq:
type: object
handlers.CustomerWalletRes:
properties:
company_id:
@ -255,6 +350,18 @@ definitions:
phoneNumber:
type: string
type: object
handlers.SupportedOperationRes:
properties:
description:
example: Betting on sport events
type: string
id:
example: 1
type: integer
name:
example: SportsBook
type: string
type: object
handlers.TicketRes:
properties:
amount:
@ -312,6 +419,39 @@ definitions:
example: true
type: boolean
type: object
handlers.TransferRes:
properties:
amount:
example: 100
type: number
cashier_id:
example: 789
type: integer
created_at:
example: "2025-04-08T12:00:00Z"
type: string
id:
example: 1
type: integer
payment_method:
example: bank
type: string
receiver_wallet_id:
example: 1
type: integer
sender_wallet_id:
example: 1
type: integer
type:
example: transfer
type: string
updated_at:
example: "2025-04-08T12:30:00Z"
type: string
verified:
example: true
type: boolean
type: object
handlers.UpdateCashOutReq:
properties:
cashedOut:
@ -370,6 +510,9 @@ definitions:
is_bettable:
example: true
type: boolean
is_transferable:
example: true
type: boolean
is_withdraw:
example: true
type: boolean
@ -689,6 +832,338 @@ paths:
summary: Updates the cashed out field
tags:
- bet
/branch:
get:
consumes:
- application/json
description: Gets all branches
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.BranchDetailRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets all branches
tags:
- branch
post:
consumes:
- application/json
description: Creates a branch
parameters:
- description: Creates branch
in: body
name: createBranch
required: true
schema:
$ref: '#/definitions/handlers.CreateBranchReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.BranchRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Create a branch
tags:
- branch
/branch/{id}:
delete:
consumes:
- application/json
description: Delete the branch
parameters:
- description: Branch 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 the branch
tags:
- branch
get:
consumes:
- application/json
description: Gets a single branch by id
parameters:
- description: Branch ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.BranchDetailRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets branch by id
tags:
- branch
put:
consumes:
- application/json
description: Updates a branch
parameters:
- description: Branch ID
in: path
name: id
required: true
type: integer
- description: Update Branch
in: body
name: updateBranch
required: true
schema:
$ref: '#/definitions/handlers.CreateBranchReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.BranchRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Updates a branch
tags:
- branch
/branch/{id}/operation:
get:
consumes:
- application/json
description: Gets branch operations
parameters:
- description: Branch ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.BranchOperationRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets branch operations
tags:
- branch
/branch/{id}/operation/{opID}:
delete:
consumes:
- application/json
description: Delete the branch operation
parameters:
- description: Branch ID
in: path
name: id
required: true
type: integer
- description: Branch Operation ID
in: path
name: opID
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 the branch operation
tags:
- branch
/company/{id}/branch:
get:
consumes:
- application/json
description: Gets branches by company id
parameters:
- description: Company ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.BranchDetailRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets branches by company id
tags:
- branch
/manager/{id}/branch:
get:
consumes:
- application/json
description: Gets a branches by manager id
parameters:
- description: User ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.BranchDetailRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets branches by manager id
tags:
- branch
/operation:
post:
consumes:
- application/json
description: Creates a operation
parameters:
- description: Creates operation
in: body
name: createBranchOperation
required: true
schema:
$ref: '#/definitions/handlers.CreateBranchOperationReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.BranchOperationRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Create a operation
tags:
- branch
/supportedOperation:
post:
consumes:
- application/json
description: Creates a supported operation
parameters:
- description: Creates supported operation
in: body
name: createSupportedOperation
required: true
schema:
$ref: '#/definitions/handlers.CreateSupportedOperationReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.SupportedOperationRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Create a supported operation
tags:
- branch
/ticket:
get:
consumes:
@ -889,6 +1364,36 @@ paths:
summary: Updates the cashed out field
tags:
- transaction
/transfer/wallet:
post:
consumes:
- application/json
description: Create a transfer to wallet
parameters:
- description: Create Transfer
in: body
name: transferToWallet
required: true
schema:
$ref: '#/definitions/handlers.CreateTransferReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.TransferRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Create a transfer to wallet
tags:
- transfer
/user/checkPhoneEmailExist:
post:
consumes:

View File

@ -155,23 +155,26 @@ type User struct {
}
type Wallet struct {
ID int64
Balance int64
IsWithdraw bool
IsBettable bool
UserID int64
IsActive bool
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type WalletTransfer struct {
ID int64
Amount int64
WalletTransfer string
WalletID int64
Verified bool
PaymentMethod int32
Balance int64
IsWithdraw bool
IsBettable bool
IsTransferable bool
UserID int64
IsActive bool
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type WalletTransfer struct {
ID int64
Amount int64
Type string
ReceiverWalletID int64
SenderWalletID pgtype.Int8
CashierID pgtype.Int8
Verified bool
PaymentMethod string
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}

View File

@ -106,6 +106,47 @@ func (q *Queries) GetAllTransactions(ctx context.Context) ([]Transaction, error)
return items, nil
}
const GetTransactionByBranch = `-- name: GetTransactionByBranch :many
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 branch_id = $1
`
func (q *Queries) GetTransactionByBranch(ctx context.Context, branchID int64) ([]Transaction, error) {
rows, err := q.db.Query(ctx, GetTransactionByBranch, branchID)
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.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,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetTransactionByID = `-- name: GetTransactionByID :one
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
`

View File

@ -7,26 +7,42 @@ package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
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, payment_method, created_at, updated_at
INSERT INTO wallet_transfer (amount, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, payment_method) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, amount, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, payment_method, created_at, updated_at
`
type CreateTransferParams struct {
Amount int64
WalletTransfer string
WalletID int64
Amount int64
Type string
ReceiverWalletID int64
SenderWalletID pgtype.Int8
CashierID pgtype.Int8
Verified bool
PaymentMethod string
}
func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams) (WalletTransfer, error) {
row := q.db.QueryRow(ctx, CreateTransfer, arg.Amount, arg.WalletTransfer, arg.WalletID)
row := q.db.QueryRow(ctx, CreateTransfer,
arg.Amount,
arg.Type,
arg.ReceiverWalletID,
arg.SenderWalletID,
arg.CashierID,
arg.Verified,
arg.PaymentMethod,
)
var i WalletTransfer
err := row.Scan(
&i.ID,
&i.Amount,
&i.WalletTransfer,
&i.WalletID,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
&i.CashierID,
&i.Verified,
&i.PaymentMethod,
&i.CreatedAt,
@ -36,7 +52,7 @@ func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams)
}
const GetAllTransfers = `-- name: GetAllTransfers :many
SELECT id, amount, wallet_transfer, wallet_id, verified, payment_method, created_at, updated_at FROM wallet_transfer
SELECT id, amount, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, payment_method, created_at, updated_at FROM wallet_transfer
`
func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error) {
@ -51,8 +67,10 @@ func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error)
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.WalletTransfer,
&i.WalletID,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
&i.CashierID,
&i.Verified,
&i.PaymentMethod,
&i.CreatedAt,
@ -69,7 +87,7 @@ func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error)
}
const GetTransferByID = `-- name: GetTransferByID :one
SELECT id, amount, wallet_transfer, wallet_id, verified, payment_method, created_at, updated_at FROM wallet_transfer WHERE id = $1
SELECT id, amount, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, payment_method, created_at, updated_at FROM wallet_transfer WHERE id = $1
`
func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer, error) {
@ -78,8 +96,10 @@ func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer
err := row.Scan(
&i.ID,
&i.Amount,
&i.WalletTransfer,
&i.WalletID,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
&i.CashierID,
&i.Verified,
&i.PaymentMethod,
&i.CreatedAt,
@ -89,11 +109,11 @@ func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer
}
const GetTransfersByWallet = `-- name: GetTransfersByWallet :many
SELECT id, amount, wallet_transfer, wallet_id, verified, payment_method, created_at, updated_at FROM wallet_transfer WHERE wallet_id = $1
SELECT id, amount, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, payment_method, created_at, updated_at FROM wallet_transfer WHERE receiver_wallet_id = $1 OR sender_wallet_id = $1
`
func (q *Queries) GetTransfersByWallet(ctx context.Context, walletID int64) ([]WalletTransfer, error) {
rows, err := q.db.Query(ctx, GetTransfersByWallet, walletID)
func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID int64) ([]WalletTransfer, error) {
rows, err := q.db.Query(ctx, GetTransfersByWallet, receiverWalletID)
if err != nil {
return nil, err
}
@ -104,8 +124,10 @@ func (q *Queries) GetTransfersByWallet(ctx context.Context, walletID int64) ([]W
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.WalletTransfer,
&i.WalletID,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
&i.CashierID,
&i.Verified,
&i.PaymentMethod,
&i.CreatedAt,

View File

@ -43,23 +43,30 @@ func (q *Queries) CreateCustomerWallet(ctx context.Context, arg CreateCustomerWa
}
const CreateWallet = `-- name: CreateWallet :one
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
INSERT INTO wallets (is_withdraw, is_bettable, is_transferable, user_id) VALUES ($1, $2, $3, $4) RETURNING id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at
`
type CreateWalletParams struct {
IsWithdraw bool
IsBettable bool
UserID int64
IsWithdraw bool
IsBettable bool
IsTransferable bool
UserID int64
}
func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wallet, error) {
row := q.db.QueryRow(ctx, CreateWallet, arg.IsWithdraw, arg.IsBettable, arg.UserID)
row := q.db.QueryRow(ctx, CreateWallet,
arg.IsWithdraw,
arg.IsBettable,
arg.IsTransferable,
arg.UserID,
)
var i Wallet
err := row.Scan(
&i.ID,
&i.Balance,
&i.IsWithdraw,
&i.IsBettable,
&i.IsTransferable,
&i.UserID,
&i.IsActive,
&i.CreatedAt,
@ -69,7 +76,7 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal
}
const GetAllWallets = `-- name: GetAllWallets :many
SELECT id, balance, is_withdraw, is_bettable, user_id, is_active, created_at, updated_at FROM wallets
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at FROM wallets
`
func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
@ -86,6 +93,7 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
&i.Balance,
&i.IsWithdraw,
&i.IsBettable,
&i.IsTransferable,
&i.UserID,
&i.IsActive,
&i.CreatedAt,
@ -156,7 +164,7 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, arg GetCustomerWalletPa
}
const GetWalletByID = `-- name: GetWalletByID :one
SELECT id, balance, is_withdraw, is_bettable, user_id, is_active, created_at, updated_at FROM wallets WHERE id = $1
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at FROM wallets WHERE id = $1
`
func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
@ -167,6 +175,7 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
&i.Balance,
&i.IsWithdraw,
&i.IsBettable,
&i.IsTransferable,
&i.UserID,
&i.IsActive,
&i.CreatedAt,
@ -176,7 +185,7 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
}
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
SELECT id, balance, is_withdraw, is_bettable, is_transferable, 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) {
@ -193,6 +202,7 @@ func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet
&i.Balance,
&i.IsWithdraw,
&i.IsBettable,
&i.IsTransferable,
&i.UserID,
&i.IsActive,
&i.CreatedAt,

View File

@ -43,6 +43,14 @@ type CreateBranch struct {
IsSelfOwned bool
}
type UpdateBranch struct {
Name string
Location string
BranchManagerID int64
CompanyID int64
IsSelfOwned bool
}
type CreateSupportedOperation struct {
Name string
Description string

View File

@ -7,34 +7,42 @@ type TransferType string
const (
DEPOSIT TransferType = "deposit"
WITHDRAW TransferType = "withdraw"
WALLET TransferType = "wallet"
)
type PaymentMethod int
type PaymentMethod string
const (
TRANSFER_CASH PaymentMethod = iota + 1
TRANSFER_BANK
TRANSFER_CHAPA
TRANSFER_ARIFPAY
TRANSFER_SANTIM
TRANSFER_ADDISPAY
TRANSFER_OTHER
TRANSFER_CASH PaymentMethod = "cash"
TRANSFER_BANK PaymentMethod = "bank"
TRANSFER_CHAPA PaymentMethod = "chapa"
TRANSFER_ARIFPAY PaymentMethod = "arifpay"
TRANSFER_SANTIMPAY PaymentMethod = "santimpay"
TRANSFER_ADDISPAY PaymentMethod = "addispay"
TRANSFER_OTHER PaymentMethod = "other"
)
// There is always a receiving wallet id
// There is a sender wallet id only if wallet transfer type
type Transfer struct {
ID int64
Amount Currency
Verified bool
WalletID int64
Type TransferType
PaymentMethod PaymentMethod
CreatedAt time.Time
UpdatedAt time.Time
ID int64
Amount Currency
Verified bool
Type TransferType
PaymentMethod PaymentMethod
ReceiverWalletID int64
SenderWalletID ValidInt64
CashierID ValidInt64
CreatedAt time.Time
UpdatedAt time.Time
}
type CreateTransfer struct {
Amount Currency
Verified bool
WalletID int64
Type TransferType
Amount Currency
Verified bool
ReceiverWalletID int64
SenderWalletID ValidInt64
CashierID ValidInt64
Type TransferType
PaymentMethod PaymentMethod
}

View File

@ -7,6 +7,7 @@ type Wallet struct {
Balance Currency
IsWithdraw bool
IsBettable bool
IsTransferable bool
IsActive bool
UserID int64
UpdatedAt time.Time
@ -36,6 +37,7 @@ type GetCustomerWallet struct {
type CreateWallet struct {
IsWithdraw bool
IsBettable bool
IsTransferable bool
UserID int64
}

View File

@ -136,7 +136,7 @@ func (s *Store) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, erro
return branches, nil
}
func (s *Store) UpdateBranch(ctx context.Context, id int64, branch domain.CreateBranch) (domain.Branch, error) {
func (s *Store) UpdateBranch(ctx context.Context, id int64, branch domain.UpdateBranch) (domain.Branch, error) {
dbBranch, err := s.queries.UpdateBranch(ctx, dbgen.UpdateBranchParams{
ID: id,
Name: branch.Name,

View File

@ -72,6 +72,19 @@ func (s *Store) GetAllTransactions(ctx context.Context) ([]domain.Transaction, e
}
return result, nil
}
func (s *Store) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) {
transaction, err := s.queries.GetTransactionByBranch(ctx, id)
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{

View File

@ -1,77 +1,96 @@
package repository
// import (
// "context"
import (
"context"
// dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
// )
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
// 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 convertDBTransfer(transfer dbgen.WalletTransfer) domain.Transfer {
return domain.Transfer{
ID: transfer.ID,
Amount: domain.Currency(transfer.Amount),
Type: domain.TransferType(transfer.Type),
Verified: transfer.Verified,
ReceiverWalletID: transfer.ReceiverWalletID,
SenderWalletID: domain.ValidInt64{
Value: transfer.SenderWalletID.Int64,
Valid: transfer.SenderWalletID.Valid,
},
CashierID: domain.ValidInt64{
Value: transfer.CashierID.Int64,
Valid: transfer.CashierID.Valid,
},
PaymentMethod: domain.PaymentMethod(transfer.PaymentMethod),
}
}
// func convertCreateTransaction(transaction domain.CreateTransaction) dbgen.CreateTransactionParams {
// return dbgen.CreateTransactionParams{
// Amount: int64(transaction.Amount),
// TransactionType: string(transaction.Type),
// WalletID: transaction.WalletID,
// }
// }
func convertCreateTransfer(transfer domain.CreateTransfer) dbgen.CreateTransferParams {
return dbgen.CreateTransferParams{
Amount: int64(transfer.Amount),
Type: string(transfer.Type),
ReceiverWalletID: transfer.ReceiverWalletID,
SenderWalletID: pgtype.Int8{
Int64: transfer.SenderWalletID.Value,
Valid: transfer.SenderWalletID.Valid,
},
CashierID: pgtype.Int8{
Int64: transfer.CashierID.Value,
Valid: transfer.CashierID.Valid,
},
PaymentMethod: string(transfer.PaymentMethod),
}
}
// 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) CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) {
newTransfer, err := s.queries.CreateTransfer(ctx, convertCreateTransfer(transfer))
if err != nil {
return domain.Transfer{}, err
}
return convertDBTransfer(newTransfer), 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))
func (s *Store) GetAllTransfers(ctx context.Context) ([]domain.Transfer, error) {
transfers, err := s.queries.GetAllTransfers(ctx)
if err != nil {
return nil, err
}
var result []domain.Transfer = make([]domain.Transfer, len(transfers))
// 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
// }
for _, transfer := range transfers {
result = append(result, convertDBTransfer(transfer))
}
return result, nil
}
func (s *Store) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.Transfer, error) {
transfers, err := s.queries.GetTransfersByWallet(ctx, walletID)
if err != nil {
return nil, err
}
// var result []domain.Transaction = make([]domain.Transaction, len(transactions))
var result []domain.Transfer = make([]domain.Transfer, len(transfers))
// for _, transaction := range transactions {
// result = append(result, convertDBTransaction(transaction))
// }
// return result, nil
// }
for _, transfer := range transfers {
result = append(result, convertDBTransfer(transfer))
}
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) GetTransferByID(ctx context.Context, id int64) (domain.Transfer, error) {
transfer, err := s.queries.GetTransferByID(ctx, id)
if err != nil {
return domain.Transfer{}, nil
}
return convertDBTransfer(transfer), nil
}
// func (s *Store) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error {
// err := s.queries.UpdateTransferVerification(ctx, dbgen.UpdateTransferVerificationParams{
// ID: id,
// Verified: verified,
// })
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
// }
return err
}

View File

@ -9,22 +9,24 @@ import (
func convertDBWallet(wallet dbgen.Wallet) domain.Wallet {
return domain.Wallet{
ID: wallet.ID,
Balance: domain.Currency(wallet.Balance),
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
IsActive: wallet.IsActive,
UserID: wallet.UserID,
UpdatedAt: wallet.UpdatedAt.Time,
CreatedAt: wallet.CreatedAt.Time,
ID: wallet.ID,
Balance: domain.Currency(wallet.Balance),
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
IsTransferable: wallet.IsTransferable,
IsActive: wallet.IsActive,
UserID: wallet.UserID,
UpdatedAt: wallet.UpdatedAt.Time,
CreatedAt: wallet.CreatedAt.Time,
}
}
func convertCreateWallet(wallet domain.CreateWallet) dbgen.CreateWalletParams {
return dbgen.CreateWalletParams{
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
UserID: wallet.UserID,
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
IsTransferable: wallet.IsTransferable,
UserID: wallet.UserID,
}
}

View File

@ -15,7 +15,7 @@ type BranchStore interface {
GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error)
GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error)
GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error)
UpdateBranch(ctx context.Context, id int64, branch domain.CreateBranch) (domain.Branch, error)
UpdateBranch(ctx context.Context, id int64, branch domain.UpdateBranch) (domain.Branch, error)
DeleteBranch(ctx context.Context, id int64) error
DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error
}

View File

@ -28,7 +28,7 @@ func (s *Service) CreateBranchOperation(ctx context.Context, branchOperation dom
func (s *Service) GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error) {
return s.branchStore.GetBranchByID(ctx, id)
}
func (s *Service) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([] domain.BranchDetail, error) {
func (s *Service) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error) {
return s.branchStore.GetBranchByManagerID(ctx, branchManagerID)
}
func (s *Service) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error) {
@ -40,7 +40,7 @@ func (s *Service) GetBranchOperations(ctx context.Context, branchID int64) ([]do
func (s *Service) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error) {
return s.branchStore.GetAllBranches(ctx)
}
func (s *Service) UpdateBranch(ctx context.Context, id int64, branch domain.CreateBranch) (domain.Branch, error) {
func (s *Service) UpdateBranch(ctx context.Context, id int64, branch domain.UpdateBranch) (domain.Branch, error) {
return s.branchStore.UpdateBranch(ctx, id, branch)
}
func (s *Service) DeleteBranch(ctx context.Context, id int64) error {

View File

@ -10,5 +10,6 @@ type TransactionStore interface {
CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error)
GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error)
GetAllTransactions(ctx context.Context) ([]domain.Transaction, error)
GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error)
UpdateTransactionVerified(ctx context.Context, id int64, verified bool) error
}

View File

@ -25,6 +25,9 @@ func (s *Service) GetTransactionByID(ctx context.Context, id int64) (domain.Tran
func (s *Service) GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) {
return s.transactionStore.GetAllTransactions(ctx)
}
func (s *Service) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) {
return s.transactionStore.GetTransactionByBranch(ctx, id)
}
func (s *Service) UpdateTransactionVerified(ctx context.Context, id int64, verified bool) error {
return s.transactionStore.UpdateTransactionVerified(ctx, id, verified)

View File

@ -1 +0,0 @@
package transfer

View File

@ -1,15 +0,0 @@
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

@ -1,33 +0,0 @@
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

@ -0,0 +1 @@
package wallet

View File

@ -16,3 +16,12 @@ type WalletStore interface {
UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error
UpdateWalletActive(ctx context.Context, id int64, isActive bool) error
}
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

@ -1,102 +1,13 @@
package wallet
import (
"context"
"errors"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type Service struct {
walletStore WalletStore
walletStore WalletStore
transferStore TransferStore
}
func NewService(walletStore WalletStore) *Service {
func NewService(walletStore WalletStore, transferStore TransferStore) *Service {
return &Service{
walletStore: walletStore,
walletStore: walletStore,
transferStore: transferStore,
}
}
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, 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) {
return s.walletStore.GetWalletByID(ctx, id)
}
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)
}
func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error {
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

@ -0,0 +1,86 @@
package wallet
import (
"context"
"errors"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
var (
ErrWalletNotTransferable = errors.New("wallet is not transferable")
)
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)
}
func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiverID int64, amount domain.Currency, paymentMethod domain.PaymentMethod, cashierID domain.ValidInt64) (domain.Transfer, error) {
senderWallet, err := s.GetWalletByID(ctx, senderID)
if err != nil {
return domain.Transfer{}, err
}
if senderWallet.IsTransferable {
return domain.Transfer{}, ErrWalletNotTransferable
}
receiverWallet, err := s.GetWalletByID(ctx, receiverID)
if err != nil {
return domain.Transfer{}, err
}
if receiverWallet.IsTransferable {
return domain.Transfer{}, ErrWalletNotTransferable
}
// Deduct from sender
if senderWallet.Balance < amount {
return domain.Transfer{}, ErrBalanceInsufficient
}
err = s.walletStore.UpdateBalance(ctx, senderID, senderWallet.Balance-amount)
if err != nil {
return domain.Transfer{}, err
}
// Add to receiver
err = s.walletStore.UpdateBalance(ctx, receiverID, receiverWallet.Balance+amount)
if err != nil {
return domain.Transfer{}, err
}
// Log the transfer so that if there is a mistake, it can be reverted
transfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
SenderWalletID: domain.ValidInt64{
Value: senderID,
Valid: true,
},
CashierID: cashierID,
ReceiverWalletID: receiverID,
Amount: amount,
Type: domain.WALLET,
PaymentMethod: paymentMethod,
Verified: true,
})
if err != nil {
return domain.Transfer{}, err
}
return transfer, nil
}

View File

@ -0,0 +1,93 @@
package wallet
import (
"context"
"errors"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
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, 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) {
return s.walletStore.GetWalletByID(ctx, id)
}
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)
}
func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error {
return s.walletStore.UpdateBalance(ctx, id, balance)
}
func (s *Service) AddToWallet(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) DeductFromWallet(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

@ -6,6 +6,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
@ -16,6 +17,7 @@ import (
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
"github.com/bytedance/sonic"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
)
type App struct {
@ -29,6 +31,7 @@ type App struct {
betSvc *bet.Service
walletSvc *wallet.Service
transactionSvc *transaction.Service
branchSvc *branch.Service
validator *customvalidator.CustomValidator
JwtConfig jwtutil.JwtConfig
Logger *slog.Logger
@ -44,6 +47,7 @@ func NewApp(
betSvc *bet.Service,
walletSvc *wallet.Service,
transactionSvc *transaction.Service,
branchSvc *branch.Service,
notidicationStore notificationservice.NotificationStore,
) *App {
app := fiber.New(fiber.Config{
@ -52,6 +56,13 @@ func NewApp(
JSONEncoder: sonic.Marshal,
JSONDecoder: sonic.Unmarshal,
})
app.Use(cors.New(cors.Config{
AllowOrigins: "http://localhost:5173", // Specify your frontend's origin
AllowMethods: "GET,POST,PUT,DELETE", // Specify the allowed HTTP methods
AllowHeaders: "Content-Type,Authorization", // Specify the allowed headers
}))
s := &App{
fiber: app,
port: port,
@ -64,6 +75,7 @@ func NewApp(
betSvc: betSvc,
walletSvc: walletSvc,
transactionSvc: transactionSvc,
branchSvc: branchSvc,
NotidicationStore: notidicationStore,
Logger: logger,
}

View File

@ -6,6 +6,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
"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"
@ -14,7 +15,6 @@ import (
type CreateBranchReq struct {
Name string `json:"name" example:"4-kilo Branch"`
Location string `json:"location" example:"Addis Ababa"`
WalletID int64 `json:"wallet_id" example:"1"`
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
CompanyID int64 `json:"company_id" example:"1"`
IsSelfOwned bool `json:"is_self_owned" example:"false"`
@ -85,12 +85,23 @@ func convertBranchDetail(branch domain.BranchDetail) BranchDetailRes {
}
}
func CreateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
// CreateBranch godoc
// @Summary Create a branch
// @Description Creates a branch
// @Tags branch
// @Accept json
// @Produce json
// @Param createBranch body CreateBranchReq true "Creates branch"
// @Success 200 {object} BranchRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /branch [post]
func CreateBranch(logger *slog.Logger, branchSvc *branch.Service, walletSvc *wallet.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
// Check if user is either branch manager / super main
// role := string(c.Locals("role").(domain.Role))
// if role != string(domain.RoleCustomer) {
// if role != string(domain.RoleAdmin) && role != string(domain.RoleSuperAdmin) && role != string(domain.RoleBranchManager) {
// logger.Error("Unauthorized access", "role", role)
// return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
// }
@ -110,10 +121,23 @@ func CreateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *cus
return nil
}
// Create Branch Wallet
newWallet, err := walletSvc.CreateWallet(c.Context(), domain.CreateWallet{
IsWithdraw: false,
IsBettable: true,
IsTransferable: true,
UserID: req.BranchManagerID,
})
if err != nil {
logger.Error("Create Branch Wallet failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create branch wallet", err, nil)
}
branch, err := branchSvc.CreateBranch(c.Context(), domain.CreateBranch{
Name: req.Name,
Location: req.Location,
WalletID: req.WalletID,
WalletID: newWallet.ID,
BranchManagerID: req.BranchManagerID,
CompanyID: req.CompanyID,
IsSelfOwned: req.IsSelfOwned,
@ -134,12 +158,23 @@ func CreateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *cus
}
// CreateSupportedOperation godoc
// @Summary Create a supported operation
// @Description Creates a supported operation
// @Tags branch
// @Accept json
// @Produce json
// @Param createSupportedOperation body CreateSupportedOperationReq true "Creates supported operation"
// @Success 200 {object} SupportedOperationRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /supportedOperation [post]
func CreateSupportedOperation(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req CreateSupportedOperationReq
if err := c.BodyParser(&req); err != nil {
logger.Error("CreateBranchReq failed", "error", err)
logger.Error("CreateSupportedOperationReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
@ -171,6 +206,17 @@ func CreateSupportedOperation(logger *slog.Logger, branchSvc *branch.Service, va
}
}
// CreateBranchOperation godoc
// @Summary Create a operation
// @Description Creates a operation
// @Tags branch
// @Accept json
// @Produce json
// @Param createBranchOperation body CreateBranchOperationReq true "Creates operation"
// @Success 200 {object} BranchOperationRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /operation [post]
func CreateBranchOperation(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req CreateBranchOperationReq
@ -202,6 +248,17 @@ func CreateBranchOperation(logger *slog.Logger, branchSvc *branch.Service, valid
}
}
// GetBranchByID godoc
// @Summary Gets branch by id
// @Description Gets a single branch by id
// @Tags branch
// @Accept json
// @Produce json
// @Param id path int true "Branch ID"
// @Success 200 {object} BranchDetailRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /branch/{id} [get]
func GetBranchByID(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchID := c.Params("id")
@ -225,9 +282,21 @@ func GetBranchByID(logger *slog.Logger, branchSvc *branch.Service, validator *cu
}
}
// /user/:id/branch
// GetBranchByManagerID godoc
// @Summary Gets branches by manager id
// @Description Gets a branches by manager id
// @Tags branch
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {array} BranchDetailRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /manager/{id}/branch [get]
func GetBranchByManagerID(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
// TODO: Restrict any who isn't branch manager or higher
userID := c.Params("id")
id, err := strconv.ParseInt(userID, 10, 64)
if err != nil {
@ -249,7 +318,17 @@ func GetBranchByManagerID(logger *slog.Logger, branchSvc *branch.Service, valida
}
}
// /company/:id/branch
// GetBranchByCompanyID godoc
// @Summary Gets branches by company id
// @Description Gets branches by company id
// @Tags branch
// @Accept json
// @Produce json
// @Param id path int true "Company ID"
// @Success 200 {array} BranchDetailRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /company/{id}/branch [get]
func GetBranchByCompanyID(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
companyID := c.Params("id")
@ -273,10 +352,19 @@ func GetBranchByCompanyID(logger *slog.Logger, branchSvc *branch.Service, valida
}
}
// GetAllBranches godoc
// @Summary Gets all branches
// @Description Gets all branches
// @Tags branch
// @Accept json
// @Produce json
// @Success 200 {array} BranchDetailRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /branch [get]
func GetAllBranches(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branches, err := branchSvc.GetAllBranches(c.Context())
if err != nil {
logger.Error("Failed to get branches", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get branches", err, nil)
@ -291,6 +379,17 @@ func GetAllBranches(logger *slog.Logger, branchSvc *branch.Service, validator *c
}
}
// GetBranchOperations godoc
// @Summary Gets branch operations
// @Description Gets branch operations
// @Tags branch
// @Accept json
// @Produce json
// @Param id path int true "Branch ID"
// @Success 200 {array} BranchOperationRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /branch/{id}/operation [get]
func GetBranchOperations(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
@ -312,7 +411,7 @@ func GetBranchOperations(logger *slog.Logger, branchSvc *branch.Service, validat
for _, branch := range operations {
result = append(result, BranchOperationRes{
Name: branch.OperationName,
Name: branch.OperationName,
Description: branch.OperationDescription,
})
}
@ -321,6 +420,18 @@ func GetBranchOperations(logger *slog.Logger, branchSvc *branch.Service, validat
}
}
// UpdateBranch godoc
// @Summary Updates a branch
// @Description Updates a branch
// @Tags branch
// @Accept json
// @Produce json
// @Param id path int true "Branch ID"
// @Param updateBranch body CreateBranchReq true "Update Branch"
// @Success 200 {object} BranchRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /branch/{id} [put]
func UpdateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchID := c.Params("id")
@ -333,7 +444,7 @@ func UpdateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *cus
var req CreateBranchReq
if err := c.BodyParser(&req); err != nil {
logger.Error("CreateBetReq failed", "error", err)
logger.Error("CreateBranchReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
@ -344,10 +455,9 @@ func UpdateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *cus
return nil
}
branch, err := branchSvc.UpdateBranch(c.Context(), id, domain.CreateBranch{
branch, err := branchSvc.UpdateBranch(c.Context(), id, domain.UpdateBranch{
Name: req.Name,
Location: req.Location,
WalletID: req.WalletID,
BranchManagerID: req.BranchManagerID,
CompanyID: req.CompanyID,
IsSelfOwned: req.IsSelfOwned,
@ -365,6 +475,17 @@ func UpdateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *cus
}
}
// DeleteBranch godoc
// @Summary Delete the branch
// @Description Delete the branch
// @Tags branch
// @Accept json
// @Produce json
// @Param id path int true "Branch ID""
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /branch/{id} [delete]
func DeleteBranch(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
@ -388,6 +509,18 @@ func DeleteBranch(logger *slog.Logger, branchSvc *branch.Service, validator *cus
}
}
// DeleteBranchOperation godoc
// @Summary Delete the branch operation
// @Description Delete the branch operation
// @Tags branch
// @Accept json
// @Produce json
// @Param id path int true "Branch ID"
// @Param opID path int true "Branch Operation ID"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /branch/{id}/operation/{opID} [delete]
func DeleteBranchOperation(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchID := c.Params("id")

View File

@ -6,6 +6,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
"github.com/gofiber/fiber/v2"
@ -127,21 +128,51 @@ func CreateTransaction(logger *slog.Logger, transactionSvc *transaction.Service,
// @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 {
func GetAllTransactions(
logger *slog.Logger,
transactionSvc *transaction.Service,
userSvc *user.Service,
validator *customvalidator.CustomValidator,
) fiber.Handler {
return func(c *fiber.Ctx) error {
transactions, err := transactionSvc.GetAllTransactions(c.Context())
// Get user_id from middleware
userID := c.Locals("user_id").(int64)
// Fetch user details
user, err := userSvc.GetUserByID(c.Context(), userID)
if err != nil {
logger.Error("Failed to fetch user details", "user_id", userID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve user details", err, nil)
}
var transactions []domain.Transaction
// Check user role and fetch transactions accordingly
switch user.Role {
case domain.RoleAdmin:
// Admin can fetch all transactions
transactions, err = transactionSvc.GetAllTransactions(c.Context())
case domain.RoleBranchManager, domain.RoleCashier:
// Branch Manager or Cashier can fetch transactions for their branch
// transactions, err = transactionSvc.GetTransactionByBranch(c.Context(), user.BranchID)
transactions, err = transactionSvc.GetAllTransactions(c.Context())
default:
// Unauthorized role
return response.WriteJSON(c, fiber.StatusForbidden, "Unauthorized", nil, nil)
}
if err != nil {
logger.Error("Failed to get transaction", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transaction", err, nil)
logger.Error("Failed to get transactions", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transactions", err, nil)
}
// Convert transactions to response format
var res []TransactionRes = make([]TransactionRes, len(transactions))
for _, transaction := range transactions {
res = append(res, convertTransaction(transaction))
for i, transaction := range transactions {
res[i] = convertTransaction(transaction)
}
return response.WriteJSON(c, fiber.StatusOK, "All Transactions Retrieved", res, nil)
return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil)
}
}

View File

@ -0,0 +1,116 @@
package handlers
import (
"log/slog"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
"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 TransferRes struct {
ID int64 `json:"id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
Verified bool `json:"verified" example:"true"`
Type string `json:"type" example:"transfer"`
PaymentMethod string `json:"payment_method" example:"bank"`
ReceiverWalletID int64 `json:"receiver_wallet_id" example:"1"`
SenderWalletID *int64 `json:"sender_wallet_id" example:"1"`
CashierID *int64 `json:"cashier_id" example:"789"`
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"`
}
func convertTransfer(transfer domain.Transfer) TransferRes {
var senderWalletID *int64
if transfer.SenderWalletID.Valid {
senderWalletID = &transfer.SenderWalletID.Value
}
var cashierID *int64
if transfer.CashierID.Valid {
cashierID = &transfer.CashierID.Value
}
return TransferRes{
ID: transfer.ID,
Amount: transfer.Amount.Float64(),
Verified: transfer.Verified,
Type: string(transfer.Type),
PaymentMethod: string(transfer.PaymentMethod),
ReceiverWalletID: transfer.ReceiverWalletID,
SenderWalletID: senderWalletID,
CashierID: cashierID,
CreatedAt: transfer.CreatedAt,
UpdatedAt: transfer.UpdatedAt,
}
}
type CreateTransferReq struct {
receiverID int64
amount float64
paymentMethod string
}
// TransferToWallet godoc
// @Summary Create a transfer to wallet
// @Description Create a transfer to wallet
// @Tags transfer
// @Accept json
// @Produce json
// @Param transferToWallet body CreateTransferReq true "Create Transfer"
// @Success 200 {object} TransferRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transfer/wallet [post]
func TransferToWallet(logger *slog.Logger, walletSvc *wallet.Service, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
// Get sender ID from the cashier
userID := c.Locals("user_id").(int64)
role := string(c.Locals("role").(domain.Role))
branchID := c.Locals("branch_id").(int64)
if role == string(domain.RoleCustomer) {
logger.Error("Unauthorized access", "userID", userID, "role", role)
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
}
branchWallet, err := branchSvc.GetBranchByID(c.Context(), branchID)
if err != nil {
logger.Error("Failed to get branch wallet", "branch ID", branchID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve branch wallet", err, nil)
}
var req CreateTransferReq
if err := c.BodyParser(&req); err != nil {
logger.Error("CreateTransferReq 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
}
transfer, err := walletSvc.TransferToWallet(c.Context(), branchWallet.ID, req.receiverID, domain.Currency(req.amount), domain.PaymentMethod(req.paymentMethod), domain.ValidInt64{Value: userID, Valid: true})
if !ok {
response.WriteJSON(c, fiber.StatusInternalServerError, "Transfer Failed", err, nil)
return nil
}
res := convertTransfer(transfer)
return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)
}
}

View File

@ -13,14 +13,57 @@ import (
)
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"`
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"`
IsTransferable bool `json:"is_transferable" 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"`
}
func convertWallet(wallet domain.Wallet) WalletRes {
return WalletRes{
ID: wallet.ID,
Balance: wallet.Balance.Float64(),
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
IsTransferable: wallet.IsTransferable,
IsActive: wallet.IsActive,
UserID: wallet.UserID,
UpdatedAt: wallet.UpdatedAt,
CreatedAt: wallet.CreatedAt,
}
}
type CustomerWalletRes struct {
ID int64 `json:"id" example:"1"`
RegularID int64 `json:"regular_id" example:"1"`
RegularBalance float32 `json:"regular_balance" example:"100.0"`
StaticID int64 `json:"static_id" example:"1"`
StaticBalance float32 `json:"static_balance" example:"100.0"`
CustomerID int64 `json:"customer_id" example:"1"`
CompanyID int64 `json:"company_id" example:"1"`
RegularUpdatedAt time.Time `json:"regular_updated_at"`
StaticUpdatedAt time.Time `json:"static_updated_at"`
CreatedAt time.Time `json:"created_at"`
}
func convertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes {
return CustomerWalletRes{
ID: wallet.ID,
RegularID: wallet.RegularID,
RegularBalance: wallet.RegularBalance.Float64(),
StaticID: wallet.StaticID,
StaticBalance: wallet.StaticBalance.Float64(),
CustomerID: wallet.CustomerID,
CompanyID: wallet.CompanyID,
RegularUpdatedAt: wallet.RegularUpdatedAt,
StaticUpdatedAt: wallet.StaticUpdatedAt,
CreatedAt: wallet.CreatedAt,
}
}
// GetWalletByID godoc
@ -51,16 +94,7 @@ func GetWalletByID(logger *slog.Logger, walletSvc *wallet.Service, validator *cu
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,
}
res := convertWallet(wallet)
return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil)
}
@ -87,16 +121,7 @@ func GetAllWallets(logger *slog.Logger, walletSvc *wallet.Service, validator *cu
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,
})
res = append(res, convertWallet(wallet))
}
return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil)
@ -148,19 +173,6 @@ func UpdateWalletActive(logger *slog.Logger, walletSvc *wallet.Service, validato
}
}
type CustomerWalletRes struct {
ID int64 `json:"id" example:"1"`
RegularID int64 `json:"regular_id" example:"1"`
RegularBalance float32 `json:"regular_balance" example:"100.0"`
StaticID int64 `json:"static_id" example:"1"`
StaticBalance float32 `json:"static_balance" example:"100.0"`
CustomerID int64 `json:"customer_id" example:"1"`
CompanyID int64 `json:"company_id" example:"1"`
RegularUpdatedAt time.Time `json:"regular_updated_at"`
StaticUpdatedAt time.Time `json:"static_updated_at"`
CreatedAt time.Time `json:"created_at"`
}
// GetCustomerWallet godoc
// @Summary Get customer wallet
// @Description Retrieve customer wallet details
@ -177,7 +189,7 @@ func GetCustomerWallet(logger *slog.Logger, walletSvc *wallet.Service, validator
return func(c *fiber.Ctx) error {
userId := c.Locals("user_id").(int64)
role := string(c.Locals("role").(domain.Role))
// role := string(c.Locals("role").(domain.Role))
vendorID, err := strconv.ParseInt(c.Get("vendor_id"), 10, 64)
if err != nil {
@ -185,27 +197,16 @@ func GetCustomerWallet(logger *slog.Logger, walletSvc *wallet.Service, validator
}
logger.Info("Company ID: " + strconv.FormatInt(vendorID, 10))
if role != string(domain.RoleCustomer) {
logger.Error("Unauthorized access", "userId", userId, "role", role)
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
}
// if role != string(domain.RoleCustomer) {
// logger.Error("Unauthorized access", "userId", userId, "role", role)
// return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
// }
wallet, err := walletSvc.GetCustomerWallet(c.Context(), userId, vendorID)
if err != nil {
logger.Error("Failed to get customer wallet", "userId", userId, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve wallet", err, nil)
}
res := CustomerWalletRes{
ID: wallet.ID,
RegularID: wallet.RegularID,
RegularBalance: wallet.RegularBalance.Float64(),
StaticID: wallet.StaticID,
StaticBalance: wallet.StaticBalance.Float64(),
CustomerID: wallet.CustomerID,
CompanyID: wallet.CompanyID,
RegularUpdatedAt: wallet.RegularUpdatedAt,
StaticUpdatedAt: wallet.StaticUpdatedAt,
CreatedAt: wallet.CreatedAt,
}
res := convertCustomerWallet(wallet)
return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil)
}

View File

@ -4,6 +4,7 @@ import (
"errors"
"strings"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
"github.com/gofiber/fiber/v2"
)
@ -39,5 +40,10 @@ func (a *App) authMiddleware(c *fiber.Ctx) error {
c.Locals("user_id", claim.UserId)
c.Locals("role", claim.Role)
c.Locals("refresh_token", refreshToken)
if claim.Role != domain.RoleCustomer {
// TODO: Add branch id here from the user
// c.Locals("branch_id", claim.)
}
return c.Next()
}

View File

@ -42,9 +42,26 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/user/wallet", a.authMiddleware, handlers.GetCustomerWallet(a.logger, a.walletSvc, a.validator))
a.fiber.Get("/manager/:id/branch", handlers.GetBranchByManagerID(a.logger, a.branchSvc, a.validator))
a.fiber.Get("/company/:id/branch", handlers.GetBranchByCompanyID(a.logger, a.branchSvc, a.validator))
// Swagger
a.fiber.Get("/swagger/*", fiberSwagger.FiberWrapHandler())
// Branch
a.fiber.Post("/branch", handlers.CreateBranch(a.logger, a.branchSvc, a.walletSvc, a.validator))
a.fiber.Get("/branch", handlers.GetAllBranches(a.logger, a.branchSvc, a.validator))
a.fiber.Get("/branch/:id", handlers.GetBranchByID(a.logger, a.branchSvc, a.validator))
a.fiber.Put("/branch/:id", handlers.UpdateBranch(a.logger, a.branchSvc, a.validator))
a.fiber.Delete("/branch/:id", handlers.DeleteBranch(a.logger, a.branchSvc, a.validator))
// Branch Operation
a.fiber.Post("/supportedOperation", handlers.CreateSupportedOperation(a.logger, a.branchSvc, a.validator))
a.fiber.Post("/operation", handlers.CreateBranchOperation(a.logger, a.branchSvc, a.validator))
a.fiber.Get("/branch/:id/operation", handlers.GetBranchOperations(a.logger, a.branchSvc, a.validator))
a.fiber.Delete("/branch/:id/operation/:opID", handlers.DeleteBranchOperation(a.logger, a.branchSvc, a.validator))
// Ticket
a.fiber.Post("/ticket", handlers.CreateTicket(a.logger, a.ticketSvc, a.validator))
a.fiber.Get("/ticket", handlers.GetAllTickets(a.logger, a.ticketSvc, a.validator))
@ -62,9 +79,13 @@ func (a *App) initAppRoutes() {
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
// Transfer
// /transfer/wallet - transfer from one wallet to another wallet
a.fiber.Post("/transfer/wallet", handlers.TransferToWallet(a.logger, a.walletSvc, a.branchSvc, a.validator))
// 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", handlers.GetAllTransactions(a.logger, a.transactionSvc, a.userSvc, 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))