fix: moving ticket logic into service
This commit is contained in:
parent
93d64d06d7
commit
b0803c968a
|
|
@ -102,7 +102,6 @@ func main() {
|
||||||
userSvc := user.NewService(store, store, cfg)
|
userSvc := user.NewService(store, store, cfg)
|
||||||
eventSvc := event.New(cfg.Bet365Token, store)
|
eventSvc := event.New(cfg.Bet365Token, store)
|
||||||
oddsSvc := odds.New(store, cfg, logger)
|
oddsSvc := odds.New(store, cfg, logger)
|
||||||
ticketSvc := ticket.NewService(store)
|
|
||||||
notificationRepo := repository.NewNotificationRepository(store)
|
notificationRepo := repository.NewNotificationRepository(store)
|
||||||
virtuaGamesRepo := repository.NewVirtualGameRepository(store)
|
virtuaGamesRepo := repository.NewVirtualGameRepository(store)
|
||||||
notificationSvc := notificationservice.New(notificationRepo, logger, cfg)
|
notificationSvc := notificationservice.New(notificationRepo, logger, cfg)
|
||||||
|
|
@ -120,6 +119,7 @@ func main() {
|
||||||
branchSvc := branch.NewService(store)
|
branchSvc := branch.NewService(store)
|
||||||
companySvc := company.NewService(store)
|
companySvc := company.NewService(store)
|
||||||
leagueSvc := league.New(store)
|
leagueSvc := league.New(store)
|
||||||
|
ticketSvc := ticket.NewService(store, eventSvc, *oddsSvc, domain.MongoDBLogger)
|
||||||
betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, logger, domain.MongoDBLogger)
|
betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, logger, domain.MongoDBLogger)
|
||||||
resultSvc := result.NewService(store, cfg, logger, *betSvc, *oddsSvc, eventSvc, leagueSvc, notificationSvc)
|
resultSvc := result.NewService(store, cfg, logger, *betSvc, *oddsSvc, eventSvc, leagueSvc, notificationSvc)
|
||||||
referalRepo := repository.NewReferralRepository(store)
|
referalRepo := repository.NewReferralRepository(store)
|
||||||
|
|
|
||||||
|
|
@ -77,3 +77,5 @@ DROP TABLE IF EXISTS otps;
|
||||||
DROP TABLE IF EXISTS odds;
|
DROP TABLE IF EXISTS odds;
|
||||||
DROP TABLE IF EXISTS events;
|
DROP TABLE IF EXISTS events;
|
||||||
DROP TABLE IF EXISTS leagues;
|
DROP TABLE IF EXISTS leagues;
|
||||||
|
DROP TABLE IF EXISTS teams;
|
||||||
|
DROP TABLE IF EXISTS settings;
|
||||||
|
|
@ -264,6 +264,12 @@ CREATE TABLE teams (
|
||||||
bet365_id INT,
|
bet365_id INT,
|
||||||
logo_url TEXT
|
logo_url TEXT
|
||||||
);
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
-- Views
|
-- Views
|
||||||
CREATE VIEW companies_details AS
|
CREATE VIEW companies_details AS
|
||||||
SELECT companies.*,
|
SELECT companies.*,
|
||||||
|
|
|
||||||
5
db/migrations/000007_setting_data.up.sql
Normal file
5
db/migrations/000007_setting_data.up.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- Settings Initial Data
|
||||||
|
INSERT INTO settings (key, value)
|
||||||
|
VALUES ('total_winnings_limit', '1000000') ON CONFLICT (key) DO
|
||||||
|
UPDATE
|
||||||
|
SET value = EXCLUDED.value;
|
||||||
9
db/query/settings.sql
Normal file
9
db/query/settings.sql
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
-- name: GetSettings :many
|
||||||
|
SELECT *
|
||||||
|
from settings;
|
||||||
|
-- name: SaveSetting :one
|
||||||
|
INSERT INTO settings (key, value, updated_at)
|
||||||
|
VALUES ($1, $2, CURRENT_TIMESTAMP) ON CONFLICT (key) DO
|
||||||
|
UPDATE
|
||||||
|
SET value = EXCLUDED.value
|
||||||
|
RETURNING *;
|
||||||
405
docs/docs.go
405
docs/docs.go
|
|
@ -811,76 +811,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/veli/launch/{game_id}": {
|
|
||||||
"get": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"BearerAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Generates authenticated launch URL for Veli games",
|
|
||||||
"tags": [
|
|
||||||
"Veli Games"
|
|
||||||
],
|
|
||||||
"summary": "Launch a Veli game",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Game ID (e.g., veli_aviator_v1)",
|
|
||||||
"name": "game_id",
|
|
||||||
"in": "path",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"default": "USD",
|
|
||||||
"description": "Currency code",
|
|
||||||
"name": "currency",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enum": [
|
|
||||||
"real",
|
|
||||||
"demo"
|
|
||||||
],
|
|
||||||
"type": "string",
|
|
||||||
"default": "real",
|
|
||||||
"description": "Game mode",
|
|
||||||
"name": "mode",
|
|
||||||
"in": "query"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Returns launch URL",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Invalid request",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal server error",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/auth/login": {
|
"/auth/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login customer",
|
"description": "Login customer",
|
||||||
|
|
@ -3377,7 +3307,7 @@ const docTemplate = `{
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/handlers.TicketRes"
|
"$ref": "#/definitions/domain.TicketRes"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -3414,7 +3344,7 @@ const docTemplate = `{
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.CreateTicketReq"
|
"$ref": "#/definitions/domain.CreateTicketReq"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -3422,7 +3352,7 @@ const docTemplate = `{
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.CreateTicketRes"
|
"$ref": "#/definitions/domain.CreateTicketRes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -3466,7 +3396,7 @@ const docTemplate = `{
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.TicketRes"
|
"$ref": "#/definitions/domain.TicketRes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -3484,6 +3414,38 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/top-leagues": {
|
||||||
|
"get": {
|
||||||
|
"description": "Retrieve all top leagues",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"prematch"
|
||||||
|
],
|
||||||
|
"summary": "Retrieve all top leagues",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.UpcomingEvent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/transaction": {
|
"/transaction": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Gets all the transactions",
|
"description": "Gets all the transactions",
|
||||||
|
|
@ -4577,70 +4539,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"/webhooks/veli": {
|
|
||||||
"post": {
|
|
||||||
"description": "Processes game round settlements from Veli",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Veli Games"
|
|
||||||
],
|
|
||||||
"summary": "Veli Games webhook handler",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "Callback payload",
|
|
||||||
"name": "payload",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/domain.VeliCallback"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Callback processed",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Invalid payload",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Invalid signature",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Processing error",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
@ -4956,6 +4854,52 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.CreateTicketOutcomeReq": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"event_id": {
|
||||||
|
"description": "TicketID int64 ` + "`" + `json:\"ticket_id\" example:\"1\"` + "`" + `",
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"market_id": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"odd_id": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.CreateTicketReq": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"amount": {
|
||||||
|
"type": "number",
|
||||||
|
"example": 100
|
||||||
|
},
|
||||||
|
"outcomes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.CreateTicketOutcomeReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.CreateTicketRes": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created_number": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 3
|
||||||
|
},
|
||||||
|
"fast_code": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1234
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.DashboardSummary": {
|
"domain.DashboardSummary": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -5132,6 +5076,10 @@ const docTemplate = `{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"example": false
|
"example": false
|
||||||
},
|
},
|
||||||
|
"is_featured": {
|
||||||
|
"type": "boolean",
|
||||||
|
"example": false
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "BPL"
|
"example": "BPL"
|
||||||
|
|
@ -5193,6 +5141,17 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.OtpProvider": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"twilio",
|
||||||
|
"aformessage"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"TwilioSms",
|
||||||
|
"AfroMessage"
|
||||||
|
]
|
||||||
|
},
|
||||||
"domain.OutcomeStatus": {
|
"domain.OutcomeStatus": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|
@ -5482,6 +5441,29 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.TicketRes": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"amount": {
|
||||||
|
"type": "number",
|
||||||
|
"example": 100
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"outcomes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.TicketOutcome"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_odds": {
|
||||||
|
"type": "number",
|
||||||
|
"example": 4.22
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.UpcomingEvent": {
|
"domain.UpcomingEvent": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -5551,51 +5533,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain.VeliCallback": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"amount": {
|
|
||||||
"description": "Transaction amount",
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"currency": {
|
|
||||||
"description": "e.g., \"USD\"",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"event_type": {
|
|
||||||
"description": "\"bet_placed\", \"game_result\", etc.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"game_id": {
|
|
||||||
"description": "e.g., \"veli_aviator_v1\"",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"multiplier": {
|
|
||||||
"description": "For games with multipliers (Aviator/Plinko)",
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"round_id": {
|
|
||||||
"description": "Unique round identifier (replaces transaction_id)",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"session_id": {
|
|
||||||
"description": "Matches VirtualGameSession.SessionToken",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"signature": {
|
|
||||||
"description": "HMAC-SHA256",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"timestamp": {
|
|
||||||
"description": "Unix timestamp",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"description": "Veli's user identifier",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domain.VirtualGame": {
|
"domain.VirtualGame": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -5994,52 +5931,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.CreateTicketOutcomeReq": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"event_id": {
|
|
||||||
"description": "TicketID int64 ` + "`" + `json:\"ticket_id\" example:\"1\"` + "`" + `",
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"market_id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"odd_id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.CreateTicketReq": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"amount": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 100
|
|
||||||
},
|
|
||||||
"outcomes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/handlers.CreateTicketOutcomeReq"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.CreateTicketRes": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"created_number": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 3
|
|
||||||
},
|
|
||||||
"fast_code": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1234
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.CreateTransactionReq": {
|
"handlers.CreateTransactionReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -6249,6 +6140,9 @@ const docTemplate = `{
|
||||||
},
|
},
|
||||||
"handlers.RegisterCodeReq": {
|
"handlers.RegisterCodeReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"provider"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -6257,11 +6151,22 @@ const docTemplate = `{
|
||||||
"phone_number": {
|
"phone_number": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "1234567890"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.OtpProvider"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "twilio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.RegisterUserReq": {
|
"handlers.RegisterUserReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"provider"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -6287,6 +6192,14 @@ const docTemplate = `{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "1234567890"
|
||||||
},
|
},
|
||||||
|
"provider": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.OtpProvider"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "twilio"
|
||||||
|
},
|
||||||
"referal_code": {
|
"referal_code": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "ABC123"
|
"example": "ABC123"
|
||||||
|
|
@ -6295,6 +6208,9 @@ const docTemplate = `{
|
||||||
},
|
},
|
||||||
"handlers.ResetCodeReq": {
|
"handlers.ResetCodeReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"provider"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -6303,6 +6219,14 @@ const docTemplate = `{
|
||||||
"phone_number": {
|
"phone_number": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "1234567890"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.OtpProvider"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "twilio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -6371,29 +6295,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.TicketRes": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"amount": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 100
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"outcomes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/domain.TicketOutcome"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"total_odds": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 4.22
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.TransactionRes": {
|
"handlers.TransactionRes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
||||||
|
|
@ -803,76 +803,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/veli/launch/{game_id}": {
|
|
||||||
"get": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"BearerAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Generates authenticated launch URL for Veli games",
|
|
||||||
"tags": [
|
|
||||||
"Veli Games"
|
|
||||||
],
|
|
||||||
"summary": "Launch a Veli game",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Game ID (e.g., veli_aviator_v1)",
|
|
||||||
"name": "game_id",
|
|
||||||
"in": "path",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"default": "USD",
|
|
||||||
"description": "Currency code",
|
|
||||||
"name": "currency",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enum": [
|
|
||||||
"real",
|
|
||||||
"demo"
|
|
||||||
],
|
|
||||||
"type": "string",
|
|
||||||
"default": "real",
|
|
||||||
"description": "Game mode",
|
|
||||||
"name": "mode",
|
|
||||||
"in": "query"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Returns launch URL",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Invalid request",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal server error",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/auth/login": {
|
"/auth/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Login customer",
|
"description": "Login customer",
|
||||||
|
|
@ -3369,7 +3299,7 @@
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/handlers.TicketRes"
|
"$ref": "#/definitions/domain.TicketRes"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -3406,7 +3336,7 @@
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.CreateTicketReq"
|
"$ref": "#/definitions/domain.CreateTicketReq"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -3414,7 +3344,7 @@
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.CreateTicketRes"
|
"$ref": "#/definitions/domain.CreateTicketRes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -3458,7 +3388,7 @@
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handlers.TicketRes"
|
"$ref": "#/definitions/domain.TicketRes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -3476,6 +3406,38 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/top-leagues": {
|
||||||
|
"get": {
|
||||||
|
"description": "Retrieve all top leagues",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"prematch"
|
||||||
|
],
|
||||||
|
"summary": "Retrieve all top leagues",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.UpcomingEvent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/transaction": {
|
"/transaction": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Gets all the transactions",
|
"description": "Gets all the transactions",
|
||||||
|
|
@ -4569,70 +4531,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"/webhooks/veli": {
|
|
||||||
"post": {
|
|
||||||
"description": "Processes game round settlements from Veli",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Veli Games"
|
|
||||||
],
|
|
||||||
"summary": "Veli Games webhook handler",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "Callback payload",
|
|
||||||
"name": "payload",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/domain.VeliCallback"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Callback processed",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Invalid payload",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Invalid signature",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Processing error",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
@ -4948,6 +4846,52 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.CreateTicketOutcomeReq": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"event_id": {
|
||||||
|
"description": "TicketID int64 `json:\"ticket_id\" example:\"1\"`",
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"market_id": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"odd_id": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.CreateTicketReq": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"amount": {
|
||||||
|
"type": "number",
|
||||||
|
"example": 100
|
||||||
|
},
|
||||||
|
"outcomes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.CreateTicketOutcomeReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.CreateTicketRes": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created_number": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 3
|
||||||
|
},
|
||||||
|
"fast_code": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1234
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.DashboardSummary": {
|
"domain.DashboardSummary": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -5124,6 +5068,10 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"example": false
|
"example": false
|
||||||
},
|
},
|
||||||
|
"is_featured": {
|
||||||
|
"type": "boolean",
|
||||||
|
"example": false
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "BPL"
|
"example": "BPL"
|
||||||
|
|
@ -5185,6 +5133,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.OtpProvider": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"twilio",
|
||||||
|
"aformessage"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"TwilioSms",
|
||||||
|
"AfroMessage"
|
||||||
|
]
|
||||||
|
},
|
||||||
"domain.OutcomeStatus": {
|
"domain.OutcomeStatus": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|
@ -5474,6 +5433,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.TicketRes": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"amount": {
|
||||||
|
"type": "number",
|
||||||
|
"example": 100
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"outcomes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.TicketOutcome"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_odds": {
|
||||||
|
"type": "number",
|
||||||
|
"example": 4.22
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.UpcomingEvent": {
|
"domain.UpcomingEvent": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -5543,51 +5525,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain.VeliCallback": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"amount": {
|
|
||||||
"description": "Transaction amount",
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"currency": {
|
|
||||||
"description": "e.g., \"USD\"",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"event_type": {
|
|
||||||
"description": "\"bet_placed\", \"game_result\", etc.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"game_id": {
|
|
||||||
"description": "e.g., \"veli_aviator_v1\"",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"multiplier": {
|
|
||||||
"description": "For games with multipliers (Aviator/Plinko)",
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"round_id": {
|
|
||||||
"description": "Unique round identifier (replaces transaction_id)",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"session_id": {
|
|
||||||
"description": "Matches VirtualGameSession.SessionToken",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"signature": {
|
|
||||||
"description": "HMAC-SHA256",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"timestamp": {
|
|
||||||
"description": "Unix timestamp",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"description": "Veli's user identifier",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domain.VirtualGame": {
|
"domain.VirtualGame": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -5986,52 +5923,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.CreateTicketOutcomeReq": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"event_id": {
|
|
||||||
"description": "TicketID int64 `json:\"ticket_id\" example:\"1\"`",
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"market_id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"odd_id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.CreateTicketReq": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"amount": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 100
|
|
||||||
},
|
|
||||||
"outcomes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/handlers.CreateTicketOutcomeReq"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.CreateTicketRes": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"created_number": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 3
|
|
||||||
},
|
|
||||||
"fast_code": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1234
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.CreateTransactionReq": {
|
"handlers.CreateTransactionReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -6241,6 +6132,9 @@
|
||||||
},
|
},
|
||||||
"handlers.RegisterCodeReq": {
|
"handlers.RegisterCodeReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"provider"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -6249,11 +6143,22 @@
|
||||||
"phone_number": {
|
"phone_number": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "1234567890"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.OtpProvider"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "twilio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.RegisterUserReq": {
|
"handlers.RegisterUserReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"provider"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -6279,6 +6184,14 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "1234567890"
|
||||||
},
|
},
|
||||||
|
"provider": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.OtpProvider"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "twilio"
|
||||||
|
},
|
||||||
"referal_code": {
|
"referal_code": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "ABC123"
|
"example": "ABC123"
|
||||||
|
|
@ -6287,6 +6200,9 @@
|
||||||
},
|
},
|
||||||
"handlers.ResetCodeReq": {
|
"handlers.ResetCodeReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"provider"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -6295,6 +6211,14 @@
|
||||||
"phone_number": {
|
"phone_number": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "1234567890"
|
"example": "1234567890"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.OtpProvider"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "twilio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -6363,29 +6287,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers.TicketRes": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"amount": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 100
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "integer",
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"outcomes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/domain.TicketOutcome"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"total_odds": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 4.22
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handlers.TransactionRes": {
|
"handlers.TransactionRes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,38 @@ definitions:
|
||||||
- $ref: '#/definitions/domain.OutcomeStatus'
|
- $ref: '#/definitions/domain.OutcomeStatus'
|
||||||
example: 1
|
example: 1
|
||||||
type: object
|
type: object
|
||||||
|
domain.CreateTicketOutcomeReq:
|
||||||
|
properties:
|
||||||
|
event_id:
|
||||||
|
description: TicketID int64 `json:"ticket_id" example:"1"`
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
market_id:
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
odd_id:
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.CreateTicketReq:
|
||||||
|
properties:
|
||||||
|
amount:
|
||||||
|
example: 100
|
||||||
|
type: number
|
||||||
|
outcomes:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.CreateTicketOutcomeReq'
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
domain.CreateTicketRes:
|
||||||
|
properties:
|
||||||
|
created_number:
|
||||||
|
example: 3
|
||||||
|
type: integer
|
||||||
|
fast_code:
|
||||||
|
example: 1234
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
domain.DashboardSummary:
|
domain.DashboardSummary:
|
||||||
properties:
|
properties:
|
||||||
active_admins:
|
active_admins:
|
||||||
|
|
@ -337,6 +369,9 @@ definitions:
|
||||||
is_active:
|
is_active:
|
||||||
example: false
|
example: false
|
||||||
type: boolean
|
type: boolean
|
||||||
|
is_featured:
|
||||||
|
example: false
|
||||||
|
type: boolean
|
||||||
name:
|
name:
|
||||||
example: BPL
|
example: BPL
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -378,6 +413,14 @@ definitions:
|
||||||
source:
|
source:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
domain.OtpProvider:
|
||||||
|
enum:
|
||||||
|
- twilio
|
||||||
|
- aformessage
|
||||||
|
type: string
|
||||||
|
x-enum-varnames:
|
||||||
|
- TwilioSms
|
||||||
|
- AfroMessage
|
||||||
domain.OutcomeStatus:
|
domain.OutcomeStatus:
|
||||||
enum:
|
enum:
|
||||||
- 0
|
- 0
|
||||||
|
|
@ -583,6 +626,22 @@ definitions:
|
||||||
example: 1
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
domain.TicketRes:
|
||||||
|
properties:
|
||||||
|
amount:
|
||||||
|
example: 100
|
||||||
|
type: number
|
||||||
|
id:
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
outcomes:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.TicketOutcome'
|
||||||
|
type: array
|
||||||
|
total_odds:
|
||||||
|
example: 4.22
|
||||||
|
type: number
|
||||||
|
type: object
|
||||||
domain.UpcomingEvent:
|
domain.UpcomingEvent:
|
||||||
properties:
|
properties:
|
||||||
away_kit_image:
|
away_kit_image:
|
||||||
|
|
@ -632,39 +691,6 @@ definitions:
|
||||||
- $ref: '#/definitions/domain.EventStatus'
|
- $ref: '#/definitions/domain.EventStatus'
|
||||||
description: Match Status for event
|
description: Match Status for event
|
||||||
type: object
|
type: object
|
||||||
domain.VeliCallback:
|
|
||||||
properties:
|
|
||||||
amount:
|
|
||||||
description: Transaction amount
|
|
||||||
type: number
|
|
||||||
currency:
|
|
||||||
description: e.g., "USD"
|
|
||||||
type: string
|
|
||||||
event_type:
|
|
||||||
description: '"bet_placed", "game_result", etc.'
|
|
||||||
type: string
|
|
||||||
game_id:
|
|
||||||
description: e.g., "veli_aviator_v1"
|
|
||||||
type: string
|
|
||||||
multiplier:
|
|
||||||
description: For games with multipliers (Aviator/Plinko)
|
|
||||||
type: number
|
|
||||||
round_id:
|
|
||||||
description: Unique round identifier (replaces transaction_id)
|
|
||||||
type: string
|
|
||||||
session_id:
|
|
||||||
description: Matches VirtualGameSession.SessionToken
|
|
||||||
type: string
|
|
||||||
signature:
|
|
||||||
description: HMAC-SHA256
|
|
||||||
type: string
|
|
||||||
timestamp:
|
|
||||||
description: Unix timestamp
|
|
||||||
type: integer
|
|
||||||
user_id:
|
|
||||||
description: Veli's user identifier
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
domain.VirtualGame:
|
domain.VirtualGame:
|
||||||
properties:
|
properties:
|
||||||
category:
|
category:
|
||||||
|
|
@ -946,38 +972,6 @@ definitions:
|
||||||
example: SportsBook
|
example: SportsBook
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
handlers.CreateTicketOutcomeReq:
|
|
||||||
properties:
|
|
||||||
event_id:
|
|
||||||
description: TicketID int64 `json:"ticket_id" example:"1"`
|
|
||||||
example: 1
|
|
||||||
type: integer
|
|
||||||
market_id:
|
|
||||||
example: 1
|
|
||||||
type: integer
|
|
||||||
odd_id:
|
|
||||||
example: 1
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
handlers.CreateTicketReq:
|
|
||||||
properties:
|
|
||||||
amount:
|
|
||||||
example: 100
|
|
||||||
type: number
|
|
||||||
outcomes:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/handlers.CreateTicketOutcomeReq'
|
|
||||||
type: array
|
|
||||||
type: object
|
|
||||||
handlers.CreateTicketRes:
|
|
||||||
properties:
|
|
||||||
created_number:
|
|
||||||
example: 3
|
|
||||||
type: integer
|
|
||||||
fast_code:
|
|
||||||
example: 1234
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
handlers.CreateTransactionReq:
|
handlers.CreateTransactionReq:
|
||||||
properties:
|
properties:
|
||||||
account_name:
|
account_name:
|
||||||
|
|
@ -1126,6 +1120,12 @@ definitions:
|
||||||
phone_number:
|
phone_number:
|
||||||
example: "1234567890"
|
example: "1234567890"
|
||||||
type: string
|
type: string
|
||||||
|
provider:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.OtpProvider'
|
||||||
|
example: twilio
|
||||||
|
required:
|
||||||
|
- provider
|
||||||
type: object
|
type: object
|
||||||
handlers.RegisterUserReq:
|
handlers.RegisterUserReq:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -1147,9 +1147,15 @@ definitions:
|
||||||
phone_number:
|
phone_number:
|
||||||
example: "1234567890"
|
example: "1234567890"
|
||||||
type: string
|
type: string
|
||||||
|
provider:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.OtpProvider'
|
||||||
|
example: twilio
|
||||||
referal_code:
|
referal_code:
|
||||||
example: ABC123
|
example: ABC123
|
||||||
type: string
|
type: string
|
||||||
|
required:
|
||||||
|
- provider
|
||||||
type: object
|
type: object
|
||||||
handlers.ResetCodeReq:
|
handlers.ResetCodeReq:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -1159,6 +1165,12 @@ definitions:
|
||||||
phone_number:
|
phone_number:
|
||||||
example: "1234567890"
|
example: "1234567890"
|
||||||
type: string
|
type: string
|
||||||
|
provider:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.OtpProvider'
|
||||||
|
example: twilio
|
||||||
|
required:
|
||||||
|
- provider
|
||||||
type: object
|
type: object
|
||||||
handlers.ResetPasswordReq:
|
handlers.ResetPasswordReq:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -1205,22 +1217,6 @@ definitions:
|
||||||
example: SportsBook
|
example: SportsBook
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
handlers.TicketRes:
|
|
||||||
properties:
|
|
||||||
amount:
|
|
||||||
example: 100
|
|
||||||
type: number
|
|
||||||
id:
|
|
||||||
example: 1
|
|
||||||
type: integer
|
|
||||||
outcomes:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/domain.TicketOutcome'
|
|
||||||
type: array
|
|
||||||
total_odds:
|
|
||||||
example: 4.22
|
|
||||||
type: number
|
|
||||||
type: object
|
|
||||||
handlers.TransactionRes:
|
handlers.TransactionRes:
|
||||||
properties:
|
properties:
|
||||||
account_name:
|
account_name:
|
||||||
|
|
@ -2077,52 +2073,6 @@ paths:
|
||||||
summary: Process Alea Play game callback
|
summary: Process Alea Play game callback
|
||||||
tags:
|
tags:
|
||||||
- Alea Virtual Games
|
- Alea Virtual Games
|
||||||
/api/veli/launch/{game_id}:
|
|
||||||
get:
|
|
||||||
description: Generates authenticated launch URL for Veli games
|
|
||||||
parameters:
|
|
||||||
- description: Game ID (e.g., veli_aviator_v1)
|
|
||||||
in: path
|
|
||||||
name: game_id
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
- default: USD
|
|
||||||
description: Currency code
|
|
||||||
in: query
|
|
||||||
name: currency
|
|
||||||
type: string
|
|
||||||
- default: real
|
|
||||||
description: Game mode
|
|
||||||
enum:
|
|
||||||
- real
|
|
||||||
- demo
|
|
||||||
in: query
|
|
||||||
name: mode
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Returns launch URL
|
|
||||||
schema:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
"400":
|
|
||||||
description: Invalid request
|
|
||||||
schema:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
"500":
|
|
||||||
description: Internal server error
|
|
||||||
schema:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
summary: Launch a Veli game
|
|
||||||
tags:
|
|
||||||
- Veli Games
|
|
||||||
/auth/login:
|
/auth/login:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
@ -3766,7 +3716,7 @@ paths:
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/handlers.TicketRes'
|
$ref: '#/definitions/domain.TicketRes'
|
||||||
type: array
|
type: array
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
|
|
@ -3789,14 +3739,14 @@ paths:
|
||||||
name: createTicket
|
name: createTicket
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handlers.CreateTicketReq'
|
$ref: '#/definitions/domain.CreateTicketReq'
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handlers.CreateTicketRes'
|
$ref: '#/definitions/domain.CreateTicketRes'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
|
|
@ -3825,7 +3775,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handlers.TicketRes'
|
$ref: '#/definitions/domain.TicketRes'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
|
|
@ -3837,6 +3787,27 @@ paths:
|
||||||
summary: Get ticket by ID
|
summary: Get ticket by ID
|
||||||
tags:
|
tags:
|
||||||
- ticket
|
- ticket
|
||||||
|
/top-leagues:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Retrieve all top leagues
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.UpcomingEvent'
|
||||||
|
type: array
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Retrieve all top leagues
|
||||||
|
tags:
|
||||||
|
- prematch
|
||||||
/transaction:
|
/transaction:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
@ -4551,48 +4522,6 @@ paths:
|
||||||
summary: Activate and Deactivate Wallet
|
summary: Activate and Deactivate Wallet
|
||||||
tags:
|
tags:
|
||||||
- wallet
|
- wallet
|
||||||
/webhooks/veli:
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Processes game round settlements from Veli
|
|
||||||
parameters:
|
|
||||||
- description: Callback payload
|
|
||||||
in: body
|
|
||||||
name: payload
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/domain.VeliCallback'
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Callback processed
|
|
||||||
schema:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
"400":
|
|
||||||
description: Invalid payload
|
|
||||||
schema:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
"403":
|
|
||||||
description: Invalid signature
|
|
||||||
schema:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
"500":
|
|
||||||
description: Processing error
|
|
||||||
schema:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
summary: Veli Games webhook handler
|
|
||||||
tags:
|
|
||||||
- Veli Games
|
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
in: header
|
in: header
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,13 @@ type Result struct {
|
||||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Setting struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type SupportedOperation struct {
|
type SupportedOperation struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
|
||||||
65
gen/db/settings.sql.go
Normal file
65
gen/db/settings.sql.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.28.0
|
||||||
|
// source: settings.sql
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const GetSettings = `-- name: GetSettings :many
|
||||||
|
SELECT key, value, created_at, updated_at
|
||||||
|
from settings
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetSettings(ctx context.Context) ([]Setting, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetSettings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Setting
|
||||||
|
for rows.Next() {
|
||||||
|
var i Setting
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.Key,
|
||||||
|
&i.Value,
|
||||||
|
&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 SaveSetting = `-- name: SaveSetting :one
|
||||||
|
INSERT INTO settings (key, value, updated_at)
|
||||||
|
VALUES ($1, $2, CURRENT_TIMESTAMP) ON CONFLICT (key) DO
|
||||||
|
UPDATE
|
||||||
|
SET value = EXCLUDED.value
|
||||||
|
RETURNING key, value, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type SaveSettingParams struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SaveSetting(ctx context.Context, arg SaveSettingParams) (Setting, error) {
|
||||||
|
row := q.db.QueryRow(ctx, SaveSetting, arg.Key, arg.Value)
|
||||||
|
var i Setting
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Key,
|
||||||
|
&i.Value,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
15
internal/domain/settings.go
Normal file
15
internal/domain/settings.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Setting struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type SettingRes struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
@ -53,3 +53,31 @@ type CreateTicket struct {
|
||||||
TotalOdds float32
|
TotalOdds float32
|
||||||
IP string
|
IP string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateTicketOutcomeReq struct {
|
||||||
|
// TicketID int64 `json:"ticket_id" example:"1"`
|
||||||
|
EventID int64 `json:"event_id" example:"1"`
|
||||||
|
OddID int64 `json:"odd_id" example:"1"`
|
||||||
|
MarketID int64 `json:"market_id" example:"1"`
|
||||||
|
// HomeTeamName string `json:"home_team_name" example:"Manchester"`
|
||||||
|
// AwayTeamName string `json:"away_team_name" example:"Liverpool"`
|
||||||
|
// MarketName string `json:"market_name" example:"Fulltime Result"`
|
||||||
|
// Odd float32 `json:"odd" example:"1.5"`
|
||||||
|
// OddName string `json:"odd_name" example:"1"`
|
||||||
|
// Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateTicketReq struct {
|
||||||
|
Outcomes []CreateTicketOutcomeReq `json:"outcomes"`
|
||||||
|
Amount float32 `json:"amount" example:"100.0"`
|
||||||
|
}
|
||||||
|
type CreateTicketRes struct {
|
||||||
|
FastCode int64 `json:"fast_code" example:"1234"`
|
||||||
|
CreatedNumber int64 `json:"created_number" example:"3"`
|
||||||
|
}
|
||||||
|
type TicketRes struct {
|
||||||
|
ID int64 `json:"id" example:"1"`
|
||||||
|
Outcomes []TicketOutcome `json:"outcomes"`
|
||||||
|
Amount float32 `json:"amount" example:"100.0"`
|
||||||
|
TotalOdds float32 `json:"total_odds" example:"4.22"`
|
||||||
|
}
|
||||||
|
|
|
||||||
49
internal/repository/settings.go
Normal file
49
internal/repository/settings.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Store) GetSettings(ctx context.Context) ([]domain.Setting, error) {
|
||||||
|
settings, err := s.queries.GetSettings(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []domain.Setting = make([]domain.Setting, 0, len(settings))
|
||||||
|
for _, setting := range settings {
|
||||||
|
result = append(result, domain.Setting{
|
||||||
|
Key: setting.Key,
|
||||||
|
Value: setting.Value,
|
||||||
|
UpdatedAt: setting.UpdatedAt.Time,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) SaveSetting(ctx context.Context, key, value string) (domain.Setting, error) {
|
||||||
|
dbSetting, err := s.queries.SaveSetting(ctx, dbgen.SaveSettingParams{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
domain.MongoDBLogger.Error("failed to update setting", zap.String("key", key), zap.String("value", value), zap.Error(err))
|
||||||
|
|
||||||
|
return domain.Setting{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
setting := domain.Setting{
|
||||||
|
Key: dbSetting.Key,
|
||||||
|
Value: dbSetting.Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,11 @@ var (
|
||||||
ErrGenerateRandomOutcome = errors.New("Failed to generate any random outcome for events")
|
ErrGenerateRandomOutcome = errors.New("Failed to generate any random outcome for events")
|
||||||
ErrOutcomesNotCompleted = errors.New("Some bet outcomes are still pending")
|
ErrOutcomesNotCompleted = errors.New("Some bet outcomes are still pending")
|
||||||
ErrEventHasBeenRemoved = errors.New("Event has been removed")
|
ErrEventHasBeenRemoved = errors.New("Event has been removed")
|
||||||
|
|
||||||
|
ErrEventHasNotEnded = errors.New("Event has not ended yet")
|
||||||
|
ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid")
|
||||||
|
ErrBranchIDRequired = errors.New("Branch ID required for this role")
|
||||||
|
ErrOutcomeLimit = errors.New("Too many outcomes on a single bet")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
|
|
@ -41,7 +46,15 @@ type Service struct {
|
||||||
mongoLogger *zap.Logger
|
mongoLogger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(betStore BetStore, eventSvc event.Service, prematchSvc odds.ServiceImpl, walletSvc wallet.Service, branchSvc branch.Service, logger *slog.Logger, mongoLogger *zap.Logger) *Service {
|
func NewService(
|
||||||
|
betStore BetStore,
|
||||||
|
eventSvc event.Service,
|
||||||
|
prematchSvc odds.ServiceImpl,
|
||||||
|
walletSvc wallet.Service,
|
||||||
|
branchSvc branch.Service,
|
||||||
|
logger *slog.Logger,
|
||||||
|
mongoLogger *zap.Logger,
|
||||||
|
) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
betStore: betStore,
|
betStore: betStore,
|
||||||
eventSvc: eventSvc,
|
eventSvc: eventSvc,
|
||||||
|
|
@ -53,13 +66,6 @@ func NewService(betStore BetStore, eventSvc event.Service, prematchSvc odds.Serv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
ErrEventHasNotEnded = errors.New("Event has not ended yet")
|
|
||||||
ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid")
|
|
||||||
ErrBranchIDRequired = errors.New("Branch ID required for this role")
|
|
||||||
ErrOutcomeLimit = errors.New("Too many outcomes on a single bet")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Service) GenerateCashoutID() (string, error) {
|
func (s *Service) GenerateCashoutID() (string, error) {
|
||||||
const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
|
const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
const length int = 13
|
const length int = 13
|
||||||
|
|
|
||||||
12
internal/services/settings/port.go
Normal file
12
internal/services/settings/port.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SettingStore interface {
|
||||||
|
GetSettings(ctx context.Context) ([]domain.Setting, error)
|
||||||
|
SaveSetting(ctx context.Context, key, value string) (domain.Setting, error)
|
||||||
|
}
|
||||||
25
internal/services/settings/service.go
Normal file
25
internal/services/settings/service.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
settingStore SettingStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(settingStore SettingStore) *Service {
|
||||||
|
return &Service{
|
||||||
|
settingStore: settingStore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetSettings(ctx context.Context) ([]domain.Setting, error) {
|
||||||
|
return s.settingStore.GetSettings(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SaveSetting(ctx context.Context, key, value string) (domain.Setting, error) {
|
||||||
|
return s.settingStore.SaveSetting(ctx, key, value)
|
||||||
|
}
|
||||||
|
|
@ -2,24 +2,231 @@ package ticket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrGenerateRandomOutcome = errors.New("Failed to generate any random outcome for events")
|
||||||
|
// ErrOutcomesNotCompleted = errors.New("Some bet outcomes are still pending")
|
||||||
|
ErrEventHasNotEnded = errors.New("Event has not ended yet")
|
||||||
|
ErrNoEventsAvailable = errors.New("Not enough events available with the given filters")
|
||||||
|
ErrEventHasBeenRemoved = errors.New("Event has been removed")
|
||||||
|
ErrTooManyOutcomesForTicket = errors.New("Too many odds/outcomes for a single ticket")
|
||||||
|
ErrTicketAmountTooHigh = errors.New("Cannot create a ticket with an amount above limit")
|
||||||
|
ErrTicketLimitForSingleUser = errors.New("Number of Ticket Limit reached")
|
||||||
|
ErrTicketWinningTooHigh = errors.New("Total Winnings over set limit")
|
||||||
|
|
||||||
|
ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
ticketStore TicketStore
|
ticketStore TicketStore
|
||||||
|
eventSvc event.Service
|
||||||
|
prematchSvc odds.ServiceImpl
|
||||||
|
mongoLogger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(ticketStore TicketStore) *Service {
|
func NewService(
|
||||||
|
ticketStore TicketStore,
|
||||||
|
eventSvc event.Service,
|
||||||
|
prematchSvc odds.ServiceImpl,
|
||||||
|
mongoLogger *zap.Logger,
|
||||||
|
) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
ticketStore: ticketStore,
|
ticketStore: ticketStore,
|
||||||
|
eventSvc: eventSvc,
|
||||||
|
prematchSvc: prematchSvc,
|
||||||
|
mongoLogger: mongoLogger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) CreateTicket(ctx context.Context, ticket domain.CreateTicket) (domain.Ticket, error) {
|
func (s *Service) GenerateTicketOutcome(ctx context.Context, eventID int64, marketID int64, oddID int64) (domain.CreateTicketOutcome, error) {
|
||||||
return s.ticketStore.CreateTicket(ctx, ticket)
|
eventIDStr := strconv.FormatInt(eventID, 10)
|
||||||
|
marketIDStr := strconv.FormatInt(marketID, 10)
|
||||||
|
oddIDStr := strconv.FormatInt(oddID, 10)
|
||||||
|
event, err := s.eventSvc.GetUpcomingEventByID(ctx, eventIDStr)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("failed to fetch upcoming event by ID",
|
||||||
|
zap.Int64("event_id", eventID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return domain.CreateTicketOutcome{}, ErrEventHasBeenRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking to make sure the event hasn't already started
|
||||||
|
currentTime := time.Now()
|
||||||
|
if event.StartTime.Before(currentTime) {
|
||||||
|
s.mongoLogger.Error("event has already started",
|
||||||
|
zap.Int64("event_id", eventID),
|
||||||
|
zap.Time("event_start_time", event.StartTime),
|
||||||
|
zap.Time("current_time", currentTime),
|
||||||
|
)
|
||||||
|
return domain.CreateTicketOutcome{}, ErrEventHasNotEnded
|
||||||
|
}
|
||||||
|
|
||||||
|
odds, err := s.prematchSvc.GetRawOddsByMarketID(ctx, marketIDStr, eventIDStr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("failed to get raw odds by market ID",
|
||||||
|
zap.Int64("event_id", eventID),
|
||||||
|
zap.Int64("market_id", marketID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
// return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid market id", err, nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
type rawOddType struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Odds string
|
||||||
|
Header string
|
||||||
|
Handicap string
|
||||||
|
}
|
||||||
|
var selectedOdd rawOddType
|
||||||
|
var isOddFound bool = false
|
||||||
|
for _, raw := range odds.RawOdds {
|
||||||
|
var rawOdd rawOddType
|
||||||
|
rawBytes, err := json.Marshal(raw)
|
||||||
|
err = json.Unmarshal(rawBytes, &rawOdd)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("failed to unmarshal raw ods",
|
||||||
|
zap.Int64("event_id", eventID),
|
||||||
|
zap.String("rawOddID", rawOdd.ID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if rawOdd.ID == oddIDStr {
|
||||||
|
selectedOdd = rawOdd
|
||||||
|
isOddFound = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isOddFound {
|
||||||
|
// return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid odd id", nil, nil)
|
||||||
|
s.mongoLogger.Error("Invalid Odd ID",
|
||||||
|
zap.Int64("event_id", eventID),
|
||||||
|
zap.String("oddIDStr", oddIDStr),
|
||||||
|
)
|
||||||
|
return domain.CreateTicketOutcome{}, ErrRawOddInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedOdd, err := strconv.ParseFloat(selectedOdd.Odds, 32)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("failed to parse selected odd value",
|
||||||
|
zap.String("odd", selectedOdd.Odds),
|
||||||
|
zap.Int64("odd_id", oddID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return domain.CreateTicketOutcome{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newOutcome := domain.CreateTicketOutcome{
|
||||||
|
EventID: eventID,
|
||||||
|
OddID: oddID,
|
||||||
|
MarketID: marketID,
|
||||||
|
HomeTeamName: event.HomeTeam,
|
||||||
|
AwayTeamName: event.AwayTeam,
|
||||||
|
MarketName: odds.MarketName,
|
||||||
|
Odd: float32(parsedOdd),
|
||||||
|
OddName: selectedOdd.Name,
|
||||||
|
OddHeader: selectedOdd.Header,
|
||||||
|
OddHandicap: selectedOdd.Handicap,
|
||||||
|
Expires: event.StartTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
// outcomes = append(outcomes, )
|
||||||
|
|
||||||
|
return newOutcome, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq, clientIP string) (domain.Ticket, int64, error) {
|
||||||
|
// TODO Validate Outcomes Here and make sure they didn't expire
|
||||||
|
// Validation for creating tickets
|
||||||
|
if len(req.Outcomes) > 30 {
|
||||||
|
// return response.WriteJSON(c, fiber.StatusBadRequest, "Too many odds/outcomes selected", nil, nil)
|
||||||
|
return domain.Ticket{}, 0, ErrTooManyOutcomesForTicket
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Amount > 100000 {
|
||||||
|
// return response.WriteJSON(c, fiber.StatusBadRequest, "Cannot create a ticket with an amount above 100,000 birr", nil, nil)
|
||||||
|
return domain.Ticket{}, 0, ErrTicketAmountTooHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := s.CountTicketByIP(ctx, clientIP)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// return response.WriteJSON(c, fiber.StatusInternalServerError, "Error fetching user info", nil, nil)
|
||||||
|
return domain.Ticket{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 50 {
|
||||||
|
// return response.WriteJSON(c, fiber.StatusBadRequest, "Ticket Limit reached", nil, nil)
|
||||||
|
return domain.Ticket{}, 0, ErrTicketLimitForSingleUser
|
||||||
|
}
|
||||||
|
var outcomes []domain.CreateTicketOutcome = make([]domain.CreateTicketOutcome, 0, len(req.Outcomes))
|
||||||
|
var totalOdds float32 = 1
|
||||||
|
for _, outcomeReq := range req.Outcomes {
|
||||||
|
newOutcome, err := s.GenerateTicketOutcome(ctx, outcomeReq.EventID, outcomeReq.MarketID, outcomeReq.OddID)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("failed to generate outcome",
|
||||||
|
zap.Int64("event_id", outcomeReq.EventID),
|
||||||
|
zap.Int64("market_id", outcomeReq.MarketID),
|
||||||
|
zap.Int64("odd_id", outcomeReq.OddID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return domain.Ticket{}, 0, err
|
||||||
|
}
|
||||||
|
totalOdds *= float32(newOutcome.Odd)
|
||||||
|
outcomes = append(outcomes, newOutcome)
|
||||||
|
}
|
||||||
|
totalWinnings := req.Amount * totalOdds
|
||||||
|
if totalWinnings > 1000000 {
|
||||||
|
s.mongoLogger.Error("Total Winnings over limit", zap.Float32("Total Odds", totalOdds), zap.Float32("amount", req.Amount))
|
||||||
|
// return response.WriteJSON(c, fiber.StatusBadRequest, "Cannot create a ticket with 1,000,000 winnings", nil, nil)
|
||||||
|
return domain.Ticket{}, 0, ErrTicketWinningTooHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
ticket, err := s.ticketStore.CreateTicket(ctx, domain.CreateTicket{
|
||||||
|
Amount: domain.ToCurrency(req.Amount),
|
||||||
|
TotalOdds: totalOdds,
|
||||||
|
IP: clientIP,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("Error Creating Ticket", zap.Float32("Total Odds", totalOdds), zap.Float32("amount", req.Amount))
|
||||||
|
return domain.Ticket{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the ticket id now that it has fetched from the database
|
||||||
|
for index := range outcomes {
|
||||||
|
outcomes[index].TicketID = ticket.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := s.CreateTicketOutcome(ctx, outcomes)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("Error Creating Ticket Outcomes", zap.Any("outcomes", outcomes))
|
||||||
|
return domain.Ticket{}, rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ticket, rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (s *Service) CreateTicket(ctx context.Context, ticket domain.CreateTicket) (domain.Ticket, error) {
|
||||||
|
// return s.ticketStore.CreateTicket(ctx, ticket)
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *Service) CreateTicketOutcome(ctx context.Context, outcomes []domain.CreateTicketOutcome) (int64, error) {
|
func (s *Service) CreateTicketOutcome(ctx context.Context, outcomes []domain.CreateTicketOutcome) (int64, error) {
|
||||||
return s.ticketStore.CreateTicketOutcome(ctx, outcomes)
|
return s.ticketStore.CreateTicketOutcome(ctx, outcomes)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,27 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateTicketOutcomeReq struct {
|
|
||||||
// TicketID int64 `json:"ticket_id" example:"1"`
|
|
||||||
EventID int64 `json:"event_id" example:"1"`
|
|
||||||
OddID int64 `json:"odd_id" example:"1"`
|
|
||||||
MarketID int64 `json:"market_id" example:"1"`
|
|
||||||
// HomeTeamName string `json:"home_team_name" example:"Manchester"`
|
|
||||||
// AwayTeamName string `json:"away_team_name" example:"Liverpool"`
|
|
||||||
// MarketName string `json:"market_name" example:"Fulltime Result"`
|
|
||||||
// Odd float32 `json:"odd" example:"1.5"`
|
|
||||||
// OddName string `json:"odd_name" example:"1"`
|
|
||||||
// Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateTicketReq struct {
|
|
||||||
Outcomes []CreateTicketOutcomeReq `json:"outcomes"`
|
|
||||||
Amount float32 `json:"amount" example:"100.0"`
|
|
||||||
}
|
|
||||||
type CreateTicketRes struct {
|
|
||||||
FastCode int64 `json:"fast_code" example:"1234"`
|
|
||||||
CreatedNumber int64 `json:"created_number" example:"3"`
|
|
||||||
}
|
|
||||||
type TicketRes struct {
|
|
||||||
ID int64 `json:"id" example:"1"`
|
|
||||||
Outcomes []domain.TicketOutcome `json:"outcomes"`
|
|
||||||
Amount float32 `json:"amount" example:"100.0"`
|
|
||||||
TotalOdds float32 `json:"total_odds" example:"4.22"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateTicket godoc
|
// CreateTicket godoc
|
||||||
// @Summary Create a temporary ticket
|
// @Summary Create a temporary ticket
|
||||||
// @Description Creates a temporary ticket
|
// @Description Creates a temporary ticket
|
||||||
// @Tags ticket
|
// @Tags ticket
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param createTicket body CreateTicketReq true "Creates ticket"
|
// @Param createTicket body domain.CreateTicketReq true "Creates ticket"
|
||||||
// @Success 200 {object} CreateTicketRes
|
// @Success 200 {object} domain.CreateTicketRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /ticket [post]
|
// @Router /ticket [post]
|
||||||
func (h *Handler) CreateTicket(c *fiber.Ctx) error {
|
func (h *Handler) CreateTicket(c *fiber.Ctx) error {
|
||||||
var req CreateTicketReq
|
var req domain.CreateTicketReq
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
h.logger.Error("Failed to parse CreateTicket request", "error", err)
|
h.logger.Error("Failed to parse CreateTicket request", "error", err)
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
|
|
@ -60,122 +31,17 @@ func (h *Handler) CreateTicket(c *fiber.Ctx) error {
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Validate Outcomes Here and make sure they didn't expire
|
newTicket, rows, err := h.ticketSvc.CreateTicket(c.Context(), req, c.IP())
|
||||||
// Validation for creating tickets
|
|
||||||
if len(req.Outcomes) > 30 {
|
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Too many odds/outcomes selected", nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Amount > 100000 {
|
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Cannot create a ticket with an amount above 100,000 birr", nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
clientIP := c.IP()
|
|
||||||
count, err := h.ticketSvc.CountTicketByIP(c.Context(), clientIP)
|
|
||||||
if err != nil {
|
|
||||||
return response.WriteJSON(c, fiber.StatusInternalServerError, "Error fetching user info", nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if count > 50 {
|
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Ticket Limit reached", nil, nil)
|
|
||||||
}
|
|
||||||
var outcomes []domain.CreateTicketOutcome = make([]domain.CreateTicketOutcome, 0, len(req.Outcomes))
|
|
||||||
var totalOdds float32 = 1
|
|
||||||
for _, outcome := range req.Outcomes {
|
|
||||||
eventIDStr := strconv.FormatInt(outcome.EventID, 10)
|
|
||||||
marketIDStr := strconv.FormatInt(outcome.MarketID, 10)
|
|
||||||
oddIDStr := strconv.FormatInt(outcome.OddID, 10)
|
|
||||||
event, err := h.eventSvc.GetUpcomingEventByID(c.Context(), eventIDStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid event id", err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking to make sure the event hasn't already started
|
|
||||||
currentTime := time.Now()
|
|
||||||
if event.StartTime.Before(currentTime) {
|
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "The event has already expired", nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
odds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketIDStr, eventIDStr)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid market id", err, nil)
|
|
||||||
}
|
|
||||||
type rawOddType struct {
|
|
||||||
ID string
|
|
||||||
Name string
|
|
||||||
Odds string
|
|
||||||
Header string
|
|
||||||
Handicap string
|
|
||||||
}
|
|
||||||
var selectedOdd rawOddType
|
|
||||||
var isOddFound bool = false
|
|
||||||
for _, raw := range odds.RawOdds {
|
|
||||||
var rawOdd rawOddType
|
|
||||||
rawBytes, err := json.Marshal(raw)
|
|
||||||
err = json.Unmarshal(rawBytes, &rawOdd)
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Error("Failed to unmarshal raw odd:", "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if rawOdd.ID == oddIDStr {
|
|
||||||
selectedOdd = rawOdd
|
|
||||||
isOddFound = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isOddFound {
|
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid odd id", nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedOdd, err := strconv.ParseFloat(selectedOdd.Odds, 32)
|
|
||||||
totalOdds = totalOdds * float32(parsedOdd)
|
|
||||||
outcomes = append(outcomes, domain.CreateTicketOutcome{
|
|
||||||
EventID: outcome.EventID,
|
|
||||||
OddID: outcome.OddID,
|
|
||||||
MarketID: outcome.MarketID,
|
|
||||||
HomeTeamName: event.HomeTeam,
|
|
||||||
AwayTeamName: event.AwayTeam,
|
|
||||||
MarketName: odds.MarketName,
|
|
||||||
Odd: float32(parsedOdd),
|
|
||||||
OddName: selectedOdd.Name,
|
|
||||||
OddHeader: selectedOdd.Header,
|
|
||||||
OddHandicap: selectedOdd.Handicap,
|
|
||||||
Expires: event.StartTime,
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
totalWinnings := req.Amount * totalOdds
|
|
||||||
if totalWinnings > 1000000 {
|
|
||||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Cannot create a ticket with 1,000,000 winnings", nil, nil)
|
|
||||||
}
|
|
||||||
ticket, err := h.ticketSvc.CreateTicket(c.Context(), domain.CreateTicket{
|
|
||||||
Amount: domain.ToCurrency(req.Amount),
|
|
||||||
TotalOdds: totalOdds,
|
|
||||||
IP: clientIP,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Error("CreateTicketReq failed", "error", err)
|
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
||||||
"error": "Internal server error",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the ticket id now that it has fetched from the database
|
|
||||||
for index := range outcomes {
|
|
||||||
outcomes[index].TicketID = ticket.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := h.ticketSvc.CreateTicketOutcome(c.Context(), outcomes)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error("CreateTicketReq failed to create outcomes", "error", err)
|
switch err {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
case ticket.ErrEventHasBeenRemoved, ticket.ErrEventHasNotEnded, ticket.ErrRawOddInvalid:
|
||||||
"error": "Internal server error",
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
})
|
}
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
res := CreateTicketRes{
|
res := domain.CreateTicketRes{
|
||||||
FastCode: ticket.ID,
|
FastCode: newTicket.ID,
|
||||||
CreatedNumber: rows,
|
CreatedNumber: rows,
|
||||||
}
|
}
|
||||||
return response.WriteJSON(c, fiber.StatusOK, "Ticket Created", res, nil)
|
return response.WriteJSON(c, fiber.StatusOK, "Ticket Created", res, nil)
|
||||||
|
|
@ -189,7 +55,7 @@ func (h *Handler) CreateTicket(c *fiber.Ctx) error {
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "Ticket ID"
|
// @Param id path int true "Ticket ID"
|
||||||
// @Success 200 {object} TicketRes
|
// @Success 200 {object} domain.TicketRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /ticket/{id} [get]
|
// @Router /ticket/{id} [get]
|
||||||
|
|
@ -207,7 +73,7 @@ func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
|
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
|
||||||
}
|
}
|
||||||
|
|
||||||
res := TicketRes{
|
res := domain.TicketRes{
|
||||||
ID: ticket.ID,
|
ID: ticket.ID,
|
||||||
Outcomes: ticket.Outcomes,
|
Outcomes: ticket.Outcomes,
|
||||||
Amount: ticket.Amount.Float32(),
|
Amount: ticket.Amount.Float32(),
|
||||||
|
|
@ -222,7 +88,7 @@ func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
|
||||||
// @Tags ticket
|
// @Tags ticket
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {array} TicketRes
|
// @Success 200 {array} domain.TicketRes
|
||||||
// @Failure 400 {object} response.APIResponse
|
// @Failure 400 {object} response.APIResponse
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /ticket [get]
|
// @Router /ticket [get]
|
||||||
|
|
@ -234,9 +100,9 @@ func (h *Handler) GetAllTickets(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve tickets")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve tickets")
|
||||||
}
|
}
|
||||||
|
|
||||||
res := make([]TicketRes, len(tickets))
|
res := make([]domain.TicketRes, len(tickets))
|
||||||
for i, ticket := range tickets {
|
for i, ticket := range tickets {
|
||||||
res[i] = TicketRes{
|
res[i] = domain.TicketRes{
|
||||||
ID: ticket.ID,
|
ID: ticket.ID,
|
||||||
Outcomes: ticket.Outcomes,
|
Outcomes: ticket.Outcomes,
|
||||||
Amount: ticket.Amount.Float32(),
|
Amount: ticket.Amount.Float32(),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user