[{"data":1,"prerenderedAt":1238},["ShallowReactive",2],{"navigation_docs":3,"-guide-going-further-stripe-setup":587,"-guide-going-further-stripe-setup-surround":1233},[4,50,155,441,504],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":49},"Get Started","i-lucide-rocket","\u002Fget-started","1.get-started",[10,15,20,25,30,35,40,45],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fget-started\u002Fintroduction","1.get-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fget-started\u002Finstallation","1.get-started\u002F2.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Project structure","\u002Fget-started\u002Fproject-structure","1.get-started\u002F3.project-structure","i-lucide-folder-tree",{"title":26,"path":27,"stem":28,"icon":29},"Configuration","\u002Fget-started\u002Fconfiguration","1.get-started\u002F4.configuration","i-lucide-settings",{"title":31,"path":32,"stem":33,"icon":34},"Authentication","\u002Fget-started\u002Fauthentication","1.get-started\u002F5.authentication","i-lucide-lock",{"title":36,"path":37,"stem":38,"icon":39},"Adding modules","\u002Fget-started\u002Fadding-modules","1.get-started\u002F6.adding-modules","i-lucide-puzzle",{"title":41,"path":42,"stem":43,"icon":44},"Database","\u002Fget-started\u002Fdatabase","1.get-started\u002F7.database","i-lucide-database",{"title":46,"path":47,"stem":48,"icon":6},"Deployment","\u002Fget-started\u002Fdeployment","1.get-started\u002F8.deployment",false,{"title":51,"icon":52,"path":53,"stem":54,"children":55,"page":49},"Guide","i-lucide-book-open","\u002Fguide","2.guide",[56,90,115,125],{"title":57,"icon":49,"path":58,"stem":59,"children":60,"page":49},"Concepts","\u002Fguide\u002Fconcepts","2.guide\u002F1.concepts",[61,66,71,76,80,85],{"title":62,"path":63,"stem":64,"icon":65},"Architecture","\u002Fguide\u002Fconcepts\u002Farchitecture","2.guide\u002F1.concepts\u002F1.architecture","i-lucide-blocks",{"title":67,"path":68,"stem":69,"icon":70},"Access control","\u002Fguide\u002Fconcepts\u002Fauth-and-scopes","2.guide\u002F1.concepts\u002F2.auth-and-scopes","i-lucide-shield-check",{"title":72,"path":73,"stem":74,"icon":75},"Extension points","\u002Fguide\u002Fconcepts\u002Fextension-points","2.guide\u002F1.concepts\u002F3.extension-points","i-lucide-plug",{"title":77,"path":78,"stem":79,"icon":44},"Database extensions","\u002Fguide\u002Fconcepts\u002Fdatabase-extensions","2.guide\u002F1.concepts\u002F4.database-extensions",{"title":81,"path":82,"stem":83,"icon":84},"Identity","\u002Fguide\u002Fconcepts\u002Fidentity","2.guide\u002F1.concepts\u002F5.identity","i-lucide-fingerprint",{"title":86,"path":87,"stem":88,"icon":89},"Glossary","\u002Fguide\u002Fconcepts\u002Fglossary","2.guide\u002F1.concepts\u002F6.glossary","i-lucide-book-a",{"title":91,"icon":49,"path":92,"stem":93,"children":94,"page":49},"UX patterns","\u002Fguide\u002Fux-patterns","2.guide\u002F2.ux-patterns",[95,100,105,110],{"title":96,"path":97,"stem":98,"icon":99},"Layouts and navigation","\u002Fguide\u002Fux-patterns\u002Flayouts-and-navigation","2.guide\u002F2.ux-patterns\u002F1.layouts-and-navigation","i-lucide-layout-template",{"title":101,"path":102,"stem":103,"icon":104},"Pages","\u002Fguide\u002Fux-patterns\u002Fpages","2.guide\u002F2.ux-patterns\u002F2.pages","i-lucide-table",{"title":106,"path":107,"stem":108,"icon":109},"Forms and actions","\u002Fguide\u002Fux-patterns\u002Fforms-and-actions","2.guide\u002F2.ux-patterns\u002F3.forms-and-actions","i-lucide-square-pen",{"title":111,"path":112,"stem":113,"icon":114},"Text and formatting","\u002Fguide\u002Fux-patterns\u002Ftext-and-formatting","2.guide\u002F2.ux-patterns\u002F4.text-and-formatting","i-lucide-type",{"title":116,"icon":49,"path":117,"stem":118,"children":119,"page":49},"Best practices","\u002Fguide\u002Fbest-practices","2.guide\u002F3.best-practices",[120],{"title":121,"path":122,"stem":123,"icon":124},"HTTP routes","\u002Fguide\u002Fbest-practices\u002Fhttp-routes","2.guide\u002F3.best-practices\u002F1.http-routes","i-lucide-route",{"title":126,"icon":49,"path":127,"stem":128,"children":129,"page":49},"Going further","\u002Fguide\u002Fgoing-further","2.guide\u002F4.going-further",[130,135,140,145,150],{"title":131,"path":132,"stem":133,"icon":134},"Use your own OAuth server","\u002Fguide\u002Fgoing-further\u002Fown-oauth-server","2.guide\u002F4.going-further\u002F1.own-oauth-server","i-lucide-key-round",{"title":136,"path":137,"stem":138,"icon":139},"Create an OAuth application","\u002Fguide\u002Fgoing-further\u002Foauth-application","2.guide\u002F4.going-further\u002F2.oauth-application","i-lucide-app-window",{"title":141,"path":142,"stem":143,"icon":144},"Signing keys","\u002Fguide\u002Fgoing-further\u002Fsigning-keys","2.guide\u002F4.going-further\u002F3.signing-keys","i-lucide-key-square",{"title":146,"path":147,"stem":148,"icon":149},"Stripe setup","\u002Fguide\u002Fgoing-further\u002Fstripe-setup","2.guide\u002F4.going-further\u002F4.stripe-setup","i-lucide-credit-card",{"title":151,"path":152,"stem":153,"icon":154},"Migrate to the permission model","\u002Fguide\u002Fgoing-further\u002Fmigrate-access-control","2.guide\u002F4.going-further\u002F5.migrate-access-control","i-lucide-arrow-right-left",{"title":156,"icon":157,"path":158,"stem":159,"children":160},"Modules","i-lucide-boxes","\u002Fmodules","3.modules",[161,163,244,273,320,375],{"title":156,"path":158,"stem":162},"3.modules\u002Findex",{"title":81,"icon":49,"path":164,"stem":165,"children":166,"page":49},"\u002Fmodules\u002Fidentity","3.modules\u002F1.identity",[167,190,194,198,220,224,240],{"title":168,"path":169,"stem":170,"children":171},"Auth","\u002Fmodules\u002Fidentity\u002Fauth","3.modules\u002F1.identity\u002F1.auth\u002Findex",[172,174,178,182,186],{"title":173,"path":169,"stem":170},"Overview",{"title":175,"path":176,"stem":177},"Server API","\u002Fmodules\u002Fidentity\u002Fauth\u002Fserver","3.modules\u002F1.identity\u002F1.auth\u002F2.server",{"title":179,"path":180,"stem":181},"Reference","\u002Fmodules\u002Fidentity\u002Fauth\u002Freference","3.modules\u002F1.identity\u002F1.auth\u002F3.reference",{"title":183,"path":184,"stem":185},"Client mode","\u002Fmodules\u002Fidentity\u002Fauth\u002Fclient-mode","3.modules\u002F1.identity\u002F1.auth\u002F4.client-mode",{"title":187,"path":188,"stem":189},"OAuth providers","\u002Fmodules\u002Fidentity\u002Fauth\u002Foauth-providers","3.modules\u002F1.identity\u002F1.auth\u002F5.oauth-providers",{"title":191,"path":192,"stem":193},"Users","\u002Fmodules\u002Fidentity\u002Fusers","3.modules\u002F1.identity\u002F2.users",{"title":195,"path":196,"stem":197},"Account","\u002Fmodules\u002Fidentity\u002Faccount","3.modules\u002F1.identity\u002F3.account",{"title":199,"path":200,"stem":201,"children":202},"Authorization","\u002Fmodules\u002Fidentity\u002Fauthorization","3.modules\u002F1.identity\u002F4.authorization\u002Findex",[203,204,208,212,216],{"title":173,"path":200,"stem":201},{"title":205,"path":206,"stem":207},"Roles and requirements","\u002Fmodules\u002Fidentity\u002Fauthorization\u002Froles-and-requirements","3.modules\u002F1.identity\u002F4.authorization\u002F2.roles-and-requirements",{"title":209,"path":210,"stem":211},"Policies","\u002Fmodules\u002Fidentity\u002Fauthorization\u002Fpolicies","3.modules\u002F1.identity\u002F4.authorization\u002F3.policies",{"title":213,"path":214,"stem":215},"Client","\u002Fmodules\u002Fidentity\u002Fauthorization\u002Fclient","3.modules\u002F1.identity\u002F4.authorization\u002F4.client",{"title":217,"path":218,"stem":219},"Admin","\u002Fmodules\u002Fidentity\u002Fauthorization\u002Fadmin","3.modules\u002F1.identity\u002F4.authorization\u002F5.admin",{"title":221,"path":222,"stem":223},"Preferences","\u002Fmodules\u002Fidentity\u002Fpreferences","3.modules\u002F1.identity\u002F5.preferences",{"title":225,"path":226,"stem":227,"children":228},"OAuth Server","\u002Fmodules\u002Fidentity\u002Foauth-server","3.modules\u002F1.identity\u002F6.oauth-server\u002Findex",[229,230,233,237],{"title":173,"path":226,"stem":227},{"title":175,"path":231,"stem":232},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Fserver","3.modules\u002F1.identity\u002F6.oauth-server\u002F2.server",{"title":234,"path":235,"stem":236},"Hooks","\u002Fmodules\u002Fidentity\u002Foauth-server\u002Fhooks","3.modules\u002F1.identity\u002F6.oauth-server\u002F3.hooks",{"title":179,"path":238,"stem":239},"\u002Fmodules\u002Fidentity\u002Foauth-server\u002Froutes-and-clients","3.modules\u002F1.identity\u002F6.oauth-server\u002F4.routes-and-clients",{"title":241,"path":242,"stem":243},"OAuth Resource","\u002Fmodules\u002Fidentity\u002Foauth-resource","3.modules\u002F1.identity\u002F8.oauth-resource",{"title":245,"icon":49,"path":246,"stem":247,"children":248,"page":49},"Commerce","\u002Fmodules\u002Fcommerce","3.modules\u002F2.commerce",[249,253,257],{"title":250,"path":251,"stem":252},"Stripe","\u002Fmodules\u002Fcommerce\u002Fstripe","3.modules\u002F2.commerce\u002F1.stripe",{"title":254,"path":255,"stem":256},"Payments","\u002Fmodules\u002Fcommerce\u002Fpayments","3.modules\u002F2.commerce\u002F2.payments",{"title":258,"path":259,"stem":260,"children":261},"Subscriptions","\u002Fmodules\u002Fcommerce\u002Fsubscriptions","3.modules\u002F2.commerce\u002F3.subscriptions\u002Findex",[262,263,266,270],{"title":173,"path":259,"stem":260},{"title":175,"path":264,"stem":265},"\u002Fmodules\u002Fcommerce\u002Fsubscriptions\u002Fserver","3.modules\u002F2.commerce\u002F3.subscriptions\u002F2.server",{"title":267,"path":268,"stem":269},"App","\u002Fmodules\u002Fcommerce\u002Fsubscriptions\u002Fapp","3.modules\u002F2.commerce\u002F3.subscriptions\u002F3.app",{"title":179,"path":271,"stem":272},"\u002Fmodules\u002Fcommerce\u002Fsubscriptions\u002Freference","3.modules\u002F2.commerce\u002F3.subscriptions\u002F4.reference",{"title":274,"icon":49,"path":275,"stem":276,"children":277,"page":49},"Features","\u002Fmodules\u002Ffeatures","3.modules\u002F3.features",[278,293,312,316],{"title":279,"path":280,"stem":281,"children":282},"Events","\u002Fmodules\u002Ffeatures\u002Fevents","3.modules\u002F3.features\u002F1.events\u002Findex",[283,284,287,290],{"title":173,"path":280,"stem":281},{"title":175,"path":285,"stem":286},"\u002Fmodules\u002Ffeatures\u002Fevents\u002Fserver","3.modules\u002F3.features\u002F1.events\u002F2.server",{"title":267,"path":288,"stem":289},"\u002Fmodules\u002Ffeatures\u002Fevents\u002Fapp","3.modules\u002F3.features\u002F1.events\u002F3.app",{"title":179,"path":291,"stem":292},"\u002Fmodules\u002Ffeatures\u002Fevents\u002Freference","3.modules\u002F3.features\u002F1.events\u002F4.reference",{"title":294,"path":295,"stem":296,"children":297},"Forms","\u002Fmodules\u002Ffeatures\u002Fforms","3.modules\u002F3.features\u002F2.forms\u002Findex",[298,299,303,306,309],{"title":173,"path":295,"stem":296},{"title":300,"path":301,"stem":302},"Definition","\u002Fmodules\u002Ffeatures\u002Fforms\u002Fdefinition","3.modules\u002F3.features\u002F2.forms\u002F2.definition",{"title":175,"path":304,"stem":305},"\u002Fmodules\u002Ffeatures\u002Fforms\u002Fserver","3.modules\u002F3.features\u002F2.forms\u002F3.server",{"title":267,"path":307,"stem":308},"\u002Fmodules\u002Ffeatures\u002Fforms\u002Fapp","3.modules\u002F3.features\u002F2.forms\u002F4.app",{"title":179,"path":310,"stem":311},"\u002Fmodules\u002Ffeatures\u002Fforms\u002Freference","3.modules\u002F3.features\u002F2.forms\u002F5.reference",{"title":313,"path":314,"stem":315},"Cookie Consent","\u002Fmodules\u002Ffeatures\u002Fcookie-consent","3.modules\u002F3.features\u002F3.cookie-consent",{"title":317,"path":318,"stem":319},"MCP","\u002Fmodules\u002Ffeatures\u002Fmcp","3.modules\u002F3.features\u002F4.mcp",{"title":321,"icon":49,"path":322,"stem":323,"children":324,"page":49},"Interface","\u002Fmodules\u002Finterface","3.modules\u002F4.interface",[325,350,353,357,361],{"title":326,"path":327,"stem":328,"children":329},"UI","\u002Fmodules\u002Finterface\u002Fui","3.modules\u002F4.interface\u002F1.ui\u002Findex",[330,331,335,339,343,347],{"title":173,"path":327,"stem":328},{"title":332,"path":333,"stem":334},"Page components","\u002Fmodules\u002Finterface\u002Fui\u002Fpage-components","3.modules\u002F4.interface\u002F1.ui\u002F2.page-components",{"title":336,"path":337,"stem":338},"App shell","\u002Fmodules\u002Finterface\u002Fui\u002Fapp-shell","3.modules\u002F4.interface\u002F1.ui\u002F3.app-shell",{"title":340,"path":341,"stem":342},"Form inputs","\u002Fmodules\u002Finterface\u002Fui\u002Fform-inputs","3.modules\u002F4.interface\u002F1.ui\u002F4.form-inputs",{"title":344,"path":345,"stem":346},"Composables and helpers","\u002Fmodules\u002Finterface\u002Fui\u002Fcomposables","3.modules\u002F4.interface\u002F1.ui\u002F5.composables",{"title":179,"path":348,"stem":349},"\u002Fmodules\u002Finterface\u002Fui\u002Freference","3.modules\u002F4.interface\u002F1.ui\u002F6.reference",{"title":217,"path":351,"stem":352},"\u002Fmodules\u002Finterface\u002Fadmin","3.modules\u002F4.interface\u002F2.admin",{"title":354,"path":355,"stem":356},"Menu","\u002Fmodules\u002Finterface\u002Fmenu","3.modules\u002F4.interface\u002F3.menu",{"title":358,"path":359,"stem":360},"Widget","\u002Fmodules\u002Finterface\u002Fwidget","3.modules\u002F4.interface\u002F4.widget",{"title":362,"path":363,"stem":364,"children":365},"Mobile","\u002Fmodules\u002Finterface\u002Fmobile","3.modules\u002F4.interface\u002F5.mobile\u002Findex",[366,367,371],{"title":173,"path":363,"stem":364},{"title":368,"path":369,"stem":370},"CLI and devices","\u002Fmodules\u002Finterface\u002Fmobile\u002Fcli-and-devices","3.modules\u002F4.interface\u002F5.mobile\u002F2.cli-and-devices",{"title":372,"path":373,"stem":374},"Splash screen","\u002Fmodules\u002Finterface\u002Fmobile\u002Fsplash-screen","3.modules\u002F4.interface\u002F5.mobile\u002F3.splash-screen",{"title":376,"icon":49,"path":377,"stem":378,"children":379,"page":49},"Platform","\u002Fmodules\u002Fplatform","3.modules\u002F5.platform",[380,402,406,423],{"title":381,"path":382,"stem":383,"children":384},"Module Kit","\u002Fmodules\u002Fplatform\u002Fmodule-kit","3.modules\u002F5.platform\u002F1.module-kit\u002Findex",[385,386,390,394,398],{"title":173,"path":382,"stem":383},{"title":387,"path":388,"stem":389},"Server toolkit","\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fserver-toolkit","3.modules\u002F5.platform\u002F1.module-kit\u002F2.server-toolkit",{"title":391,"path":392,"stem":393},"Registries and menus","\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fregistries-and-menus","3.modules\u002F5.platform\u002F1.module-kit\u002F3.registries-and-menus",{"title":395,"path":396,"stem":397},"Translations","\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fi18n","3.modules\u002F5.platform\u002F1.module-kit\u002F4.i18n",{"title":399,"path":400,"stem":401},"Database helpers","\u002Fmodules\u002Fplatform\u002Fmodule-kit\u002Fdatabase","3.modules\u002F5.platform\u002F1.module-kit\u002F5.database",{"title":403,"path":404,"stem":405},"Notify","\u002Fmodules\u002Fplatform\u002Fnotify","3.modules\u002F5.platform\u002F2.notify",{"title":407,"path":408,"stem":409,"children":410},"Email Templates","\u002Fmodules\u002Fplatform\u002Femail-templates","3.modules\u002F5.platform\u002F3.email-templates\u002Findex",[411,412,416,419],{"title":173,"path":408,"stem":409},{"title":413,"path":414,"stem":415},"Email components","\u002Fmodules\u002Fplatform\u002Femail-templates\u002Fcomponents","3.modules\u002F5.platform\u002F3.email-templates\u002F2.components",{"title":175,"path":417,"stem":418},"\u002Fmodules\u002Fplatform\u002Femail-templates\u002Fserver","3.modules\u002F5.platform\u002F3.email-templates\u002F3.server",{"title":420,"path":421,"stem":422},"Module emails","\u002Fmodules\u002Fplatform\u002Femail-templates\u002Fmodule-emails","3.modules\u002F5.platform\u002F3.email-templates\u002F4.module-emails",{"title":424,"path":425,"stem":426,"children":427},"Base Layer","\u002Fmodules\u002Fplatform\u002Fbase-layer","3.modules\u002F5.platform\u002F4.base-layer\u002Findex",[428,429,433,437],{"title":173,"path":425,"stem":426},{"title":430,"path":431,"stem":432},"Content collections","\u002Fmodules\u002Fplatform\u002Fbase-layer\u002Fcontent-collections","3.modules\u002F5.platform\u002F4.base-layer\u002F2.content-collections",{"title":434,"path":435,"stem":436},"Layouts and routing","\u002Fmodules\u002Fplatform\u002Fbase-layer\u002Flayouts-and-routing","3.modules\u002F5.platform\u002F4.base-layer\u002F3.layouts-and-routing",{"title":438,"path":439,"stem":440},"Composables and utils","\u002Fmodules\u002Fplatform\u002Fbase-layer\u002Fcomposables-and-utils","3.modules\u002F5.platform\u002F4.base-layer\u002F4.composables-and-utils",{"title":442,"icon":443,"path":444,"stem":445,"children":446,"page":49},"Examples","i-lucide-code-xml","\u002Fexamples","4.examples",[447,466,476],{"title":448,"icon":49,"path":449,"stem":450,"children":451,"page":49},"Starters","\u002Fexamples\u002Fstarters","4.examples\u002F1.starters",[452,456,461],{"title":453,"path":454,"stem":455,"icon":6},"Workspace starter","\u002Fexamples\u002Fstarters\u002Fworkspace","4.examples\u002F1.starters\u002F1.workspace",{"title":457,"path":458,"stem":459,"icon":460},"Mobile starter","\u002Fexamples\u002Fstarters\u002Fmobile","4.examples\u002F1.starters\u002F2.mobile","i-lucide-smartphone",{"title":462,"path":463,"stem":464,"icon":465},"Standalone mobile starter","\u002Fexamples\u002Fstarters\u002Fmobile-standalone","4.examples\u002F1.starters\u002F3.mobile-standalone","i-lucide-tablet-smartphone",{"title":467,"icon":49,"path":468,"stem":469,"children":470,"page":49},"Apps","\u002Fexamples\u002Fapps","4.examples\u002F2.apps",[471],{"title":472,"path":473,"stem":474,"icon":475},"Frigear website app","\u002Fexamples\u002Fapps\u002Ffrigear-nu-website","4.examples\u002F2.apps\u002F1.frigear-nu-website","i-lucide-layout-dashboard",{"title":477,"icon":49,"path":478,"stem":479,"children":480,"page":49},"Tasks","\u002Fexamples\u002Ftasks","4.examples\u002F3.tasks",[481,485,489,494,499],{"title":482,"path":483,"stem":484,"icon":70},"Protect a route","\u002Fexamples\u002Ftasks\u002Fprotect-a-route","4.examples\u002F3.tasks\u002F1.protect-a-route",{"title":486,"path":487,"stem":488,"icon":475},"Add an admin page","\u002Fexamples\u002Ftasks\u002Fadd-an-admin-page","4.examples\u002F3.tasks\u002F2.add-an-admin-page",{"title":490,"path":491,"stem":492,"icon":493},"Sell event tickets","\u002Fexamples\u002Ftasks\u002Fsell-event-tickets","4.examples\u002F3.tasks\u002F3.sell-event-tickets","i-lucide-ticket",{"title":495,"path":496,"stem":497,"icon":498},"Build a form","\u002Fexamples\u002Ftasks\u002Fbuild-a-form","4.examples\u002F3.tasks\u002F4.build-a-form","i-lucide-clipboard-list",{"title":500,"path":501,"stem":502,"icon":503},"Send an email","\u002Fexamples\u002Ftasks\u002Fsend-an-email","4.examples\u002F3.tasks\u002F5.send-an-email","i-lucide-mail",{"title":505,"icon":506,"path":507,"stem":508,"children":509,"page":49},"Contribute","i-lucide-git-pull-request","\u002Fcontribute","5.contribute",[510,513,518,544,549,552,556,571],{"title":173,"path":511,"stem":512,"icon":506},"\u002Fcontribute\u002Foverview","5.contribute\u002F1.overview",{"title":514,"path":515,"stem":516,"icon":517},"Local development","\u002Fcontribute\u002Flocal-development","5.contribute\u002F2.local-development","i-lucide-terminal",{"title":519,"icon":49,"path":520,"stem":521,"children":522,"page":49},"Writing a module","\u002Fcontribute\u002Fwriting-a-module","5.contribute\u002F3.writing-a-module",[523,527,532,536,541],{"title":524,"path":525,"stem":526,"icon":39},"Scaffold a module","\u002Fcontribute\u002Fwriting-a-module\u002Fscaffold","5.contribute\u002F3.writing-a-module\u002F1.scaffold",{"title":528,"path":529,"stem":530,"icon":531},"Setup checklist","\u002Fcontribute\u002Fwriting-a-module\u002Fsetup-checklist","5.contribute\u002F3.writing-a-module\u002F2.setup-checklist","i-lucide-list-checks",{"title":533,"path":534,"stem":535,"icon":44},"Database tables","\u002Fcontribute\u002Fwriting-a-module\u002Fdatabase-tables","5.contribute\u002F3.writing-a-module\u002F3.database-tables",{"title":537,"path":538,"stem":539,"icon":540},"Testing","\u002Fcontribute\u002Fwriting-a-module\u002Ftesting","5.contribute\u002F3.writing-a-module\u002F4.testing","i-lucide-flask-conical",{"title":67,"path":542,"stem":543,"icon":70},"\u002Fcontribute\u002Fwriting-a-module\u002Faccess-control","5.contribute\u002F3.writing-a-module\u002F5.access-control",{"title":545,"path":546,"stem":547,"icon":548},"Publishing","\u002Fcontribute\u002Fpublishing","5.contribute\u002F4.publishing","i-lucide-package-check",{"title":448,"path":550,"stem":551,"icon":6},"\u002Fcontribute\u002Fstarters","5.contribute\u002F5.starters",{"title":553,"path":554,"stem":555,"icon":52},"Documentation","\u002Fcontribute\u002Fdocumentation","5.contribute\u002F6.documentation",{"title":557,"icon":49,"path":558,"stem":559,"children":560,"page":49},"Tooling","\u002Fcontribute\u002Ftooling","5.contribute\u002F7.tooling",[561,566],{"title":562,"path":563,"stem":564,"icon":565},"CLI","\u002Fcontribute\u002Ftooling\u002Fcli","5.contribute\u002F7.tooling\u002F1.cli","i-lucide-square-terminal",{"title":567,"path":568,"stem":569,"icon":570},"ESLint config","\u002Fcontribute\u002Ftooling\u002Feslint-config","5.contribute\u002F7.tooling\u002F2.eslint-config","i-lucide-spell-check",{"title":572,"icon":49,"path":573,"stem":574,"children":575},"Decisions","\u002Fcontribute\u002Fdecisions","5.contribute\u002F8.decisions\u002Findex",[576,578,583],{"title":572,"path":573,"stem":574,"icon":577},"i-lucide-scale",{"title":579,"path":580,"stem":581,"icon":582},"Session lifetimes per OAuth client","\u002Fcontribute\u002Fdecisions\u002Foauth-token-lifetimes","5.contribute\u002F8.decisions\u002F1.oauth-token-lifetimes","i-lucide-timer",{"title":584,"path":585,"stem":586,"icon":70},"One permission model","\u002Fcontribute\u002Fdecisions\u002Fpermission-model","5.contribute\u002F8.decisions\u002F2.permission-model",{"id":588,"title":146,"body":589,"description":1226,"extension":1227,"links":1228,"meta":1229,"navigation":1230,"path":147,"seo":1231,"stem":148,"__hash__":1232},"docs\u002F2.guide\u002F4.going-further\u002F4.stripe-setup.md",{"type":590,"value":591,"toc":1216},"minimark",[592,596,615,621,626,660,852,867,871,1146,1149,1154,1176,1195,1199,1212],[593,594,595],"p",{},"Payments and Subscriptions learn that money moved only when Stripe tells them, through a webhook. Until that endpoint,\nits events and the keys are set up, a paid checkout changes nothing in your app. This page sets up the Stripe side\nonce, so every commerce module works, in production and in local development.",[597,598,600,604,605,608,609,611,612,614],"callout",{"icon":599},"i-lucide-users",[601,602,603],"strong",{},"For:"," app developers who install ",[606,607,254],"a",{"href":255},",\n",[606,610,258],{"href":259}," or ",[606,613,279],{"href":280},".",[593,616,617,618,620],{},"The ",[606,619,250],{"href":251}," module owns one verified webhook endpoint. Payments and Subscriptions register\nlisteners on it for the events they need. Stripe stays the source of truth for catalog data. The modules store no\nproducts or prices, only the Stripe customer id of each user.",[622,623,625],"h2",{"id":624},"before-you-start","Before you start",[627,628,629,645,654],"ul",{},[630,631,632,635,636,640,641,644],"li",{},[606,633,634],{"href":87},"NuxtHub"," (",[637,638,639],"code",{},"@nuxthub\u002Fcore",") with key-value storage (KV) enabled: ",[637,642,643],{},"hub.kv",", which is set by\ndefault. No extra table is needed.",[630,646,647,650,651,614],{},[637,648,649],{},"unstorage"," resolvable by the ",[606,652,653],{"href":87},"host app",[630,655,656,657,659],{},"The modules installed and configured, as in the quick setup of ",[606,658,250],{"href":251},":",[661,662,668],"pre",{"className":663,"code":664,"filename":665,"language":666,"meta":667,"style":667},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export default defineNuxtConfig({\n  modules: ['@nuxthub\u002Fcore', '@frigear-nu\u002Fusers', '@frigear-nu\u002Fstripe'],\n  hub: { db: { dialect: 'sqlite', driver: 'libsql' }, kv: true },\n  stripe: {\n    settings: { webhookPath: '\u002Fapi\u002Fstripe\u002Fwebhook' }, \u002F\u002F default\n  },\n})\n","nuxt.config.ts","ts","",[637,669,670,694,739,798,809,837,843],{"__ignoreMap":667},[671,672,675,679,682,686,690],"span",{"class":673,"line":674},"line",1,[671,676,678],{"class":677},"s7zQu","export",[671,680,681],{"class":677}," default",[671,683,685],{"class":684},"s2Zo4"," defineNuxtConfig",[671,687,689],{"class":688},"sTEyZ","(",[671,691,693],{"class":692},"sMK4o","{\n",[671,695,697,701,703,706,709,712,714,717,720,723,725,727,729,732,734,737],{"class":673,"line":696},2,[671,698,700],{"class":699},"swJcz","  modules",[671,702,659],{"class":692},[671,704,705],{"class":688}," [",[671,707,708],{"class":692},"'",[671,710,639],{"class":711},"sfazB",[671,713,708],{"class":692},[671,715,716],{"class":692},",",[671,718,719],{"class":692}," '",[671,721,722],{"class":711},"@frigear-nu\u002Fusers",[671,724,708],{"class":692},[671,726,716],{"class":692},[671,728,719],{"class":692},[671,730,731],{"class":711},"@frigear-nu\u002Fstripe",[671,733,708],{"class":692},[671,735,736],{"class":688},"]",[671,738,608],{"class":692},[671,740,742,745,747,750,753,755,757,760,762,764,767,769,771,774,776,778,781,783,786,789,791,795],{"class":673,"line":741},3,[671,743,744],{"class":699},"  hub",[671,746,659],{"class":692},[671,748,749],{"class":692}," {",[671,751,752],{"class":699}," db",[671,754,659],{"class":692},[671,756,749],{"class":692},[671,758,759],{"class":699}," dialect",[671,761,659],{"class":692},[671,763,719],{"class":692},[671,765,766],{"class":711},"sqlite",[671,768,708],{"class":692},[671,770,716],{"class":692},[671,772,773],{"class":699}," driver",[671,775,659],{"class":692},[671,777,719],{"class":692},[671,779,780],{"class":711},"libsql",[671,782,708],{"class":692},[671,784,785],{"class":692}," },",[671,787,788],{"class":699}," kv",[671,790,659],{"class":692},[671,792,794],{"class":793},"sfNiH"," true",[671,796,797],{"class":692}," },\n",[671,799,801,804,806],{"class":673,"line":800},4,[671,802,803],{"class":699},"  stripe",[671,805,659],{"class":692},[671,807,808],{"class":692}," {\n",[671,810,812,815,817,819,822,824,826,829,831,833],{"class":673,"line":811},5,[671,813,814],{"class":699},"    settings",[671,816,659],{"class":692},[671,818,749],{"class":692},[671,820,821],{"class":699}," webhookPath",[671,823,659],{"class":692},[671,825,719],{"class":692},[671,827,828],{"class":711},"\u002Fapi\u002Fstripe\u002Fwebhook",[671,830,708],{"class":692},[671,832,785],{"class":692},[671,834,836],{"class":835},"sHwdD"," \u002F\u002F default\n",[671,838,840],{"class":673,"line":839},6,[671,841,842],{"class":692},"  },\n",[671,844,846,849],{"class":673,"line":845},7,[671,847,848],{"class":692},"}",[671,850,851],{"class":688},")\n",[853,854,855,856,862,863,866],"note",{},"Webhook idempotency stores processed event ids in ",[606,857,861],{"href":858,"rel":859},"https:\u002F\u002Fhub.nuxt.com\u002Fdocs\u002Fkv",[860],"nofollow","NuxtHub KV",", under\n",[637,864,865],{},"stripe:event:\u003Cevent id>",". That is why KV is required.",[622,868,870],{"id":869},"set-up-stripe","Set up Stripe",[872,873,874,879,886,914,1001,1004,1008,1014,1102,1111,1115,1124],"steps",{},[875,876,878],"h3",{"id":877},"add-the-webhook-endpoint","Add the webhook endpoint",[880,881,882,883,614],"warning",{},"Put no auth or CSRF middleware in front of the webhook route. Stripe's requests must reach it untouched: the route\nreads the raw body and verifies the signature itself, with ",[637,884,885],{},"constructEventAsync",[887,888,889,895,905,908],"ol",{},[630,890,891,892,614],{},"In the Stripe dashboard, open ",[601,893,894],{},"Developers > Webhooks",[630,896,897,898,901,902,614],{},"Add an endpoint that points at ",[637,899,900],{},"https:\u002F\u002F\u003Cyour-host>\u002Fapi\u002Fstripe\u002Fwebhook",", or at your ",[637,903,904],{},"settings.webhookPath",[630,906,907],{},"Select the events your installed modules need, from the table below.",[630,909,910,911,614],{},"Copy the signing secret into ",[637,912,913],{},"NUXT_STRIPE_WEBHOOK_SECRET",[915,916,917,929],"table",{},[918,919,920],"thead",{},[921,922,923,927],"tr",{},[924,925,926],"th",{},"Module",[924,928,279],{},[930,931,932,957,981],"tbody",{},[921,933,934,939],{},[935,936,937],"td",{},[606,938,254],{"href":255},[935,940,941,944,945,944,948,944,951,944,954],{},[637,942,943],{},"checkout.session.completed",", ",[637,946,947],{},"checkout.session.async_payment_succeeded",[637,949,950],{},"checkout.session.async_payment_failed",[637,952,953],{},"checkout.session.expired",[637,955,956],{},"charge.refunded",[921,958,959,963],{},[935,960,961],{},[606,962,258],{"href":259},[935,964,965,944,968,944,971,944,974,977,978],{},[637,966,967],{},"customer.subscription.created",[637,969,970],{},".updated",[637,972,973],{},".deleted",[637,975,976],{},".paused"," and ",[637,979,980],{},".resumed",[921,982,983,986],{},[935,984,985],{},"Subscriptions with Payments installed",[935,987,988,944,991,944,994,977,997,1000],{},[637,989,990],{},"invoice.paid",[637,992,993],{},"invoice.payment_failed",[637,995,996],{},"invoice.voided",[637,998,999],{},"invoice.marked_uncollectible",", for subscription invoices",[593,1002,1003],{},"Events sells tickets through Payments, so it needs the Payments events. Without Payments, no invoice event is handled.",[875,1005,1007],{"id":1006},"set-the-keys","Set the keys",[593,1009,1010,1011,614],{},"Read them through runtime config, never ",[637,1012,1013],{},"process.env",[915,1015,1016,1029],{},[918,1017,1018],{},[921,1019,1020,1023,1026],{},[924,1021,1022],{},"Variable",[924,1024,1025],{},"Runtime config",[924,1027,1028],{},"Purpose",[930,1030,1031,1050,1068,1083],{},[921,1032,1033,1038,1043],{},[935,1034,1035],{},[637,1036,1037],{},"NUXT_STRIPE_SECRET_KEY",[935,1039,1040],{},[637,1041,1042],{},"stripe.secretKey",[935,1044,1045,1046,1049],{},"The server-side secret API key (",[637,1047,1048],{},"sk_...",", or a restricted key).",[921,1051,1052,1056,1061],{},[935,1053,1054],{},[637,1055,913],{},[935,1057,1058],{},[637,1059,1060],{},"stripe.webhookSecret",[935,1062,1063,1064,1067],{},"The signing secret of the webhook endpoint (",[637,1065,1066],{},"whsec_...",").",[921,1069,1070,1075,1080],{},[935,1071,1072],{},[637,1073,1074],{},"NUXT_STRIPE_API_VERSION",[935,1076,1077],{},[637,1078,1079],{},"stripe.apiVersion",[935,1081,1082],{},"Optional. Overrides the pinned API version.",[921,1084,1085,1090,1095],{},[935,1086,1087],{},[637,1088,1089],{},"NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY",[935,1091,1092],{},[637,1093,1094],{},"public.stripe.publishableKey",[935,1096,1097,1098,1101],{},"The publishable key (",[637,1099,1100],{},"pk_...","), exposed to the client.",[593,1103,1104,1105,1108,1109,614],{},"Without the secret key, the Stripe client throws ",[637,1106,1107],{},"501 not_configured",". Without the webhook secret, the endpoint answers\n",[637,1110,1107],{},[875,1112,1114],{"id":1113},"prepare-products-and-billing","Prepare products and billing",[593,1116,1117,1118,1120,1121,614],{},"This step applies only to ",[606,1119,258],{"href":259},". One-time payments need no products or\nprices: their checkout sessions use inline ",[637,1122,1123],{},"price_data",[627,1125,1126,1132],{},[630,1127,1128,1131],{},[601,1129,1130],{},"Products and prices."," Create recurring prices. Use lookup keys to keep config the same across environments,\nbecause test and live mode have different price ids. Stripe needs no metadata or copy.",[630,1133,1134,1137,1138,1141,1142,1145],{},[601,1135,1136],{},"Billing portal."," In ",[601,1139,1140],{},"Settings > Billing > Customer portal",", enable cancellation and payment method updates.\nEnable subscription updates only with ",[637,1143,1144],{},"allowChangeInPortal: true",", and list the prices the portal may switch\nbetween.",[622,1147,514],{"id":1148},"local-development",[593,1150,1151,1152,659],{},"Forward events to your dev server with the Stripe CLI. Use the secret it prints as ",[637,1153,913],{},[661,1155,1159],{"className":1156,"code":1157,"language":1158,"meta":667,"style":667},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","stripe listen --forward-to localhost:3000\u002Fapi\u002Fstripe\u002Fwebhook\n","bash",[637,1160,1161],{"__ignoreMap":667},[671,1162,1163,1167,1170,1173],{"class":673,"line":674},[671,1164,1166],{"class":1165},"sBMFI","stripe",[671,1168,1169],{"class":711}," listen",[671,1171,1172],{"class":711}," --forward-to",[671,1174,1175],{"class":711}," localhost:3000\u002Fapi\u002Fstripe\u002Fwebhook\n",[597,1177,1180,1183,1184,1187,1188,1191,1192,614],{"icon":1178,"color":1179},"i-lucide-check","success",[601,1181,1182],{},"What you should see:"," after a test checkout, ",[637,1185,1186],{},"stripe listen"," shows the events with status ",[637,1189,1190],{},"200",", and the endpoint\nanswers ",[637,1193,1194],{},"{ received: true }",[622,1196,1198],{"id":1197},"next-steps","Next steps",[627,1200,1201,1206],{},[630,1202,1203,1205],{},[606,1204,490],{"href":491}," – use this setup end to end.",[630,1207,1208,1211],{},[606,1209,250],{"href":1210},"\u002Fmodules\u002Fcommerce\u002Fstripe#routes"," – what the webhook answers, how retries work and how to listen to events.",[1213,1214,1215],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":667,"searchDepth":696,"depth":696,"links":1217},[1218,1219,1224,1225],{"id":624,"depth":696,"text":625},{"id":869,"depth":696,"text":870,"children":1220},[1221,1222,1223],{"id":877,"depth":741,"text":878},{"id":1006,"depth":741,"text":1007},{"id":1113,"depth":741,"text":1114},{"id":1148,"depth":696,"text":514},{"id":1197,"depth":696,"text":1198},"Set up the Stripe dashboard, webhook endpoint, keys and KV once, so Stripe, Payments and Subscriptions all work, in production and locally.","md",null,{},{"icon":149},{"title":146,"description":1226},"pnt1n8dk8C8MkqwErdn3pV9BqbxoAWni1TIDMzx-jOU",[1234,1236],{"title":141,"path":142,"stem":143,"description":1235,"icon":144,"children":-1},"Give OAuth Server the private key it signs tokens with, per environment, and rotate it without signing anyone out.",{"title":151,"path":152,"stem":153,"description":1237,"icon":154,"children":-1},"Move a host app or module from abilities, role-name checks, RBAC and per-module authorize hooks to permissions, roles, scopes and policies.",1794503151701]