[{"data":1,"prerenderedAt":2212},["ShallowReactive",2],{"navigation":3,"external-navigation":214,"external_ember:\u002Fember\u002Fdomain_overview":443},[4,8,201],{"title":5,"path":6,"stem":7},"Documentation","\u002F","README",{"title":9,"path":10,"stem":11,"children":12},"EOS","\u002Feos","eos\u002FREADME",[13,44,121,181],{"title":14,"collapsed":15,"path":16,"stem":17,"children":18,"page":43},"Adrs",true,"\u002Feos\u002Fadrs","eos\u002Fadrs",[19,23,27,31,35,39],{"title":20,"path":21,"stem":22},"Use Turborepo for the EOS monorepo","\u002Feos\u002Fadrs\u002F0001-use-turborepo-for-eos","eos\u002Fadrs\u002F0001-use-turborepo-for-eos",{"title":24,"path":25,"stem":26},"Use Oxc for linting and formatting","\u002Feos\u002Fadrs\u002F0003-use-oxc-for-linting-and-formatting","eos\u002Fadrs\u002F0003-use-oxc-for-linting-and-formatting",{"title":28,"path":29,"stem":30},"Lint Markdown and validate links in CI","\u002Feos\u002Fadrs\u002F0004-lint-markdown-in-ci","eos\u002Fadrs\u002F0004-lint-markdown-in-ci",{"title":32,"path":33,"stem":34},"Use Lefthook for repository-managed Git hooks","\u002Feos\u002Fadrs\u002F0005-use-lefthook-for-repository-git-hooks","eos\u002Fadrs\u002F0005-use-lefthook-for-repository-git-hooks",{"title":36,"path":37,"stem":38},"Use Commitlint's conventional configuration","\u002Feos\u002Fadrs\u002F0006-use-commitlint-conventional-configuration","eos\u002Fadrs\u002F0006-use-commitlint-conventional-configuration",{"title":40,"path":41,"stem":42},"Use Nx for EOS task execution","\u002Feos\u002Fadrs\u002F0007-use-nx-for-eos","eos\u002Fadrs\u002F0007-use-nx-for-eos",false,{"title":45,"path":46,"stem":47,"children":48},"Harbourmaster","\u002Feos\u002Fharbourmaster","eos\u002Fharbourmaster\u002FREADME",[49,58,71,96],{"title":50,"path":51,"stem":52,"children":53,"page":43},"Decisions","\u002Feos\u002Fharbourmaster\u002Fdecisions","eos\u002Fharbourmaster\u002Fdecisions",[54],{"title":55,"path":56,"stem":57},"AI-50: GitHub auth and fork pull request policy","\u002Feos\u002Fharbourmaster\u002Fdecisions\u002Fai-50-auth-and-fork-policy","eos\u002Fharbourmaster\u002Fdecisions\u002Fai-50-auth-and-fork-policy",{"title":59,"path":60,"stem":61,"children":62,"page":43},"Explanation","\u002Feos\u002Fharbourmaster\u002Fexplanation","eos\u002Fharbourmaster\u002Fexplanation",[63,67],{"title":64,"path":65,"stem":66},"The Harbourmaster review pipeline","\u002Feos\u002Fharbourmaster\u002Fexplanation\u002Freview-pipeline","eos\u002Fharbourmaster\u002Fexplanation\u002Freview-pipeline",{"title":68,"path":69,"stem":70},"The Harbourmaster shared-action trust boundary","\u002Feos\u002Fharbourmaster\u002Fexplanation\u002Fshared-action-trust-boundary","eos\u002Fharbourmaster\u002Fexplanation\u002Fshared-action-trust-boundary",{"title":72,"path":73,"stem":74,"children":75,"page":43},"How To","\u002Feos\u002Fharbourmaster\u002Fhow-to","eos\u002Fharbourmaster\u002Fhow-to",[76,80,84,88,92],{"title":77,"path":78,"stem":79},"How to add Harbourmaster reviews to a repository","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fadd-harbourmaster-to-a-repository","eos\u002Fharbourmaster\u002Fhow-to\u002Fadd-harbourmaster-to-a-repository",{"title":81,"path":82,"stem":83},"How to build a pilot dashboard","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fbuild-pilot-dashboard","eos\u002Fharbourmaster\u002Fhow-to\u002Fbuild-pilot-dashboard",{"title":85,"path":86,"stem":87},"How to configure model routing","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fconfigure-model-routing","eos\u002Fharbourmaster\u002Fhow-to\u002Fconfigure-model-routing",{"title":89,"path":90,"stem":91},"How to request a Harbourmaster review","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Frequest-a-review","eos\u002Fharbourmaster\u002Fhow-to\u002Frequest-a-review",{"title":93,"path":94,"stem":95},"How to diagnose a Harbourmaster review failure","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Ftroubleshoot-a-review","eos\u002Fharbourmaster\u002Fhow-to\u002Ftroubleshoot-a-review",{"title":97,"path":98,"stem":99,"children":100,"page":43},"Reference","\u002Feos\u002Fharbourmaster\u002Freference","eos\u002Fharbourmaster\u002Freference",[101,105,109,113,117],{"title":102,"path":103,"stem":104},"Harbourmaster command-line interface","\u002Feos\u002Fharbourmaster\u002Freference\u002Fcli","eos\u002Fharbourmaster\u002Freference\u002Fcli",{"title":106,"path":107,"stem":108},"Harbourmaster configuration","\u002Feos\u002Fharbourmaster\u002Freference\u002Fconfiguration","eos\u002Fharbourmaster\u002Freference\u002Fconfiguration",{"title":110,"path":111,"stem":112},"Harbourmaster GitHub Action","\u002Feos\u002Fharbourmaster\u002Freference\u002Fgithub-action","eos\u002Fharbourmaster\u002Freference\u002Fgithub-action",{"title":114,"path":115,"stem":116},"Harbourmaster review behavior","\u002Feos\u002Fharbourmaster\u002Freference\u002Freview-behavior","eos\u002Fharbourmaster\u002Freference\u002Freview-behavior",{"title":118,"path":119,"stem":120},"Harbourmaster telemetry","\u002Feos\u002Fharbourmaster\u002Freference\u002Ftelemetry","eos\u002Fharbourmaster\u002Freference\u002Ftelemetry",{"title":72,"path":122,"stem":123,"children":124,"page":43},"\u002Feos\u002Fhow-to","eos\u002Fhow-to",[125,129,133,137,141,145,149,153,157,161,165,169,173,177],{"title":126,"path":127,"stem":128},"How to add an external documentation source","\u002Feos\u002Fhow-to\u002Fadd-external-documentation-source","eos\u002Fhow-to\u002Fadd-external-documentation-source",{"title":130,"path":131,"stem":132},"How to connect SDLC integrations","\u002Feos\u002Fhow-to\u002Fconnect-sdlc-integrations","eos\u002Fhow-to\u002Fconnect-sdlc-integrations",{"title":134,"path":135,"stem":136},"How to consume the EOS plugin marketplace","\u002Feos\u002Fhow-to\u002Fconsume-eos-marketplace","eos\u002Fhow-to\u002Fconsume-eos-marketplace",{"title":138,"path":139,"stem":140},"How to create a spike ticket","\u002Feos\u002Fhow-to\u002Fcreate-spike-ticket","eos\u002Fhow-to\u002Fcreate-spike-ticket",{"title":142,"path":143,"stem":144},"How to finalise a repository change","\u002Feos\u002Fhow-to\u002Ffinalise-change","eos\u002Fhow-to\u002Ffinalise-change",{"title":146,"path":147,"stem":148},"How to install the EOS Docs MCP server","\u002Feos\u002Fhow-to\u002Finstall-eos-docs-mcp","eos\u002Fhow-to\u002Finstall-eos-docs-mcp",{"title":150,"path":151,"stem":152},"How to resolve a spike ticket","\u002Feos\u002Fhow-to\u002Fresolve-spike-ticket","eos\u002Fhow-to\u002Fresolve-spike-ticket",{"title":154,"path":155,"stem":156},"How to run Git hook checks","\u002Feos\u002Fhow-to\u002Frun-git-hook-checks","eos\u002Fhow-to\u002Frun-git-hook-checks",{"title":158,"path":159,"stem":160},"How to use the AI-enabled SDLC","\u002Feos\u002Fhow-to\u002Fuse-ai-enabled-sdlc","eos\u002Fhow-to\u002Fuse-ai-enabled-sdlc",{"title":162,"path":163,"stem":164},"Use the Aikido plugin","\u002Feos\u002Fhow-to\u002Fuse-aikido-plugin","eos\u002Fhow-to\u002Fuse-aikido-plugin",{"title":166,"path":167,"stem":168},"How to use Compass","\u002Feos\u002Fhow-to\u002Fuse-compass","eos\u002Fhow-to\u002Fuse-compass",{"title":170,"path":171,"stem":172},"How to use Fallow","\u002Feos\u002Fhow-to\u002Fuse-fallow","eos\u002Fhow-to\u002Fuse-fallow",{"title":174,"path":175,"stem":176},"How to use incremental mutation tests","\u002Feos\u002Fhow-to\u002Fuse-incremental-mutation-tests","eos\u002Fhow-to\u002Fuse-incremental-mutation-tests",{"title":178,"path":179,"stem":180},"How to use Scout","\u002Feos\u002Fhow-to\u002Fuse-scout","eos\u002Fhow-to\u002Fuse-scout",{"title":97,"path":182,"stem":183,"children":184,"page":43},"\u002Feos\u002Freference","eos\u002Freference",[185,189,193,197],{"title":186,"path":187,"stem":188},"Compass CLI","\u002Feos\u002Freference\u002Fcompass-cli","eos\u002Freference\u002Fcompass-cli",{"title":190,"path":191,"stem":192},"EOS marketplace skills","\u002Feos\u002Freference\u002Fmarketplace-skills","eos\u002Freference\u002Fmarketplace-skills",{"title":194,"path":195,"stem":196},"Nx task execution and cache","\u002Feos\u002Freference\u002Fnx-task-execution","eos\u002Freference\u002Fnx-task-execution",{"title":198,"path":199,"stem":200},"Scout CLI","\u002Feos\u002Freference\u002Fscout-cli","eos\u002Freference\u002Fscout-cli",{"title":202,"path":203,"stem":204,"children":205},"Product engineering","\u002Fproduct-engineering","product-engineering\u002FREADME",[206],{"title":14,"collapsed":15,"path":207,"stem":208,"children":209,"page":43},"\u002Fproduct-engineering\u002Fadrs","product-engineering\u002Fadrs",[210],{"title":211,"path":212,"stem":213},"Codify Engineering Standards as Skills","\u002Fproduct-engineering\u002Fadrs\u002F0002-codify-engineering-standards-as-skills","product-engineering\u002Fadrs\u002F0002-codify-engineering-standards-as-skills",[215,268],{"nav":216,"source":265},[217],{"title":218,"path":219,"stem":220,"children":221},"Ember","\u002Fember","ember",[222,225,257,261],{"title":223,"path":219,"stem":224},"StoreFront Documentation","ember\u002Findex",{"title":14,"path":226,"stem":227,"children":228,"page":43},"\u002Fember\u002Fadrs","ember\u002Fadrs",[229,233,237,241,245,249,253],{"title":230,"path":231,"stem":232},"Add Infection as a non-blocking mutation testing tool for Ember","\u002Fember\u002Fadrs\u002F0001-infection-mutation-testing","ember\u002Fadrs\u002F0001-infection-mutation-testing",{"title":234,"path":235,"stem":236},"Process DELETE scheduled changes before CREATE_UPDATE at the same instant","\u002Fember\u002Fadrs\u002F0002-scheduled-change-processing-order","ember\u002Fadrs\u002F0002-scheduled-change-processing-order",{"title":238,"path":239,"stem":240},"Standard rate scheduling is permanent until overridden","\u002Fember\u002Fadrs\u002F0003-standard-rate-scheduling-is-permanent","ember\u002Fadrs\u002F0003-standard-rate-scheduling-is-permanent",{"title":242,"path":243,"stem":244},"Promotions require an existing standard rate","\u002Fember\u002Fadrs\u002F0004-promotions-require-existing-standard-rate","ember\u002Fadrs\u002F0004-promotions-require-existing-standard-rate",{"title":246,"path":247,"stem":248},"UTC datetime contract between Storefront and Hub","\u002Fember\u002Fadrs\u002F0005-utc-datetime-contract-between-storefront-and-hub","ember\u002Fadrs\u002F0005-utc-datetime-contract-between-storefront-and-hub",{"title":250,"path":251,"stem":252},"Elapsed unprocessed scheduled changes do not block rate changes","\u002Fember\u002Fadrs\u002F0006-stale-scheduler-rows-do-not-block-rate-changes","ember\u002Fadrs\u002F0006-stale-scheduler-rows-do-not-block-rate-changes",{"title":254,"path":255,"stem":256},"ADR-0007: Delete ConfigureTenantAuth routing command once OIDC setup migrates to Hub","\u002Fember\u002Fadrs\u002F0007-delete-configure-tenant-auth-routing-command","ember\u002Fadrs\u002F0007-delete-configure-tenant-auth-routing-command",{"title":258,"path":259,"stem":260},"Domain Overview","\u002Fember\u002Fdomain_overview","ember\u002Fdomain_overview",{"title":262,"path":263,"stem":264},"Main Flows","\u002Fember\u002Fkey_flows","ember\u002Fkey_flows",{"collectionName":266,"label":267,"prefix":220},"external_ember","StoreFront (Ember)",{"nav":269,"source":441},[270],{"title":271,"path":272,"stem":273,"children":274},"Core","\u002Fcore","core",[275,278,306,310,332,336,346,363,420,424],{"title":276,"path":272,"stem":277},"Documentation Index","core\u002Findex",{"title":14,"path":279,"stem":280,"children":281,"page":43},"\u002Fcore\u002Fadrs","core\u002Fadrs",[282,286,290,294,298,302],{"title":283,"path":284,"stem":285},"ADR-XXXX: Short, descriptive title","\u002Fcore\u002Fadrs\u002F0000-template","core\u002Fadrs\u002F0000-template",{"title":287,"path":288,"stem":289},"ADR-1: Promotions endpoint","\u002Fcore\u002Fadrs\u002F0001-add-promotions-endpoint","core\u002Fadrs\u002F0001-add-promotions-endpoint",{"title":291,"path":292,"stem":293},"ADR-2: EOM partner report job dispatch","\u002Fcore\u002Fadrs\u002F0002-eom-report-job-dispatch","core\u002Fadrs\u002F0002-eom-report-job-dispatch",{"title":295,"path":296,"stem":297},"ADR-3: Replace PB fee waive flag with a Zero scheme override","\u002Fcore\u002Fadrs\u002F0003-replace-pb-fee-waive-with-zero-scheme","core\u002Fadrs\u002F0003-replace-pb-fee-waive-with-zero-scheme",{"title":299,"path":300,"stem":301},"ADR-4: No processor Digital Top-up capability gate in Reloadable Commercials","\u002Fcore\u002Fadrs\u002F0004-no-processor-top-up-capability-gate","core\u002Fadrs\u002F0004-no-processor-top-up-capability-gate",{"title":303,"path":304,"stem":305},"ADR-5: Sparse polymorphic sale fee override reason stamp","\u002Fcore\u002Fadrs\u002F0005-sale-fee-override-reason-stamp","core\u002Fadrs\u002F0005-sale-fee-override-reason-stamp",{"title":307,"path":308,"stem":309},"Agent Workflow","\u002Fcore\u002Fai-workflow","core\u002Fai-workflow",{"title":311,"path":312,"stem":313,"children":314},"Backend Guide","\u002Fcore\u002Fbackend","core\u002Fbackend\u002Findex",[315,316,320,324,328],{"title":311,"path":312,"stem":313},{"title":317,"path":318,"stem":319},"Backend AI Tooling","\u002Fcore\u002Fbackend\u002Fai-tooling","core\u002Fbackend\u002Fai-tooling",{"title":321,"path":322,"stem":323},"Backend Linting and Formatting","\u002Fcore\u002Fbackend\u002Flinting-and-formatting","core\u002Fbackend\u002Flinting-and-formatting",{"title":325,"path":326,"stem":327},"Backend Setup","\u002Fcore\u002Fbackend\u002Fsetup","core\u002Fbackend\u002Fsetup",{"title":329,"path":330,"stem":331},"Backend Testing","\u002Fcore\u002Fbackend\u002Ftesting","core\u002Fbackend\u002Ftesting",{"title":333,"path":334,"stem":335},"Documentation Health","\u002Fcore\u002Fdoc-health","core\u002Fdoc-health",{"title":337,"path":338,"stem":339,"children":340},"Frontend Guide","\u002Fcore\u002Ffrontend","core\u002Ffrontend\u002Findex",[341,342],{"title":337,"path":338,"stem":339},{"title":343,"path":344,"stem":345},"Frontend Development","\u002Fcore\u002Ffrontend\u002Fdevelopment","core\u002Ffrontend\u002Fdevelopment",{"title":347,"path":348,"stem":349,"children":350,"page":43},"Modules","\u002Fcore\u002Fmodules","core\u002Fmodules",[351,355],{"title":352,"path":353,"stem":354},"Connect API","\u002Fcore\u002Fmodules\u002Fconnect-api","core\u002Fmodules\u002Fconnect-api",{"title":356,"path":357,"stem":358,"children":359,"page":43},"Secure Links","\u002Fcore\u002Fmodules\u002Fsecure-links","core\u002Fmodules\u002Fsecure-links",[360],{"title":356,"path":361,"stem":362},"\u002Fcore\u002Fmodules\u002Fsecure-links\u002Fsecure-links","core\u002Fmodules\u002Fsecure-links\u002Fsecure-links",{"title":364,"path":365,"stem":366,"children":367,"page":43},"Processors","\u002Fcore\u002Fprocessors","core\u002Fprocessors",[368,372,376,380,384,388,392,396,400,404,408,412,416],{"title":369,"path":370,"stem":371},"Amazon Processor and API","\u002Fcore\u002Fprocessors\u002Famazon","core\u002Fprocessors\u002Famazon",{"title":373,"path":374,"stem":375},"Amilon Processor and API","\u002Fcore\u002Fprocessors\u002Familon","core\u002Fprocessors\u002Familon",{"title":377,"path":378,"stem":379},"Cadooz Processor and API","\u002Fcore\u002Fprocessors\u002Fcadooz","core\u002Fprocessors\u002Fcadooz",{"title":381,"path":382,"stem":383},"Choice Digital Processor and API","\u002Fcore\u002Fprocessors\u002Fchoice-digital","core\u002Fprocessors\u002Fchoice-digital",{"title":385,"path":386,"stem":387},"Diggecard processor reference","\u002Fcore\u002Fprocessors\u002Fdiggecard","core\u002Fprocessors\u002Fdiggecard",{"title":389,"path":390,"stem":391},"ePay Processor and API","\u002Fcore\u002Fprocessors\u002Fepay","core\u002Fprocessors\u002Fepay",{"title":393,"path":394,"stem":395},"GoGift v2 Processor and API","\u002Fcore\u002Fprocessors\u002Fgo-gift-v2","core\u002Fprocessors\u002Fgo-gift-v2",{"title":397,"path":398,"stem":399},"InComm Processor and API","\u002Fcore\u002Fprocessors\u002Fincomm","core\u002Fprocessors\u002Fincomm",{"title":401,"path":402,"stem":403},"Ogloba Processor and API","\u002Fcore\u002Fprocessors\u002Fogloba","core\u002Fprocessors\u002Fogloba",{"title":405,"path":406,"stem":407},"SVSECard Processor and API","\u002Fcore\u002Fprocessors\u002Fsvs-ecard","core\u002Fprocessors\u002Fsvs-ecard",{"title":409,"path":410,"stem":411},"Vananam Processor and API","\u002Fcore\u002Fprocessors\u002Fvananam","core\u002Fprocessors\u002Fvananam",{"title":413,"path":414,"stem":415},"Vouchers Depot processor reference","\u002Fcore\u002Fprocessors\u002Fvouchers-depot","core\u002Fprocessors\u002Fvouchers-depot",{"title":417,"path":418,"stem":419},"You Got a Gift Processor and API","\u002Fcore\u002Fprocessors\u002Fyou-got-a-gift","core\u002Fprocessors\u002Fyou-got-a-gift",{"title":421,"path":422,"stem":423},"Development Quickstart","\u002Fcore\u002Fsetup","core\u002Fsetup",{"title":425,"path":426,"stem":427,"children":428,"page":43},"Workflows","\u002Fcore\u002Fworkflows","core\u002Fworkflows",[429,433,437],{"title":430,"path":431,"stem":432},"Hub Endpoint Workflow","\u002Fcore\u002Fworkflows\u002Fhub-endpoints","core\u002Fworkflows\u002Fhub-endpoints",{"title":434,"path":435,"stem":436},"OpenAPI Tooling","\u002Fcore\u002Fworkflows\u002Fopenapi-tooling","core\u002Fworkflows\u002Fopenapi-tooling",{"title":438,"path":439,"stem":440},"Processor Integration Workflow","\u002Fcore\u002Fworkflows\u002Fprocessors","core\u002Fworkflows\u002Fprocessors",{"collectionName":442,"label":271,"prefix":273},"external_core",{"page":444,"surround":2209},{"id":445,"title":258,"body":446,"config":2204,"description":452,"extension":2205,"meta":2206,"navigation":15,"path":259,"seo":2207,"stem":260,"__hash__":2208},"external_ember\u002Fember\u002Fdomain_overview.md",{"type":447,"value":448,"toc":2185},"minimark",[449,453,470,473,478,577,579,583,588,598,627,632,737,753,755,759,783,787,872,880,882,886,891,905,909,1057,1066,1068,1072,1077,1081,1141,1146,1280,1282,1286,1303,1307,1469,1471,1475,1484,1498,1502,1631,1633,1637,1669,1673,1794,1796,1800,1809,1813,1909,1918,1920,1924,1948,1952,2032,2034,2038,2047,2051,2115,2117,2121,2126,2130],[450,451,452],"p",{},"This document describes the key business and technical domains, how they interact, and where to find the most important code.",[450,454,455,456,463,464,469],{},"For platform context (Tillo, Core, Buyers, purchase schemes), see the ",[457,458,459],"strong",{},[460,461,462],"a",{"href":219},"documentation index",". For sequence and flow diagrams, see ",[457,465,466],{},[460,467,468],{"href":263},"key flows",".",[471,472],"hr",{},[474,475,477],"h2",{"id":476},"domain-map","Domain Map",[479,480,481,494],"table",{},[482,483,484],"thead",{},[485,486,487,491],"tr",{},[488,489,490],"th",{},"Layer",[488,492,493],{},"Members",[495,496,497,509,532,567],"tbody",{},[485,498,499,506],{},[500,501,502,505],"td",{},[457,503,504],{},"Tenant Storefront"," (Inertia\u002FVue)",[500,507,508],{},"Auth, Catalogue \u002F Product, Basket, Checkout, Order Success",[485,510,511,529],{},[500,512,513,516,517,521,522,521,525,528],{},[457,514,515],{},"Core Commerce"," (legacy ",[518,519,520],"code",{},"app\u002FModels",", ",[518,523,524],{},"app\u002FService",[518,526,527],{},"app\u002FHttp",")",[500,530,531],{},"Tenant Domain, Order \u002F Fulfillment, Product, Pricing",[485,533,534,564],{},[500,535,536,539,540,521,543,521,546,521,549,521,552,521,555,521,558,521,561,528],{},[457,537,538],{},"DDD Modules"," (",[518,541,542],{},"app\u002FPaymentGateway",[518,544,545],{},"app\u002FTillo",[518,547,548],{},"app\u002FHub",[518,550,551],{},"app\u002FAuth",[518,553,554],{},"app\u002FAuditing",[518,556,557],{},"app\u002FWebhook",[518,559,560],{},"app\u002FReporting",[518,562,563],{},"app\u002FShared",[500,565,566],{},"PaymentGateway, Tillo, Hub (admin API), Auth Providers, Auditing, Webhook (outbound), Reporting, Shared",[485,568,569,574],{},[500,570,571],{},[457,572,573],{},"External Systems",[500,575,576],{},"Tillo API, Stripe, Checkout.com, TrueLayer, PPS \u002F LeisureChoice, DynamoDB (audit), Redis (queues)",[471,578],{},[474,580,582],{"id":581},"domains-in-detail","Domains in Detail",[584,585,587],"h3",{"id":586},"tenant","Tenant",[450,589,590,593,594,597],{},[457,591,592],{},"What it does:"," Resolves the current tenant from the HTTP host, initialises a shared ",[518,595,596],{},"Tenancy"," context for the\nrequest, and provides Eloquent scoping so that all queries are automatically filtered to the current tenant.",[450,599,600,603,604,607,608,611,612,615,616,618,619,622,623,626],{},[457,601,602],{},"Hosting and DNS:"," Production runs on ",[457,605,606],{},"EKS"," under ",[518,609,610],{},"giftstorefront.com",". Buyers point a ",[457,613,614],{},"custom domain"," at StoreFront—historically via a reverse proxy to ",[518,617,610],{}," with ",[518,620,621],{},"X-Forwarded-Host"," (or equivalent) set to their domain, which middleware uses to look up the tenant. Domain management is increasingly ",[457,624,625],{},"in-house"," (e.g. Cloudflare entries created for the Buyer’s domain).",[450,628,629],{},[457,630,631],{},"Key files:",[479,633,634,644],{},[482,635,636],{},[485,637,638,641],{},[488,639,640],{},"File",[488,642,643],{},"Purpose",[495,645,646,665,681,694,711,724],{},[485,647,648,656],{},[500,649,650],{},[460,651,653],{"href":652},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTenant\u002FMiddleware\u002FTenantDomainMiddleware.php",[518,654,655],{},"app\u002FTenant\u002FMiddleware\u002FTenantDomainMiddleware.php",[500,657,658,659,661,662],{},"Matches the HTTP host to a ",[518,660,587],{}," model and calls ",[518,663,664],{},"Tenancy::init()",[485,666,667,675],{},[500,668,669],{},[460,670,672],{"href":671},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTenant\u002FTenancy.php",[518,673,674],{},"app\u002FTenant\u002FTenancy.php",[500,676,677,678,680],{},"Singleton container; holds the resolved ",[518,679,587],{}," for the request",[485,682,683,691],{},[500,684,685],{},[460,686,688],{"href":687},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTenant\u002FBelongsToTenant.php",[518,689,690],{},"app\u002FTenant\u002FBelongsToTenant.php",[500,692,693],{},"Trait; automatically applies the tenant scope to any model that uses it",[485,695,696,704],{},[500,697,698],{},[460,699,701],{"href":700},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTenant\u002FScopes\u002FTenantScope.php",[518,702,703],{},"app\u002FTenant\u002FScopes\u002FTenantScope.php",[500,705,706,707,710],{},"Global Eloquent scope that adds ",[518,708,709],{},"WHERE tenant_id = ?"," to all queries",[485,712,713,721],{},[500,714,715],{},[460,716,718],{"href":717},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTenant\u002FEvents\u002FTenantAssigned.php",[518,719,720],{},"app\u002FTenant\u002FEvents\u002FTenantAssigned.php",[500,722,723],{},"Dispatched after tenant resolution; lets other parts of the system react",[485,725,726,734],{},[500,727,728],{},[460,729,731],{"href":730},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHttp\u002FMiddleware\u002FHandleInertiaRequests.php",[518,732,733],{},"app\u002FHttp\u002FMiddleware\u002FHandleInertiaRequests.php",[500,735,736],{},"Shares tenant theme, locale, and deferred wallet data with every Vue page",[450,738,739,742,743,745,746,752],{},[457,740,741],{},"Journey middleware:"," The ",[518,744,586],{}," middleware group additionally includes\n",[460,747,749],{"href":748},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTenant\u002FMiddleware\u002FJourneyMiddleware.php",[518,750,751],{},"JourneyMiddleware",", which controls whether an anonymous customer is\nallowed to browse or is redirected to log in first (depending on the tenant's configuration).",[471,754],{},[584,756,758],{"id":757},"product","Product",[450,760,761,763,764,767,768,770,771,774,775,778,779,782],{},[457,762,592],{}," Manages the gift card catalogue—brands, denominations (fixed or open), categories, images, discounts,\ncashback rates, and tenant-specific overrides. The ",[457,765,766],{},"mall"," (catalogue) page exposes a hero banner, promoted-brand carousel, and searchable\u002Ffilterable catalogue with discount\u002Fcashback labels. The ",[457,769,757],{}," (brand) page shows denominations or open value, brand details and terms, and scheme rates; Buyers can ",[457,772,773],{},"deep-link"," authenticated users to a brand URL from their own site. A separate ",[457,776,777],{},"Buy as a Gift"," path on the brand page checks out a ",[457,780,781],{},"single brand"," in isolation (similar to Amazon’s gift flow), outside the mixed basket journey.",[450,784,785],{},[457,786,631],{},[479,788,789,797],{},[482,790,791],{},[485,792,793,795],{},[488,794,640],{},[488,796,643],{},[495,798,799,815,828,841,857],{},[485,800,801,809],{},[500,802,803],{},[460,804,806],{"href":805},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FModels\u002FProduct.php",[518,807,808],{},"app\u002FModels\u002FProduct.php",[500,810,811,812,814],{},"Core product model; relationships to ",[518,813,587],{},", denominations, categories",[485,816,817,825],{},[500,818,819],{},[460,820,822],{"href":821},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FService\u002FProductService.php",[518,823,824],{},"app\u002FService\u002FProductService.php",[500,826,827],{},"Business logic for querying and mutating products",[485,829,830,838],{},[500,831,832],{},[460,833,835],{"href":834},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FService\u002FPricingService.php",[518,836,837],{},"app\u002FService\u002FPricingService.php",[500,839,840],{},"Calculates the final price a customer pays (discount \u002F cashback \u002F face value)",[485,842,843,851],{},[500,844,845],{},[460,846,848],{"href":847},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHttp\u002FControllers\u002FCatalogueController.php",[518,849,850],{},"app\u002FHttp\u002FControllers\u002FCatalogueController.php",[500,852,853,854,856],{},"Serves the ",[518,855,766],{}," Inertia page (product listing)",[485,858,859,867],{},[500,860,861],{},[460,862,864],{"href":863},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHttp\u002FControllers\u002FProductController.php",[518,865,866],{},"app\u002FHttp\u002FControllers\u002FProductController.php",[500,868,853,869,871],{},[518,870,757],{}," Inertia page (product detail)",[450,873,874,875,879],{},"Product data is synced from Tillo via background jobs (see the ",[460,876,878],{"href":877},"#tillo","Tillo"," section).",[471,881],{},[584,883,885],{"id":884},"order","Order",[450,887,888,890],{},[457,889,592],{}," The Order domain spans the entire customer purchase journey, from the basket through payment to\npost-purchase fulfilment.",[450,892,893,896,897,900,901,904],{},[457,894,895],{},"Customer journey (self-purchase):"," catalogue → brand page (value + add to basket) → basket → checkout (name, email, payment method) → ",[457,898,899],{},"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 ",[457,902,903],{},"before and after"," StoreFront (campaigns, deep links, cashback in their app) even though StoreFront UI is not white-label editable.",[450,906,907],{},[457,908,631],{},[479,910,911,919],{},[482,912,913],{},[485,914,915,917],{},[488,916,640],{},[488,918,643],{},[495,920,921,934,947,960,973,986,999,1012,1035],{},[485,922,923,931],{},[500,924,925],{},[460,926,928],{"href":927},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FModels\u002FOrder.php",[518,929,930],{},"app\u002FModels\u002FOrder.php",[500,932,933],{},"Core order model; tracks status, payment, customer, items",[485,935,936,944],{},[500,937,938],{},[460,939,941],{"href":940},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FModels\u002FOrderItem.php",[518,942,943],{},"app\u002FModels\u002FOrderItem.php",[500,945,946],{},"One row per gift card line in the order",[485,948,949,957],{},[500,950,951],{},[460,952,954],{"href":953},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FService\u002FOrderService.php",[518,955,956],{},"app\u002FService\u002FOrderService.php",[500,958,959],{},"Central orchestrator: creates\u002Fupdates orders, triggers payment and fulfilment",[485,961,962,970],{},[500,963,964],{},[460,965,967],{"href":966},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHttp\u002FControllers\u002FBasketController.php",[518,968,969],{},"app\u002FHttp\u002FControllers\u002FBasketController.php",[500,971,972],{},"Basket page and basket mutations",[485,974,975,983],{},[500,976,977],{},[460,978,980],{"href":979},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHttp\u002FControllers\u002FCheckoutController.php",[518,981,982],{},"app\u002FHttp\u002FControllers\u002FCheckoutController.php",[500,984,985],{},"Selects the checkout variant and delegates to the active payment gateway",[485,987,988,996],{},[500,989,990],{},[460,991,993],{"href":992},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHttp\u002FControllers\u002FConfirmationController.php",[518,994,995],{},"app\u002FHttp\u002FControllers\u002FConfirmationController.php",[500,997,998],{},"Resolves the post-payment success or fraud-verification page",[485,1000,1001,1009],{},[500,1002,1003],{},[460,1004,1006],{"href":1005},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FOrder\u002FServices\u002FOrderItemService.php",[518,1007,1008],{},"app\u002FOrder\u002FServices\u002FOrderItemService.php",[500,1010,1011],{},"Builds the Tillo order card request for each order item",[485,1013,1014,1022],{},[500,1015,1016],{},[460,1017,1019],{"href":1018},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FConsole\u002FCommands\u002FOrder\u002F",[518,1020,1021],{},"app\u002FConsole\u002FCommands\u002FOrder\u002F",[500,1023,1024,1025,521,1028,521,1031,1034],{},"Artisan commands for order lifecycle steps (",[518,1026,1027],{},"CreateFulfillment",[518,1029,1030],{},"CompleteOrder",[518,1032,1033],{},"ExpireOrder",", …)",[485,1036,1037,1045],{},[500,1038,1039],{},[460,1040,1042],{"href":1041},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FJobs\u002FOrder\u002F",[518,1043,1044],{},"app\u002FJobs\u002FOrder\u002F",[500,1046,1047,1048,521,1051,521,1054,1034],{},"Queued jobs (",[518,1049,1050],{},"OrderGiftCard",[518,1052,1053],{},"CheckFulfillment",[518,1055,1056],{},"SyncGiftCard",[450,1058,1059,1062,1063],{},[457,1060,1061],{},"Order lifecycle states:"," ",[518,1064,1065],{},"awaiting payment → paid → fulfillment → completed \u002F refunded \u002F cancelled",[471,1067],{},[584,1069,1071],{"id":1070},"paymentgateway","PaymentGateway",[450,1073,1074,1076],{},[457,1075,592],{}," An abstraction layer over multiple payment processors. Each gateway implements the same abstract base\nclass and is wired in as the active gateway via the tenant's configuration.",[450,1078,1079],{},[457,1080,631],{},[479,1082,1083,1091],{},[482,1084,1085],{},[485,1086,1087,1089],{},[488,1088,640],{},[488,1090,643],{},[495,1092,1093,1115,1128],{},[485,1094,1095,1103],{},[500,1096,1097],{},[460,1098,1100],{"href":1099},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FPaymentGateway\u002FPaymentGateway.php",[518,1101,1102],{},"app\u002FPaymentGateway\u002FPaymentGateway.php",[500,1104,1105,1106,521,1109,521,1112],{},"Abstract base class all gateways extend; defines ",[518,1107,1108],{},"checkout()",[518,1110,1111],{},"renderSuccessPage()",[518,1113,1114],{},"refund()",[485,1116,1117,1125],{},[500,1118,1119],{},[460,1120,1122],{"href":1121},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FService\u002FPaymentGatewayService.php",[518,1123,1124],{},"app\u002FService\u002FPaymentGatewayService.php",[500,1126,1127],{},"Resolves the active gateway for a tenant and coordinates payment operations",[485,1129,1130,1138],{},[500,1131,1132],{},[460,1133,1135],{"href":1134},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FPaymentGateway\u002FShared\u002FEnums\u002FPaymentGatewayName.php",[518,1136,1137],{},"app\u002FPaymentGateway\u002FShared\u002FEnums\u002FPaymentGatewayName.php",[500,1139,1140],{},"Enum of all supported gateway names",[450,1142,1143],{},[457,1144,1145],{},"Supported gateways:",[479,1147,1148,1161],{},[482,1149,1150],{},[485,1151,1152,1155,1158],{},[488,1153,1154],{},"Gateway",[488,1156,1157],{},"Checkout page variant",[488,1159,1160],{},"Webhook entry point",[495,1162,1163,1194,1215,1233,1248,1261],{},[485,1164,1165,1170,1176],{},[500,1166,1167],{},[457,1168,1169],{},"Stripe",[500,1171,1172,1175],{},[518,1173,1174],{},"stripePayment"," (Inertia page)",[500,1177,1178,1179,1182,1183,1186,1187,1186,1190,1193],{},"Cashier ",[518,1180,1181],{},"WebhookReceived"," → ",[518,1184,1185],{},"CheckoutSessionCompleted"," \u002F ",[518,1188,1189],{},"CheckoutSessionExpired",[518,1191,1192],{},"ChargeDispute*"," listeners",[485,1195,1196,1201,1207],{},[500,1197,1198],{},[457,1199,1200],{},"Checkout.com",[500,1202,1203,1206],{},[518,1204,1205],{},"checkout"," (embedded)",[500,1208,1209],{},[460,1210,1212],{"href":1211},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FPaymentGateway\u002FCheckoutCom\u002FHttp\u002FControllers\u002FWebhookController.php",[518,1213,1214],{},"app\u002FPaymentGateway\u002FCheckoutCom\u002FHttp\u002FControllers\u002FWebhookController.php",[485,1216,1217,1222,1225],{},[500,1218,1219],{},[457,1220,1221],{},"TrueLayer",[500,1223,1224],{},"Redirect (HPP)",[500,1226,1227],{},[460,1228,1230],{"href":1229},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FPaymentGateway\u002FTrueLayer\u002FHttp\u002FWebhookController.php",[518,1231,1232],{},"app\u002FPaymentGateway\u002FTrueLayer\u002FHttp\u002FWebhookController.php",[485,1234,1235,1240,1245],{},[500,1236,1237],{},[457,1238,1239],{},"PPS",[500,1241,1242],{},[518,1243,1244],{},"checkoutVoucher",[500,1246,1247],{},"— (synchronous card issuance)",[485,1249,1250,1255,1259],{},[500,1251,1252],{},[457,1253,1254],{},"LeisureChoice",[500,1256,1257],{},[518,1258,1244],{},[500,1260,1247],{},[485,1262,1263,1268,1273],{},[500,1264,1265],{},[457,1266,1267],{},"TenantWallet",[500,1269,1270],{},[518,1271,1272],{},"checkoutTenantWallet",[500,1274,1275,1276,1279],{},"— (internal balance debit); also used for ",[457,1277,1278],{},"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",[471,1281],{},[584,1283,1285],{"id":1284},"fulfillment-tillo","Fulfillment & Tillo",[450,1287,1288,1290,1291,1294,1295,1298,1299,1302],{},[457,1289,592],{}," After payment is confirmed, fulfilment creates gift card records and calls the ",[457,1292,1293],{},"Tillo API"," to issue\nreal card codes to the customer. Tillo exposes both synchronous and asynchronous fulfilment endpoints; ",[457,1296,1297],{},"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 ",[457,1300,1301],{},"Core webhooks"," and background jobs.",[450,1304,1305],{},[457,1306,631],{},[479,1308,1309,1317],{},[482,1310,1311],{},[485,1312,1313,1315],{},[488,1314,640],{},[488,1316,643],{},[495,1318,1319,1332,1345,1368,1385,1398,1414,1430,1443,1456],{},[485,1320,1321,1329],{},[500,1322,1323],{},[460,1324,1326],{"href":1325},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FModels\u002FFulfillment.php",[518,1327,1328],{},"app\u002FModels\u002FFulfillment.php",[500,1330,1331],{},"Top-level fulfilment record per order",[485,1333,1334,1342],{},[500,1335,1336],{},[460,1337,1339],{"href":1338},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FModels\u002FFulfillmentItem.php",[518,1340,1341],{},"app\u002FModels\u002FFulfillmentItem.php",[500,1343,1344],{},"One record per gift card line; tracks issuance status and the card code",[485,1346,1347,1355],{},[500,1348,1349],{},[460,1350,1352],{"href":1351},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FService\u002FFulfillmentService.php",[518,1353,1354],{},"app\u002FService\u002FFulfillmentService.php",[500,1356,1357,1358,1186,1361,1364,1365,1367],{},"Creates ",[518,1359,1360],{},"Fulfillment",[518,1362,1363],{},"FulfillmentItem"," rows and dispatches ",[518,1366,1050],{}," jobs",[485,1369,1370,1378],{},[500,1371,1372],{},[460,1373,1375],{"href":1374},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FJobs\u002FOrder\u002FOrderGiftCard.php",[518,1376,1377],{},"app\u002FJobs\u002FOrder\u002FOrderGiftCard.php",[500,1379,1380,1381,1384],{},"Queued job that calls ",[518,1382,1383],{},"Tillo\\Http\\ApiClient::orderCard()"," for a single item",[485,1386,1387,1395],{},[500,1388,1389],{},[460,1390,1392],{"href":1391},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTillo\u002FHttp\u002FApiClient.php",[518,1393,1394],{},"app\u002FTillo\u002FHttp\u002FApiClient.php",[500,1396,1397],{},"HTTP client for all Tillo API requests (order card, order status, list brands)",[485,1399,1400,1408],{},[500,1401,1402],{},[460,1403,1405],{"href":1404},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTillo\u002FHttp\u002FControllers\u002FWebhookController.php",[518,1406,1407],{},"app\u002FTillo\u002FHttp\u002FControllers\u002FWebhookController.php",[500,1409,1410,1411],{},"Receives Tillo-originated webhook events and dispatches ",[518,1412,1413],{},"TilloWebhookReceived",[485,1415,1416,1424],{},[500,1417,1418],{},[460,1419,1421],{"href":1420},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTillo\u002FListeners\u002FTilloWebhookListener.php",[518,1422,1423],{},"app\u002FTillo\u002FListeners\u002FTilloWebhookListener.php",[500,1425,1426,1427,1429],{},"Handles ",[518,1428,1413],{},"; updates fulfilment item status",[485,1431,1432,1440],{},[500,1433,1434],{},[460,1435,1437],{"href":1436},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTillo\u002FJobs\u002FSyncProductStatusJob.php",[518,1438,1439],{},"app\u002FTillo\u002FJobs\u002FSyncProductStatusJob.php",[500,1441,1442],{},"Syncs Tillo brand active\u002Finactive statuses to local products",[485,1444,1445,1453],{},[500,1446,1447],{},[460,1448,1450],{"href":1449},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTillo\u002FJobs\u002FSyncProductDenominationsJob.php",[518,1451,1452],{},"app\u002FTillo\u002FJobs\u002FSyncProductDenominationsJob.php",[500,1454,1455],{},"Syncs denomination lists from Tillo to local products",[485,1457,1458,1466],{},[500,1459,1460],{},[460,1461,1463],{"href":1462},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fconfig\u002Ftillo.php",[518,1464,1465],{},"config\u002Ftillo.php",[500,1467,1468],{},"Tillo API credentials and environment config",[471,1470],{},[584,1472,1474],{"id":1473},"hub-internal-admin-api","Hub (internal admin API)",[450,1476,1477,1479,1480,1483],{},[457,1478,592],{}," A JSON REST API used by the ",[457,1481,1482],{},"new Hub admin UI"," (Nuxt), which replaces the legacy Hub embedded in Core for StoreFront operations. It provides read\u002Fwrite operations for orders, fulfilment items, tenants, policies, and audit record viewing— including end-customer and payment detail that Core’s Hub cannot show.",[450,1485,1486,1489,1490,1493,1494,1497],{},[457,1487,1488],{},"Access pattern:"," All Hub routes are under ",[518,1491,1492],{},"\u002Fapi\u002Finternal\u002Fhub\u002Fv1\u002F",". Requests must carry a Bearer JWT issued by the\nHub platform. The ",[518,1495,1496],{},"TenantLookupMiddleware"," maps a Tillo Core user ID to their permitted tenant IDs.",[450,1499,1500],{},[457,1501,631],{},[479,1503,1504,1512],{},[482,1505,1506],{},[485,1507,1508,1510],{},[488,1509,640],{},[488,1511,643],{},[495,1513,1514,1527,1540,1553,1566,1579,1592,1605,1618],{},[485,1515,1516,1524],{},[500,1517,1518],{},[460,1519,1521],{"href":1520},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002FShared\u002Froutes\u002Fapi.php",[518,1522,1523],{},"app\u002FHub\u002FShared\u002Froutes\u002Fapi.php",[500,1525,1526],{},"All Hub API route definitions",[485,1528,1529,1537],{},[500,1530,1531],{},[460,1532,1534],{"href":1533},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002FShared\u002FServices\u002FJWTService.php",[518,1535,1536],{},"app\u002FHub\u002FShared\u002FServices\u002FJWTService.php",[500,1538,1539],{},"Verifies and decodes Hub JWTs",[485,1541,1542,1550],{},[500,1543,1544],{},[460,1545,1547],{"href":1546},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002FShared\u002FHttp\u002FMiddleware\u002FTenantLookupMiddleware.php",[518,1548,1549],{},"app\u002FHub\u002FShared\u002FHttp\u002FMiddleware\u002FTenantLookupMiddleware.php",[500,1551,1552],{},"Resolves tenant list from the JWT caller identity",[485,1554,1555,1563],{},[500,1556,1557],{},[460,1558,1560],{"href":1559},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002FOrders\u002FHttp\u002FControllers\u002FOrderController.php",[518,1561,1562],{},"app\u002FHub\u002FOrders\u002FHttp\u002FControllers\u002FOrderController.php",[500,1564,1565],{},"List, view, and manage orders",[485,1567,1568,1576],{},[500,1569,1570],{},[460,1571,1573],{"href":1572},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002FOrders\u002FHttp\u002FControllers\u002FFulfillmentItemController.php",[518,1574,1575],{},"app\u002FHub\u002FOrders\u002FHttp\u002FControllers\u002FFulfillmentItemController.php",[500,1577,1578],{},"Refund \u002F cancel fulfilment items",[485,1580,1581,1589],{},[500,1582,1583],{},[460,1584,1586],{"href":1585},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002FOrders\u002FHttp\u002FControllers\u002FViewOrderAuditRecordsController.php",[518,1587,1588],{},"app\u002FHub\u002FOrders\u002FHttp\u002FControllers\u002FViewOrderAuditRecordsController.php",[500,1590,1591],{},"Return DynamoDB audit trail for an order",[485,1593,1594,1602],{},[500,1595,1596],{},[460,1597,1599],{"href":1598},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002FTenants\u002FHttp\u002FControllers\u002FTenantController.php",[518,1600,1601],{},"app\u002FHub\u002FTenants\u002FHttp\u002FControllers\u002FTenantController.php",[500,1603,1604],{},"Manage tenant configuration",[485,1606,1607,1615],{},[500,1608,1609],{},[460,1610,1612],{"href":1611},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002FTenants\u002FHttp\u002FControllers\u002FPolicyController.php",[518,1613,1614],{},"app\u002FHub\u002FTenants\u002FHttp\u002FControllers\u002FPolicyController.php",[500,1616,1617],{},"Manage tenant policy sections (terms\u002Fprivacy)",[485,1619,1620,1628],{},[500,1621,1622],{},[460,1623,1625],{"href":1624},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHub\u002Fconfig\u002Fhub.php",[518,1626,1627],{},"app\u002FHub\u002Fconfig\u002Fhub.php",[500,1629,1630],{},"Hub-specific configuration",[471,1632],{},[584,1634,1636],{"id":1635},"auth","Auth",[450,1638,1639,1641,1642,1645,1646,521,1649,1652,1653,1656,1657,1660,1661,1664,1665,1668],{},[457,1640,592],{}," Storefront authentication is ",[457,1643,1644],{},"pluggable per tenant",". After tenant resolution, the configured protocol (",[457,1647,1648],{},"JWT",[457,1650,1651],{},"OAuth",", or ",[457,1654,1655],{},"OIDC",") is loaded from encrypted credentials in the database. On success, StoreFront creates a ",[457,1658,1659],{},"session"," (and cookie) keyed by the IdP’s ",[518,1662,1663],{},"sub"," (or equivalent) as the external user ID. Sessions ",[457,1666,1667],{},"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.",[450,1670,1671],{},[457,1672,631],{},[479,1674,1675,1683],{},[482,1676,1677],{},[485,1678,1679,1681],{},[488,1680,640],{},[488,1682,643],{},[495,1684,1685,1702,1715,1728,1741,1754,1770,1783],{},[485,1686,1687,1695],{},[500,1688,1689],{},[460,1690,1692],{"href":1691},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuth\u002FAuthProviderFactory.php",[518,1693,1694],{},"app\u002FAuth\u002FAuthProviderFactory.php",[500,1696,1697,1698,1701],{},"Creates the correct ",[518,1699,1700],{},"AuthProvider"," implementation for the current tenant",[485,1703,1704,1712],{},[500,1705,1706],{},[460,1707,1709],{"href":1708},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuth\u002FAuthProviderInterface.php",[518,1710,1711],{},"app\u002FAuth\u002FAuthProviderInterface.php",[500,1713,1714],{},"Contract all storefront auth providers implement",[485,1716,1717,1725],{},[500,1718,1719],{},[460,1720,1722],{"href":1721},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuth\u002FJWTProvider.php",[518,1723,1724],{},"app\u002FAuth\u002FJWTProvider.php",[500,1726,1727],{},"JWT-based storefront auth (most tenants)",[485,1729,1730,1738],{},[500,1731,1732],{},[460,1733,1735],{"href":1734},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuth\u002FOIDCProvider.php",[518,1736,1737],{},"app\u002FAuth\u002FOIDCProvider.php",[500,1739,1740],{},"OIDC-based storefront auth",[485,1742,1743,1751],{},[500,1744,1745],{},[460,1746,1748],{"href":1747},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuth\u002FOAuthProvider.php",[518,1749,1750],{},"app\u002FAuth\u002FOAuthProvider.php",[500,1752,1753],{},"OAuth-based storefront auth",[485,1755,1756,1764],{},[500,1757,1758],{},[460,1759,1761],{"href":1760},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FHttp\u002FControllers\u002FAuthController.php",[518,1762,1763],{},"app\u002FHttp\u002FControllers\u002FAuthController.php",[500,1765,1766,1767],{},"Handles storefront login\u002Fcallback; upserts ",[518,1768,1769],{},"Customer",[485,1771,1772,1780],{},[500,1773,1774],{},[460,1775,1777],{"href":1776},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FTillo\u002FHttp\u002FControllers\u002FTenantLoginController.php",[518,1778,1779],{},"app\u002FTillo\u002FHttp\u002FControllers\u002FTenantLoginController.php",[500,1781,1782],{},"Tillo staff tenant-picker and signed cross-tenant login",[485,1784,1785,1791],{},[500,1786,1787],{},[460,1788,1789],{"href":1533},[518,1790,1536],{},[500,1792,1793],{},"Hub JWT verification (separate from storefront auth)",[471,1795],{},[584,1797,1799],{"id":1798},"auditing","Auditing",[450,1801,1802,1804,1805,1808],{},[457,1803,592],{}," Records an immutable, versioned audit trail for domain events. Audit records are stored in\n",[457,1806,1807],{},"AWS DynamoDB"," (a local DynamoDB container is available via Sail). PII in audit payloads is tokenised before\nstorage.",[450,1810,1811],{},[457,1812,631],{},[479,1814,1815,1823],{},[482,1816,1817],{},[485,1818,1819,1821],{},[488,1820,640],{},[488,1822,643],{},[495,1824,1825,1841,1857,1870,1883,1896],{},[485,1826,1827,1835],{},[500,1828,1829],{},[460,1830,1832],{"href":1831},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuditing\u002FListeners\u002FEventAuditingListener.php",[518,1833,1834],{},"app\u002FAuditing\u002FListeners\u002FEventAuditingListener.php",[500,1836,1837,1838,1840],{},"Queued listener (queue: ",[518,1839,1798],{},") that persists audit DTOs for each registered domain event",[485,1842,1843,1851],{},[500,1844,1845],{},[460,1846,1848],{"href":1847},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuditing\u002FServices\u002FAuditingService.php",[518,1849,1850],{},"app\u002FAuditing\u002FServices\u002FAuditingService.php",[500,1852,1853,1854],{},"Builds and stores audit records via ",[518,1855,1856],{},"DynamoDbService",[485,1858,1859,1867],{},[500,1860,1861],{},[460,1862,1864],{"href":1863},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuditing\u002FServices\u002FDynamoDbService.php",[518,1865,1866],{},"app\u002FAuditing\u002FServices\u002FDynamoDbService.php",[500,1868,1869],{},"DynamoDB read\u002Fwrite wrapper",[485,1871,1872,1880],{},[500,1873,1874],{},[460,1875,1877],{"href":1876},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuditing\u002FDtos\u002FV1\u002F",[518,1878,1879],{},"app\u002FAuditing\u002FDtos\u002FV1\u002F",[500,1881,1882],{},"Versioned audit DTO classes (one per audited event)",[485,1884,1885,1893],{},[500,1886,1887],{},[460,1888,1890],{"href":1889},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FEvents\u002FInterfaces\u002FSupportsAuditingInterface.php",[518,1891,1892],{},"app\u002FEvents\u002FInterfaces\u002FSupportsAuditingInterface.php",[500,1894,1895],{},"Marker interface; events that implement it will be audited",[485,1897,1898,1906],{},[500,1899,1900],{},[460,1901,1903],{"href":1902},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FAuditing\u002Fconfig\u002Fauditing.php",[518,1904,1905],{},"app\u002FAuditing\u002Fconfig\u002Fauditing.php",[500,1907,1908],{},"DynamoDB table names and other auditing configuration",[450,1910,1911,1912,469],{},"Events registered for auditing can be found in\n",[460,1913,1915],{"href":1914},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FProviders\u002FEventServiceProvider.php",[518,1916,1917],{},"app\u002FProviders\u002FEventServiceProvider.php",[471,1919],{},[584,1921,1923],{"id":1922},"outbound-webhooks","Outbound Webhooks",[450,1925,1926,1928,1929,521,1932,1935,1936,1939,1940,1943,1944,1947],{},[457,1927,592],{}," After key order events, the application sends signed webhook payloads to the tenant's configured\nendpoint (e.g. ",[518,1930,1931],{},"ItemFulfilled",[518,1933,1934],{},"FulfillmentItemRefunded","). This is optional per Buyer but critical for ",[457,1937,1938],{},"real-time cashback"," and loyalty integrations—typically ",[457,1941,1942],{},"one webhook per fulfilled gift card",", carrying order ID, item ID, brand, amount, user ID, and discount\u002Fcashback metadata. Payloads are versioned (V1 \u002F V2) and dispatched via a dedicated ",[518,1945,1946],{},"webhooks"," queue.",[450,1949,1950],{},[457,1951,631],{},[479,1953,1954,1962],{},[482,1955,1956],{},[485,1957,1958,1960],{},[488,1959,640],{},[488,1961,643],{},[495,1963,1964,1977,1990,2003,2016],{},[485,1965,1966,1974],{},[500,1967,1968],{},[460,1969,1971],{"href":1970},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FWebhook\u002FListener\u002FSendWebhookListener.php",[518,1972,1973],{},"app\u002FWebhook\u002FListener\u002FSendWebhookListener.php",[500,1975,1976],{},"Reacts to domain events and schedules a webhook dispatch",[485,1978,1979,1987],{},[500,1980,1981],{},[460,1982,1984],{"href":1983},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FWebhook\u002FWebhookJob.php",[518,1985,1986],{},"app\u002FWebhook\u002FWebhookJob.php",[500,1988,1989],{},"Queued job that performs the HTTP POST to the tenant endpoint",[485,1991,1992,2000],{},[500,1993,1994],{},[460,1995,1997],{"href":1996},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FWebhook\u002FWebhookSigner.php",[518,1998,1999],{},"app\u002FWebhook\u002FWebhookSigner.php",[500,2001,2002],{},"Signs payloads with the tenant's webhook secret",[485,2004,2005,2013],{},[500,2006,2007],{},[460,2008,2010],{"href":2009},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FWebhook\u002FDto\u002FWebhookHandlerFactory.php",[518,2011,2012],{},"app\u002FWebhook\u002FDto\u002FWebhookHandlerFactory.php",[500,2014,2015],{},"Resolves the correct versioned handler (V1\u002FV2) for an event type",[485,2017,2018,2026],{},[500,2019,2020],{},[460,2021,2023],{"href":2022},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fconfig\u002Fwebhook-server.php",[518,2024,2025],{},"config\u002Fwebhook-server.php",[500,2027,2028,2029,2031],{},"Default queue name (",[518,2030,1946],{},") and retry \u002F backoff configuration",[471,2033],{},[584,2035,2037],{"id":2036},"reporting","Reporting",[450,2039,2040,2042,2043,2046],{},[457,2041,592],{}," Generates and emails ",[457,2044,2045],{},"monthly transaction reports"," directly to Buyers (alongside separate monthly invoicing from Tillo finance). Also contains billing\u002Ffee scheme logic for calculating monthly revenue and dispute reporting.",[450,2048,2049],{},[457,2050,631],{},[479,2052,2053,2061],{},[482,2054,2055],{},[485,2056,2057,2059],{},[488,2058,640],{},[488,2060,643],{},[495,2062,2063,2076,2089,2102],{},[485,2064,2065,2073],{},[500,2066,2067],{},[460,2068,2070],{"href":2069},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FReporting\u002FService\u002FReportingService.php",[518,2071,2072],{},"app\u002FReporting\u002FService\u002FReportingService.php",[500,2074,2075],{},"Entry point for report generation",[485,2077,2078,2086],{},[500,2079,2080],{},[460,2081,2083],{"href":2082},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FReporting\u002FService\u002FReportBuilder\u002F",[518,2084,2085],{},"app\u002FReporting\u002FService\u002FReportBuilder\u002F",[500,2087,2088],{},"Concrete report builders per tenant\u002Ftype",[485,2090,2091,2099],{},[500,2092,2093],{},[460,2094,2096],{"href":2095},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FReporting\u002FBilling\u002FFees\u002FFeeService.php",[518,2097,2098],{},"app\u002FReporting\u002FBilling\u002FFees\u002FFeeService.php",[500,2100,2101],{},"Calculates fees owed based on a tenant's fee scheme",[485,2103,2104,2112],{},[500,2105,2106],{},[460,2107,2109],{"href":2108},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FReporting\u002FCashback\u002FCashbackHandler.php",[518,2110,2111],{},"app\u002FReporting\u002FCashback\u002FCashbackHandler.php",[500,2113,2114],{},"Records cashback transactions",[471,2116],{},[584,2118,2120],{"id":2119},"shared","Shared",[450,2122,2123,2125],{},[457,2124,592],{}," Cross-cutting utilities used by multiple domains.",[450,2127,2128],{},[457,2129,631],{},[479,2131,2132,2140],{},[482,2133,2134],{},[485,2135,2136,2138],{},[488,2137,640],{},[488,2139,643],{},[495,2141,2142,2155,2172],{},[485,2143,2144,2152],{},[500,2145,2146],{},[460,2147,2149],{"href":2148},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FShared\u002FDtos\u002FActorDto.php",[518,2150,2151],{},"app\u002FShared\u002FDtos\u002FActorDto.php",[500,2153,2154],{},"Identifies the actor (user or system job) responsible for a domain action",[485,2156,2157,2165],{},[500,2158,2159],{},[460,2160,2162],{"href":2161},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FShared\u002FProviders\u002FActorDtoProvider.php",[518,2163,2164],{},"app\u002FShared\u002FProviders\u002FActorDtoProvider.php",[500,2166,2167,2168,2171],{},"Resolves the ",[518,2169,2170],{},"ActorDto"," from the current request or job context",[485,2173,2174,2182],{},[500,2175,2176],{},[460,2177,2179],{"href":2178},"https:\u002F\u002Fgithub.com\u002FTilloTech\u002Fember\u002Fblob\u002Fdevelop\u002Fapp\u002FShared\u002FTraits\u002FHasOccurredAtData.php",[518,2180,2181],{},"app\u002FShared\u002FTraits\u002FHasOccurredAtData.php",[500,2183,2184],{},"Timestamp trait for events and DTOs",{"title":2186,"searchDepth":2187,"depth":2187,"links":2188},"",3,[2189,2191],{"id":476,"depth":2190,"text":477},2,{"id":581,"depth":2190,"text":582,"children":2192},[2193,2194,2195,2196,2197,2198,2199,2200,2201,2202,2203],{"id":586,"depth":2187,"text":587},{"id":757,"depth":2187,"text":758},{"id":884,"depth":2187,"text":885},{"id":1070,"depth":2187,"text":1071},{"id":1284,"depth":2187,"text":1285},{"id":1473,"depth":2187,"text":1474},{"id":1635,"depth":2187,"text":1636},{"id":1798,"depth":2187,"text":1799},{"id":1922,"depth":2187,"text":1923},{"id":2036,"depth":2187,"text":2037},{"id":2119,"depth":2187,"text":2120},{},"md",{},{"title":258,"description":452},"NlhTH1UW15qH4SPOoXa-JVco1G26aTwhMcGk-IzMpCc",[2210,2211],{"title":254,"path":255,"stem":256,"children":-1},{"title":262,"path":263,"stem":264,"children":-1},1789777548272]