Domain Overview
This document describes the key business and technical domains, how they interact, and where to find the most important code.
For platform context (Tillo, Core, Buyers, purchase schemes), see the documentation index. For sequence and flow diagrams, see key flows.
Domain Map
| Layer | Members |
|---|---|
| Tenant Storefront (Inertia/Vue) | Auth, Catalogue / Product, Basket, Checkout, Order Success |
Core Commerce (legacy app/Models, app/Service, app/Http) | Tenant Domain, Order / Fulfillment, Product, Pricing |
DDD Modules (app/PaymentGateway, app/Tillo, app/Hub, app/Auth, app/Auditing, app/Webhook, app/Reporting, app/Shared) | PaymentGateway, Tillo, Hub (admin API), Auth Providers, Auditing, Webhook (outbound), Reporting, Shared |
| External Systems | Tillo API, Stripe, Checkout.com, TrueLayer, PPS / LeisureChoice, DynamoDB (audit), Redis (queues) |
Domains in Detail
Tenant
What it does: Resolves the current tenant from the HTTP host, initialises a shared Tenancy context for the
request, and provides Eloquent scoping so that all queries are automatically filtered to the current tenant.
Hosting and DNS: Production runs on EKS under giftstorefront.com. Buyers point a custom domain at StoreFront—historically via a reverse proxy to giftstorefront.com with X-Forwarded-Host (or equivalent) set to their domain, which middleware uses to look up the tenant. Domain management is increasingly in-house (e.g. Cloudflare entries created for the Buyer’s domain).
Key files:
| File | Purpose |
|---|---|
app/Tenant/Middleware/TenantDomainMiddleware.php | Matches the HTTP host to a Tenant model and calls Tenancy::init() |
app/Tenant/Tenancy.php | Singleton container; holds the resolved Tenant for the request |
app/Tenant/BelongsToTenant.php | Trait; automatically applies the tenant scope to any model that uses it |
app/Tenant/Scopes/TenantScope.php | Global Eloquent scope that adds WHERE tenant_id = ? to all queries |
app/Tenant/Events/TenantAssigned.php | Dispatched after tenant resolution; lets other parts of the system react |
app/Http/Middleware/HandleInertiaRequests.php | Shares tenant theme, locale, and deferred wallet data with every Vue page |
Journey middleware: The tenant middleware group additionally includes
JourneyMiddleware, which controls whether an anonymous customer is
allowed to browse or is redirected to log in first (depending on the tenant's configuration).
Product
What it does: Manages the gift card catalogue—brands, denominations (fixed or open), categories, images, discounts, cashback rates, and tenant-specific overrides. The mall (catalogue) page exposes a hero banner, promoted-brand carousel, and searchable/filterable catalogue with discount/cashback labels. The product (brand) page shows denominations or open value, brand details and terms, and scheme rates; Buyers can deep-link authenticated users to a brand URL from their own site. A separate Buy as a Gift path on the brand page checks out a single brand in isolation (similar to Amazon’s gift flow), outside the mixed basket journey.
Key files:
| File | Purpose |
|---|---|
app/Models/Product.php | Core product model; relationships to Tenant, denominations, categories |
app/Service/ProductService.php | Business logic for querying and mutating products |
app/Service/PricingService.php | Calculates the final price a customer pays (discount / cashback / face value) |
app/Http/Controllers/CatalogueController.php | Serves the mall Inertia page (product listing) |
app/Http/Controllers/ProductController.php | Serves the product Inertia page (product detail) |
Product data is synced from Tillo via background jobs (see the Tillo section).
Order
What it does: The Order domain spans the entire customer purchase journey, from the basket through payment to post-purchase fulfilment.
Customer journey (self-purchase): catalogue → brand page (value + add to basket) → basket → checkout (name, email, payment method) → hosted payment (Stripe, Checkout.com, TrueLayer, etc.) or tenant-specific flows (voucher sync, tenant wallet) → success page with order confirmation and async gift-card redemption links once fulfilment completes (typically within seconds). Buyers control experience before and after StoreFront (campaigns, deep links, cashback in their app) even though StoreFront UI is not white-label editable.
Key files:
| File | Purpose |
|---|---|
app/Models/Order.php | Core order model; tracks status, payment, customer, items |
app/Models/OrderItem.php | One row per gift card line in the order |
app/Service/OrderService.php | Central orchestrator: creates/updates orders, triggers payment and fulfilment |
app/Http/Controllers/BasketController.php | Basket page and basket mutations |
app/Http/Controllers/CheckoutController.php | Selects the checkout variant and delegates to the active payment gateway |
app/Http/Controllers/ConfirmationController.php | Resolves the post-payment success or fraud-verification page |
app/Order/Services/OrderItemService.php | Builds the Tillo order card request for each order item |
app/Console/Commands/Order/ | Artisan commands for order lifecycle steps (CreateFulfillment, CompleteOrder, ExpireOrder, …) |
app/Jobs/Order/ | Queued jobs (OrderGiftCard, CheckFulfillment, SyncGiftCard, …) |
Order lifecycle states: awaiting payment → paid → fulfillment → completed / refunded / cancelled
PaymentGateway
What it does: An abstraction layer over multiple payment processors. Each gateway implements the same abstract base class and is wired in as the active gateway via the tenant's configuration.
Key files:
| File | Purpose |
|---|---|
app/PaymentGateway/PaymentGateway.php | Abstract base class all gateways extend; defines checkout(), renderSuccessPage(), refund() |
app/Service/PaymentGatewayService.php | Resolves the active gateway for a tenant and coordinates payment operations |
app/PaymentGateway/Shared/Enums/PaymentGatewayName.php | Enum of all supported gateway names |
Supported gateways:
| Gateway | Checkout page variant | Webhook entry point |
|---|---|---|
| Stripe | stripePayment (Inertia page) | Cashier WebhookReceived → CheckoutSessionCompleted / CheckoutSessionExpired / ChargeDispute* listeners |
| Checkout.com | checkout (embedded) | app/PaymentGateway/CheckoutCom/Http/Controllers/WebhookController.php |
| TrueLayer | Redirect (HPP) | app/PaymentGateway/TrueLayer/Http/WebhookController.php |
| PPS | checkoutVoucher | — (synchronous card issuance) |
| LeisureChoice | checkoutVoucher | — (synchronous card issuance) |
| TenantWallet | checkoutTenantWallet | — (internal balance debit); also used for Salary Finance-style flows where the Buyer exposes an authorisation endpoint (customer ID from auth, amount, brand) and StoreFront completes the sale on approval, then sends fulfilment webhooks |
Fulfillment & Tillo
What it does: After payment is confirmed, fulfilment creates gift card records and calls the Tillo API to issue real card codes to the customer. Tillo exposes both synchronous and asynchronous fulfilment endpoints; StoreFront uses only the asynchronous API—a retryable job places the order, then another job checks status until the card is ready. Tillo also provides product catalogue data (brands, denominations, statuses) kept in sync via Core webhooks and background jobs.
Key files:
| File | Purpose |
|---|---|
app/Models/Fulfillment.php | Top-level fulfilment record per order |
app/Models/FulfillmentItem.php | One record per gift card line; tracks issuance status and the card code |
app/Service/FulfillmentService.php | Creates Fulfillment / FulfillmentItem rows and dispatches OrderGiftCard jobs |
app/Jobs/Order/OrderGiftCard.php | Queued job that calls Tillo\Http\ApiClient::orderCard() for a single item |
app/Tillo/Http/ApiClient.php | HTTP client for all Tillo API requests (order card, order status, list brands) |
app/Tillo/Http/Controllers/WebhookController.php | Receives Tillo-originated webhook events and dispatches TilloWebhookReceived |
app/Tillo/Listeners/TilloWebhookListener.php | Handles TilloWebhookReceived; updates fulfilment item status |
app/Tillo/Jobs/SyncProductStatusJob.php | Syncs Tillo brand active/inactive statuses to local products |
app/Tillo/Jobs/SyncProductDenominationsJob.php | Syncs denomination lists from Tillo to local products |
config/tillo.php | Tillo API credentials and environment config |
Hub (internal admin API)
What it does: A JSON REST API used by the new Hub admin UI (Nuxt), which replaces the legacy Hub embedded in Core for StoreFront operations. It provides read/write operations for orders, fulfilment items, tenants, policies, and audit record viewing— including end-customer and payment detail that Core’s Hub cannot show.
Access pattern: All Hub routes are under /api/internal/hub/v1/. Requests must carry a Bearer JWT issued by the
Hub platform. The TenantLookupMiddleware maps a Tillo Core user ID to their permitted tenant IDs.
Key files:
| File | Purpose |
|---|---|
app/Hub/Shared/routes/api.php | All Hub API route definitions |
app/Hub/Shared/Services/JWTService.php | Verifies and decodes Hub JWTs |
app/Hub/Shared/Http/Middleware/TenantLookupMiddleware.php | Resolves tenant list from the JWT caller identity |
app/Hub/Orders/Http/Controllers/OrderController.php | List, view, and manage orders |
app/Hub/Orders/Http/Controllers/FulfillmentItemController.php | Refund / cancel fulfilment items |
app/Hub/Orders/Http/Controllers/ViewOrderAuditRecordsController.php | Return DynamoDB audit trail for an order |
app/Hub/Tenants/Http/Controllers/TenantController.php | Manage tenant configuration |
app/Hub/Tenants/Http/Controllers/PolicyController.php | Manage tenant policy sections (terms/privacy) |
app/Hub/config/hub.php | Hub-specific configuration |
Auth
What it does: Storefront authentication is pluggable per tenant. After tenant resolution, the configured protocol (JWT, OAuth, or OIDC) is loaded from encrypted credentials in the database. On success, StoreFront creates a session (and cookie) keyed by the IdP’s sub (or equivalent) as the external user ID. Sessions expire after two hours; the customer is sent back to the Buyer’s site and, if still logged in there, returns to StoreFront with a fresh token. The Hub uses a completely separate JWT-based auth path.
Key files:
| File | Purpose |
|---|---|
app/Auth/AuthProviderFactory.php | Creates the correct AuthProvider implementation for the current tenant |
app/Auth/AuthProviderInterface.php | Contract all storefront auth providers implement |
app/Auth/JWTProvider.php | JWT-based storefront auth (most tenants) |
app/Auth/OIDCProvider.php | OIDC-based storefront auth |
app/Auth/OAuthProvider.php | OAuth-based storefront auth |
app/Http/Controllers/AuthController.php | Handles storefront login/callback; upserts Customer |
app/Tillo/Http/Controllers/TenantLoginController.php | Tillo staff tenant-picker and signed cross-tenant login |
app/Hub/Shared/Services/JWTService.php | Hub JWT verification (separate from storefront auth) |
Auditing
What it does: Records an immutable, versioned audit trail for domain events. Audit records are stored in AWS DynamoDB (a local DynamoDB container is available via Sail). PII in audit payloads is tokenised before storage.
Key files:
| File | Purpose |
|---|---|
app/Auditing/Listeners/EventAuditingListener.php | Queued listener (queue: auditing) that persists audit DTOs for each registered domain event |
app/Auditing/Services/AuditingService.php | Builds and stores audit records via DynamoDbService |
app/Auditing/Services/DynamoDbService.php | DynamoDB read/write wrapper |
app/Auditing/Dtos/V1/ | Versioned audit DTO classes (one per audited event) |
app/Events/Interfaces/SupportsAuditingInterface.php | Marker interface; events that implement it will be audited |
app/Auditing/config/auditing.php | DynamoDB table names and other auditing configuration |
Events registered for auditing can be found in
app/Providers/EventServiceProvider.php.
Outbound Webhooks
What it does: After key order events, the application sends signed webhook payloads to the tenant's configured
endpoint (e.g. ItemFulfilled, FulfillmentItemRefunded). This is optional per Buyer but critical for real-time cashback and loyalty integrations—typically one webhook per fulfilled gift card, carrying order ID, item ID, brand, amount, user ID, and discount/cashback metadata. Payloads are versioned (V1 / V2) and dispatched via a dedicated webhooks queue.
Key files:
| File | Purpose |
|---|---|
app/Webhook/Listener/SendWebhookListener.php | Reacts to domain events and schedules a webhook dispatch |
app/Webhook/WebhookJob.php | Queued job that performs the HTTP POST to the tenant endpoint |
app/Webhook/WebhookSigner.php | Signs payloads with the tenant's webhook secret |
app/Webhook/Dto/WebhookHandlerFactory.php | Resolves the correct versioned handler (V1/V2) for an event type |
config/webhook-server.php | Default queue name (webhooks) and retry / backoff configuration |
Reporting
What it does: Generates and emails monthly transaction reports directly to Buyers (alongside separate monthly invoicing from Tillo finance). Also contains billing/fee scheme logic for calculating monthly revenue and dispute reporting.
Key files:
| File | Purpose |
|---|---|
app/Reporting/Service/ReportingService.php | Entry point for report generation |
app/Reporting/Service/ReportBuilder/ | Concrete report builders per tenant/type |
app/Reporting/Billing/Fees/FeeService.php | Calculates fees owed based on a tenant's fee scheme |
app/Reporting/Cashback/CashbackHandler.php | Records cashback transactions |
Shared
What it does: Cross-cutting utilities used by multiple domains.
Key files:
| File | Purpose |
|---|---|
app/Shared/Dtos/ActorDto.php | Identifies the actor (user or system job) responsible for a domain action |
app/Shared/Providers/ActorDtoProvider.php | Resolves the ActorDto from the current request or job context |
app/Shared/Traits/HasOccurredAtData.php | Timestamp trait for events and DTOs |