Veli Games
This commit is contained in:
parent
9793854596
commit
ee07d469eb
10
cmd/main.go
10
cmd/main.go
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
||||||
virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
|
virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
|
||||||
alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea"
|
alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||||
|
|
||||||
// "github.com/SamuelTariku/FortuneBet-Backend/internal/utils"
|
// "github.com/SamuelTariku/FortuneBet-Backend/internal/utils"
|
||||||
|
|
@ -99,13 +100,20 @@ func main() {
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
veliService := veli.NewVeliPlayService(
|
||||||
|
vitualGameRepo,
|
||||||
|
*walletSvc,
|
||||||
|
cfg,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
|
||||||
httpserver.StartDataFetchingCrons(eventSvc, oddsSvc, resultSvc)
|
httpserver.StartDataFetchingCrons(eventSvc, oddsSvc, resultSvc)
|
||||||
|
|
||||||
app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{
|
app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{
|
||||||
JwtAccessKey: cfg.JwtKey,
|
JwtAccessKey: cfg.JwtKey,
|
||||||
JwtAccessExpiry: cfg.AccessExpiry,
|
JwtAccessExpiry: cfg.AccessExpiry,
|
||||||
}, userSvc,
|
}, userSvc,
|
||||||
ticketSvc, betSvc, walletSvc, transactionSvc, branchSvc, companySvc, notificationSvc, oddsSvc, eventSvc, referalSvc, virtualGameSvc, aleaService, resultSvc, cfg)
|
ticketSvc, betSvc, walletSvc, transactionSvc, branchSvc, companySvc, notificationSvc, oddsSvc, eventSvc, referalSvc, virtualGameSvc, aleaService, veliService, resultSvc, cfg)
|
||||||
logger.Info("Starting server", "port", cfg.Port)
|
logger.Info("Starting server", "port", cfg.Port)
|
||||||
|
|
||||||
if err := app.Run(); err != nil {
|
if err := app.Run(); err != nil {
|
||||||
|
|
|
||||||
179
docs/docs.go
179
docs/docs.go
|
|
@ -442,6 +442,76 @@ 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",
|
||||||
|
|
@ -3627,6 +3697,70 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/webhooks/veli": {
|
||||||
|
"post": {
|
||||||
|
"description": "Processes game round settlements from Veli",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Virtual 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": {
|
||||||
|
|
@ -4291,6 +4425,51 @@ 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.VerifyTransactionResponse": {
|
"domain.VerifyTransactionResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
||||||
|
|
@ -434,6 +434,76 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/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",
|
||||||
|
|
@ -3619,6 +3689,70 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/webhooks/veli": {
|
||||||
|
"post": {
|
||||||
|
"description": "Processes game round settlements from Veli",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Virtual 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": {
|
||||||
|
|
@ -4283,6 +4417,51 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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.VerifyTransactionResponse": {
|
"domain.VerifyTransactionResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
||||||
|
|
@ -454,6 +454,39 @@ definitions:
|
||||||
description: Converted from "time" field in UNIX format
|
description: Converted from "time" field in UNIX format
|
||||||
type: string
|
type: string
|
||||||
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.VerifyTransactionResponse:
|
domain.VerifyTransactionResponse:
|
||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
|
|
@ -1598,6 +1631,52 @@ 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:
|
||||||
|
|
@ -3689,6 +3768,48 @@ 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:
|
||||||
|
- Virtual Games
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
in: header
|
in: header
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -26,20 +27,32 @@ var (
|
||||||
ErrInvalidPopOKSecretKey = errors.New("PopOK secret key is invalid")
|
ErrInvalidPopOKSecretKey = errors.New("PopOK secret key is invalid")
|
||||||
ErrInvalidPopOKBaseURL = errors.New("PopOK base URL is invalid")
|
ErrInvalidPopOKBaseURL = errors.New("PopOK base URL is invalid")
|
||||||
ErrInvalidPopOKCallbackURL = errors.New("PopOK callback URL is invalid")
|
ErrInvalidPopOKCallbackURL = errors.New("PopOK callback URL is invalid")
|
||||||
|
ErrInvalidVeliAPIURL = errors.New("Veli API URL is invalid")
|
||||||
|
ErrInvalidVeliOperatorKey = errors.New("Veli operator key is invalid")
|
||||||
|
ErrInvalidVeliSecretKey = errors.New("Veli secret key is invalid")
|
||||||
)
|
)
|
||||||
|
|
||||||
type AleaPlayConfig struct {
|
type AleaPlayConfig struct {
|
||||||
Enabled bool `mapstructure:"enabled"`
|
Enabled bool `mapstructure:"enabled"`
|
||||||
BaseURL string `mapstructure:"base_url"` // "https://api.aleaplay.com"
|
BaseURL string `mapstructure:"base_url"` // "https://api.aleaplay.com"
|
||||||
OperatorID string `mapstructure:"operator_id"` // Your operator ID with Alea
|
OperatorID string `mapstructure:"operator_id"` // Your operator ID with Alea
|
||||||
SecretKey string `mapstructure:"secret_key"` // API secret for signatures
|
SecretKey string `mapstructure:"secret_key"` // API secret for signatures
|
||||||
GameListURL string `mapstructure:"game_list_url"` // Endpoint to fetch available games
|
GameListURL string `mapstructure:"game_list_url"` // Endpoint to fetch available games
|
||||||
|
|
||||||
// Optional settings
|
|
||||||
DefaultCurrency string `mapstructure:"default_currency"` // "USD", "EUR", etc.
|
DefaultCurrency string `mapstructure:"default_currency"` // "USD", "EUR", etc.
|
||||||
SessionTimeout int `mapstructure:"session_timeout"` // In hours
|
SessionTimeout int `mapstructure:"session_timeout"` // In hours
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VeliGamesConfig struct {
|
||||||
|
Enabled bool `mapstructure:"enabled"`
|
||||||
|
APIURL string `mapstructure:"api_url"`
|
||||||
|
OperatorKey string `mapstructure:"operator_key"`
|
||||||
|
SecretKey string `mapstructure:"secret_key"`
|
||||||
|
DefaultCurrency string `mapstructure:"default_currency"`
|
||||||
|
GameIDs struct {
|
||||||
|
Aviator string `mapstructure:"aviator"`
|
||||||
|
} `mapstructure:"game_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Port int
|
Port int
|
||||||
DbUrl string
|
DbUrl string
|
||||||
|
|
@ -60,7 +73,8 @@ type Config struct {
|
||||||
CHAPA_RETURN_URL string
|
CHAPA_RETURN_URL string
|
||||||
Bet365Token string
|
Bet365Token string
|
||||||
PopOK domain.PopOKConfig
|
PopOK domain.PopOKConfig
|
||||||
AleaPlay AleaPlayConfig `mapstructure:"alea_play"`
|
AleaPlay AleaPlayConfig `mapstructure:"alea_play"`
|
||||||
|
VeliGames VeliGamesConfig `mapstructure:"veli_games"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig() (*Config, error) {
|
func NewConfig() (*Config, error) {
|
||||||
|
|
@ -135,6 +149,7 @@ func (c *Config) loadEnv() error {
|
||||||
return ErrInvalidLevel
|
return ErrInvalidLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Chapa
|
||||||
c.CHAPA_SECRET_KEY = os.Getenv("CHAPA_SECRET_KEY")
|
c.CHAPA_SECRET_KEY = os.Getenv("CHAPA_SECRET_KEY")
|
||||||
c.CHAPA_PUBLIC_KEY = os.Getenv("CHAPA_PUBLIC_KEY")
|
c.CHAPA_PUBLIC_KEY = os.Getenv("CHAPA_PUBLIC_KEY")
|
||||||
c.CHAPA_ENCRYPTION_KEY = os.Getenv("CHAPA_ENCRYPTION_KEY")
|
c.CHAPA_ENCRYPTION_KEY = os.Getenv("CHAPA_ENCRYPTION_KEY")
|
||||||
|
|
@ -145,6 +160,84 @@ func (c *Config) loadEnv() error {
|
||||||
c.CHAPA_CALLBACK_URL = os.Getenv("CHAPA_CALLBACK_URL")
|
c.CHAPA_CALLBACK_URL = os.Getenv("CHAPA_CALLBACK_URL")
|
||||||
c.CHAPA_RETURN_URL = os.Getenv("CHAPA_RETURN_URL")
|
c.CHAPA_RETURN_URL = os.Getenv("CHAPA_RETURN_URL")
|
||||||
|
|
||||||
|
//Alea Play
|
||||||
|
aleaEnabled := os.Getenv("ALEA_ENABLED")
|
||||||
|
if aleaEnabled == "" {
|
||||||
|
aleaEnabled = "false" // Default disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled, err := strconv.ParseBool(aleaEnabled); err != nil {
|
||||||
|
return fmt.Errorf("invalid ALEA_ENABLED value: %w", err)
|
||||||
|
} else {
|
||||||
|
c.AleaPlay.Enabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AleaPlay.BaseURL = os.Getenv("ALEA_BASE_URL")
|
||||||
|
if c.AleaPlay.BaseURL == "" && c.AleaPlay.Enabled {
|
||||||
|
return errors.New("ALEA_BASE_URL is required when Alea is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AleaPlay.OperatorID = os.Getenv("ALEA_OPERATOR_ID")
|
||||||
|
if c.AleaPlay.OperatorID == "" && c.AleaPlay.Enabled {
|
||||||
|
return errors.New("ALEA_OPERATOR_ID is required when Alea is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AleaPlay.SecretKey = os.Getenv("ALEA_SECRET_KEY")
|
||||||
|
if c.AleaPlay.SecretKey == "" && c.AleaPlay.Enabled {
|
||||||
|
return errors.New("ALEA_SECRET_KEY is required when Alea is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AleaPlay.GameListURL = os.Getenv("ALEA_GAME_LIST_URL")
|
||||||
|
c.AleaPlay.DefaultCurrency = os.Getenv("ALEA_DEFAULT_CURRENCY")
|
||||||
|
if c.AleaPlay.DefaultCurrency == "" {
|
||||||
|
c.AleaPlay.DefaultCurrency = "USD"
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionTimeoutStr := os.Getenv("ALEA_SESSION_TIMEOUT")
|
||||||
|
if sessionTimeoutStr != "" {
|
||||||
|
timeout, err := strconv.Atoi(sessionTimeoutStr)
|
||||||
|
if err == nil {
|
||||||
|
c.AleaPlay.SessionTimeout = timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Veli Games
|
||||||
|
veliEnabled := os.Getenv("VELI_ENABLED")
|
||||||
|
if veliEnabled == "" {
|
||||||
|
veliEnabled = "false" // Default to disabled if not specified
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled, err := strconv.ParseBool(veliEnabled); err != nil {
|
||||||
|
return fmt.Errorf("invalid VELI_ENABLED value: %w", err)
|
||||||
|
} else {
|
||||||
|
c.VeliGames.Enabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
apiURL := os.Getenv("VELI_API_URL")
|
||||||
|
if apiURL == "" {
|
||||||
|
apiURL = "https://api.velitech.games" // Default production URL
|
||||||
|
}
|
||||||
|
c.VeliGames.APIURL = apiURL
|
||||||
|
|
||||||
|
operatorKey := os.Getenv("VELI_OPERATOR_KEY")
|
||||||
|
if operatorKey == "" && c.VeliGames.Enabled {
|
||||||
|
return ErrInvalidVeliOperatorKey
|
||||||
|
}
|
||||||
|
c.VeliGames.OperatorKey = operatorKey
|
||||||
|
|
||||||
|
secretKey := os.Getenv("VELI_SECRET_KEY")
|
||||||
|
if secretKey == "" && c.VeliGames.Enabled {
|
||||||
|
return ErrInvalidVeliSecretKey
|
||||||
|
}
|
||||||
|
c.VeliGames.SecretKey = secretKey
|
||||||
|
c.VeliGames.GameIDs.Aviator = os.Getenv("VELI_GAME_ID_AVIATOR")
|
||||||
|
|
||||||
|
defaultCurrency := os.Getenv("VELI_DEFAULT_CURRENCY")
|
||||||
|
if defaultCurrency == "" {
|
||||||
|
defaultCurrency = "USD" // Default currency
|
||||||
|
}
|
||||||
|
c.VeliGames.DefaultCurrency = defaultCurrency
|
||||||
|
|
||||||
c.LogLevel = lvl
|
c.LogLevel = lvl
|
||||||
|
|
||||||
c.AFRO_SMS_API_KEY = os.Getenv("AFRO_SMS_API_KEY")
|
c.AFRO_SMS_API_KEY = os.Getenv("AFRO_SMS_API_KEY")
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,9 @@ type VirtualGameTransaction struct {
|
||||||
Multiplier float64 `json:"multiplier"` // For games like Aviator
|
Multiplier float64 `json:"multiplier"` // For games like Aviator
|
||||||
IsFreeRound bool `json:"is_free_round"` // For bonus play
|
IsFreeRound bool `json:"is_free_round"` // For bonus play
|
||||||
OperatorID string `json:"operator_id"` // Your operator ID
|
OperatorID string `json:"operator_id"` // Your operator ID
|
||||||
|
|
||||||
|
// Veli specific fields
|
||||||
|
GameSpecificData GameSpecificData `json:"game_specific_data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// type VirtualGameTransaction struct {
|
// type VirtualGameTransaction struct {
|
||||||
|
|
@ -96,19 +99,21 @@ type AleaPlayCallback struct {
|
||||||
Signature string `json:"signature"`
|
Signature string `json:"signature"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Extend VirtualGameTransaction for Alea compatibility
|
type VeliCallback struct {
|
||||||
// type VirtualGameTransaction struct {
|
EventType string `json:"event_type"` // "bet_placed", "game_result", etc.
|
||||||
// ID string `json:"id"`
|
RoundID string `json:"round_id"` // Unique round identifier (replaces transaction_id)
|
||||||
// SessionID string `json:"session_id"`
|
SessionID string `json:"session_id"` // Matches VirtualGameSession.SessionToken
|
||||||
// UserID int64 `json:"user_id"`
|
UserID string `json:"user_id"` // Veli's user identifier
|
||||||
// WalletID int64 `json:"wallet_id"`
|
GameID string `json:"game_id"` // e.g., "veli_aviator_v1"
|
||||||
// TransactionType string `json:"transaction_type"` // Matches Alea's types
|
Amount float64 `json:"amount"` // Transaction amount
|
||||||
// Amount int64 `json:"amount"` // In cents
|
Multiplier float64 `json:"multiplier"` // For games with multipliers (Aviator/Plinko)
|
||||||
// Currency string `json:"currency"`
|
Currency string `json:"currency"` // e.g., "USD"
|
||||||
// ExternalTransactionID string `json:"external_transaction_id"`
|
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||||
// Status string `json:"status"`
|
Signature string `json:"signature"` // HMAC-SHA256
|
||||||
// GameID string `json:"game_id"` // Track which game this was for
|
}
|
||||||
// RoundID string `json:"round_id,omitempty"`
|
|
||||||
// CreatedAt time.Time `json:"created_at"`
|
type GameSpecificData struct {
|
||||||
// UpdatedAt time.Time `json:"updated_at"`
|
Multiplier float64 `json:"multiplier,omitempty"`
|
||||||
// }
|
RiskLevel string `json:"risk_level,omitempty"` // For Mines
|
||||||
|
BucketIndex int `json:"bucket_index,omitempty"` // For Plinko
|
||||||
|
}
|
||||||
|
|
|
||||||
13
internal/services/virtualGame/veli/port.go
Normal file
13
internal/services/virtualGame/veli/port.go
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// services/veli/service.go
|
||||||
|
package veli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VeliVirtualGameService interface {
|
||||||
|
GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error)
|
||||||
|
HandleCallback(ctx context.Context, callback *domain.VeliCallback) error
|
||||||
|
}
|
||||||
161
internal/services/virtualGame/veli/service.go
Normal file
161
internal/services/virtualGame/veli/service.go
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
package veli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VeliPlayService struct {
|
||||||
|
repo repository.VirtualGameRepository
|
||||||
|
walletSvc wallet.Service
|
||||||
|
config *config.VeliGamesConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVeliPlayService(
|
||||||
|
repo repository.VirtualGameRepository,
|
||||||
|
walletSvc wallet.Service,
|
||||||
|
cfg *config.Config,
|
||||||
|
logger *slog.Logger,
|
||||||
|
) *VeliPlayService {
|
||||||
|
return &VeliPlayService{
|
||||||
|
repo: repo,
|
||||||
|
walletSvc: walletSvc,
|
||||||
|
config: &cfg.VeliGames,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateGameLaunchURL mirrors Alea's pattern but uses Veli's auth requirements
|
||||||
|
func (s *VeliPlayService) GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) {
|
||||||
|
session := &domain.VirtualGameSession{
|
||||||
|
UserID: userID,
|
||||||
|
GameID: gameID,
|
||||||
|
SessionToken: generateSessionToken(userID),
|
||||||
|
Currency: currency,
|
||||||
|
Status: "ACTIVE",
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.repo.CreateVirtualGameSession(ctx, session); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create game session: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Veli-specific parameters
|
||||||
|
params := url.Values{
|
||||||
|
"operator_key": []string{s.config.OperatorKey}, // Different from Alea's operator_id
|
||||||
|
"user_id": []string{fmt.Sprintf("%d", userID)},
|
||||||
|
"game_id": []string{gameID},
|
||||||
|
"currency": []string{currency},
|
||||||
|
"mode": []string{mode},
|
||||||
|
"timestamp": []string{fmt.Sprintf("%d", time.Now().Unix())},
|
||||||
|
}
|
||||||
|
|
||||||
|
signature := s.generateSignature(params.Encode())
|
||||||
|
params.Add("signature", signature)
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/launch?%s", s.config.APIURL, params.Encode()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleCallback processes Veli's webhooks (similar structure to Alea)
|
||||||
|
func (s *VeliPlayService) HandleCallback(ctx context.Context, callback *domain.VeliCallback) error {
|
||||||
|
if !s.verifyCallbackSignature(callback) {
|
||||||
|
return errors.New("invalid callback signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Veli uses round_id instead of transaction_id for idempotency
|
||||||
|
existing, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, callback.RoundID)
|
||||||
|
if err != nil || existing != nil {
|
||||||
|
s.logger.Warn("duplicate round detected", "round_id", callback.RoundID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := s.repo.GetVirtualGameSessionByToken(ctx, callback.SessionID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get game session: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert amount based on event type (BET, WIN, etc.)
|
||||||
|
amount := convertAmount(callback.Amount, callback.EventType)
|
||||||
|
|
||||||
|
tx := &domain.VirtualGameTransaction{
|
||||||
|
SessionID: session.ID,
|
||||||
|
UserID: session.UserID,
|
||||||
|
TransactionType: callback.EventType, // e.g., "bet_placed", "game_result"
|
||||||
|
Amount: amount,
|
||||||
|
Currency: callback.Currency,
|
||||||
|
ExternalTransactionID: callback.RoundID, // Veli uses round_id as the unique identifier
|
||||||
|
Status: "COMPLETED",
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
GameSpecificData: domain.GameSpecificData{
|
||||||
|
Multiplier: callback.Multiplier, // Used for Aviator/Plinko
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.processTransaction(ctx, tx, session.UserID); err != nil {
|
||||||
|
return fmt.Errorf("failed to process transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared helper methods (same pattern as Alea)
|
||||||
|
func (s *VeliPlayService) generateSignature(data string) string {
|
||||||
|
h := hmac.New(sha256.New, []byte(s.config.SecretKey))
|
||||||
|
h.Write([]byte(data))
|
||||||
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VeliPlayService) verifyCallbackSignature(cb *domain.VeliCallback) bool {
|
||||||
|
signData := fmt.Sprintf("%s%s%s%.2f%s%d",
|
||||||
|
cb.RoundID, // Veli uses round_id instead of transaction_id
|
||||||
|
cb.SessionID,
|
||||||
|
cb.EventType,
|
||||||
|
cb.Amount,
|
||||||
|
cb.Currency,
|
||||||
|
cb.Timestamp,
|
||||||
|
)
|
||||||
|
expectedSig := s.generateSignature(signData)
|
||||||
|
return expectedSig == cb.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertAmount(amount float64, eventType string) int64 {
|
||||||
|
cents := int64(amount * 100)
|
||||||
|
if eventType == "bet_placed" {
|
||||||
|
return -cents // Debit for bets
|
||||||
|
}
|
||||||
|
return cents // Credit for wins/results
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSessionToken(userID int64) string {
|
||||||
|
return fmt.Sprintf("veli-%d-%d", userID, time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VeliPlayService) processTransaction(ctx context.Context, tx *domain.VirtualGameTransaction, userID int64) error {
|
||||||
|
wallets, err := s.walletSvc.GetWalletsByUser(ctx, userID)
|
||||||
|
if err != nil || len(wallets) == 0 {
|
||||||
|
return errors.New("no wallet available for user")
|
||||||
|
}
|
||||||
|
tx.WalletID = wallets[0].ID
|
||||||
|
|
||||||
|
if err := s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount)); err != nil {
|
||||||
|
return fmt.Errorf("wallet update failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.repo.CreateVirtualGameTransaction(ctx, tx)
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
||||||
virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
|
virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
|
||||||
alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea"
|
alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||||
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
|
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
|
||||||
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
|
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
|
||||||
|
|
@ -39,6 +40,7 @@ type App struct {
|
||||||
betSvc *bet.Service
|
betSvc *bet.Service
|
||||||
virtualGameSvc virtualgameservice.VirtualGameService
|
virtualGameSvc virtualgameservice.VirtualGameService
|
||||||
aleaVirtualGameService alea.AleaVirtualGameService
|
aleaVirtualGameService alea.AleaVirtualGameService
|
||||||
|
veliVirtualGameService veli.VeliVirtualGameService
|
||||||
walletSvc *wallet.Service
|
walletSvc *wallet.Service
|
||||||
transactionSvc *transaction.Service
|
transactionSvc *transaction.Service
|
||||||
ticketSvc *ticket.Service
|
ticketSvc *ticket.Service
|
||||||
|
|
@ -71,6 +73,7 @@ func NewApp(
|
||||||
referralSvc referralservice.ReferralStore,
|
referralSvc referralservice.ReferralStore,
|
||||||
virtualGameSvc virtualgameservice.VirtualGameService,
|
virtualGameSvc virtualgameservice.VirtualGameService,
|
||||||
aleaVirtualGameService alea.AleaVirtualGameService,
|
aleaVirtualGameService alea.AleaVirtualGameService,
|
||||||
|
veliVirtualGameService veli.VeliVirtualGameService,
|
||||||
resultSvc *result.Service,
|
resultSvc *result.Service,
|
||||||
cfg *config.Config,
|
cfg *config.Config,
|
||||||
) *App {
|
) *App {
|
||||||
|
|
@ -109,6 +112,7 @@ func NewApp(
|
||||||
eventSvc: eventSvc,
|
eventSvc: eventSvc,
|
||||||
virtualGameSvc: virtualGameSvc,
|
virtualGameSvc: virtualGameSvc,
|
||||||
aleaVirtualGameService: aleaVirtualGameService,
|
aleaVirtualGameService: aleaVirtualGameService,
|
||||||
|
veliVirtualGameService: veliVirtualGameService,
|
||||||
resultSvc: resultSvc,
|
resultSvc: resultSvc,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
||||||
virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
|
virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
|
||||||
alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea"
|
alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||||
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
|
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
|
||||||
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
|
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
|
||||||
|
|
@ -37,6 +38,7 @@ type Handler struct {
|
||||||
eventSvc event.Service
|
eventSvc event.Service
|
||||||
virtualGameSvc virtualgameservice.VirtualGameService
|
virtualGameSvc virtualgameservice.VirtualGameService
|
||||||
aleaVirtualGameSvc alea.AleaVirtualGameService
|
aleaVirtualGameSvc alea.AleaVirtualGameService
|
||||||
|
veliVirtualGameSvc veli.VeliVirtualGameService
|
||||||
authSvc *authentication.Service
|
authSvc *authentication.Service
|
||||||
jwtConfig jwtutil.JwtConfig
|
jwtConfig jwtutil.JwtConfig
|
||||||
validator *customvalidator.CustomValidator
|
validator *customvalidator.CustomValidator
|
||||||
|
|
@ -51,6 +53,7 @@ func New(
|
||||||
referralSvc referralservice.ReferralStore,
|
referralSvc referralservice.ReferralStore,
|
||||||
virtualGameSvc virtualgameservice.VirtualGameService,
|
virtualGameSvc virtualgameservice.VirtualGameService,
|
||||||
aleaVirtualGameSvc alea.AleaVirtualGameService,
|
aleaVirtualGameSvc alea.AleaVirtualGameService,
|
||||||
|
veliVirtualGameSvc veli.VeliVirtualGameService,
|
||||||
userSvc *user.Service,
|
userSvc *user.Service,
|
||||||
transactionSvc *transaction.Service,
|
transactionSvc *transaction.Service,
|
||||||
ticketSvc *ticket.Service,
|
ticketSvc *ticket.Service,
|
||||||
|
|
@ -79,6 +82,7 @@ func New(
|
||||||
eventSvc: eventSvc,
|
eventSvc: eventSvc,
|
||||||
virtualGameSvc: virtualGameSvc,
|
virtualGameSvc: virtualGameSvc,
|
||||||
aleaVirtualGameSvc: aleaVirtualGameSvc,
|
aleaVirtualGameSvc: aleaVirtualGameSvc,
|
||||||
|
veliVirtualGameSvc: veliVirtualGameSvc,
|
||||||
authSvc: authSvc,
|
authSvc: authSvc,
|
||||||
jwtConfig: jwtConfig,
|
jwtConfig: jwtConfig,
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
|
|
|
||||||
75
internal/web_server/handlers/veli_games.go
Normal file
75
internal/web_server/handlers/veli_games.go
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LaunchVeliGame godoc
|
||||||
|
// @Summary Launch a Veli game
|
||||||
|
// @Description Generates authenticated launch URL for Veli games
|
||||||
|
// @Tags Veli Games
|
||||||
|
// @Security BearerAuth
|
||||||
|
// @Param game_id path string true "Game ID (e.g., veli_aviator_v1)"
|
||||||
|
// @Param currency query string false "Currency code" default(USD)
|
||||||
|
// @Param mode query string false "Game mode" Enums(real, demo) default(real)
|
||||||
|
// @Success 200 {object} map[string]string "Returns launch URL"
|
||||||
|
// @Failure 400 {object} map[string]string "Invalid request"
|
||||||
|
// @Failure 500 {object} map[string]string "Internal server error"
|
||||||
|
// @Router /api/veli/launch/{game_id} [get]
|
||||||
|
func (h *Handler) LaunchVeliGame(c *fiber.Ctx) error {
|
||||||
|
userID := c.Locals("userID").(int64)
|
||||||
|
gameID := c.Params("game_id")
|
||||||
|
currency := c.Query("currency", "USD")
|
||||||
|
mode := c.Query("mode", "real")
|
||||||
|
|
||||||
|
launchURL, err := h.veliVirtualGameSvc.GenerateGameLaunchURL(c.Context(), userID, gameID, currency, mode)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("failed to generate Veli launch URL",
|
||||||
|
"error", err,
|
||||||
|
"userID", userID,
|
||||||
|
"gameID", gameID)
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"error": "failed to launch game",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"launch_url": launchURL,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleVeliCallback godoc
|
||||||
|
// @Summary Veli Games webhook handler
|
||||||
|
// @Description Processes game round settlements from Veli
|
||||||
|
// @Tags Virtual Games
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param payload body domain.VeliCallback true "Callback payload"
|
||||||
|
// @Success 200 {object} map[string]string "Callback processed"
|
||||||
|
// @Failure 400 {object} map[string]string "Invalid payload"
|
||||||
|
// @Failure 403 {object} map[string]string "Invalid signature"
|
||||||
|
// @Failure 500 {object} map[string]string "Processing error"
|
||||||
|
// @Router /webhooks/veli [post]
|
||||||
|
func (h *Handler) HandleVeliCallback(c *fiber.Ctx) error {
|
||||||
|
var cb domain.VeliCallback
|
||||||
|
if err := c.BodyParser(&cb); err != nil {
|
||||||
|
h.logger.Error("invalid Veli callback format", "error", err)
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"error": "invalid payload format",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.veliVirtualGameSvc.HandleCallback(c.Context(), &cb); err != nil {
|
||||||
|
h.logger.Error("failed to process Veli callback",
|
||||||
|
"roundID", cb.RoundID,
|
||||||
|
"error", err)
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"error": "failed to process callback",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"status": "processed",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ func (a *App) initAppRoutes() {
|
||||||
a.referralSvc,
|
a.referralSvc,
|
||||||
a.virtualGameSvc,
|
a.virtualGameSvc,
|
||||||
a.aleaVirtualGameService,
|
a.aleaVirtualGameService,
|
||||||
|
a.veliVirtualGameService,
|
||||||
a.userSvc,
|
a.userSvc,
|
||||||
a.transactionSvc,
|
a.transactionSvc,
|
||||||
a.ticketSvc,
|
a.ticketSvc,
|
||||||
|
|
@ -166,17 +167,18 @@ func (a *App) initAppRoutes() {
|
||||||
a.fiber.Post("/transfer/refill/:id", a.authMiddleware, h.RefillWallet)
|
a.fiber.Post("/transfer/refill/:id", a.authMiddleware, h.RefillWallet)
|
||||||
|
|
||||||
//Chapa Routes
|
//Chapa Routes
|
||||||
a.fiber.Post("/api/v1/chapa/payments/initialize", h.InitializePayment)
|
group := a.fiber.Group("/api/v1")
|
||||||
a.fiber.Get("/api/v1/chapa/payments/verify/:tx_ref", h.VerifyTransaction)
|
|
||||||
a.fiber.Post("/api/v1/chapa/payments/callback", h.ReceiveWebhook)
|
group.Post("/chapa/payments/initialize", h.InitializePayment)
|
||||||
a.fiber.Get("/api/v1/chapa/banks", h.GetBanks)
|
group.Get("/chapa/payments/verify/:tx_ref", h.VerifyTransaction)
|
||||||
a.fiber.Post("/api/v1/chapa/transfers", h.CreateTransfer)
|
group.Post("/chapa/payments/callback", h.ReceiveWebhook)
|
||||||
a.fiber.Get("/api/v1/chapa/transfers/verify/:transfer_ref", h.VerifyTransfer)
|
group.Get("/chapa/banks", h.GetBanks)
|
||||||
|
group.Post("/chapa/transfers", h.CreateTransfer)
|
||||||
|
group.Get("/chapa/transfers/verify/:transfer_ref", h.VerifyTransfer)
|
||||||
|
|
||||||
//Alea Play Virtual Game Routes
|
//Alea Play Virtual Game Routes
|
||||||
a.fiber.Get("/api/v1/alea-games/launch", a.authMiddleware, h.LaunchAleaGame)
|
group.Get("/alea-games/launch", a.authMiddleware, h.LaunchAleaGame)
|
||||||
a.fiber.Post("/api/v1/webhooks/alea", a.authMiddleware, h.HandleAleaCallback)
|
group.Post("/webhooks/alea-games", a.authMiddleware, h.HandleAleaCallback)
|
||||||
// a.fiber.Post("/webhooks/alea", middleware.AleaWebhookMiddleware(a.cfg.AleaPlay.SecretKey), h.HandleAleaCallback)
|
|
||||||
|
|
||||||
// Transactions /transactions
|
// Transactions /transactions
|
||||||
a.fiber.Post("/transaction", a.authMiddleware, h.CreateTransaction)
|
a.fiber.Post("/transaction", a.authMiddleware, h.CreateTransaction)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user