feat: added settings routes

This commit is contained in:
Samuel Tariku 2025-08-11 21:10:33 +03:00
parent 696b713699
commit 5615c93fbb
13 changed files with 461 additions and 84 deletions

View File

@ -1,21 +1,3 @@
CREATE TABLE direct_deposits (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES users(id),
wallet_id BIGINT NOT NULL REFERENCES wallets(id),
amount NUMERIC(15, 2) NOT NULL,
bank_reference TEXT NOT NULL,
sender_account TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('pending', 'completed', 'rejected')),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
verified_by BIGINT REFERENCES users(id),
verification_notes TEXT,
verified_at TIMESTAMP
);
CREATE INDEX idx_direct_deposits_status ON direct_deposits(status);
CREATE INDEX idx_direct_deposits_customer ON direct_deposits(customer_id);
CREATE INDEX idx_direct_deposits_reference ON direct_deposits(bank_reference);
CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY,
first_name VARCHAR(255) NOT NULL,
@ -398,6 +380,22 @@ CREATE TABLE flags (
)
)
);
CREATE TABLE direct_deposits (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES users(id),
wallet_id BIGINT NOT NULL REFERENCES wallets(id),
amount NUMERIC(15, 2) NOT NULL,
bank_reference TEXT NOT NULL,
sender_account TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('pending', 'completed', 'rejected')),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
verified_by BIGINT REFERENCES users(id),
verification_notes TEXT,
verified_at TIMESTAMP
);
CREATE INDEX idx_direct_deposits_status ON direct_deposits(status);
CREATE INDEX idx_direct_deposits_customer ON direct_deposits(customer_id);
CREATE INDEX idx_direct_deposits_reference ON direct_deposits(bank_reference);
-- Views
CREATE VIEW companies_details AS
SELECT companies.*,

View File

@ -1,20 +1,11 @@
-- Settings Initial Data
INSERT INTO settings (key, value)
VALUES ('sms_provider', '30'),
<<<<<<< HEAD
('max_number_of_outcomes', '30'),
('bet_amount_limit', '100000'),
=======
('max_number_of_outcomes', '30'),
('bet_amount_limit', '10000000'),
>>>>>>> 7d8d824a94381bd82c40398654c3bd78218c5950
('daily_ticket_limit', '50'),
('total_winnings_limit', '1000000'),
('amount_for_bet_referral', '1000000'),
('cashback_amount_cap', '1000') ON CONFLICT (key) DO
UPDATE
<<<<<<< HEAD
SET value = EXCLUDED.value;
=======
SET value = EXCLUDED.value;
>>>>>>> 7d8d824a94381bd82c40398654c3bd78218c5950
SET value = EXCLUDED.value;

View File

@ -5,9 +5,8 @@ FROM settings;
SELECT *
FROM settings
WHERE key = $1;
-- name: SaveSetting :one
INSERT INTO settings (key, value, updated_at)
VALUES ($1, $2, CURRENT_TIMESTAMP) ON CONFLICT (key) DO
UPDATE
SET value = EXCLUDED.value
RETURNING *;
-- name: UpdateSetting :exec
UPDATE settings
SET value = $2,
updated_at = CURRENT_TIMESTAMP
WHERE key = $1;

View File

