Skip to content

Commit bfd1631

Browse files
committed
feat: Implement checkout expiration process and related commands
1 parent 56780be commit bfd1631

5 files changed

Lines changed: 97 additions & 3 deletions

File tree

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ build: ## Build the application
4141
go build -o bin/api ./cmd/api
4242
go build -o bin/migrate ./cmd/migrate
4343
go build -o bin/seed ./cmd/seed
44+
go build -o bin/expire-checkouts ./cmd/expire-checkouts
4445

4546
run: db-start ## Run the application locally with database
4647
@echo "Starting database and waiting for it to be ready..."
@@ -105,3 +106,7 @@ vet: ## Run go vet
105106

106107
mod-tidy: ## Tidy Go modules
107108
go mod tidy
109+
110+
# Maintenance commands
111+
expire-checkouts: ## Expire old checkouts manually
112+
go run ./cmd/expire-checkouts

cmd/api/main.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ func main() {
4848
// Initialize API server
4949
server := api.NewServer(cfg, db, logger)
5050

51+
// Start background checkout expiry process
52+
go startCheckoutExpiryProcess(server, logger)
53+
5154
// Start server in a goroutine
5255
go func() {
5356
logger.Info("Starting server on port %s", cfg.Server.Port)
@@ -73,3 +76,33 @@ func main() {
7376

7477
logger.Info("Server exited properly")
7578
}
79+
80+
// startCheckoutExpiryProcess runs a background process to expire old checkouts
81+
func startCheckoutExpiryProcess(server *api.Server, logger logger.Logger) {
82+
// Run every 15 minutes
83+
ticker := time.NewTicker(15 * time.Minute)
84+
defer ticker.Stop()
85+
86+
// Run immediately on startup
87+
expireCheckouts(server, logger)
88+
89+
for range ticker.C {
90+
expireCheckouts(server, logger)
91+
}
92+
}
93+
94+
// expireCheckouts expires old checkouts
95+
func expireCheckouts(server *api.Server, logger logger.Logger) {
96+
checkoutUseCase := server.GetContainer().UseCases().CheckoutUseCase()
97+
if checkoutUseCase == nil {
98+
logger.Error("CheckoutUseCase not available")
99+
return
100+
}
101+
102+
amountExpired, err := checkoutUseCase.ExpireOldCheckouts()
103+
if err != nil {
104+
logger.Error("Failed to expire old checkouts: %v", err)
105+
} else {
106+
logger.Info("Expired %d old checkouts", amountExpired)
107+
}
108+
}

cmd/expire-checkouts/main.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package main
2+
3+
import (
4+
"log"
5+
6+
"github.com/joho/godotenv"
7+
"github.com/zenfulcode/commercify/config"
8+
"github.com/zenfulcode/commercify/internal/infrastructure/container"
9+
"github.com/zenfulcode/commercify/internal/infrastructure/database"
10+
"github.com/zenfulcode/commercify/internal/infrastructure/logger"
11+
)
12+
13+
func main() {
14+
// Load environment variables
15+
if err := godotenv.Load(); err != nil {
16+
log.Println("No .env file found, using environment variables")
17+
}
18+
19+
// Initialize logger
20+
logger := logger.NewLogger()
21+
logger.Info("Starting checkout expiry cleanup tool")
22+
23+
// Load configuration
24+
cfg, err := config.LoadConfig()
25+
if err != nil {
26+
logger.Fatal("Failed to load configuration: %v", err)
27+
}
28+
29+
// Connect to database
30+
db, err := database.NewPostgresConnection(cfg.Database)
31+
if err != nil {
32+
logger.Fatal("Failed to connect to database: %v", err)
33+
}
34+
defer db.Close()
35+
36+
// Initialize dependency container
37+
diContainer := container.NewContainer(cfg, db, logger)
38+
39+
// Get checkout use case
40+
checkoutUseCase := diContainer.UseCases().CheckoutUseCase()
41+
42+
// Expire old checkouts
43+
amountExpired, err := checkoutUseCase.ExpireOldCheckouts()
44+
if err != nil {
45+
logger.Fatal("Failed to expire old checkouts: %v", err)
46+
}
47+
48+
logger.Info("Expired %d old checkouts", amountExpired)
49+
}

internal/application/usecase/checkout_usecase.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,13 +492,15 @@ func (uc *CheckoutUseCase) RemoveDiscountCode(checkout *entity.Checkout) (*entit
492492
}
493493

494494
// ExpireOldCheckouts marks expired checkouts as expired
495-
func (uc *CheckoutUseCase) ExpireOldCheckouts() error {
495+
func (uc *CheckoutUseCase) ExpireOldCheckouts() (int, error) {
496496
// Get expired checkouts
497497
expiredCheckouts, err := uc.checkoutRepo.GetExpiredCheckouts()
498498
if err != nil {
499-
return err
499+
return 0, err
500500
}
501501

502+
amountExpired := len(expiredCheckouts)
503+
502504
// Mark each as expired
503505
for _, checkout := range expiredCheckouts {
504506
checkout.MarkAsExpired()
@@ -509,7 +511,7 @@ func (uc *CheckoutUseCase) ExpireOldCheckouts() error {
509511
}
510512
}
511513

512-
return nil
514+
return amountExpired, nil
513515
}
514516

515517
// CreateOrderFromCheckout creates an order from a checkout

internal/interfaces/api/server.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ func (s *Server) setupRoutes() {
237237
admin.HandleFunc("/variants/{variantId:[0-9]+}/prices/{currency}", productHandler.RemoveVariantPrice).Methods(http.MethodDelete)
238238
}
239239

240+
// GetContainer returns the dependency injection container
241+
func (s *Server) GetContainer() container.Container {
242+
return s.container
243+
}
244+
240245
// setupStripeWebhooks configures Stripe webhooks
241246
func (s *Server) setupStripeWebhooks(api *mux.Router, webhookHandler *handler.WebhookHandler) {
242247
if !s.config.Stripe.Enabled {

0 commit comments

Comments
 (0)