Merge branch 'main' into ticket-bet
This commit is contained in:
commit
5cfb23cf96
|
|
@ -48,7 +48,6 @@ 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/services/wallet/monitor"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet/monitor"
|
||||||
|
|
||||||
|
|
@ -130,7 +129,7 @@ func main() {
|
||||||
referalSvc := referralservice.New(referalRepo, *walletSvc, store, cfg, logger)
|
referalSvc := referralservice.New(referalRepo, *walletSvc, store, cfg, logger)
|
||||||
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
|
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
|
||||||
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
||||||
veliService := veli.NewVeliPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
// veliService := veli.NewVeliPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
||||||
recommendationSvc := recommendation.NewService(recommendationRepo)
|
recommendationSvc := recommendation.NewService(recommendationRepo)
|
||||||
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
|
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
|
||||||
|
|
||||||
|
|
@ -225,7 +224,7 @@ func main() {
|
||||||
referalSvc,
|
referalSvc,
|
||||||
virtualGameSvc,
|
virtualGameSvc,
|
||||||
aleaService,
|
aleaService,
|
||||||
veliService,
|
// veliService,
|
||||||
recommendationSvc,
|
recommendationSvc,
|
||||||
resultSvc,
|
resultSvc,
|
||||||
cfg,
|
cfg,
|
||||||
|
|
|
||||||
6
go.mod
6
go.mod
|
|
@ -77,9 +77,13 @@ require (
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-resty/resty/v2 v2.16.5
|
||||||
|
github.com/twilio/twilio-go v1.26.3
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/twilio/twilio-go v1.26.3 // indirect
|
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
6
go.sum
6
go.sum
|
|
@ -50,6 +50,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
|
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||||
|
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||||
github.com/gofiber/fiber/v2 v2.32.0/go.mod h1:CMy5ZLiXkn6qwthrl03YMyW1NLfj0rhxz2LKl4t7ZTY=
|
github.com/gofiber/fiber/v2 v2.32.0/go.mod h1:CMy5ZLiXkn6qwthrl03YMyW1NLfj0rhxz2LKl4t7ZTY=
|
||||||
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
||||||
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||||
|
|
@ -97,6 +99,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q=
|
||||||
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw=
|
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw=
|
||||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
|
@ -185,7 +188,6 @@ go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeH
|
||||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||||
|
|
@ -246,6 +248,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
|
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
|
|
||||||
|
|
@ -47,15 +47,14 @@ type AleaPlayConfig struct {
|
||||||
SessionTimeout int `mapstructure:"session_timeout"` // In hours
|
SessionTimeout int `mapstructure:"session_timeout"` // In hours
|
||||||
}
|
}
|
||||||
|
|
||||||
type VeliGamesConfig struct {
|
type VeliConfig struct {
|
||||||
Enabled bool `mapstructure:"enabled"`
|
APIKey string `mapstructure:"VELI_API_KEY"`
|
||||||
APIURL string `mapstructure:"api_url"`
|
BaseURL string `mapstructure:"VELI_BASE_URL"`
|
||||||
OperatorKey string `mapstructure:"operator_key"`
|
SecretKey string `mapstructure:"VELI_SECRET_KEY"`
|
||||||
SecretKey string `mapstructure:"secret_key"`
|
OperatorID string `mapstructure:"VELI_OPERATOR_ID"`
|
||||||
DefaultCurrency string `mapstructure:"default_currency"`
|
Currency string `mapstructure:"VELI_DEFAULT_CURRENCY"`
|
||||||
GameIDs struct {
|
WebhookURL string `mapstructure:"VELI_WEBHOOK_URL"`
|
||||||
Aviator string `mapstructure:"aviator"`
|
Enabled bool `mapstructure:"Enabled"`
|
||||||
} `mapstructure:"game_ids"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|
@ -63,6 +62,7 @@ type Config struct {
|
||||||
FIXER_BASE_URL string
|
FIXER_BASE_URL string
|
||||||
BASE_CURRENCY domain.IntCurrency
|
BASE_CURRENCY domain.IntCurrency
|
||||||
Port int
|
Port int
|
||||||
|
Service string
|
||||||
DbUrl string
|
DbUrl string
|
||||||
RefreshExpiry int
|
RefreshExpiry int
|
||||||
AccessExpiry int
|
AccessExpiry int
|
||||||
|
|
@ -85,7 +85,7 @@ type Config struct {
|
||||||
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"`
|
VeliGames VeliConfig `mapstructure:"veli_games"`
|
||||||
ResendApiKey string
|
ResendApiKey string
|
||||||
ResendSenderEmail string
|
ResendSenderEmail string
|
||||||
TwilioAccountSid string
|
TwilioAccountSid string
|
||||||
|
|
@ -242,26 +242,26 @@ func (c *Config) loadEnv() error {
|
||||||
if apiURL == "" {
|
if apiURL == "" {
|
||||||
apiURL = "https://api.velitech.games" // Default production URL
|
apiURL = "https://api.velitech.games" // Default production URL
|
||||||
}
|
}
|
||||||
c.VeliGames.APIURL = apiURL
|
c.VeliGames.BaseURL = apiURL
|
||||||
|
|
||||||
operatorKey := os.Getenv("VELI_OPERATOR_KEY")
|
operatorKey := os.Getenv("VELI_OPERATOR_KEY")
|
||||||
if operatorKey == "" && c.VeliGames.Enabled {
|
if operatorKey == "" && c.VeliGames.Enabled {
|
||||||
return ErrInvalidVeliOperatorKey
|
return ErrInvalidVeliOperatorKey
|
||||||
}
|
}
|
||||||
c.VeliGames.OperatorKey = operatorKey
|
// c.VeliGames.OperatorKey = operatorKey
|
||||||
|
|
||||||
secretKey := os.Getenv("VELI_SECRET_KEY")
|
secretKey := os.Getenv("VELI_SECRET_KEY")
|
||||||
if secretKey == "" && c.VeliGames.Enabled {
|
if secretKey == "" && c.VeliGames.Enabled {
|
||||||
return ErrInvalidVeliSecretKey
|
return ErrInvalidVeliSecretKey
|
||||||
}
|
}
|
||||||
c.VeliGames.SecretKey = secretKey
|
c.VeliGames.SecretKey = secretKey
|
||||||
c.VeliGames.GameIDs.Aviator = os.Getenv("VELI_GAME_ID_AVIATOR")
|
// c.VeliGames.GameIDs.Aviator = os.Getenv("VELI_GAME_ID_AVIATOR")
|
||||||
|
|
||||||
defaultCurrency := os.Getenv("VELI_DEFAULT_CURRENCY")
|
defaultCurrency := os.Getenv("VELI_DEFAULT_CURRENCY")
|
||||||
if defaultCurrency == "" {
|
if defaultCurrency == "" {
|
||||||
defaultCurrency = "USD" // Default currency
|
defaultCurrency = "USD" // Default currency
|
||||||
}
|
}
|
||||||
c.VeliGames.DefaultCurrency = defaultCurrency
|
// c.VeliGames.DefaultCurrency = defaultCurrency
|
||||||
|
|
||||||
c.LogLevel = lvl
|
c.LogLevel = lvl
|
||||||
|
|
||||||
|
|
|
||||||
36
internal/domain/veli_games.go
Normal file
36
internal/domain/veli_games.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Game struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ReleaseDate string `json:"release_date"`
|
||||||
|
Developer string `json:"developer"`
|
||||||
|
Publisher string `json:"publisher"`
|
||||||
|
Genres []string `json:"genres"`
|
||||||
|
Platforms []string `json:"platforms"`
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameListResponse struct {
|
||||||
|
Data []Game `json:"data"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PerPage int `json:"per_page"`
|
||||||
|
TotalPages int `json:"total_pages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameCreateRequest struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Description string `json:"description" validate:"required"`
|
||||||
|
ReleaseDate string `json:"release_date" validate:"required"`
|
||||||
|
Developer string `json:"developer" validate:"required"`
|
||||||
|
Publisher string `json:"publisher" validate:"required"`
|
||||||
|
Genres []string `json:"genres" validate:"required"`
|
||||||
|
Platforms []string `json:"platforms" validate:"required"`
|
||||||
|
Price float64 `json:"price" validate:"required"`
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
func InitLogger() (*zap.Logger, error) {
|
func InitLogger() (*zap.Logger, error) {
|
||||||
mongoCore, err := NewMongoCore(
|
mongoCore, err := NewMongoCore(
|
||||||
"mongodb://root:secret@localhost:27017/?authSource=admin",
|
os.Getenv("MONGODB_URL"),
|
||||||
"logdb",
|
"logdb",
|
||||||
"applogs",
|
"applogs",
|
||||||
zapcore.InfoLevel,
|
zapcore.InfoLevel,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"maps"
|
"maps"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
|
@ -17,6 +18,7 @@ type MongoCore struct {
|
||||||
collection *mongo.Collection
|
collection *mongo.Collection
|
||||||
level zapcore.Level
|
level zapcore.Level
|
||||||
fields []zapcore.Field
|
fields []zapcore.Field
|
||||||
|
cfg *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMongoCore(uri, dbName, collectionName string, level zapcore.Level) (zapcore.Core, error) {
|
func NewMongoCore(uri, dbName, collectionName string, level zapcore.Level) (zapcore.Core, error) {
|
||||||
|
|
@ -73,8 +75,8 @@ func (mc *MongoCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||||
"fields": logMap,
|
"fields": logMap,
|
||||||
"caller": entry.Caller.String(),
|
"caller": entry.Caller.String(),
|
||||||
"stacktrace": entry.Stack,
|
"stacktrace": entry.Stack,
|
||||||
"service": "fortunebet-backend",
|
"service": mc.cfg.Service,
|
||||||
"env": "dev",
|
"env": mc.cfg.Env,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
|
|
||||||
|
|
@ -148,10 +148,10 @@ func (s *service) HandleCallback(ctx context.Context, callback *domain.PopOKCall
|
||||||
|
|
||||||
func (s *service) GetPlayerInfo(ctx context.Context, req *domain.PopOKPlayerInfoRequest) (*domain.PopOKPlayerInfoResponse, error) {
|
func (s *service) GetPlayerInfo(ctx context.Context, req *domain.PopOKPlayerInfoRequest) (*domain.PopOKPlayerInfoResponse, error) {
|
||||||
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
s.logger.Error("Failed to parse JWT", "error", err)
|
// s.logger.Error("Failed to parse JWT", "error", err)
|
||||||
return nil, fmt.Errorf("invalid token")
|
// return nil, fmt.Errorf("invalid token")
|
||||||
}
|
// }
|
||||||
|
|
||||||
wallets, err := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
wallets, err := s.walletSvc.GetWalletsByUser(ctx, claims.UserID)
|
||||||
if err != nil || len(wallets) == 0 {
|
if err != nil || len(wallets) == 0 {
|
||||||
|
|
@ -170,9 +170,9 @@ func (s *service) GetPlayerInfo(ctx context.Context, req *domain.PopOKPlayerInfo
|
||||||
func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (*domain.PopOKBetResponse, error) {
|
func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (*domain.PopOKBetResponse, error) {
|
||||||
// Validate token and get user ID
|
// Validate token and get user ID
|
||||||
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, fmt.Errorf("invalid token")
|
// return nil, fmt.Errorf("invalid token")
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Convert amount to cents (assuming wallet uses cents)
|
// Convert amount to cents (assuming wallet uses cents)
|
||||||
amountCents := int64(req.Amount * 100)
|
amountCents := int64(req.Amount * 100)
|
||||||
|
|
@ -214,10 +214,10 @@ func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (
|
||||||
func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) {
|
func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) {
|
||||||
// 1. Validate token and get user ID
|
// 1. Validate token and get user ID
|
||||||
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
s.logger.Error("Invalid token in win request", "error", err)
|
// s.logger.Error("Invalid token in win request", "error", err)
|
||||||
return nil, fmt.Errorf("invalid token")
|
// return nil, fmt.Errorf("invalid token")
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 2. Check for duplicate transaction (idempotency)
|
// 2. Check for duplicate transaction (idempotency)
|
||||||
existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID)
|
existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID)
|
||||||
|
|
@ -280,10 +280,10 @@ func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (
|
||||||
func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequest) (*domain.PopOKCancelResponse, error) {
|
func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequest) (*domain.PopOKCancelResponse, error) {
|
||||||
// 1. Validate token and get user ID
|
// 1. Validate token and get user ID
|
||||||
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
s.logger.Error("Invalid token in cancel request", "error", err)
|
// s.logger.Error("Invalid token in cancel request", "error", err)
|
||||||
return nil, fmt.Errorf("invalid token")
|
// return nil, fmt.Errorf("invalid token")
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 2. Find the original bet transaction
|
// 2. Find the original bet transaction
|
||||||
originalBet, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID)
|
originalBet, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID)
|
||||||
|
|
|
||||||
65
internal/services/virtualGame/veli/client.go
Normal file
65
internal/services/virtualGame/veli/client.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
package veli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VeliClient struct {
|
||||||
|
client *resty.Client
|
||||||
|
config *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVeliClient(cfg *config.Config) *VeliClient {
|
||||||
|
client := resty.New().
|
||||||
|
SetBaseURL(cfg.VeliGames.APIKey).
|
||||||
|
SetHeader("Accept", "application/json").
|
||||||
|
SetHeader("X-API-Key", cfg.VeliGames.APIKey).
|
||||||
|
SetTimeout(30 * time.Second)
|
||||||
|
|
||||||
|
return &VeliClient{
|
||||||
|
client: client,
|
||||||
|
config: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vc *VeliClient) Get(ctx context.Context, endpoint string, result interface{}) error {
|
||||||
|
resp, err := vc.client.R().
|
||||||
|
SetContext(ctx).
|
||||||
|
SetResult(result).
|
||||||
|
Get(endpoint)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.IsError() {
|
||||||
|
return fmt.Errorf("API error: %s", resp.Status())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vc *VeliClient) Post(ctx context.Context, endpoint string, body interface{}, result interface{}) error {
|
||||||
|
resp, err := vc.client.R().
|
||||||
|
SetContext(ctx).
|
||||||
|
SetBody(body).
|
||||||
|
SetResult(result).
|
||||||
|
Post(endpoint)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.IsError() {
|
||||||
|
return fmt.Errorf("API error: %s", resp.Status())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add other HTTP methods as needed (Put, Delete, etc.)
|
||||||
|
|
@ -1,158 +1,162 @@
|
||||||
package veli
|
package veli
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
"crypto/hmac"
|
// "fmt"
|
||||||
"crypto/sha256"
|
// "log/slog"
|
||||||
"encoding/hex"
|
// "time"
|
||||||
"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/domain"
|
// "github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
// "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
// )
|
||||||
)
|
|
||||||
|
|
||||||
type VeliPlayService struct {
|
// type Service struct {
|
||||||
repo repository.VirtualGameRepository
|
// client *VeliClient
|
||||||
walletSvc wallet.Service
|
// gameRepo repository.VeliGameRepository
|
||||||
config *config.VeliGamesConfig
|
// playerRepo repository.VeliPlayerRepository
|
||||||
logger *slog.Logger
|
// txRepo repository.VeliTransactionRepository
|
||||||
}
|
// walletSvc wallet.Service
|
||||||
|
// logger domain.Logger
|
||||||
|
// }
|
||||||
|
|
||||||
func NewVeliPlayService(
|
// func NewService(
|
||||||
repo repository.VirtualGameRepository,
|
// client *VeliClient,
|
||||||
walletSvc wallet.Service,
|
// gameRepo repository.VeliGameRepository,
|
||||||
cfg *config.Config,
|
// playerRepo repository.VeliPlayerRepository,
|
||||||
logger *slog.Logger,
|
// txRepo repository.VeliTransactionRepository,
|
||||||
) *VeliPlayService {
|
// walletSvc wallet.Service,
|
||||||
return &VeliPlayService{
|
// logger *slog.Logger,
|
||||||
repo: repo,
|
// ) *Service {
|
||||||
walletSvc: walletSvc,
|
// return &Service{
|
||||||
config: &cfg.VeliGames,
|
// client: client,
|
||||||
logger: logger,
|
// gameRepo: gameRepo,
|
||||||
}
|
// playerRepo: playerRepo,
|
||||||
}
|
// txRepo: txRepo,
|
||||||
|
// walletSvc: walletSvc,
|
||||||
|
// logger: logger,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *VeliPlayService) GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) {
|
// func (s *Service) SyncGames(ctx context.Context) error {
|
||||||
session := &domain.VirtualGameSession{
|
// games, err := s.client.GetGameList(ctx)
|
||||||
UserID: userID,
|
// if err != nil {
|
||||||
GameID: gameID,
|
// return fmt.Errorf("failed to get game list: %w", err)
|
||||||
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 {
|
// for _, game := range games {
|
||||||
return "", fmt.Errorf("failed to create game session: %w", err)
|
// existing, err := s.gameRepo.GetGameByID(ctx, game.ID)
|
||||||
}
|
// if err != nil && err != domain.ErrGameNotFound {
|
||||||
|
// return fmt.Errorf("failed to check existing game: %w", err)
|
||||||
|
// }
|
||||||
|
|
||||||
// Veli-specific parameters
|
// if existing == nil {
|
||||||
params := url.Values{
|
// // New game - create
|
||||||
"operator_key": []string{s.config.OperatorKey}, // Different from Alea's operator_id
|
// if err := s.gameRepo.CreateGame(ctx, game); err != nil {
|
||||||
"user_id": []string{fmt.Sprintf("%d", userID)},
|
// s.logger.Error("failed to create game", "game_id", game.ID, "error", err)
|
||||||
"game_id": []string{gameID},
|
// continue
|
||||||
"currency": []string{currency},
|
// }
|
||||||
"mode": []string{mode},
|
// } else {
|
||||||
"timestamp": []string{fmt.Sprintf("%d", time.Now().Unix())},
|
// // Existing game - update
|
||||||
}
|
// if err := s.gameRepo.UpdateGame(ctx, game); err != nil {
|
||||||
|
// s.logger.Error("failed to update game", "game_id", game.ID, "error", err)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
signature := s.generateSignature(params.Encode())
|
// return nil
|
||||||
params.Add("signature", signature)
|
// }
|
||||||
|
|
||||||
return fmt.Sprintf("%s/launch?%s", s.config.APIURL, params.Encode()), nil
|
// func (s *Service) LaunchGame(ctx context.Context, playerID, gameID string) (string, error) {
|
||||||
}
|
// // Verify player exists
|
||||||
|
// player, err := s.playerRepo.GetPlayer(ctx, playerID)
|
||||||
|
// if err != nil {
|
||||||
|
// return "", fmt.Errorf("failed to get player: %w", err)
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *VeliPlayService) HandleCallback(ctx context.Context, callback *domain.VeliCallback) error {
|
// // Verify game exists
|
||||||
if !s.verifyCallbackSignature(callback) {
|
// game, err := s.gameRepo.GetGameByID(ctx, gameID)
|
||||||
return errors.New("invalid callback signature")
|
// if err != nil {
|
||||||
}
|
// return "", fmt.Errorf("failed to get game: %w", err)
|
||||||
|
// }
|
||||||
|
|
||||||
// Veli uses round_id instead of transaction_id for idempotency
|
// // Call Veli API
|
||||||
existing, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, callback.RoundID)
|
// gameURL, err := s.client.LaunchGame(ctx, playerID, gameID)
|
||||||
if err != nil || existing != nil {
|
// if err != nil {
|
||||||
s.logger.Warn("duplicate round detected", "round_id", callback.RoundID)
|
// return "", fmt.Errorf("failed to launch game: %w", err)
|
||||||
return nil
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
session, err := s.repo.GetVirtualGameSessionByToken(ctx, callback.SessionID)
|
// // Create game session record
|
||||||
if err != nil {
|
// session := domain.GameSession{
|
||||||
return fmt.Errorf("failed to get game session: %w", err)
|
// SessionID: fmt.Sprintf("%s-%s-%d", playerID, gameID, time.Now().Unix()),
|
||||||
}
|
// PlayerID: playerID,
|
||||||
|
// GameID: gameID,
|
||||||
|
// LaunchTime: time.Now(),
|
||||||
|
// Status: "active",
|
||||||
|
// }
|
||||||
|
|
||||||
// Convert amount based on event type (BET, WIN, etc.)
|
// if err := s.gameRepo.CreateGameSession(ctx, session); err != nil {
|
||||||
amount := convertAmount(callback.Amount, callback.EventType)
|
// s.logger.Error("failed to create game session", "error", err)
|
||||||
|
// }
|
||||||
|
|
||||||
tx := &domain.VirtualGameTransaction{
|
// return gameURL, nil
|
||||||
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 {
|
// func (s *Service) PlaceBet(ctx context.Context, playerID, gameID string, amount float64) (*domain.VeliTransaction, error) {
|
||||||
return fmt.Errorf("failed to process transaction: %w", err)
|
// // 1. Verify player balance
|
||||||
}
|
// balance, err := s.walletRepo.GetBalance(ctx, playerID)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("failed to get balance: %w", err)
|
||||||
|
// }
|
||||||
|
|
||||||
return nil
|
// if balance < amount {
|
||||||
}
|
// return nil, domain.ErrInsufficientBalance
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *VeliPlayService) generateSignature(data string) string {
|
// // 2. Create transaction record
|
||||||
h := hmac.New(sha256.New, []byte(s.config.SecretKey))
|
// tx := domain.VeliTransaction{
|
||||||
h.Write([]byte(data))
|
// TransactionID: generateTransactionID(),
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
// PlayerID: playerID,
|
||||||
}
|
// GameID: gameID,
|
||||||
|
// Amount: amount,
|
||||||
|
// Type: "bet",
|
||||||
|
// Status: "pending",
|
||||||
|
// CreatedAt: time.Now(),
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *VeliPlayService) verifyCallbackSignature(cb *domain.VeliCallback) bool {
|
// if err := s.txRepo.CreateTransaction(ctx, tx); err != nil {
|
||||||
signData := fmt.Sprintf("%s%s%s%.2f%s%d",
|
// return nil, fmt.Errorf("failed to create transaction: %w", err)
|
||||||
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 {
|
// // 3. Call Veli API
|
||||||
cents := int64(amount * 100)
|
// if err := s.client.PlaceBet(ctx, tx.TransactionID, playerID, gameID, amount); err != nil {
|
||||||
if eventType == "bet_placed" {
|
// // Update transaction status
|
||||||
return -cents // Debit for bets
|
// tx.Status = "failed"
|
||||||
}
|
// _ = s.txRepo.UpdateTransaction(ctx, tx)
|
||||||
return cents // Credit for wins/results
|
// return nil, fmt.Errorf("failed to place bet: %w", err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func generateSessionToken(userID int64) string {
|
// // 4. Deduct from wallet
|
||||||
return fmt.Sprintf("veli-%d-%d", userID, time.Now().UnixNano())
|
// if err := s.walletRepo.DeductBalance(ctx, playerID, amount); err != nil {
|
||||||
}
|
// // Attempt to rollback
|
||||||
|
// _ = s.client.RollbackBet(ctx, tx.TransactionID)
|
||||||
|
// tx.Status = "failed"
|
||||||
|
// _ = s.txRepo.UpdateTransaction(ctx, tx)
|
||||||
|
// return nil, fmt.Errorf("failed to deduct balance: %w", err)
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *VeliPlayService) processTransaction(ctx context.Context, tx *domain.VirtualGameTransaction, userID int64) error {
|
// // 5. Update transaction status
|
||||||
wallets, err := s.walletSvc.GetWalletsByUser(ctx, userID)
|
// tx.Status = "completed"
|
||||||
if err != nil || len(wallets) == 0 {
|
// if err := s.txRepo.UpdateTransaction(ctx, tx); err != nil {
|
||||||
return errors.New("no wallet available for user")
|
// s.logger.Error("failed to update transaction status", "error", err)
|
||||||
}
|
// }
|
||||||
tx.WalletID = wallets[0].ID
|
|
||||||
|
|
||||||
if _, err := s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil {
|
// return &tx, nil
|
||||||
return fmt.Errorf("wallet update failed: %w", err)
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
return s.repo.CreateVirtualGameTransaction(ctx, tx)
|
// // Implement SettleBet, RollbackBet, GetBalance, etc. following similar patterns
|
||||||
}
|
|
||||||
|
// func generateTransactionID() string {
|
||||||
|
// return fmt.Sprintf("tx-%d", time.Now().UnixNano())
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ func NewApp(
|
||||||
referralSvc referralservice.ReferralStore,
|
referralSvc referralservice.ReferralStore,
|
||||||
virtualGameSvc virtualgameservice.VirtualGameService,
|
virtualGameSvc virtualgameservice.VirtualGameService,
|
||||||
aleaVirtualGameService alea.AleaVirtualGameService,
|
aleaVirtualGameService alea.AleaVirtualGameService,
|
||||||
veliVirtualGameService veli.VeliVirtualGameService,
|
// veliVirtualGameService veli.VeliVirtualGameService,
|
||||||
recommendationSvc recommendation.RecommendationService,
|
recommendationSvc recommendation.RecommendationService,
|
||||||
resultSvc *result.Service,
|
resultSvc *result.Service,
|
||||||
cfg *config.Config,
|
cfg *config.Config,
|
||||||
|
|
@ -131,7 +131,7 @@ func NewApp(
|
||||||
leagueSvc: leagueSvc,
|
leagueSvc: leagueSvc,
|
||||||
virtualGameSvc: virtualGameSvc,
|
virtualGameSvc: virtualGameSvc,
|
||||||
aleaVirtualGameService: aleaVirtualGameService,
|
aleaVirtualGameService: aleaVirtualGameService,
|
||||||
veliVirtualGameService: veliVirtualGameService,
|
// veliVirtualGameService: veliVirtualGameService,
|
||||||
recommendationSvc: recommendationSvc,
|
recommendationSvc: recommendationSvc,
|
||||||
resultSvc: resultSvc,
|
resultSvc: resultSvc,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ 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"
|
||||||
|
|
@ -48,7 +47,7 @@ type Handler struct {
|
||||||
leagueSvc league.Service
|
leagueSvc league.Service
|
||||||
virtualGameSvc virtualgameservice.VirtualGameService
|
virtualGameSvc virtualgameservice.VirtualGameService
|
||||||
aleaVirtualGameSvc alea.AleaVirtualGameService
|
aleaVirtualGameSvc alea.AleaVirtualGameService
|
||||||
veliVirtualGameSvc veli.VeliVirtualGameService
|
// veliVirtualGameSvc veli.VeliVirtualGameService
|
||||||
recommendationSvc recommendation.RecommendationService
|
recommendationSvc recommendation.RecommendationService
|
||||||
authSvc *authentication.Service
|
authSvc *authentication.Service
|
||||||
resultSvc result.Service
|
resultSvc result.Service
|
||||||
|
|
@ -68,7 +67,7 @@ func New(
|
||||||
referralSvc referralservice.ReferralStore,
|
referralSvc referralservice.ReferralStore,
|
||||||
virtualGameSvc virtualgameservice.VirtualGameService,
|
virtualGameSvc virtualgameservice.VirtualGameService,
|
||||||
aleaVirtualGameSvc alea.AleaVirtualGameService,
|
aleaVirtualGameSvc alea.AleaVirtualGameService,
|
||||||
veliVirtualGameSvc veli.VeliVirtualGameService,
|
// veliVirtualGameSvc veli.VeliVirtualGameService,
|
||||||
recommendationSvc recommendation.RecommendationService,
|
recommendationSvc recommendation.RecommendationService,
|
||||||
userSvc *user.Service,
|
userSvc *user.Service,
|
||||||
transactionSvc *transaction.Service,
|
transactionSvc *transaction.Service,
|
||||||
|
|
@ -104,7 +103,7 @@ func New(
|
||||||
leagueSvc: leagueSvc,
|
leagueSvc: leagueSvc,
|
||||||
virtualGameSvc: virtualGameSvc,
|
virtualGameSvc: virtualGameSvc,
|
||||||
aleaVirtualGameSvc: aleaVirtualGameSvc,
|
aleaVirtualGameSvc: aleaVirtualGameSvc,
|
||||||
veliVirtualGameSvc: veliVirtualGameSvc,
|
// veliVirtualGameSvc: veliVirtualGameSvc,
|
||||||
recommendationSvc: recommendationSvc,
|
recommendationSvc: recommendationSvc,
|
||||||
authSvc: authSvc,
|
authSvc: authSvc,
|
||||||
resultSvc: resultSvc,
|
resultSvc: resultSvc,
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,122 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/gofiber/fiber/v2"
|
// "github.com/gofiber/fiber/v2"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// LaunchVeliGame godoc
|
// // @Summary Get Veli games list
|
||||||
// @Summary Launch a Veli game
|
// // @Description Get list of available Veli games
|
||||||
// @Description Generates authenticated launch URL for Veli games
|
// // @Tags Virtual Games - Veli Games
|
||||||
// @Tags Veli Games
|
// // @Produce json
|
||||||
// @Security BearerAuth
|
// // @Success 200 {array} domain.VeliGame
|
||||||
// @Param game_id path string true "Game ID (e.g., veli_aviator_v1)"
|
// // @Failure 500 {object} domain.ErrorResponse
|
||||||
// @Param currency query string false "Currency code" default(USD)
|
// // @Router /veli/games [get]
|
||||||
// @Param mode query string false "Game mode" Enums(real, demo) default(real)
|
// func (h *Handler) GetGames(c *fiber.Ctx) error {
|
||||||
// @Success 200 {object} map[string]string "Returns launch URL"
|
// games, err := h.service.GetGames(c.Context())
|
||||||
// @Failure 400 {object} map[string]string "Invalid request"
|
// if err != nil {
|
||||||
// @Failure 500 {object} map[string]string "Internal server error"
|
// return domain.UnExpectedErrorResponse(c)
|
||||||
// @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)
|
// return c.Status(fiber.StatusOK).JSON(games)
|
||||||
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{
|
// // @Summary Launch Veli game
|
||||||
"launch_url": launchURL,
|
// // @Description Get URL to launch a Veli game
|
||||||
})
|
// // @Tags Virtual Games - Veli Games
|
||||||
}
|
// // @Accept json
|
||||||
|
// // @Produce json
|
||||||
|
// // @Param request body LaunchGameRequest true "Launch game request"
|
||||||
|
// // @Success 200 {object} LaunchGameResponse
|
||||||
|
// // @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// // @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// // @Router /veli/games/launch [post]
|
||||||
|
// func (h *Handler) LaunchGame(c *fiber.Ctx) error {
|
||||||
|
// var req struct {
|
||||||
|
// PlayerID string `json:"player_id" validate:"required"`
|
||||||
|
// GameID string `json:"game_id" validate:"required"`
|
||||||
|
// }
|
||||||
|
|
||||||
// HandleVeliCallback godoc
|
// if err := c.BodyParser(&req); err != nil {
|
||||||
// @Summary Veli Games webhook handler
|
// return domain.BadRequestResponse(c)
|
||||||
// @Description Processes game round settlements from Veli
|
// }
|
||||||
// @Tags Veli 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 {
|
// gameURL, err := h.service.LaunchGame(c.Context(), req.PlayerID, req.GameID)
|
||||||
h.logger.Error("failed to process Veli callback",
|
// if err != nil {
|
||||||
"roundID", cb.RoundID,
|
// return domain.UnExpectedErrorResponse(c)
|
||||||
"error", err)
|
// }
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
||||||
"error": "failed to process callback",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
// return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||||||
"status": "processed",
|
// "url": gameURL,
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// // @Summary Place bet
|
||||||
|
// // @Description Place a bet on a Veli game
|
||||||
|
// // @Tags Virtual Games - Veli Games
|
||||||
|
// // @Accept json
|
||||||
|
// // @Produce json
|
||||||
|
// // @Param request body PlaceBetRequest true "Place bet request"
|
||||||
|
// // @Success 200 {object} domain.VeliTransaction
|
||||||
|
// // @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// // @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// // @Router /veli/bets [post]
|
||||||
|
// func (h *Handler) PlaceBet(c *fiber.Ctx) error {
|
||||||
|
// var req struct {
|
||||||
|
// PlayerID string `json:"player_id" validate:"required"`
|
||||||
|
// GameID string `json:"game_id" validate:"required"`
|
||||||
|
// Amount float64 `json:"amount" validate:"required,gt=0"`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err := c.BodyParser(&req); err != nil {
|
||||||
|
// return domain.BadRequestResponse(c)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// tx, err := h.service.PlaceBet(c.Context(), req.PlayerID, req.GameID, req.Amount)
|
||||||
|
// if err != nil {
|
||||||
|
// if err == domain.ErrInsufficientBalance {
|
||||||
|
// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
// Message: "Insufficient balance",
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// return domain.UnExpectedErrorResponse(c)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return c.Status(fiber.StatusOK).JSON(tx)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // @Summary Bet settlement webhook
|
||||||
|
// // @Description Handle bet settlement from Veli
|
||||||
|
// // @Tags Virtual Games - Veli Games
|
||||||
|
// // @Accept json
|
||||||
|
// // @Produce json
|
||||||
|
// // @Param request body SettlementRequest true "Settlement request"
|
||||||
|
// // @Success 200 {object} domain.Response
|
||||||
|
// // @Failure 400 {object} domain.ErrorResponse
|
||||||
|
// // @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// // @Router /veli/webhooks/settlement [post]
|
||||||
|
// func (h *Handler) HandleSettlement(c *fiber.Ctx) error {
|
||||||
|
// var req struct {
|
||||||
|
// TransactionID string `json:"transaction_id" validate:"required"`
|
||||||
|
// PlayerID string `json:"player_id" validate:"required"`
|
||||||
|
// Amount float64 `json:"amount" validate:"required"`
|
||||||
|
// IsWin bool `json:"is_win"`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err := c.BodyParser(&req); err != nil {
|
||||||
|
// return domain.BadRequestResponse(c)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Verify signature
|
||||||
|
// if !h.service.VerifyWebhookSignature(c.Request().Body(), c.Get("X-Signature")) {
|
||||||
|
// return domain.UnauthorizedResponse(c)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Process settlement
|
||||||
|
// tx, err := h.service.SettleBet(c.Context(), req.TransactionID, req.PlayerID, req.Amount, req.IsWin)
|
||||||
|
// if err != nil {
|
||||||
|
// return domain.UnExpectedErrorResponse(c)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return c.Status(fiber.StatusOK).JSON(tx)
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -103,16 +103,16 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid bet request")
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid bet request")
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.virtualGameSvc.ProcessBet(c.Context(), &req)
|
resp, _ := h.virtualGameSvc.ProcessBet(c.Context(), &req)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
code := fiber.StatusInternalServerError
|
// code := fiber.StatusInternalServerError
|
||||||
if err.Error() == "invalid token" {
|
// // if err.Error() == "invalid token" {
|
||||||
code = fiber.StatusUnauthorized
|
// // code = fiber.StatusUnauthorized
|
||||||
} else if err.Error() == "insufficient balance" {
|
// // } else if err.Error() == "insufficient balance" {
|
||||||
code = fiber.StatusBadRequest
|
// // code = fiber.StatusBadRequest
|
||||||
}
|
// // }
|
||||||
return fiber.NewError(code, err.Error())
|
// return fiber.NewError(code, err.Error())
|
||||||
}
|
// }
|
||||||
|
|
||||||
return response.WriteJSON(c, fiber.StatusOK, "Bet processed", resp, nil)
|
return response.WriteJSON(c, fiber.StatusOK, "Bet processed", resp, nil)
|
||||||
}
|
}
|
||||||
|
|
@ -123,14 +123,14 @@ func (h *Handler) HandleWin(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid win request")
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid win request")
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.virtualGameSvc.ProcessWin(c.Context(), &req)
|
resp, _ := h.virtualGameSvc.ProcessWin(c.Context(), &req)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
code := fiber.StatusInternalServerError
|
// code := fiber.StatusInternalServerError
|
||||||
if err.Error() == "invalid token" {
|
// if err.Error() == "invalid token" {
|
||||||
code = fiber.StatusUnauthorized
|
// code = fiber.StatusUnauthorized
|
||||||
}
|
// }
|
||||||
return fiber.NewError(code, err.Error())
|
// return fiber.NewError(code, err.Error())
|
||||||
}
|
// }
|
||||||
|
|
||||||
return response.WriteJSON(c, fiber.StatusOK, "Win processed", resp, nil)
|
return response.WriteJSON(c, fiber.StatusOK, "Win processed", resp, nil)
|
||||||
}
|
}
|
||||||
|
|
@ -141,17 +141,17 @@ func (h *Handler) HandleCancel(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid cancel request")
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid cancel request")
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.virtualGameSvc.ProcessCancel(c.Context(), &req)
|
resp, _ := h.virtualGameSvc.ProcessCancel(c.Context(), &req)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
code := fiber.StatusInternalServerError
|
// code := fiber.StatusInternalServerError
|
||||||
switch err.Error() {
|
// switch err.Error() {
|
||||||
case "invalid token":
|
// case "invalid token":
|
||||||
code = fiber.StatusUnauthorized
|
// code = fiber.StatusUnauthorized
|
||||||
case "original bet not found", "invalid original transaction":
|
// case "original bet not found", "invalid original transaction":
|
||||||
code = fiber.StatusBadRequest
|
// code = fiber.StatusBadRequest
|
||||||
}
|
// }
|
||||||
return fiber.NewError(code, err.Error())
|
// return fiber.NewError(code, err.Error())
|
||||||
}
|
// }
|
||||||
|
|
||||||
return response.WriteJSON(c, fiber.StatusOK, "Cancel processed", resp, nil)
|
return response.WriteJSON(c, fiber.StatusOK, "Cancel processed", resp, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ func (a *App) initAppRoutes() {
|
||||||
a.referralSvc,
|
a.referralSvc,
|
||||||
a.virtualGameSvc,
|
a.virtualGameSvc,
|
||||||
a.aleaVirtualGameService,
|
a.aleaVirtualGameService,
|
||||||
a.veliVirtualGameService,
|
// a.veliVirtualGameService,
|
||||||
a.recommendationSvc,
|
a.recommendationSvc,
|
||||||
a.userSvc,
|
a.userSvc,
|
||||||
a.transactionSvc,
|
a.transactionSvc,
|
||||||
|
|
@ -240,8 +240,8 @@ func (a *App) initAppRoutes() {
|
||||||
group.Post("/webhooks/alea-play", a.authMiddleware, h.HandleAleaCallback)
|
group.Post("/webhooks/alea-play", a.authMiddleware, h.HandleAleaCallback)
|
||||||
|
|
||||||
//Veli Virtual Game Routes
|
//Veli Virtual Game Routes
|
||||||
group.Get("/veli-games/launch", h.LaunchVeliGame)
|
// group.Get("/veli-games/launch", h.LaunchVeliGame)
|
||||||
group.Post("/webhooks/veli-games", h.HandleVeliCallback)
|
// group.Post("/webhooks/veli-games", h.HandleVeliCallback)
|
||||||
|
|
||||||
//mongoDB logs
|
//mongoDB logs
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user