@ -57,27 +57,19 @@ func (q *Queries) GetSettings(ctx context.Context) ([]Setting, error) {
return items, nil
}
const SaveSetting = `-- name: SaveSetting :one
INSERT INTO settings (key, value, updated_at)
VALUES ($1, $2, CURRENT_TIMESTAMP) ON CONFLICT (key) DO
UPDATE
SET value = EXCLUDED.value
RETURNING key, value, created_at, updated_at
const UpdateSetting = `-- name: UpdateSetting :exec
UPDATE settings
SET value = $2,
updated_at = CURRENT_TIMESTAMP
WHERE key = $1
`
type SaveSettingParams struct {
type UpdateSettingParams struct {
Key string `json:"key"`
Value string `json:"value"`
}
func (q *Queries) SaveSetting(ctx context.Context, arg SaveSettingParams) (Setting, error) {
row := q.db.QueryRow(ctx, SaveSetting, arg.Key, arg.Value)
var i Setting
err := row.Scan(
&i.Key,
&i.Value,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
func (q *Queries) UpdateSetting(ctx context.Context, arg UpdateSettingParams) error {
_, err := q.db.Exec(ctx, UpdateSetting, arg.Key, arg.Value)
return err
}

View File

@ -99,6 +99,69 @@ func (v ValidBool) ToPG() pgtype.Bool {
}
}
func ConvertInt64Ptr(value *int64) ValidInt64 {
if value == nil {
return ValidInt64{}
}
return ValidInt64{
Value: *value,
Valid: true,
}
}
func ConvertInt32Ptr(value *int32) ValidInt32 {
if value == nil {
return ValidInt32{}
}
return ValidInt32{
Value: *value,
Valid: true,
}
}
func ConvertStringPtr(value *string) ValidString {
if value == nil {
return ValidString{}
}
return ValidString{
Value: *value,
Valid: true,
}
}
func ConvertFloat32Ptr(value *float32) ValidFloat32 {
if value == nil {
return ValidFloat32{}
}
return ValidFloat32{
Value: *value,
Valid: true,
}
}
func ConvertCurrencyFloatPtr(value *float32) ValidInt64 {
if value == nil {
return ValidInt64{}
}
convertedCurrency := ToCurrency(*value)
return ValidInt64{
Value: int64(convertedCurrency),
Valid: true,
}
}
func ConvertBoolPtr(value *bool) ValidBool {
if value == nil {
return ValidBool{}
}
return ValidBool{
Value: *value,
Valid: true,
}
}
type Currency int64
// ToCurrency converts a float32 to Currency

View File

@ -1,6 +1,7 @@
package domain
import (
"strconv"
"time"
)
@ -11,9 +12,19 @@ type Setting struct {
}
type SettingRes struct {
Key string `json:"key"`
Value string `json:"value"`
UpdatedAt string `json:"updated_at"`
Key string `json:"key"`
Value string `json:"value"`
UpdatedAt time.Time `json:"updated_at"`
}
type UpdateSettingListReq struct {
SMSProvider *string `json:"sms_provider,omitempty"`
MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"`
BetAmountLimit *float32 `json:"bet_amount_limit,omitempty"`
DailyTicketPerIP *int64 `json:"daily_ticket_limit,omitempty"`
TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"`
AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"`
CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"`
}
type SettingList struct {
@ -26,7 +37,17 @@ type SettingList struct {
CashbackAmountCap Currency `json:"cashback_amount_cap"`
}
type DBSettingList struct {
type SettingListRes struct {
SMSProvider SMSProvider `json:"sms_provider"`
MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"`
BetAmountLimit float32 `json:"bet_amount_limit"`
DailyTicketPerIP int64 `json:"daily_ticket_limit"`
TotalWinningLimit float32 `json:"total_winning_limit"`
AmountForBetReferral float32 `json:"amount_for_bet_referral"`
CashbackAmountCap float32 `json:"cashback_amount_cap"`
}
type ValidSettingList struct {
SMSProvider ValidString
MaxNumberOfOutcomes ValidInt64
BetAmountLimit ValidInt64
@ -36,7 +57,7 @@ type DBSettingList struct {
CashbackAmountCap ValidInt64
}
func ConvertInt64SettingsMap(dbSettingList *DBSettingList) map[string]*ValidInt64 {
func ConvertInt64SettingsMap(dbSettingList *ValidSettingList) map[string]*ValidInt64 {
return map[string]*ValidInt64{
"max_number_of_outcomes": &dbSettingList.MaxNumberOfOutcomes,
"bet_amount_limit": &dbSettingList.BetAmountLimit,
@ -47,25 +68,26 @@ func ConvertInt64SettingsMap(dbSettingList *DBSettingList) map[string]*ValidInt6
}
}
func ConvertStringSettingsMap(dbSettingList *DBSettingList) map[string]*ValidString {
func ConvertStringSettingsMap(dbSettingList *ValidSettingList) map[string]*ValidString {
return map[string]*ValidString{
"sms_provider": &dbSettingList.SMSProvider,
}
}
func ConvertBoolSettingsMap(dbSettingList *DBSettingList) map[string]*ValidBool {
func ConvertBoolSettingsMap(dbSettingList *ValidSettingList) map[string]*ValidBool {
return map[string]*ValidBool{}
}
func ConvertFloat32SettingsMap(dbSettingList *DBSettingList) map[string]*ValidFloat32 {
func ConvertFloat32SettingsMap(dbSettingList *ValidSettingList) map[string]*ValidFloat32 {
return map[string]*ValidFloat32{}
}
func ConvertTimeSettingsMap(dbSettingList *DBSettingList) map[string]*ValidTime {
func ConvertTimeSettingsMap(dbSettingList *ValidSettingList) map[string]*ValidTime {
return map[string]*ValidTime{}
}
func ConvertDBSetting(dbSettingList DBSettingList) SettingList {
func ValidateSettingList(dbSettingList ValidSettingList) SettingList {
// TODO: Add validation here
return SettingList{
SMSProvider: SMSProvider(dbSettingList.SMSProvider.Value),
MaxNumberOfOutcomes: dbSettingList.MaxNumberOfOutcomes.Value,
@ -76,3 +98,96 @@ func ConvertDBSetting(dbSettingList DBSettingList) SettingList {
CashbackAmountCap: Currency(dbSettingList.CashbackAmountCap.Value),
}
}
func ConvertValidSettingList(settingList ValidSettingList) []Setting {
var convertedSettings []Setting
// if settingList.AmountForBetReferral.Valid {
// newValue := strconv.FormatInt(settingList.AmountForBetReferral.Value, 10)
// settings = append(settings, Setting{
// Key: "amount_for_bet_referral",
// Value: newValue,
// })
// }
int64SettingsMap := ConvertInt64SettingsMap(&settingList)
stringSettingsMap := ConvertStringSettingsMap(&settingList)
boolSettingsMap := ConvertBoolSettingsMap(&settingList)
float32SettingsMap := ConvertFloat32SettingsMap(&settingList)
timeSettingsMap := ConvertTimeSettingsMap(&settingList)
for key, settingPtr := range int64SettingsMap {
setting := *settingPtr
if setting.Valid {
stringVal := strconv.FormatInt(setting.Value, 10)
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: stringVal,
})
}
}
for key, settingPtr := range stringSettingsMap {
setting := *settingPtr
if setting.Valid {
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: setting.Value,
})
}
}
for key, settingPtr := range boolSettingsMap {
setting := *settingPtr
if setting.Valid {
stringVal := strconv.FormatBool(setting.Value)
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: stringVal,
})
}
}
for key, settingPtr := range float32SettingsMap {
setting := *settingPtr
if setting.Valid {
stringVal := strconv.FormatFloat(float64(setting.Value), 'E', -1, 64)
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: stringVal,
})
}
}
for key, settingPtr := range timeSettingsMap {
setting := *settingPtr
if setting.Valid {
var stringVal string = setting.Value.Format("2006-01-02 15:04:05")
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: stringVal,
})
}
}
return convertedSettings
}
func ConvertSetting(setting Setting) SettingRes {
return SettingRes{
Key: setting.Key,
Value: setting.Value,
UpdatedAt: setting.UpdatedAt,
}
}
func ConvertUpdateSettingListReq(settings UpdateSettingListReq) ValidSettingList {
return ValidSettingList{
SMSProvider: ConvertStringPtr(settings.SMSProvider),
MaxNumberOfOutcomes: ConvertInt64Ptr(settings.MaxNumberOfOutcomes),
BetAmountLimit: ConvertCurrencyFloatPtr(settings.BetAmountLimit),
DailyTicketPerIP: ConvertInt64Ptr(settings.DailyTicketPerIP),
TotalWinningLimit: ConvertCurrencyFloatPtr(settings.TotalWinningLimit),
AmountForBetReferral: ConvertCurrencyFloatPtr(settings.AmountForBetReferral),
CashbackAmountCap: ConvertCurrencyFloatPtr(settings.CashbackAmountCap),
}
}

