Yimaru-BackEnd/cmd/main.go
Yared Yemane 1f7b38861e Integrate Chapa for learner subscription payments
Add Chapa checkout, verify, webhook, and callback flows so subscriptions activate only after confirmed payment. Route subscription checkout through Chapa while keeping ArifPay for direct payments. Include integration docs and a Postman collection.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 03:35:57 -07:00

515 lines
15 KiB
Go

package main
import (
// "context"
// "context"
_ "Yimaru-Backend/docs"
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/config"
"Yimaru-Backend/internal/domain"
customlogger "Yimaru-Backend/internal/logger"
"Yimaru-Backend/internal/logger/mongoLogger"
minioclient "Yimaru-Backend/internal/pkgs/minio"
"Yimaru-Backend/internal/repository"
activitylogservice "Yimaru-Backend/internal/services/activity_log"
"Yimaru-Backend/internal/services/arifpay"
"Yimaru-Backend/internal/services/chapa"
"Yimaru-Backend/internal/services/assessment"
"Yimaru-Backend/internal/services/authentication"
cloudconvertservice "Yimaru-Backend/internal/services/cloudconvert"
coursesservice "Yimaru-Backend/internal/services/courses"
"Yimaru-Backend/internal/services/examprep"
"Yimaru-Backend/internal/services/faqs"
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
lessonsservice "Yimaru-Backend/internal/services/lessons"
"Yimaru-Backend/internal/services/lmsprogress"
"Yimaru-Backend/internal/services/messenger"
minioservice "Yimaru-Backend/internal/services/minio"
moduleservice "Yimaru-Backend/internal/services/modules"
notificationservice "Yimaru-Backend/internal/services/notification"
personasservice "Yimaru-Backend/internal/services/personas"
practicesservice "Yimaru-Backend/internal/services/practices"
programsservice "Yimaru-Backend/internal/services/programs"
"Yimaru-Backend/internal/services/questions"
ratingsservice "Yimaru-Backend/internal/services/ratings"
rbacservice "Yimaru-Backend/internal/services/rbac"
"Yimaru-Backend/internal/services/recommendation"
"Yimaru-Backend/internal/services/settings"
"Yimaru-Backend/internal/services/subscriptions"
"Yimaru-Backend/internal/services/team"
vimeoservice "Yimaru-Backend/internal/services/vimeo"
"context"
// referralservice "Yimaru-Backend/internal/services/referal"
"Yimaru-Backend/internal/services/transaction"
"Yimaru-Backend/internal/services/user"
httpserver "Yimaru-Backend/internal/web_server"
jwtutil "Yimaru-Backend/internal/web_server/jwt"
customvalidator "Yimaru-Backend/internal/web_server/validator"
"fmt"
"log"
"log/slog"
"net/http"
"os"
"time"
"github.com/go-playground/validator/v10"
"go.uber.org/zap"
)
// @title Yimaru API
// @version 1.0.1
// @description This is server for Yimaru.
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @SecurityDefinitions.apiKey Bearer
// @in header
// @name Authorization
// @BasePath /
func main() {
cfg, err := config.NewConfig()
if err != nil {
slog.Error("Config error:", "err", err)
os.Exit(1)
}
db, _, err := repository.OpenDB(cfg.DbUrl)
if err != nil {
fmt.Println("Database error:", err)
os.Exit(1)
}
logger := customlogger.NewLogger(cfg.Env, cfg.LogLevel)
domain.MongoDBLogger, err = mongoLogger.InitLogger(cfg)
if err != nil {
log.Fatalf("Logger initialization failed: %v", err)
}
defer domain.MongoDBLogger.Sync()
zap.ReplaceGlobals(domain.MongoDBLogger)
store := repository.NewStore(db)
analyticsDB := dbgen.New(db)
v := customvalidator.NewCustomValidator(validator.New())
// Initialize services
settingRepo := repository.NewSettingStore(store)
if err := settingRepo.EnsureAllSettingsExist(context.Background()); err != nil {
log.Fatalf("failed to ensure settings: %v", err)
}
settingSvc := settings.NewService(settingRepo)
messengerSvc := messenger.NewService(settingSvc, cfg)
// statSvc := stats.NewService(
// repository.NewCompanyStatStore(store),
// repository.NewBranchStatStore(store),
// )
userSvc := user.NewService(
repository.NewTokenStore(store),
repository.NewUserStore(store),
repository.NewOTPStore(store),
messengerSvc,
cfg,
)
authSvc := authentication.NewService(
repository.NewOTPStore(store),
repository.NewUserStore(store),
*userSvc,
repository.NewTokenStore(store),
cfg.RefreshExpiry,
)
authSvc.InitGoogleOAuth(cfg.GoogleOAuthClientID, cfg.GoogleOAuthClientSecret, cfg.GoogleOAuthRedirectURL)
// leagueSvc := league.New(repository.NewLeagueStore(store))
// eventSvc := event.New(
// cfg.Bet365Token,
// repository.NewEventStore(store),
// repository.NewEventHistoryStore(store),
// *leagueSvc,
// settingSvc,
// domain.MongoDBLogger,
// cfg,
// )
// marketSettingRepo := repository.NewMarketSettingStore(store)
// if err := marketSettingRepo.EnsureAllMarketSettingsExist(context.Background()); err != nil {
// log.Fatalf("failed to ensure market settings: %v", err)
// }
// oddsSvc := odds.New(
// repository.NewOddStore(store),
// marketSettingRepo,
// cfg,
// eventSvc,
// logger,
// domain.MongoDBLogger,
// )
// virtuaGamesRepo := repository.NewVirtualGameRepository(store)
// Initialize producer
// topic := "wallet-balance-topic"
// producer := kafka.NewProducer(cfg.KafkaBrokers, topic)
notificationSvc := notificationservice.New(
repository.NewNotificationStore(store),
domain.MongoDBLogger,
logger,
cfg,
messengerSvc,
userSvc,
)
// / := wallet.NewService(
// repository.NewWalletStore(store),
// repository.NewTransferStore(store),
// // repository.NewDirectDepositStore(store),
// notificationSvc,
// userSvc,
// domain.MongoDBLogger,
// logger,
// )
// branchSvc := branch.NewService(repository.NewBranchStore(store))
// companySvc := company.NewService(repository.NewCompanyStore(store))
// ticketSvc := ticke.NewService(
// repository.NewTicketStore(store),
// // eventSvc,
// // *oddsSvc,
// domain.MongoDBLogger,
// settingSvc,
// )
// betSvc := bet.NewService(
// repository.NewBetStore(store),
// eventSvc,
// *oddsSvc,
// ,
// *branchSvc,
// *companySvc,
// *settingSvc,
// *userSvc,
// notificationSvc,
// logger,
// domain.MongoDBLogger,
// )
// resultSvc := result.NewService(
// repository.NewResultLogStore(store),
// cfg,
// logger,
// domain.MongoDBLogger,
// // *betSvc,
// // *oddsSvc,
// // eventSvc,
// // leagueSvc,
// notificationSvc,
// messengerSvc,
// *userSvc,
// )
// bonusSvc := bonus.NewService(
// repository.NewBonusStore(store),
// settingSvc,
// notificationSvc,
// domain.MongoDBLogger,
// )
// virtualGamesRepo := repository.NewVirtualGameRepository(store)
recommendationRepo := repository.NewRecommendationRepository(store)
// referalSvc := referralservice.New(
// repository.NewReferralStore(store),
// *settingSvc,
// cfg,
// logger,
// domain.MongoDBLogger,
// )
// raffleSvc := raffle.NewService(
// repository.NewRaffleStore(store),
// )
// virtualGameSvc := virtualgameservice.New(virtualGamesRepo,, store, cfg, logger)
// aleaService := alea.NewAleaPlayService(virtualGamesRepo,, cfg, logger)
// veliCLient := veli.NewClient(cfg)
// veliVirtualGameService := veli.New(virtualGameSvc, virtualGamesRepo, *store, veliCLient, repository.NewTransferStore(store), domain.MongoDBLogger, cfg)
// orchestrationSvc := orchestration.New(
// virtualGameSvc,
// virtualGamesRepo,
// cfg,
// veliCLient,
// )
// atlasClient := atlas.NewClient(cfg)
// atlasVirtualGameService := atlas.New(virtualGameSvc, virtualGamesRepo, atlasClient, repository.NewTransferStore(store), cfg)
recommendationSvc := recommendation.NewService(recommendationRepo)
// chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
// chapaSvc := chapa.NewService(
// repository.NewUserStore(store),
// cfg,
// chapaClient,
// )
// currRepo := repository.NewCurrencyPostgresRepository(store)
// fixerFertcherSvc := currency.NewFixerFetcher(
// cfg.FIXER_API_KEY,
// cfg.FIXER_BASE_URL,
// )
transactionSvc := transaction.NewService(
repository.NewTransactionStore(store),
*userSvc,
)
// reportSvc := report.NewService(
// repository.NewVirtualGameReportStore(store),
// repository.NewBetStore(store),
// repository.NewWalletStore(store),
// repository.NewTransactionStore(store),
// repository.NewBranchStore(store),
// repository.NewUserStore(store),
// repository.NewOldRepositoryStore(store),
// repository.NewCompanyStore(store),
// repository.NewVirtualGameRepository(store),
// repository.NewNotificationStore(store),
// notificationSvc,
// statSvc,
// logger,
// domain.MongoDBLogger,
// cfg,
// )
// enePulseSvc := enetpulse.New(
// *cfg,
// store,
// )
// go httpserver.StartEnetPulseCron(enePulseSvc, domain.MongoDBLogger)
// go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, orchestrationSvc, "C:/Users/User/Desktop")
// go httpserver.ProcessBetCashback(context.TODO(), betSvc)
// bankRepository := repository.NewBankRepository(store)
// instSvc := institutions.New(bankRepository)
// Initialize report worker with CSV exporter
// csvExporter := infrastructure.CSVExporter{
// ExportPath: cfg.ReportExportPath, // Make sure to add this to your config
// }
// reportWorker := worker.NewReportWorker(
// reportSvc,
// csvExporter,
// )
// Start cron jobs for automated reporting
// directdeposit := directdeposit.NewService(
// repository.NewDirectDepositRepository(store),
// notificationSvc,
// userSvc,
// )
// enetPulseSvc := enetpulse.New(
// *cfg,
// store,
// )
// Initialize wallet monitoring service
// walletMonitorSvc := monitor.NewService(
// ,
// *branchSvc,
// notificationSvc,
// logger,
// 5*time.Minute,
// )
// currSvc := currency.NewService(
// currRepo,
// cfg.BASE_CURRENCY,
// fixerFertcherSvc,
// )
// exchangeWorker := currency.NewExchangeRateWorker(fixerFertcherSvc, logger, cfg)
// exchangeWorker.Start(context.Background())
// defer exchangeWorker.Stop()
// go walletMonitorSvc.Start()
// // httpserver.StartBetAPIDataFetchingCrons(eventSvc, *oddsSvc, resultSvc, domain.MongoDBLogger)
// httpserver.StartCleanupCrons(*ticketSvc, notificationSvc, domain.MongoDBLogger)
// httpserver.StartStatCrons(statSvc, domain.MongoDBLogger)
// httpserver.StartReportCrons(reportSvc, domain.MongoDBLogger)
issueReportingRepo := repository.NewReportedIssueRepository(store)
issueReportingSvc := issuereporting.New(issueReportingRepo)
// transferStore := repository.NewTransferStore(store)
// walletStore := wallet.WalletStore(store)
assessmentSvc := assessment.NewService(
repository.NewUserStore(store),
store, // Use store directly as it implements QuestionStore
notificationSvc,
cfg,
)
// Vimeo service for video hosting
var vimeoSvc *vimeoservice.Service
if cfg.Vimeo.Enabled && cfg.Vimeo.AccessToken != "" {
vimeoSvc = vimeoservice.NewService(cfg.Vimeo.AccessToken, domain.MongoDBLogger)
logger.Info("Vimeo service initialized")
} else {
logger.Info("Vimeo service disabled (VIMEO_ENABLED not set or missing access token)")
}
// CloudConvert service for image/video optimization
var ccSvc *cloudconvertservice.Service
if cfg.CloudConvert.Enabled && cfg.CloudConvert.APIKey != "" {
ccSvc = cloudconvertservice.NewService(cfg.CloudConvert.APIKey, domain.MongoDBLogger)
logger.Info("CloudConvert service initialized")
} else {
logger.Info("CloudConvert service disabled (CLOUDCONVERT_ENABLED not set or missing API key)")
}
// MinIO service for file storage
var minioSvc *minioservice.Service
if cfg.MinIO.Enabled && cfg.MinIO.Endpoint != "" {
mc, err := minioclient.NewClient(cfg.MinIO.Endpoint, cfg.MinIO.AccessKey, cfg.MinIO.SecretKey, cfg.MinIO.UseSSL, cfg.MinIO.Bucket)
if err != nil {
log.Fatalf("failed to create MinIO client: %v", err)
}
minioSvc = minioservice.NewService(mc, domain.MongoDBLogger)
if err := minioSvc.Init(context.Background()); err != nil {
log.Fatalf("failed to initialize MinIO bucket: %v", err)
}
logger.Info("MinIO service initialized", "endpoint", cfg.MinIO.Endpoint, "bucket", cfg.MinIO.Bucket)
} else {
logger.Info("MinIO service disabled (MINIO_ENABLED not set or missing endpoint)")
}
// Questions service (unified questions system)
questionsSvc := questions.NewService(store)
faqSvc := faqs.NewService(repository.NewFAQStore(store))
personasSvc := personasservice.NewService(store)
examPrepSvc := examprep.NewService(store)
// LMS programs (top-level hierarchy)
programSvc := programsservice.NewService(store)
// LMS courses (under programs)
courseSvc := coursesservice.NewService(store, store)
// LMS modules (under courses)
moduleSvc := moduleservice.NewService(store, store)
// LMS lessons (under modules)
lessonSvc := lessonsservice.NewService(store, store)
lmsProgressSvc := lmsprogress.NewService(store)
// LMS practices (under course, module, or lesson)
practiceSvc := practicesservice.NewService(store, store, store, store, store, store)
// Subscriptions service
subscriptionsSvc := subscriptions.NewService(store)
// ArifPay service (direct/legacy payment flows)
arifpaySvc := arifpay.NewArifpayService(
cfg,
&http.Client{Timeout: 30 * time.Second},
store, // implements PaymentStore
store, // implements SubscriptionStore
)
// Chapa service for subscription checkout payments
chapaSvc := chapa.NewService(
cfg,
&http.Client{Timeout: 30 * time.Second},
store,
store,
store,
)
// Team management service
teamSvc := team.NewService(repository.NewTeamStore(store), cfg.RefreshExpiry)
// santimpayClient := santimpay.NewSantimPayClient(cfg)
// santimpaySvc := santimpay.NewSantimPayService(santimpayClient, cfg, transferStore)
// telebirrSvc := telebirr.NewTelebirrService(cfg, transferStore)
// Activity Log service
activityLogSvc := activitylogservice.NewService(store, domain.MongoDBLogger)
// Ratings service
ratingSvc := ratingsservice.NewService(repository.NewRatingStore(store))
// RBAC service
rbacSvc := rbacservice.NewService(repository.NewRBACStore(store), logger)
if err := rbacSvc.SeedPermissions(context.Background()); err != nil {
log.Fatalf("failed to seed RBAC permissions: %v", err)
}
if err := rbacSvc.SeedDefaultRolePermissions(context.Background()); err != nil {
log.Fatalf("failed to seed default role permissions: %v", err)
}
if err := rbacSvc.Reload(context.Background()); err != nil {
log.Fatalf("failed to load RBAC cache: %v", err)
}
// Initialize and start HTTP server
app := httpserver.NewApp(
assessmentSvc,
questionsSvc,
faqSvc,
personasSvc,
examPrepSvc,
programSvc,
courseSvc,
moduleSvc,
lessonSvc,
lmsProgressSvc,
practiceSvc,
subscriptionsSvc,
arifpaySvc,
chapaSvc,
issueReportingSvc,
vimeoSvc,
teamSvc,
activityLogSvc,
ccSvc,
minioSvc,
ratingSvc,
cfg.Port,
v,
settingSvc,
authSvc,
logger,
jwtutil.JwtConfig{
JwtAccessKey: cfg.JwtKey,
JwtAccessExpiry: cfg.AccessExpiry,
},
userSvc,
transactionSvc,
notificationSvc,
recommendationSvc,
cfg,
domain.MongoDBLogger,
analyticsDB,
rbacSvc,
)
logger.Info("Starting server", "port", cfg.Port)
if err := app.Run(); err != nil {
logger.Error("Failed to start server", "error", err)
os.Exit(1)
}
select {}
}