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

@ -7,6 +7,9 @@ SELECT * FROM transactions;
-- 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

@ -159,6 +159,7 @@ type Wallet struct {
Balance int64
IsWithdraw bool
IsBettable bool
IsTransferable bool
UserID int64
IsActive bool
CreatedAt pgtype.Timestamp
@ -168,10 +169,12 @@ type Wallet struct {
type WalletTransfer struct {
ID int64
Amount int64
WalletTransfer string
WalletID int64
Type string
ReceiverWalletID int64
SenderWalletID pgtype.Int8
CashierID pgtype.Int8
Verified bool
PaymentMethod int32
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
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
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,27 +7,32 @@ 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
ReceiverWalletID int64
SenderWalletID ValidInt64
CashierID ValidInt64
CreatedAt time.Time
UpdatedAt time.Time
}
@ -35,6 +40,9 @@ type Transfer struct {
type CreateTransfer struct {
Amount Currency
Verified bool
WalletID int64
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

@ -13,6 +13,7 @@ func convertDBWallet(wallet dbgen.Wallet) domain.Wallet {
Balance: domain.Currency(wallet.Balance),
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
IsTransferable: wallet.IsTransferable,
IsActive: wallet.IsActive,
UserID: wallet.UserID,
UpdatedAt: wallet.UpdatedAt.Time,
@ -24,6 +25,7 @@ func convertCreateWallet(wallet domain.CreateWallet) dbgen.CreateWalletParams {
return dbgen.CreateWalletParams{
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

@ -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
transferStore TransferStore
}
func NewService(walletStore WalletStore) *Service {
func NewService(walletStore WalletStore, transferStore TransferStore) *Service {
return &Service{
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 {
@ -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

@ -17,12 +17,55 @@ type WalletRes struct {
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
// @Summary Get wallet by ID
// @Description Retrieve wallet details by wallet ID
@ -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))