View File

@ -12,7 +12,7 @@ import (
)
func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
var dbSettingList domain.DBSettingList
var dbSettingList domain.ValidSettingList
var int64SettingsMap = domain.ConvertInt64SettingsMap(&dbSettingList)
var stringSettingsMap = domain.ConvertStringSettingsMap(&dbSettingList)
@ -21,7 +21,7 @@ func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
var timeSettingsMap = domain.ConvertTimeSettingsMap(&dbSettingList)
for _, setting := range settings {
is_setting_unknown := true
isSettingUnknown := true
for key, dbSetting := range int64SettingsMap {
if setting.Key == key {
value, err := strconv.ParseInt(setting.Value, 10, 64)
@ -32,7 +32,7 @@ func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
Value: value,
Valid: true,
}
is_setting_unknown = false
isSettingUnknown = false
}
}
@ -42,7 +42,7 @@ func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
Value: setting.Value,
Valid: true,
}
is_setting_unknown = false
isSettingUnknown = false
}
}
@ -56,7 +56,7 @@ func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
Value: value,
Valid: true,
}
is_setting_unknown = false
isSettingUnknown = false
}
}
@ -70,7 +70,7 @@ func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
Value: float32(value),
Valid: true,
}
is_setting_unknown = false
isSettingUnknown = false
}
}
for key, dbSetting := range timeSettingsMap {
@ -83,11 +83,11 @@ func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
Value: value,
Valid: true,
}
is_setting_unknown = false
isSettingUnknown = false
}
}
if is_setting_unknown {
if isSettingUnknown {
domain.MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
}
}
@ -99,7 +99,7 @@ func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
}
}
return domain.ConvertDBSetting(dbSettingList), nil
return domain.ValidateSettingList(dbSettingList), nil
}
func (s *Store) GetSettingList(ctx context.Context) (domain.SettingList, error) {
settings, err := s.queries.GetSettings(ctx)
@ -145,8 +145,8 @@ func (s *Store) GetSetting(ctx context.Context, key string) (domain.Setting, err
return result, nil
}
func (s *Store) SaveSetting(ctx context.Context, key, value string) (domain.Setting, error) {
dbSetting, err := s.queries.SaveSetting(ctx, dbgen.SaveSettingParams{
func (s *Store) UpdateSetting(ctx context.Context, key, value string) error {
err := s.queries.UpdateSetting(ctx, dbgen.UpdateSettingParams{
Key: key,
Value: value,
})
@ -154,14 +154,22 @@ func (s *Store) SaveSetting(ctx context.Context, key, value string) (domain.Sett
if err != nil {
domain.MongoDBLogger.Error("failed to update setting", zap.String("key", key), zap.String("value", value), zap.Error(err))
return domain.Setting{}, err
return err
}
setting := domain.Setting{
Key: dbSetting.Key,
Value: dbSetting.Value,
}
return setting, err
return err
}
func (s *Store) UpdateSettingList(ctx context.Context, settingList domain.ValidSettingList) error {
convertedSettings := domain.ConvertValidSettingList(settingList)
for _, setting := range convertedSettings {
err := s.UpdateSetting(ctx, setting.Key, setting.Value)
if err != nil {
domain.MongoDBLogger.Warn("failed to update setting list", zap.String("key", setting.Key), zap.Error(err))
return err
}
}
return nil
}

View File

@ -10,5 +10,6 @@ type SettingStore interface {
GetSettingList(ctx context.Context) (domain.SettingList, error)
GetSettings(ctx context.Context) ([]domain.Setting, error)
GetSetting(ctx context.Context, key string) (domain.Setting, error)
SaveSetting(ctx context.Context, key, value string) (domain.Setting, error)
UpdateSetting(ctx context.Context, key, value string) error
UpdateSettingList(ctx context.Context, settingList domain.ValidSettingList) error
}

View File

@ -27,6 +27,10 @@ func (s *Service) GetSettings(ctx context.Context) ([]domain.Setting, error) {
func (s *Service) GetSetting(ctx context.Context, key string) (domain.Setting, error) {
return s.settingStore.GetSetting(ctx, key)
}
func (s *Service) SaveSetting(ctx context.Context, key, value string) (domain.Setting, error) {
return s.settingStore.SaveSetting(ctx, key, value)
func (s *Service) UpdateSetting(ctx context.Context, key, value string) error {
return s.settingStore.UpdateSetting(ctx, key, value)
}
func (s *Service) UpdateSettingList(ctx context.Context, settingList domain.ValidSettingList) error {
return s.settingStore.UpdateSettingList(ctx, settingList)
}

View File

@ -863,7 +863,7 @@ func (h *Handler) UpdateBranch(c *fiber.Ctx) error {
var req domain.UpdateBranchReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse CreateBranchReq",
h.mongoLoggerSvc.Info("Failed to parse UpdateBranchReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
@ -879,7 +879,6 @@ func (h *Handler) UpdateBranch(c *fiber.Ctx) error {
h.mongoLoggerSvc.Info("Failed to validate UpdateBranchReq",
zap.String("branchID", branchID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.String("errMsg", errMsg),
zap.Time("timestamp", time.Now()),
)

View File

@ -0,0 +1,105 @@
package handlers
import (
"fmt"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
func (h *Handler) GetSettingList(c *fiber.Ctx) error {
settingsList, err := h.settingSvc.GetSettingList(c.Context())
if err != nil {
h.mongoLoggerSvc.Error("Failed to fetch settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", settingsList, nil)
}
func (h *Handler) GetSettingByKey(c *fiber.Ctx) error {
settingKey := c.Params("key")
if settingKey == "" {
h.mongoLoggerSvc.Info("empty setting key",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "setting key must be passed")
}
setting, err := h.settingSvc.GetSetting(c.Context(), settingKey)
if err != nil {
h.mongoLoggerSvc.Info("invalid setting key",
zap.String("setting_key", settingKey),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "setting key is invalid")
}
res := domain.ConvertSetting(setting)
return response.WriteJSON(c, fiber.StatusOK, "setting retrieved successfully", res, nil)
}
func (h *Handler) UpdateSettingList(c *fiber.Ctx) error {
var req domain.UpdateSettingListReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse UpdateSettingListReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate UpdateSettingListReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("errMsg", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
settingList := domain.ConvertUpdateSettingListReq(req)
err := h.settingSvc.UpdateSettingList(c.Context(), settingList)
if err != nil {
h.mongoLoggerSvc.Info("failed to update setting",
zap.Any("setting_list", settingList),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "failed to update setting")
}
settingsList, err := h.settingSvc.GetSettingList(c.Context())
if err != nil {
h.mongoLoggerSvc.Error("Failed to fetch settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "setting updated", settingsList, nil)
}

View File

@ -336,4 +336,10 @@ func (a *App) initAppRoutes() {
groupV1.Get("/issues", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllIssues)
groupV1.Patch("/issues/:issue_id/status", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateIssueStatus)
groupV1.Delete("/issues/:issue_id", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteIssue)
// Settings
groupV1.Get("/settings", a.authMiddleware, h.GetSettingList)
groupV1.Get("/settings/:key", a.authMiddleware, h.GetSettingByKey)
groupV1.Put("/settings", a.authMiddleware, h.UpdateSettingList)
}

96
new.env Executable file
View File

@ -0,0 +1,96 @@
# REPORT_EXPORT_PATH="C:\\ProgramData\\FortuneBet\\exported_reports" #prod env
# REPORT_EXPORT_PATH ="./exported_reports" #dev env
# Telebirr
TELEBIRR_BASE_URL="https://developerportal.ethiotelebirr.et:38443/payment/web/paygate?"
TELEBIRR_APP_SECRET=your_telebirr_app_secret
TELEBIRR_FABRIC_APP_ID=your_telebirr_fabric_app_id
TELEBIRR_MERCHANT_CODE=your_telebirr_merchant_code
TELEBIRR_CALLBACK_URL=https://api.fortunebets.net/api/v1/telebirr/callback
RESEND_SENDER_EMAIL=email
RESEND_API_KEY=123
ENV=development
PORT=8080
DB_URL=postgresql://root:secret@localhost:5422/gh?sslmode=disable
REFRESH_EXPIRY=2592000
JWT_KEY=mysecretkey
ACCESS_EXPIRY=600
LOG_LEVEL=debug
AFRO_SMS_API_KEY=1
AFRO_SMS_SENDER_NAME=
AFRO_SMS_RECEIVER_PHONE_NUMBER=
BET365_TOKEN=158046-hesJDP2Cay2M5G
POPOK_CLIENT_ID=1
POPOK_PLATFORM=111
POPOK_SECRET_KEY=XwFQ76Y59zBxGryh
# POPOK_BASE_URL=https://api.pokgaming.com/game/launch #Production
# POPOK_BASE_URL=https://games.pokgaming.com/launch #Production
# POPOK_BASE_URL=https://sandbox.pokgaming.com/game/launch #Staging
# POPOK_BASE_URL=https://test-api.pokgaming.com/launch #Staging
POPOK_BASE_URL=https://st.pokgaming.com/ #Staging
POPOK_CALLBACK_URL=https://api.fortunebets.net
#Muli-currency Support
FIXER_API_KEY=3b0f1eb30d-63c875026d-sxy9pl
BASE_CURRENCY=ETB
FIXER_BASE_URL=https://api.apilayer.com/fixer
# Chapa API Configuration
CHAPA_TRANSFER_TYPE=Payout
CHAPA_PAYMENT_TYPE=API
CHAPA_BASE_URL=https://api.chapa.co/v1
CHAPA_ENCRYPTION_KEY=zLdYrjnBCknMvFikmP5jBfen
CHAPA_PUBLIC_KEY=CHAPUBK_TEST-HJR0qhQRPLTkauNy9Q8UrmskPTOR31aC
CHAPA_SECRET_KEY=CHASECK_TEST-q3jypwmFK6XJGYOK3aX4z9Kogd9KaHhF
CHAPA_CALLBACK_URL=https://api.fortunebets.net/api/v1/payments/webhook/verify
CHAPA_RETURN_URL=https://fortunebets.net/dashboard
#Alea Play
ALEA_ENABLED=true
ALEA_BASE_URL=https://api.aleaplay.com
ALEA_OPERATOR_ID=operator_id
ALEA_SECRET_KEY=hmac_secret
ALEA_GAME_LIST_URL=https://api.aleaplay.com/games/list # Optional
ALEA_DEFAULT_CURRENCY=USD # Optional (default: USD)
ALEA_SESSION_TIMEOUT=24 # Optional (hours, default: 24)
ALEA_GAME_ID_AVIATOR=aviator_prod
# Veli Games
VELI_ENABLED=true
VELI_BASE_URL=https://stage-api.velitech.games
VELI_REGIONAL_URL=https://stage-api.velitech.games
VELI_WEBHOOK_URL=https://api.fortunebets.net/
VELI_SECRET_KEY=qPfg7PyhyOI9d2_bcx3ovKqzMqtjaVen
VELI_OPERATOR_KEY=111
VELI_OPERATOR_ID=fortune_bets
VELI_BRAND_ID=fortune_bets
VELI_DEFAULT_CURRENCY=ETB
# Arifpay
ARIFPAY_API_KEY=OV7MLkA39tgWiEApLLSg6aNf4F1RDobV
ARIFPAY_CANCEL_URL=https://api.fortunebets.net/api/v1/payments/arifpay/cancel
ARIFPAY_ERROR_URL=https://api.fortunebets.net/api/v1/payments/arifpay/error
ARIFPAY_NOTIFY_URL=https://api.fortunebets.net/api/v1/payments/arifpay/notify
ARIFPAY_SUCCESS_URL=https://api.fortunebets.net/api/v1/payments/arifpay/success
# Santimpay
SANTIMPAY_SECRET_KEY=your_santim_pay_secret_key
SANTIMPAY_MERCHANT_ID=your_santim_pay_merchant_id
SANTIMPAY_Base_URL=https://services.santimpay.com/api/v1/gateway
#MongoDB
MONGODB_URL=mongodb://root:secret@mongo:27017/?authSource=admin
# Twilio SMS
TWILIO_ACCOUNT_SID=123
TWILIO_AUTH_TOKEN=2334eadf
TWILIO_SENDER_PHONE_NUMBER=0912345678
# Redis
REDIS_ADDR=redis:6379