@wix/agent-skills 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/README.md +4 -1
  2. package/package.json +1 -1
  3. package/skills/README.md +23 -0
  4. package/skills/wix-app/SKILL.md +10 -2
  5. package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
  6. package/skills/wix-app/references/CODE_QUALITY.md +1 -0
  7. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
  9. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +23 -9
  10. package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
  11. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
  12. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
  13. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
  14. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
  15. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
  16. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
  17. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
  18. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
  19. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
  20. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
  21. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
  22. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
  23. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
  24. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
  25. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
  26. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
  27. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
  28. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
  29. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
  30. package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
  31. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
  32. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
  33. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
  34. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +17 -105
  35. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
  36. package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
  37. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +15 -15
  38. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
  39. package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
  40. package/skills/wix-auth/SKILL.md +12 -0
  41. package/skills/wix-auth/references/device-flow.md +140 -0
  42. package/skills/wix-docs/SKILL.md +188 -0
  43. package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
  44. package/skills/wix-docs/references/EXTRACTING.md +120 -0
  45. package/skills/wix-headless/SKILL.md +84 -130
  46. package/skills/wix-headless/entry/bootstrap.mjs +93 -0
  47. package/skills/wix-headless/entry/skill.md +78 -0
  48. package/skills/wix-headless/references/AI_FEATURES.md +178 -0
  49. package/skills/wix-headless/references/CAPABILITIES.md +84 -0
  50. package/skills/wix-headless/references/DISCOVERY.md +34 -56
  51. package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
  52. package/skills/wix-headless/references/FEEDBACK.md +118 -0
  53. package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
  54. package/skills/wix-headless/references/SDK_HANDOFF.md +97 -0
  55. package/skills/wix-headless/references/SEED.md +44 -188
  56. package/skills/wix-headless/references/SETUP.md +32 -103
  57. package/skills/wix-headless/references/astro.md +98 -0
  58. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
  59. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
  60. package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
  61. package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
  62. package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
  63. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
  64. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
  65. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
  66. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
  67. package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
  68. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
  69. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
  70. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +173 -0
  71. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
  72. package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
  73. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +228 -0
  74. package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
  75. package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
  76. package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
  77. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +176 -0
  78. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +153 -0
  79. package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
  80. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +161 -0
  81. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +117 -0
  82. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
  83. package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
  84. package/skills/wix-headless/references/managed/CONNECT.md +74 -0
  85. package/skills/wix-headless/references/managed/CREATE.md +130 -0
  86. package/skills/wix-headless/references/managed/DEPLOYMENT.md +57 -0
  87. package/skills/wix-headless/references/non-astro.md +59 -0
  88. package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
  89. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +7 -0
  90. package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
  91. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +63 -0
  92. package/skills/wix-manage/SKILL.md +24 -1
  93. package/skills/wix-manage/references/analytics/query-site-analytics.md +241 -0
  94. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
  95. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +265 -0
  96. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +436 -0
  97. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +209 -0
  98. package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
  99. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +51 -0
  100. package/skills/wix-vibe-headless/SKILL.md +136 -0
  101. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
  102. package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
  103. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +153 -0
  104. package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
  105. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +215 -0
  106. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
  107. package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
  108. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
  109. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +135 -0
  110. package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
  111. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
  112. package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
  113. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
  114. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +194 -0
  115. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
  116. package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
  117. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
  118. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
  119. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
  120. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
  121. package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
  122. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +155 -0
  123. package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
  124. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +142 -0
  125. package/skills/wix-headless/references/BUILD-astro.md +0 -253
  126. package/skills/wix-headless/references/BUILD-own-build.md +0 -167
  127. package/skills/wix-headless/references/BUILD.md +0 -105
  128. package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
  129. package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
  130. package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
  131. package/skills/wix-headless/references/PLAN-connect.md +0 -19
  132. package/skills/wix-headless/references/PLAN-create.md +0 -17
  133. package/skills/wix-headless/references/PLAN.md +0 -92
  134. package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
  135. package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
  136. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
  137. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
  138. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
  139. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
  140. package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
  141. package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
  142. package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
  143. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
  144. package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
  145. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
  146. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
  147. package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
  148. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
  149. package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
  150. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
  151. package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
  152. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
  153. package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
  154. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
  155. package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
  156. package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
  157. package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
  158. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
  159. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
  160. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
  161. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
  162. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
  163. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
  164. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
  165. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
  166. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
  167. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
  168. package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
  169. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
  170. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
  171. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
  172. package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
  173. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
  174. package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
  175. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
  176. package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
  177. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
  178. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
  179. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
  180. package/skills/wix-headless/references/astro/templates/global.css +0 -288
  181. package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
  182. package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
  183. package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
  184. package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
  185. package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
  186. package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
  187. package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
  188. package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
  189. package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
  190. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
  191. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
  192. package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
  193. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
  194. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
  195. package/skills/wix-headless/references/bookings/FLOW.md +0 -246
  196. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
  197. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
  198. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
  199. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
  200. package/skills/wix-headless/references/commands/known-apps.json +0 -6
  201. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
  202. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
  203. package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
  204. package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
  205. package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
  206. package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
  207. package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
  208. package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
  209. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
  210. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
  211. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
  212. package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
  213. package/skills/wix-headless/references/seed-recipes.md +0 -49
  214. package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
  215. package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
  216. package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
  217. package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
  218. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
  219. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
  220. package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
  221. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
  222. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
  223. package/skills/wix-headless/references/shared/STYLING.md +0 -140
  224. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
  225. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
  226. package/skills/wix-headless/references/verticals/_schema.json +0 -74
  227. package/skills/wix-headless/references/verticals/_schema.md +0 -95
  228. package/skills/wix-headless/references/verticals/blog.md +0 -31
  229. package/skills/wix-headless/references/verticals/bookings.md +0 -41
  230. package/skills/wix-headless/references/verticals/cms.md +0 -30
  231. package/skills/wix-headless/references/verticals/ecom.md +0 -25
  232. package/skills/wix-headless/references/verticals/forms.md +0 -26
  233. package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
  234. package/skills/wix-headless/references/verticals/stores.md +0 -33
  235. package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
  236. package/skills/wix-headless/scripts/compose.mjs +0 -520
  237. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
  238. package/skills/wix-headless/scripts/flatten.sh +0 -61
  239. package/skills/wix-headless/scripts/generate-images.mjs +0 -242
  240. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -154
  241. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
  242. package/skills/wix-headless/scripts/scaffold.sh +0 -139
  243. package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
  244. package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
  245. package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
  246. package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
  247. package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: "How to Code Members (non-Astro)"
