package orders import ( "context" "net/http" "github.com/gin-gonic/gin" "github.com/google/uuid" "backend/internal/platform/middleware" ) // SettingsProvider and VerificationChecker are defined on the consumer // side (this package), matching ProductFinder/UnitFinder/PriceResolver // above: orders never imports the site or customerverification packages // directly, main.go just needs to supply something satisfying these shapes. type SettingsProvider interface { CustomerCheckoutSettings(ctx context.Context) (accountsEnabled, verificationRequired bool, err error) } type VerificationChecker interface { IsApproved(ctx context.Context, userID uuid.UUID) (bool, error) } // OrdersGate reports the site-wide "vitrine vs boutique" switch // (site.Settings.OrdersEnabled). It runs before every other order gate: a // pure showcase site rejects order creation outright, regardless of the // customer-account settings below. type OrdersGate interface { OrdersEnabled(ctx context.Context) (bool, error) } // RequireOrdersEnabled blocks order creation entirely while the admin has // switched the site to showcase-only mode. func RequireOrdersEnabled(gate OrdersGate) gin.HandlerFunc { return func(c *gin.Context) { enabled, err := gate.OrdersEnabled(c.Request.Context()) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to check orders setting"}) return } if !enabled { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "ordering is currently disabled for this shop"}) return } c.Next() } } // RequireAccountsEnabled blocks order creation entirely while the admin // hasn't turned on customer accounts. This is intentionally stricter than // "guest checkout allowed by default": some shops (e.g. selling // age/ID-restricted goods) need every order tied to a verified account, so // the admin opts into that by flipping this one setting, and until they // do, nobody -- guest or not -- can check out. func RequireAccountsEnabled(settings SettingsProvider) gin.HandlerFunc { return func(c *gin.Context) { enabled, _, err := settings.CustomerCheckoutSettings(c.Request.Context()) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to check checkout settings"}) return } if !enabled { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "ordering requires a customer account, which is currently disabled"}) return } c.Next() } } // RequireApprovedVerificationIfNeeded must run after RequireAccountsEnabled // and middleware.RequireCustomer (so a userID is already in context). It is // a no-op unless the admin also turned on identity verification. func RequireApprovedVerificationIfNeeded(settings SettingsProvider, verification VerificationChecker) gin.HandlerFunc { return func(c *gin.Context) { _, verificationRequired, err := settings.CustomerCheckoutSettings(c.Request.Context()) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to check checkout settings"}) return } if !verificationRequired { c.Next() return } userID, ok := middleware.GetUserID(c) if !ok { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } approved, err := verification.IsApproved(c.Request.Context(), userID) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to check verification status"}) return } if !approved { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "identity verification must be approved before ordering"}) return } c.Next() } }