3
+ description: The frontend contract for member sign-up / log-in / log-out and member-gated surfaces on a **non-Astro** frontend (Vite/React/Vue SPA or static HTML, managed-non-Astro or self-managed) — the manual `OAuthStrategy` handshake off the *same* visitor client non-astro.md already builds (`generateOAuthData → getAuthUrl → parseFromUrl → getMemberTokens → setTokens`, `logout`, `loggedIn()`), token persistence + `renewToken`, the published-site and allow-listed-redirectUri preconditions, defaulting to the Wix login page, reading the current member with `@wix/members`, and the no-`auth.elevate` rule. Specifies the *how* for the non-Astro axis only — read `how-to-code-members-astro.md` for an Astro frontend.
4
+ ---
5
+ **RECIPE**: How to Code Member Auth on a **non-Astro** Frontend (manual `OAuthStrategy`, `@wix/members`)
6
+
7
+ A concise contract for wiring **login / sign-up / logout and member-gated surfaces** into a non-Astro frontend — a Vite/React/Vue SPA or plain static HTML, whether managed-non-Astro or self-managed. **This is the *how* (which calls, which preconditions, which failure modes), not the *what*** — which pages are gated, what the account page shows, and the design come from the request you're fulfilling.
8
+
9
+ > **⚠️ AXIS GUARD — this recipe is for non-Astro only.** Here you drive the OAuth handshake yourself on the same manual client `non-astro.md` builds for visitors. If the frontend **is** Astro, **stop and read `how-to-code-members-astro.md`** — Astro ships built-in `/api/auth/login` routes and auto-auth, and building the `OAuthStrategy` handshake below on Astro is wrong (and 500s at SSR). Don't cross the streams.
10
+
11
+ > **One mechanism, not three.** Sign-up, log-in and log-out are the *same* flow — the Wix login page **logs in an existing member or registers a new one** in one step; you never build a separate "sign up" call. Member tokens are the **same shape** as visitor tokens, just `role: member`: login just swaps the token set on the client you already have.
12
+
13
+ Pinned docs (read before wiring — `curl` the `.md` directly):
14
+ - Wix-managed login via the JS SDK: <https://dev.wix.com/docs/go-headless/self-managed-headless/authentication/members/wix-login-page/wix-managed-login-using-the-js-sdk.md>
15
+ - `OAuthStrategy` reference (member flows, token/session methods): <https://dev.wix.com/docs/sdk/core-modules/sdk/oauth-strategy.md>
16
+ - Handle visitors & members via the JS SDK: <https://dev.wix.com/docs/go-headless/self-managed-headless/authentication/visitors/handle-visitors-using-the-js-sdk.md>
17
+ - Current member (SDK): <https://dev.wix.com/docs/sdk/frontend-modules/members/current-member/introduction> (open with `?apiView=SDK`)
18
+
19
+ ---
20
+
21
+ ## Preconditions — get these right or `getAuthUrl` fails (read first)
22
+
23
+ - **`clientId` only, never the secret.** Member auth uses the same **public** OAuth client id as the visitor client (`non-astro.md` N6). No `client_secret` in the frontend, ever.
24
+ - **The connected site must be PUBLISHED.** `getAuthUrl` serves the Wix login page; against an unpublished site it fails the redirect with a non-obvious error. Publish before testing login (like the events payment-method precondition).
25
+ - **The `redirectUri` must be in the OAuth app's allowed redirect URIs — exact match, and YOU must register it (it is not automatic).** A mismatch (or a missing entry) is the classic login 4xx — login is dead until it's registered. `wix release` auto-registers the deployed **origin** (`allowedRedirectDomains`), but **not** this member-login **callback** (`allowedRedirectUris`) — that's a separate field and a separate, manual step. Because the callback embeds the deployed origin, it's a **post-deploy** step: register it right after release. **⚠️ Do not conclude `allowedRedirectUris` is read-only/dashboard-only** — the `UpdateOAuthApp` doc may not list it prominently, but a **masked `PATCH`** sets it (the field mask is required or it silently no-ops). **Mechanics: `managed/DEPLOYMENT.md` → "Member login on a non-Astro frontend".** Register both the exact callback (`window.location.origin + '/callback'`) and the `https://*-<host>/callback` wildcard; don't rely on trailing-slash/path variants matching.
26
+ - **Default to the Wix login page** — the sub-type this recipe uses. Three login sub-types exist (Wix login page / custom login page / externally-managed); the Wix page needs **no UI to build** and is deterministic. **If the brief explicitly asks for a custom/branded in-app login form or custom sign-up fields** (full name / username / address / arbitrary fields), **stop and read `how-to-code-members-custom-login.md`** — that path builds the form and drives `client.auth.register`/`login` directly (works on any project type). Use externally-managed only when specifically requested. When no custom form or extra fields are named, the Wix login page below is the default — don't build a custom form unprompted.
27
+
28
+ ---
29
+
30
+ ## Identity vs. profile — two layers, don't conflate them
31
+
32
+ - **Identity / auth** — *"is this caller a logged-in member?"*, log in, log out. Native to the headless OAuth app. **No app install needed** — everything in "The login flow" and `loggedIn()`-based gating below runs on this layer alone.
33
+ - **Member profile / Members Area** — reading name / photo / roles / badges, an editable my-account page. Served by the **Wix Members Area app**, which **must be installed** (see `SETUP.md`). `@wix/members` `getCurrentMember()` returns data only once that app is present.
34
+
35
+ If `getCurrentMember()` returns empty/errors on a site where `loggedIn()` is `true`, suspect the **Members Area app isn't installed** — not a code bug.
36
+
37
+ ---
38
+
39
+ ## The login flow — the same client, plus a round-trip
40
+
41
+ Reuse the one visitor client `non-astro.md` builds (`createClient({ modules, auth: OAuthStrategy({ clientId }) })`) — don't make a second client. Login is three steps across a redirect.
42
+
43
+ ```js
44
+ // 1 · start login (sign-up is the SAME call) — from a "Log in" button
45
+ const redirectUri = window.location.origin + '/callback'; // MUST be allow-listed, exact match
46
+ const originalUri = window.location.href; // where to send the member back after
47
+ const oAuthData = client.auth.generateOAuthData(redirectUri, originalUri); // PKCE: state + codeVerifier
48
+ localStorage.setItem('wixOAuthData', JSON.stringify(oAuthData)); // survive the redirect
49
+ const { authUrl } = await client.auth.getAuthUrl(oAuthData);
50
+ window.location.href = authUrl; // → Wix login page (login or register)
51
+
52
+ // 2 · on the callback page (/callback)
53
+ const oAuthData = JSON.parse(localStorage.getItem('wixOAuthData'));
54
+ const returned = client.auth.parseFromUrl(); // { code, state } or { error }
55
+ if (returned.error) { /* surface returned.errorDescription; do NOT loop back into getAuthUrl */ }
56
+ const tokens = await client.auth.getMemberTokens(returned.code, returned.state, oAuthData);
57
+ client.auth.setTokens(tokens); // subsequent SDK calls now run as the member
58
+ persistTokens(tokens); // see persistence below
59
+ window.location.href = returned.originalUri ?? '/'; // back to where login started
60
+
61
+ // 3 · logout
62
+ const { logoutUrl } = await client.auth.logout(window.location.href);
63
+ clearPersistedTokens();
64
+ window.location.href = logoutUrl;
65
+ ```
66
+
67
+ Check auth state anywhere: **`client.auth.loggedIn()`** — `false` = anonymous visitor, `true` = logged-in member. Gate a view on it (render the account UI only when `true`; otherwise show a "Log in" control that runs step 1).
68
+
69
+ ---
70
+
71
+ ## Token persistence & renewal — treat member tokens like visitor tokens
72
+
73
+ Member tokens are the **same token set** the visitor session machinery already handles; login just swaps them in.
74
+
75
+ - **Persist** the token set (localStorage or a cookie) after `setTokens` so a reload stays logged in. On app boot, if you have a stored set, `client.auth.setTokens(stored)` before first render.
76
+ - **Renew** with `client.auth.renewToken(refreshToken)` (or the SDK's automatic renewal when you re-hydrate via `setTokens`) — same as the visitor refresh flow. A member session expiring is a normal state; refresh, don't force a re-login unless the refresh token is gone.
77
+ - On logout, **clear** the persisted set so the next boot is a clean anonymous visitor.
78
+
79
+ ---
80
+
81
+ ## Read the current member — `@wix/members`, not the dev-preview package
82
+
83
+ ```js
84
+ import { members } from '@wix/members';
85
+ // with the member tokens set on the client:
86
+ const { member } = await client.members.getCurrentMember({ fieldsets: ['FULL'] });
87
+ // member.profile?.nickname, member.profile?.photo, member.loginEmail, member.contactId, member.roles
88
+ ```
89
+
90
+ - **⚠️ The SDK export is `getCurrentMember`, NOT `getMyMember`.** The REST method is named *Get My Member* and the SDK docs page may show `GetMyMember`, but `@wix/members` exports it as **`client.members.getCurrentMember`** — calling `getMyMember(...)` throws `is not a function` at runtime. It's a silent trap: a logged-out smoke test never reaches the call, so it only fails once a real member loads the account page.
91
+ - **⚠️ Use `@wix/members`** (`getCurrentMember` / `getMember` / `updateMember`) — visitor/member auth, production-ready. **Do NOT use `@wix/site-members`** (`wixSiteMembers`, "Current Member") — it's in **Developer Preview** ("not for production"). Only if a profile-privacy toggle is specifically requested.
92
+ - The **photo** is a `wix:image://` identifier — resolve with `media.getScaledToFillImageUrl` (`non-astro.md` N7); never hand-build the CDN URL.
93
+ - **Another member by id → PUBLIC fieldset only**; a **private** profile returns nothing to a member/visitor identity (relevant to any "look up author by id" lookup).
94
+
95
+ ---
96
+
97
+ ## ⚠️ Do NOT `auth.elevate()` — and on a SPA you can't anyway
98
+
99
+ - **Login** is the **identity** axis (anonymous → a specific member). **`auth.elevate()`** is the **permission** axis (→ app/admin scope). A member reading **their own** data (own orders/bookings/subscriptions, plan-gated content, their profile) is authorized under the **member token, no elevation** — reaching for elevate to "make member data work" is the wrong axis and doesn't help.
100
+ - A pure SPA / static site has **no server, so there is no `auth.elevate` at all** (`non-astro.md` N5). Admin/site-wide reads (everyone's orders, all members) genuinely can't run on this path — that's a real limitation, but **member features don't need it**, so it does not block login / account / gated content. If a feature truly needs an admin read, it needs a server (out of scope for a static SPA).
101
+
102
+ ---
103
+
104
+ ## pricing-plans is a HARD dependency on members
105
+
106
+ If the site has pricing-plans, **login is required, not optional**. Ordering a plan (`orders.createOnlineOrder(planId)`) orders it **for a logged-in member**; anonymous → the Wix flow forces sign-up. `orders.memberListOrders()` and "my subscription" reads return **nothing** for an anonymous visitor. So the plans grid is public, but the **subscribe** button and the **my-subscription** surface both need the login flow above; a logged-in member calling `orders.createOnlineOrder` needs **no `onBehalf`**. Everywhere else (stores "my orders", bookings "my bookings", events "my registrations"), login is a *soft* add-on — the purchase/RSVP/book action runs fine as an anonymous visitor; only the account view of it needs a member.
107
+
108
+ ---
109
+
110
+ ## Conclusion
111
+ Correct member auth on a non-Astro frontend:
112
+ - reuses the **one visitor `OAuthStrategy` client** and drives the handshake — `generateOAuthData → getAuthUrl → parseFromUrl → getMemberTokens → setTokens`, `logout`, `loggedIn()` — **never** an Astro `/api/auth/*` route;
113
+ - gets the preconditions right up front: **`clientId` only**, **site published**, **`redirectUri` allow-listed exact-match**, **default to the Wix login page**;
114
+ - **persists and renews** member tokens like visitor tokens (`renewToken`), re-hydrating with `setTokens` on boot and clearing them on logout;
115
+ - reads the current member via **`@wix/members` `getCurrentMember`** (not the dev-preview `@wix/site-members`), resolving the photo `wix:image://` URI, expecting PUBLIC-only fields for other members;
116
+ - does **no `auth.elevate`** (wrong axis for own-data reads; unavailable on a serverless SPA anyway);
117
+ - treats **login as required** whenever pricing-plans is present, and as a soft add-on for the "my …" surfaces of the other verticals;
118
+ - needs the **Wix Members Area app installed** only for profile *data* — pure "logged-in vs not" gating runs on the identity layer with no install.
@@ -0,0 +1,132 @@
1
+ ---
2
+ name: "How to Code Portfolio"
3
+ description: The frontend read contract for a Wix Portfolio backend — which @wix/portfolio modules, the exact list/query/detail calls, and the failure modes the docs omit (`_id` not `id`, object-param query NOT a builder, cover-image `.url` vs project-item `wix:image://` string). The *how*; which projects/collections to render come from the request.
4
+ ---
5
+ **RECIPE**: How to Code a Portfolio Frontend (Wix Portfolio v1 — display-only, no eCommerce)
6
+
7
+ A contract for the **frontend code** of a portfolio/showcase site. The *how* (modules, calls, fields), not the *what* (which projects to show, the page design, the framework — those come from the request).
8
+
9
+ > **This recipe is for CODING, not seeding.** It assumes the backend already exists — collections and projects were created by `setup-portfolio.md`. It says nothing about creating data; it only reads and renders it. Portfolio is **display-only**: there is no cart, checkout, or eCommerce — do **not** pull in `@wix/ecom`/`@wix/redirects`.
10
+
11
+ > **⚠️ Reading rule — append `?apiView=SDK` to every doc link below.** Wix renders two views of each page: the bare/REST view shows `id`; the `?apiView=SDK` view shows **`_id`**. The SDK is what the frontend calls, so the REST view will actively mislead you (reading `project.id` yields `undefined` and detonates every downstream key/route). Always read the SDK view.
12
+
13
+ ---
14
+
15
+ ## The modules and the client (read this first)
16
+
17
+ **No app-id constant is needed** — Portfolio has no `catalogReference`/eCommerce surface. Everything is a plain read from `@wix/portfolio`.
18
+
19
+ **Module table** — import only what the site uses:
20
+
21
+ | Need | Package | Module | Key methods |
22
+ |---|---|---|---|
23
+ | Collections (groupings) | `@wix/portfolio` | `collections` | `listCollections(options?)`, `queryCollections(query, options?)`, `getCollection(id, options?)` |
24
+ | Projects (the showcased work) | `@wix/portfolio` | `projects` | `listProjects(options?)`, `queryProjects(query, options?)`, `getProject(id, options?)` |
25
+ | A project's media items (deeper project-page gallery) | `@wix/portfolio` | `projectItems` | `listProjectItems(projectId, options?)` |
26
+ | Resolve image media strings (covers **and** project items) | `@wix/sdk` | `media` | `getScaledToFillImageUrl(id, w, h, {})` |
27
+ | Build the client (non-Astro only) | `@wix/sdk` | `createClient`, `OAuthStrategy` | — |
28
+
29
+ - **There is one Portfolio API version (v1)** — no V1-vs-V3 trap like Stores.
30
+ - **`queryX` accepts BOTH an object-param and a builder overload** — `queryProjects({ filter, cursorPaging })` **and** `queryProjects().eq(…).find()` both type-check (unlike Stores/Bookings, whose object-param shape was removed). Either compiles; this recipe prefers a **client-side filter** (see Filtering) so you needn't pick.
31
+ - **Public read — no `auth.elevate`.** Portfolio content is public on the live site, so a visitor client reads collections/projects/items with **no elevation**. (The docs show `auth.elevate(...)` examples — those are for backend/admin jobs; a visitor-facing read does not need them. Elevation is the separate admin axis.)
32
+
33
+ **Auth / client — the framework split:**
34
+
35
+ - **Astro (Wix-managed, the default):** auth is **ambient**. Import the modules and call them directly from server components / `src/pages/api/*` — **no `createClient`, no `OAuthStrategy`, no `clientId`.**
36
+ ```ts
37
+ import { collections, projects } from '@wix/portfolio';
38
+ const { collections: cols } = await collections.listCollections();
39
+ const { projects: projs } = await projects.listProjects();
40
+ ```
41
+ - **Non-Astro (Vite/React/Vue/static):** build **one** manual visitor client and reuse it. The `clientId` is the app's **public** OAuth id (not a secret; `client_secret` never reaches the frontend).
42
+ ```ts
43
+ import { createClient, OAuthStrategy } from '@wix/sdk';
44
+ import { collections, projects, projectItems } from '@wix/portfolio';
45
+ const wixClient = createClient({
46
+ modules: { collections, projects, projectItems },
47
+ auth: OAuthStrategy({ clientId: import.meta.env.PUBLIC_WIX_CLIENT_ID }),
48
+ });
49
+ // then: await wixClient.projects.listProjects()
50
+ ```
51
+ A mis-wired public env var inlines as `undefined` and 400s **every** call — after building, confirm the actual `clientId` value is in the bundle.
52
+
53
+ ---
54
+
55
+ ## The features (build the ones the site needs)
56
+
57
+ Each subsection is self-contained — implement only what the site uses, in any order. Ordering matters only *within* a feature (resolve the collection/project before you filter/render by it).
58
+
59
+ ### Listing the gallery (projects + collections) — and the `_id` rule
60
+
61
+ The gallery is two reads: **all projects** and **all collections**. Prefer the **`list*` methods** — they take an optional `options` (`{ paging: { limit, cursor }, includePageUrl }`) and need no query object:
62
+
63
+ ```ts
64
+ const { projects: projs = [] } = await projects.listProjects(); // Astro: module; non-Astro: wixClient.projects…
65
+ const { collections: cols = [] } = await collections.listCollections();
66
+ ```
67
+ Docs (read `?apiView=SDK`):
68
+ - <https://dev.wix.com/docs/api-reference/business-solutions/portfolio/projects/list-projects?apiView=SDK>
69
+ - <https://dev.wix.com/docs/api-reference/business-solutions/portfolio/collections/list-collections?apiView=SDK>
70
+
71
+ **⚠️ CRITICAL — the id field is `_id`, never `id`.** Under `?apiView=SDK`, both `Project` and `Collection` expose **`_id`** (a GUID), `title`, `description`, `slug`, `hidden`, `coverImage`; `Project` also has `collectionIds: string[]` and `details`. Reading `.id` returns `undefined` — every `key={p.id}` and every `/[slug]` route built from it breaks silently. If you find yourself reading `id`, you're looking at the REST doc view.
72
+
73
+ **⚠️ The list arrays and `_id` are nullable under `strict`.** `listProjects()`/`listCollections()`/`listProjectItems()` return the array as `T[] | undefined` (default it with `= []` as above, or `?? []`), and `_id`/`slug` are `string | null | undefined`. So narrow before you key/route on them (`if (!p._id) continue`, `p.slug === params.slug`) — a bare `.filter`/`.includes(col._id)` won't type-check without the guard.
74
+
75
+ **⚠️ Only `Collection` has `sortOrder` — `Project` does not.** Sort **collections** by their numeric `sortOrder` for the owner's intended order (`[...cols].sort((a,b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))`). **`Project` exposes no `sortOrder`** (reading it is `TS2339`) — render projects in the list-response order, or sort by a field they *do* have (`title`, `_createdDate`) if you need a stable one. Don't sort projects by `sortOrder`.
76
+
77
+ **⚠️ Respect `hidden`.** An entity with `hidden: true` should not appear publicly. Filter it out client-side (`projs.filter(p => !p.hidden)`) — and remember an *omitted* `hidden` comes back **absent** (proto3), so test `!p.hidden` (absent ⇒ falsy ⇒ shown), never `p.hidden === false`.
78
+
79
+ ### Grouping / filtering by collection
80
+
81
+ A project links to its collections via **`project.collectionIds: string[]`** (a project can be in several). To show a collection's projects, the **default is a client-side filter** over the already-listed projects (avoids a second round-trip; narrow the nullable `_id` first):
82
+
83
+ ```ts
84
+ const colId = collection._id;
85
+ const inCollection = colId ? projs.filter(p => p.collectionIds?.includes(colId)) : [];
86
+ ```
87
+ This avoids the server-filter fragility and one extra round-trip; portfolios are small.
88
+
89
+ **If you filter server-side, `queryProjects`/`queryCollections` accepts EITHER shape — both compile.** The installed SDK exposes two overloads: an object-param `queryProjects({ filter, cursorPaging, sort })` (e.g. `projects.queryProjects({ cursorPaging: { limit: 100 } })`) **and** a fluent builder `queryProjects().eq('…', …).find()`. Either type-checks — pick one and confirm the filter matches before shipping. (Client-side filtering above sidesteps the choice.)
90
+ - <https://dev.wix.com/docs/api-reference/business-solutions/portfolio/projects/query-projects?apiView=SDK>
91
+
92
+ ### Project detail page (resolve by slug, then its media)
93
+
94
+ Detail routes resolve a **`slug`** from the URL (the site is SSR — resolve at request time, no `getStaticPaths`). Resolve the project **client-side from the list** (reliable), then optionally load its media items:
95
+
96
+ ```ts
97
+ const project = projs.find(p => p.slug === params.slug); // from listProjects()
98
+ const { items } = await projectItems.listProjectItems(project._id); // media gallery (owner-added)
99
+ ```
100
+ - `projectItems.listProjectItems(projectId, options?)` — first arg is the **project `_id`** (positional), not an options object.
101
+ - <https://dev.wix.com/docs/api-reference/business-solutions/portfolio/project-items/list-project-items?apiView=SDK>
102
+
103
+ Render `project.details` — an array of **`{ label, text }`** pairs (Role, Year, Client…) the seed may have set; it can be `[]`, so guard before mapping.
104
+
105
+ ### Rendering images — ONE `Image` shape, always a media STRING
106
+
107
+ **⚠️ CRITICAL — `imageInfo` is a bare media STRING, for covers AND project items alike. There is no ready `.url`.** The installed SDK types **one** `Image { imageInfo?: string; focalPoint? }`, reused by `Project.coverImage`, `Collection.coverImage`, **and** `Item.image`. So `coverImage.imageInfo` and `item.image?.imageInfo` are both **strings** (a `wix:image://…` identifier) — reading `.url` on either is a type error (`TS2339: Property 'url' does not exist on type 'string'`). (The *REST* API returns `coverImage.imageInfo` as an object with a populated `url`, but the **SDK** — what the frontend calls — types it as a string; the package wins.) Resolve every portfolio image through the same `@wix/sdk` media helper — never drop a `wix:image://` value straight into `<img src>` (→ `ERR_UNKNOWN_URL_SCHEME`), never hand-build a `static.wixstatic.com` URL from it (→ 403):
108
+
109
+ ```ts
110
+ import { media } from '@wix/sdk';
111
+
112
+ // cover (project or collection) — optional, guard it
113
+ const coverId = project.coverImage?.imageInfo; // string | undefined
114
+ const cover = coverId ? media.getScaledToFillImageUrl(coverId, 1200, 900, {}) : undefined;
115
+
116
+ // project-item media (same shape, same helper)
117
+ const itemId = item.image?.imageInfo; // string | undefined
118
+ const src = itemId ? media.getScaledToFillImageUrl(itemId, 1200, 900, {}) : undefined;
119
+ ```
120
+
121
+ - **`media.getScaledToFillImageUrl` takes FOUR arguments** — `(wixMediaIdentifier, width, height, options)`; the `options` object is **required** (pass `{}`). A 3-arg call fails `TS2554`.
122
+ - **Covers are optional.** A text-only-seeded project/collection has **no** `coverImage` (absent), so the resolved value is `undefined` → render the **themed-block fallback** (`IMAGE_GENERATION.md`), never a broken `<img>`.
123
+
124
+ ---
125
+
126
+ ## Conclusion
127
+ - **Modules:** `collections`, `projects`, `projectItems` from `@wix/portfolio`; `media` + `createClient`/`OAuthStrategy` from `@wix/sdk`. **No** `@wix/ecom`/`@wix/redirects` — portfolio is display-only.
128
+ - **`_id`, never `id`** — read entities under `?apiView=SDK`; `.id` is `undefined`. List arrays are `T[] | undefined` (default `= []`); `_id`/`slug` are nullable — narrow before keying/routing.
129
+ - **Query has both overloads** — `queryProjects({ filter, cursorPaging })` **and** `queryProjects().eq().find()` compile; prefer client-side filtering by `collectionIds`/`slug`.
130
+ - **Only collections have `sortOrder`** (sort collections by it); projects don't — render in list order. **Respect `hidden`** (test `!hidden` — an omitted `hidden` is absent).
131
+ - **One `Image` shape:** `coverImage.imageInfo` and `item.image.imageInfo` are both bare media **strings** (no `.url`) → resolve every image via `media.getScaledToFillImageUrl(id, w, h, {})` (4 args); covers are optional → themed block. Never hand-build wixstatic URLs.
132
+ - **Public read, no `auth.elevate`** — visitor client reads all content; elevation is the separate admin axis.
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: "How to Code Pricing Plans"
3
+ description: The frontend contract for a Wix Pricing Plans (Plans V3) site — which `@wix/pricing-plans` modules to import, how to list plans for the grid, order a plan for a logged-in member, read the member's own subscriptions, and (the integration) let a member book a Bookings service with their membership. Specifies the *how* (modules + exact calls + the failure modes the docs omit); which plans to render and how the page looks come from the request.
4
+ ---
5
+ **RECIPE**: How to Code a Wix Pricing Plans Frontend (Plans V3 + members + the Bookings-membership integration)
6
+
7
+ A contract for the **frontend code** of a pricing-plans site: showing the plans grid, subscribing (ordering) a plan, a member's "my subscription" surface, and — when the site also has Bookings — booking a service **with** an active membership. **This recipe is the *how* (which modules, which calls, which fields), not the *what*** — which plans to show, how the page looks, and the framework come from the request you're fulfilling.
8
+
9
+ > **This recipe is for CODING, not seeding.** It assumes a Plans V3 backend already exists (plans created, and — for the integration — bookings services attached to a plan via Benefit Programs; see `setup-pricing-plans.md`). It says nothing about creating plans — only how to read and buy them from frontend code.
10
+
11
+ > **⚠️ Reading rule — append `.md?apiView=SDK` to every doc link below.** Wix docs render two views: the **bare/REST view shows `id`**, the **`?apiView=SDK` view shows `_id`** — the SDK is what your frontend calls. A surprising field name usually means you're reading the REST view. Discover any shape not pinned here with `SearchWixSDKDocumentation`, not by guessing a URL.
12
+
13
+ > **⚠️ pricing-plans is a HARD dependency on members.** Ordering a plan and the "my subscription" surface both require a **logged-in member** (browsing the grid is public). So this recipe is always paired with member auth — read the matching **`how-to-code-members-astro.md`** or **`how-to-code-members-non-astro.md`** for the login flow. A logged-in member ordering their own plan needs **no `auth.elevate`** and **no `onBehalf`**.
14
+
15
+ ---
16
+
17
+ ## The modules and the client (read this first)
18
+
19
+ **⚠️ Two different packages — use the headless one.** The Wix docs surface `checkout.startOnlinePurchase()` / `checkout.createOnlineOrder()` under **`@wix/site-pricing-plans`** — that is the **Wix-site (Velo / `$w` page-code) package**, and `startOnlinePurchase` drives the **Wix Pay frontend UI** that only exists inside a hosted Wix page. **It is NOT the headless path** — do not import `@wix/site-pricing-plans` in a headless frontend. Use **`@wix/pricing-plans`** (the universal/headless SDK), whose `orders.createOnlineOrder` creates the order and leaves payment to a redirect you drive (see *Subscribing*).
20
+
21
+ | Need | Package | Module / namespace |
22
+ |---|---|---|
23
+ | List / read plans (the grid) | `@wix/pricing-plans` | `plansV3` (Plans V3 — `queryPlans`, `getPlan`) |
24
+ | Order a plan + read a member's orders | `@wix/pricing-plans` | `orders` (`createOnlineOrder`, `memberListOrders`, `memberGetOrder`) |
25
+ | Member login / current member | `@wix/members` + `@wix/sdk` auth | see the members recipe (`getCurrentMember`, `loggedIn()`) |
26
+ | Book a service with a membership (integration) | `@wix/bookings` + `@wix/ecom` (+ `@wix/redirects`) | `bookings` (`createBooking`), ecom `checkout` (`membershipOptions`) — see *Booking with a membership* |
27
+
28
+ **Never** import `@wix/site-pricing-plans` in headless code, and don't reach for a V1 `plans`-collection query — Plans V3 (`queryPlans` → `pricingVariants`) is the shape the seed creates.
29
+
30
+ > **⚠️ The read module is `plansV3`, not `plans`.** In the `@wix/pricing-plans` SDK, `queryPlans`/`getPlan` live on the **`plansV3`** namespace (`import { plansV3, orders } from '@wix/pricing-plans'`). Importing `plans` and calling `plans.queryPlans()` fails to type-check (`Property 'queryPlans' does not exist`). `orders` keeps its own namespace.
31
+
32
+ **Auth / client — framework split** (same split as every other coding recipe):
33
+ - **Astro (Wix-managed):** auth is ambient — call `plansV3` / `orders` directly from server components / `src/pages/api/*`. Member identity rides on the call automatically after login (`how-to-code-members-astro.md`). **No `createClient`, no `OAuthStrategy`, no `clientId`.** A member reading their own orders needs **no `auth.elevate`**.
34
+ - **Non-Astro (Vite/React/Vue/static):** build one manual client and reuse it — the **same** `OAuthStrategy` client the members/visitor flow already builds (don't make a second one). After the member-login handshake sets member tokens on it, `orders.*` runs as that member:
35
+ ```js
36
+ import { createClient, OAuthStrategy } from '@wix/sdk';
37
+ import { plansV3, orders } from '@wix/pricing-plans';
38
+ const client = createClient({ modules: { plansV3, orders }, auth: OAuthStrategy({ clientId: /* public OAuth id */ }) });
39
+ ```
40
+
41
+ > **The connected site must be PUBLISHED** — the Pricing Plans APIs return nothing / error against an unpublished site and don't work in preview (same precondition as member login). Publish before testing.
42
+
43
+ ---
44
+
45
+ ## The features (build the ones the site needs)
46
+
47
+ Each subsection is self-contained — build only what the site uses.
48
+
49
+ ### Listing plans for the grid (public, and the `_id` rule)
50
+
51
+ ```js
52
+ const { items } = await plansV3.queryPlans()
53
+ .eq('visibility', 'PUBLIC')
54
+ .find(); // items[] of Plan
55
+ ```
56
+ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/pricing-plans/plans-v3/query-plans.md?apiView=SDK>
57
+
58
+ - **The grid read is public** — a visitor token lists `PUBLIC` plans; no login needed just to browse.
59
+ - **⚠️ Entity id is `_id`, not `id`** — `plan._id` is what you order by and key React lists on. (`plan.id` is `undefined` in SDK code — you're reading the REST view if you see `id`.)
60
+ - **⚠️ Price lives in `pricingVariants[]`, NOT a top-level `price`.** Read the amount from `plan.pricingVariants[0].pricingStrategies[0].flatRate.amount` (a **decimal string** — parse before math) and the cycle from `plan.pricingVariants[0].billingTerms.billingCycle` (`{ period: "MONTH", count: "1" }`). A free plan has no `flatRate` amount; a one-time plan has different `billingTerms` (see the seed recipe). `plan.currency` is the site's currency — format from it, don't assume USD.
61
+ - **Show only `buyable` plans with a buy button.** `visibility: "PUBLIC"` can still be `buyable: false` (assign-only) — render those without a subscribe action, or filter them out.
62
+ - **Perks** for the plan card are `plan.perks[]` (each `{ _id, description }`) — display-only text. (`queryPlans` returns them; if a summary trims them, `plansV3.getPlan(planId)` returns the full object.)
63
+
64
+ ### Subscribing (ordering) a plan — login-gated
65
+
66
+ Ordering is a **member** action. Gate the subscribe button on `client.auth.loggedIn()` (non-Astro) / a resolved member (Astro) and bounce anonymous users into the login flow first (members recipe). Then:
67
+
68
+ ```js
69
+ const { order } = await orders.createOnlineOrder(planId); // planId = plan._id; logged-in member ⇒ no onBehalf
70
+ // order.status: "DRAFT" (payment not yet made) | "ACTIVE" (free plan — already applied)
71
+ ```
72
+ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/pricing-plans/orders/create-online-order.md?apiView=SDK>
73
+
74
+ - **⚠️ A logged-in member needs no `onBehalf`** — the order is created on their behalf from the member identity. `onBehalf.memberId` is only for an app/admin identity ordering *for* someone (and needs elevation) — don't reach for it in a normal member flow.
75
+ - **⚠️ `DRAFT` ≠ subscribed.** `createOnlineOrder` **orders but does not pay** — a paid plan comes back `status: "DRAFT"` and is not active until payment completes. Do not show "you're subscribed" off the `createOnlineOrder` return for a paid plan.
76
+ - **Free plan:** returns `status: "ACTIVE"` (`lastPaymentStatus: "NOT_APPLICABLE"`) directly — no payment step; render success immediately. **Branch on the plan being free** (no `flatRate` amount / total `0`) to skip the redirect below.
77
+ - **⚠️ Paid plan — the payment redirect handoff is the one piece not pinned by the docs for headless.** The all-in-one `startOnlinePurchase` that completes payment is the **site-package** method (above), unavailable headless; the headless `createOnlineOrder` returns a `DRAFT` order carrying a `wixPayOrderId`, and the member must be sent to a hosted payment flow to complete it. **⚠️ VERIFY IN A LIVE BUILD:** confirm the exact headless redirect — whether you pass the order to `@wix/redirects` `createRedirectSession({ paymentCheckout: { … } })`, or a pricing-plans-specific redirect — before shipping the paid path. Do not assert a specific call here until a real build confirms it. (The `origin`/`postFlowUrl` allowlist + `https`-host rules from `how-to-code-a-store.md`/`how-to-code-bookings.md` apply to whatever redirect is used.) The **free-plan** path above is fully client-only and needs no redirect.
78
+ - **⚠️ SCOPE — the frontend's job ends at `createOnlineOrder` + the payment redirect. STOP THERE.** Do **NOT** try to *complete or activate* the purchase from code: don't connect a payments provider (`payments/v1/wix-payments-account/connect`), don't `PATCH`/update-plan to force a state, don't call mark-as-paid, and don't hunt for an "admin way to activate the order" or to "enable payments for a $0 order." Payment completing (a paid order flipping `DRAFT → ACTIVE`) happens **out-of-band** on Wix's hosted flow / by the merchant configuring payments — it is **not** a frontend step, and chasing it is a rabbit hole (it burns the run and ships nothing extra). A **free** plan already returns `ACTIVE` with no payment; a **paid** plan is `DRAFT` until the member pays through the redirect. If the site has **no payment provider configured**, that's a **site-setup precondition** (like the events paid-ticket precondition) — surface it, don't try to fix it in code.
79
+
80
+ ### The member's "my subscription" surface — login-gated
81
+
82
+ ```js
83
+ // the logged-in member's own orders:
84
+ const res = await orders.memberListOrders(); // filtered: orders.memberListOrders({ planIds, orderStatuses, paymentStatuses, limit, offset })
85
+ // ⚠️ member-scoped reads are memberListOrders / memberGetOrder — there is NO `orders.listOrders`/`getOrder` (those are the admin `managementListOrders`/`managementGetOrder`, server/elevate only).
86
+ // ⚠️ filter args are TOP-LEVEL limit/offset, NOT a nested `paging` object.
87
+ // each order: { _id, planId, subscriptionId, status, lastPaymentStatus, startDate, endDate, currentCycle, planName, pricing, planPrice }
88
+ ```
89
+ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/pricing-plans/orders/introduction.md?apiView=SDK>
90
+
91
+ - **⚠️ Login required — returns nothing for an anonymous visitor.** Gate this surface behind auth; don't render "my subscription" for a logged-out user.
92
+ - **"Currently active" = `status === "ACTIVE"` AND now is within `startDate`…`endDate`.** A `CANCELED` order can still be within its paid period until `endDate`; `autoRenewCanceled: true` means it won't renew but may still be active now. Don't treat `CANCELED` as "no access" without checking the date window.
93
+ - Same **no-`auth.elevate`** rule: a member reading their own orders is authorized under the member token. Listing *all* members' orders is the admin/elevate axis (server-only) — not this.
94
+
95
+ ### Booking a service with a membership (the integration — only when the site also has Bookings)
96
+
97
+ This is the payoff of the seed's STEP 2: a member who holds a plan that **covers** a service books it **with the membership** instead of paying per booking. Build the normal Bookings flow from **`how-to-code-bookings.md`** (list services → pick a slot → collect the form → `createBooking` → ecom cart/checkout); the membership is a **delta on that flow**, not a separate path:
98
+
99
+ 1. **Require login** — membership payment resolves against the member identity (a covered service booked anonymously just falls back to pay-per-booking).
100
+ 2. **On `createBooking`, set `selectedPaymentOption: "MEMBERSHIP"`** (instead of `ONLINE`/`OFFLINE`). Enum values: `ONLINE`, `OFFLINE`, **`MEMBERSHIP`** (pay with a pricing plan), `MEMBERSHIP_OFFLINE`. Doc: <https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-writer-v2/create-booking.md?apiView=SDK>
101
+ 3. **The booking still rides the ecom checkout** (Bookings is an ecom catalog, appId `13d21c63-b5ec-5912-8397-c3a5ddb27a97`) — the membership is applied **on the checkout**, not on the booking object. The eligible plan surfaces on the **ecom Checkout** entity's `membershipOptions`:
102
+ - `membershipOptions.eligibleMemberships[]` — the member's plans that can cover the line item (each `{ _id, appId, name, credits }`).
103
+ - apply one via `checkout.updateCheckout(...)` setting `membershipOptions.selectedMemberships.memberships = [{ _id, appId, lineItemIds: [<booking line item id>] }]`. When the membership covers the full price, `payNow` drops to `0`.
104
+ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/checkout/checkout/get-checkout.md?apiView=SDK>
105
+ 4. **⚠️ Do NOT call `confirmBooking`** — for a membership payment Wix **auto-confirms** the booking on redemption (the same "no confirmBooking" rule as `how-to-code-bookings.md`; the membership drives confirmation).
106
+
107
+ - **⚠️ Eligibility / coverage is a LIVE read, never a hardcoded plan→service map.** A new eligible plan, or a service newly added to a plan's coverage in the backoffice, **must be honored with no code change** — so never freeze the seed-time `{ planId → serviceIds }` map into the frontend. **Preferred:** read `checkout.membershipOptions.eligibleMemberships` — the plans that can cover *this* line item, computed server-side from live coverage. (It's tagged "reserved for internal use" in the schema, so **VERIFY IN A LIVE BUILD** whether a headless member token reads it client-side.) **Fallback if that field comes back empty for a member token:** list the member's active orders (`orders.memberListOrders`, `status: ACTIVE` within the date window) and match each `planId` against the plan's **live** Benefit-Program coverage (query the program's items — the same API `setup-pricing-plans.md` STEP 2 wrote to — at request time; do **not** read a coverage map baked into code). Set `selectedPaymentOption: "MEMBERSHIP"` + `selectedMemberships` from whichever read succeeds.
108
+ - **⚠️ A `MEMBERSHIP` booking on a service the plan does NOT cover fails at checkout** (unless `skipSelectedPaymentOptionValidation`, which needs elevation — don't use in a member flow). Only offer "book with membership" for services actually covered by one of the member's active plans; otherwise fall back to the normal paid booking (`how-to-code-bookings.md`).
109
+ - **Credit/session plans:** for a limited (non-unlimited) plan, `eligibleMemberships[].credits.remaining` matters — `0` remaining is not eligible. An unlimited plan (seed `price:"0"`) has no credit ceiling.
110
+
111
+ ### Out of scope (do not build these)
112
+ - **Completing/activating payment from code** — connecting a payments account, marking orders paid, forcing an order `ACTIVE`, "activating" a subscription server-side. Payment is the member's hosted-flow step or a merchant dashboard/config task, never frontend code (see the SCOPE callout under *Subscribing*).
113
+ - **Configuring which services a plan covers** — that coverage is wired at seed time via Benefit Programs (`setup-pricing-plans.md` STEP 2); the frontend only *reads/uses* it, it doesn't create or edit it.
114
+ - **Cancelling / pausing / refunding a subscription, and dunning/renewal management** — post-purchase lifecycle beyond a read-only "my subscription" view is the hosted member area / dashboard, not this build.
115
+ - **Offline orders** (`createOfflineOrder` / mark-as-paid) — a merchant/admin flow, not a member-facing frontend one.
116
+
117
+ ---
118
+
119
+ ## Conclusion
120
+ A correct Pricing Plans frontend:
121
+ - imports **`@wix/pricing-plans`** (`plansV3`, `orders`) — **never** `@wix/site-pricing-plans` (its `startOnlinePurchase` is Wix-site page-code, not headless);
122
+ - lists the grid publicly with **`plansV3.queryPlans().eq('visibility','PUBLIC')`**, reads **`plan._id`** and price from **`pricingVariants[].pricingStrategies[].flatRate.amount`** (decimal string) — never a top-level `price` — and only shows a buy button on `buyable` plans;
123
+ - treats **subscribe and my-subscription as login-gated** (the hard members dependency), orders with **`orders.createOnlineOrder(planId)`** (no `onBehalf`, no elevate), renders free plans as `ACTIVE` immediately, and drives paid plans through a payment redirect (**exact headless redirect to be confirmed in a live build**) — and **stops at the redirect**: no payments-account connect, no order activation / mark-as-paid from code (see *Out of scope*);
124
+ - for the Bookings integration, books a covered service by setting **`selectedPaymentOption: "MEMBERSHIP"`** on `createBooking`, applies the membership on the **ecom checkout** (`membershipOptions`), never calls `confirmBooking`, and falls back to matching the member's active-order `planId`s to the service's coverage when the checkout eligibility field isn't readable client-side.
@@ -0,0 +1,188 @@
1
+ ---
2
+ name: "How to Code Restaurant Orders"
3
+ description: The frontend cart/checkout contract for a Wix Restaurants Online-Ordering site — which SDK modules to import, how to read the ordering operation + fulfillment methods, the exact restaurant add-to-cart shape (appId = Orders app, options = operationId+menuId+sectionId, NOT variantId), and checkout via eCommerce. Specifies the *how* (modules + exact calls + failure modes); which menu/items to sell come from the request.
4
+ ---
5
+ **RECIPE**: How to Code a Wix Restaurants Ordering Frontend (Online Orders + eCommerce cart)
6
+
7
+ A contract for the **frontend code** that lets a visitor **order** from a Wix Restaurants menu: reading the ordering operation and fulfillment methods, adding a menu item to the eCommerce cart with the restaurant `catalogReference`, and checking out. **This recipe is the *how* (which modules, which calls, which fields), not the *what*** — which menu/items to sell and how the page looks come from the request you're fulfilling.
8
+
9
+ > **This recipe is for CODING ordering, not seeding it.** It assumes the backend already exists — a Restaurants **Menus** backend (menu → sections → items) **and** the Restaurants **Orders** app installed and configured (an `ENABLED` operation with fulfillment methods, every menu ordering-enabled). See `setup-restaurants.md` + `setup-restaurant-orders.md` for the backend. This recipe says nothing about creating any of it — only how to read and purchase from frontend code.
10
+
11
+ > **Reading (displaying) the menu is a SEPARATE recipe — pair this with it.** `how-to-code-restaurants.md` covers listing menus/sections/items, assembling the hierarchy in `sectionIds`/`itemIds` order, reading `item.priceInfo.price`, and rendering images. That recipe is **display-only** (no cart). **This** recipe adds the **ordering** layer on top: it consumes the same `menus`/`sections`/`items` reads and needs each rendered item's `_id`, its section's `_id`, and the menu's `_id` to build a cart line. Don't duplicate the menu-reading here — read it there, order it here.
12
+
13
+ > **⚠️ Reading rule — always append `.md?apiView=SDK` to every doc link below.** The Wix docs render two views of the same page. The **bare / REST view shows `id`**; the **`?apiView=SDK` view shows `_id`** — and the SDK is what your frontend calls. Reading the REST view by mistake is the most common source of the `entity.id`-is-`undefined` cart bug. Fetch the `.md?apiView=SDK` form directly; don't re-discover these with search.
14
+
15
+ ---
16
+
17
+ ## The modules and the client (read this first)
18
+
19
+ **Restaurants Orders app id** (a constant you need for the cart's `catalogReference.appId`):
20
+ `9a5d83fd-8570-482e-81ab-cfa88942ee60`
21
+
22
+ **⚠️ This is the ORDERS app id, NOT the Stores app id.** A restaurant menu item added to the cart must carry `appId: '9a5d83fd-…'` (Orders). Using the Stores id (`215238eb-…`) — muscle memory from `how-to-code-a-store.md` — makes eCommerce resolve the line against the wrong catalog and the add fails. Menu items are **not** Stores products.
23
+
24
+ | Need | Package | Module (namespace) |
25
+ |---|---|---|
26
+ | Menus / sections / items (display) | `@wix/restaurants` | `menus` / `sections` / `items` — see `how-to-code-restaurants.md` |
27
+ | The ordering operation (for `operationId`) | `@wix/restaurants` | `operations` |
28
+ | Fulfillment methods (pickup/delivery to show) | `@wix/restaurants` | `fulfillmentMethods` |
29
+ | Cart (add / get / checkout) | `@wix/ecom` | `currentCart` |
30
+ | Redirect to hosted checkout | `@wix/redirects` | `redirects` |
31
+
32
+ **Auth / client — framework split:**
33
+ - **Astro (Wix-managed):** authentication is ambient. Call `operations` / `fulfillmentMethods` / `currentCart` directly from server components and backend routes (`src/pages/api/*.ts`) — **no `createClient`, no `OAuthStrategy`, no `clientId`.**
34
+ ```js
35
+ import { operations, fulfillmentMethods } from '@wix/restaurants';
36
+ import { currentCart } from '@wix/ecom';
37
+ const { operations: ops } = await operations.listOperations();
38
+ ```
39
+ - **Non-Astro (Vite/React/Vue/static):** build one manual visitor client and reuse it:
40
+ ```js
41
+ import { createClient, OAuthStrategy } from '@wix/sdk';
42
+ import { menus, sections, items, operations, fulfillmentMethods } from '@wix/restaurants';
43
+ import { currentCart } from '@wix/ecom';
44
+ import { redirects } from '@wix/redirects';
45
+
46
+ const client = createClient({
47
+ modules: { menus, sections, items, operations, fulfillmentMethods, currentCart, redirects },
48
+ auth: OAuthStrategy({ clientId: /* the project's PUBLIC OAuth client id */ }),
49
+ });
50
+ // then: await client.operations.listOperations(), await client.currentCart.addToCurrentCart({...})
51
+ ```
52
+ The `clientId` is public, not a secret. A mis-wired public env var inlines as `undefined` and 400s every call.
53
+
54
+ ---
55
+
56
+ ## The shapes you read (field cheat-sheet)
57
+
58
+ These are **SDK read shapes** (`?apiView=SDK`), so entity ids are **`_id`**. Prices/fees are decimal **strings**. The cart-add body (under *Adding a menu item to the cart*) is a separate **write** shape; the `_id` rule applies to read **entities**, not to method-return wrappers (note `checkoutId`).
59
+
60
+ ```jsonc
61
+ // operations.listOperations() → { operations: [...] } (no arguments)
62
+ operation = {
63
+ _id, // → cart options.operationId (NOT .id → undefined)
64
+ name, default, // the auto-created ordering operation has default: true
65
+ onlineOrderingStatus, // "ENABLED" when the site is taking orders
66
+ fulfillmentIds, // ids of the fulfillment methods attached to this operation
67
+ defaultFulfillmentType, // "PICKUP" | "DELIVERY"
68
+ }
69
+
70
+ // fulfillmentMethods.listFulfillmentMethods() → { fulfillmentMethods: [...] }
71
+ fulfillmentMethod = {
72
+ _id, type, // "PICKUP" | "DELIVERY"
73
+ name, enabled, // show only enabled:true to the visitor
74
+ fee, minOrderPrice, // decimal STRINGS ("0", "5") — no currency symbol
75
+ pickupOptions, // present when type PICKUP
76
+ deliveryOptions, // present when type DELIVERY (deliveryTimeInMinutes, deliveryArea, …)
77
+ }
78
+
79
+ // item (from items.listItems — see how-to-code-restaurants.md)
80
+ item = { _id, name, priceInfo: { price } } // price is a decimal STRING; _id → cart catalogItemId
81
+
82
+ // currentCart.getCurrentCart() → { lineItems: [...] }
83
+ lineItem = { quantity, price: { amount }, image } // amount is the string price; image is wix:image:// → resolve
84
+
85
+ // currentCart.createCheckoutFromCurrentCart({ channelType }) → { checkoutId } // a STRING — NOT { checkout }, NOT _id
86
+ // redirects.createRedirectSession({ ecomCheckout: { checkoutId }, callbacks }) → { redirectSession: { fullUrl } }
87
+ ```
88
+
89
+ ---
90
+
91
+ ## The features (build the ones the site needs)
92
+
93
+ Each subsection is a **self-contained feature** — implement only what the site uses. The only ordering is *within* a feature (read the operation before you build a cart line that references it).
94
+
95
+ ### Reading the ordering operation (and the `_id` rule)
96
+
97
+ Get the operation so you have the **`operationId`** every cart line needs. `listOperations()` takes **no arguments** and returns a plain array.
98
+
99
+ ```js
100
+ const { operations: ops = [] } = await operations.listOperations();
101
+ const op = ops.find((o) => o.onlineOrderingStatus === 'ENABLED') ?? ops.find((o) => o.default) ?? ops[0];
102
+ const operationId = op._id;
103
+ ```
104
+ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/operations/list-operations.md?apiView=SDK>
105
+
106
+ **⚠️ `list*` return the array as OPTIONAL — default it (`= []`) or the strict build fails.** `listOperations()` / `listFulfillmentMethods()` / `listItems()` type the returned array as `T[] | undefined`, so destructuring `{ operations: ops }` and calling `ops.find(...)` directly errors under `strict` / `astro check` (`'ops' is possibly 'undefined'`, TS18048) — a build-breaker on managed Astro. Default it in the destructure (`{ operations: ops = [] }`) or guard with `?? []`.
107
+
108
+ **⚠️ CRITICAL: the entity id is `_id`, NOT `id`.** `operation.id` / `item.id` / `section.id` are `undefined` in SDK code. Feeding `operation.id` into `options.operationId` (or `item.id` into `catalogItemId`) sends an empty string and the add-to-cart fails. Use `_id` everywhere. (A surprising `id` field means you're reading the REST doc view — re-open with `?apiView=SDK`.)
109
+
110
+ **Read the operation once and reuse `operationId`** for every add-to-cart on the page — don't call `listOperations()` per line item.
111
+
112
+ ### Reading fulfillment methods (pickup / delivery to show)
113
+
114
+ List the methods to render the visitor's pickup/delivery choices (fee, minimum, type). Show only `enabled: true`.
115
+
116
+ ```js
117
+ const { fulfillmentMethods: methods = [] } = await fulfillmentMethods.listFulfillmentMethods();
118
+ const offered = methods.filter((m) => m.enabled);
119
+ ```
120
+ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/fulfillment-methods/list-fulfillment-methods.md?apiView=SDK> (default the array `= []` — see the optional-array note above)
121
+
122
+ **⚠️ `listFulfillmentMethods` takes an OPTIONS object, not a query builder.** It accepts an optional `{ paging }` object and returns a `Promise` of `{ fulfillmentMethods }` directly — do **not** chain `.find()`/`.eq()` on it (that's the `queryProducts` builder pattern from stores; it does not apply here). `fee` and `minOrderPrice` are decimal **strings** — render them with your currency, and `Number(...)` only if you need arithmetic. Which method the buyer actually uses (and the delivery time slot) is chosen on the **hosted checkout**; listing here is for display, not a required pre-checkout step.
123
+
124
+ ### Adding a menu item to the cart — the restaurant `catalogReference`
125
+
126
+ This is the one shape that differs materially from a Stores cart. Build a line item whose `catalogReference` names the **Orders app** and carries **`operationId` + `menuId` + `sectionId`** in `options`.
127
+
128
+ ```js
129
+ await currentCart.addToCurrentCart({
130
+ lineItems: [{
131
+ quantity,
132
+ catalogReference: {
133
+ catalogItemId: item._id, // the MENU ITEM's _id
134
+ appId: '9a5d83fd-8570-482e-81ab-cfa88942ee60', // the ORDERS app id (not Stores)
135
+ options: {
136
+ operationId, // from listOperations()
137
+ menuId, // the _id of the menu the item is shown in
138
+ sectionId, // the _id of the section the item is shown under
139
+ },
140
+ },
141
+ }],
142
+ });
143
+ ```
144
+ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart/add-to-current-cart.md?apiView=SDK>
145
+
146
+ **⚠️ CRITICAL: `options` MUST carry `operationId`, `menuId`, AND `sectionId` — all three.** A restaurant line item is identified by *where in the menu it was ordered from*, not by a variant. Omitting any of the three makes eCommerce unable to resolve the item against the Restaurants catalog — the add fails or the line can't be checked out. This is the restaurant analog of the store's mandatory `variantId`, and it fails the same quiet way.
147
+
148
+ **⚠️ CRITICAL: there is NO `variantId` here.** `options.variantId` is a **Stores** concept — menu items have no variants in this flow. Don't copy the store recipe's `variantId` resolution; a restaurant line uses `operationId`/`menuId`/`sectionId` instead. (Menu item *modifiers* — "extra cheese" — are a separate concern and out of scope for the basic order flow.)
149
+
150
+ **⚠️ Thread `sectionId` and `menuId` from the render, not a lookup.** When you assemble the menu (menu → sections → items, per `how-to-code-restaurants.md`), each item is rendered **inside** a known section and menu — capture that `section._id` and `menu._id` at render time and pass them to the add-to-cart handler alongside `item._id`. Re-deriving an item's section afterward is unnecessary and error-prone; you already have it in the render context.
151
+
152
+ Optional: `options.onlineOrderingPageUrl` (e.g. `"/online-ordering"`) lets the buyer click the cart line to return to the item — include it only if your site has such a page.
153
+
154
+ ### Checkout
155
+
156
+ Create a checkout from the current cart, then redirect the buyer to the hosted checkout (identical to the storefront flow — restaurant orders ride on the same eCommerce checkout).
157
+ Docs: <https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart/create-checkout-from-current-cart.md?apiView=SDK> · <https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart/get-current-cart.md?apiView=SDK>
158
+
159
+ ```js
160
+ const checkout = await currentCart.createCheckoutFromCurrentCart({ channelType: currentCart.ChannelType.WEB });
161
+ const session = await redirects.createRedirectSession({
162
+ ecomCheckout: { checkoutId: checkout.checkoutId }, // checkout.checkoutId — NOT checkout._id
163
+ callbacks: { postFlowUrl: `${origin}/`, thankYouPageUrl: `${origin}/` },
164
+ });
165
+ window.location.href = session.redirectSession.fullUrl; // the hosted-checkout URL (fulfillment + time slot chosen here)
166
+ ```
167
+
168
+ **⚠️ `createCheckoutFromCurrentCart` returns `checkout.checkoutId`** (a string), **not** `checkout._id`. Reading `checkout._id` (over-applying the `_id` rule) throws *"Cannot read properties of undefined (reading '_id')"* — the silent checkout crash. The `_id` rule is for read **entities**, not method-return wrappers.
169
+
170
+ **⚠️ CRITICAL: `origin` for `postFlowUrl`/`thankYouPageUrl` MUST be the `https://` published host — derive it from `window.location.origin`, NEVER `new URL(request.url).origin`.** The Headless redirect allowlist registers the site's **`https://`** host and treats `http://<same host>` as a different, unlisted origin. If you build the redirect session in a **server route** (`src/pages/api/*`), `new URL(request.url).origin` resolves to `http://` behind Wix's TLS-terminating proxy → the buyer's return redirect **403s** with *"… isn't listed as an allowed redirect domain."* Pass `window.location.origin` from the client into the route (or force the scheme to `https`). Doc: <https://dev.wix.com/docs/go-headless/getting-started/setup/manage-urls/add-allowed-redirect-domains>.
171
+
172
+ **⚠️ LIVE PAID CHECKOUT PRECONDITION.** A visitor can add to cart and reach the hosted checkout with just this code, but **completing a paid order** needs the site to have a **premium plan and a configured payment method**. That's site provisioning, not a frontend bug — if checkout can't collect payment, the setup is incomplete, not the code.
173
+
174
+ ### Reading the price and the cart
175
+
176
+ Menu item price is **`item.priceInfo.price`** — a decimal **string** with no currency symbol (see `how-to-code-restaurants.md`; the SDK `PriceInfo` type has only `price`, no `formattedPrice`). A cart line's price is **`lineItem.price.amount`** (also a string). Prefix your own currency in the UI. Cart line images are `wix:image://` identifiers — resolve them with `media.getScaledToFillImageUrl(id, w, h, {})` (4 args), never raw (see the store/menu recipes' image callout).
177
+
178
+ ---
179
+
180
+ ## Conclusion
181
+ A correct Restaurants ordering frontend:
182
+ - imports **`operations` / `fulfillmentMethods` from `@wix/restaurants`** (plus `menus`/`sections`/`items` for display) and **`currentCart` / `redirects`** from `@wix/ecom` / `@wix/redirects`;
183
+ - uses **`_id`** (never `id`) for the operation, menu, section, and item;
184
+ - reads the **`operationId`** once from `listOperations()` (the `ENABLED`/`default` operation) and reuses it;
185
+ - adds menu items with the **Orders app id `9a5d83fd-…`** (never the Stores id) and `options` carrying **`operationId` + `menuId` + `sectionId`** (all three) — **no `variantId`**;
186
+ - threads `menuId`/`sectionId` from the render context, not a re-lookup;
187
+ - checks out via **`createCheckoutFromCurrentCart` → `checkout.checkoutId` → `redirects`**, with an **`https://` `window.location.origin`** for the callbacks; the hosted checkout collects fulfillment + payment (which needs a premium plan + payment method).
188
+ </content>