Foundaro Spokes Docs
Store
The Store module turns any Front site into a fully functional e-commerce storefront. A single site can host multiple stores (e.g. a consumer shop and a B2B wholesale store). Manage products, categories, and orders in the Hub; customers browse and purchase through your branded Twig theme.
Last updated:
Store
The Store module adds e-commerce functionality to any Front site. You manage products and categories through the Foundaro Hub, and customers experience a fully branded shop on your site.
Multiple Stores per Site
A single Front site can host multiple independent stores. Common use cases include running a consumer retail shop and a B2B wholesale store on the same domain, or separating product lines into distinct storefronts.
Store List
Navigate to Stores in the sidebar to see all stores for your organisation. The list supports card, list, and table views and can be filtered by name or sorted by creation date.
Click New Store to create a store. You will be asked for a name, a slug, and which site to attach it to.
Store Slugs
Each store has a unique slug (URL-friendly identifier, e.g. wholesale). The slug determines the store's public URL:
/site/:siteSlug/shop/:storeSlug/
Slugs must be unique within your organisation and can only contain lowercase letters, numbers, and hyphens.
Store Picker
When a site has more than one store, visiting /site/:siteSlug/shop shows a store picker page where customers can choose which storefront to enter. Each store card displays the store name, description, and type badge.
The store picker is theme-aware and renders using the site's active Twig theme.
Legacy URLs
The legacy /shop/ path (without a store slug) continues to work. Requests to /site/:siteSlug/shop/products resolve to the site's primary store — the first store attached to the site. No links need to be updated.
Store Dashboard
The Store Dashboard is the entry point for all commerce activity within a specific store. It shows:
- Products — total product count with a link to the product list.
- Categories — total category count.
- Orders — total order count (updated in real time when Stripe webhooks are received).
- Currency — the store's configured currency.
If no store exists yet for the selected site, the dashboard shows a setup prompt. Only Admins can create a store.
Each store inherits the site's Twig theme — no separate design work required.
Products
Products are the items customers can browse and purchase. Navigate to Store › Products to see the full product list.
The list supports three view layouts (cards, list, table) and can be filtered by:
- Status — Draft, Published, or Archived.
- Search — by product name or slug.
Each product card shows the featured image (or a placeholder), name, status badge, variant count, and starting price.
Hover over a card to reveal edit and delete action buttons.
Creating a Product
Click New Product in the header. Enter a name and slug in the dialog, then click Create — you will be taken directly to the product editor.
Product Editor
The product editor has three tabs:
Details tab
| Field | Description |
|---|---|
| Name | Display name shown to customers. |
| Slug | URL-friendly identifier. Used in the storefront URL: /shop/:storeSlug/products/:slug. |
| Short Description | One-line summary shown in listing cards. |
| Description | Full rich description shown on the product detail page. |
| Product Type | Physical, Digital, or Service. Controls shipping requirement logic. |
| Tags | Comma-separated labels for filtering and search. |
| Taxable | Whether the product is subject to tax (used in future tax calculations). |
| Requires Shipping | Disable for digital downloads or services. |
Variants tab
Each product must have at least one variant to be purchasable. A variant represents a specific sellable configuration (e.g., "Small / Red").
| Variant field | Description |
|---|---|
| Name | Describes the variant combination: "Small", "Large / Blue". |
| Price | Price in the smallest currency unit (cents for USD). |
| Compare At | Optional original price shown struck through to indicate a discount. |
| SKU | Optional stock-keeping unit code for fulfilment. |
Click Add Variant to create additional variants. Variants can be deleted individually.
SEO tab
Override the default meta title and meta description for the product detail page.
Status
Products have three statuses:
- Draft — not visible in the storefront.
- Published — live and browsable by customers.
- Archived — hidden from the storefront; kept for record-keeping.
Change the status from the dropdown in the editor topbar. The change is saved immediately.
Product Categories
Categories organise your products into navigable groups. Navigate to Store › Categories.
Each category has:
- Name — displayed in the storefront navigation.
- Slug — used in the category URL:
/shop/:storeSlug/categories/:slug. - Description — optional text shown on the category page.
- Parent — optional parent category for nested hierarchies.
- Weight — controls display order (lower = first).
Click any row to open the inline edit dialog. Use the delete button to remove a category — products in the deleted category become uncategorised.
Import and Export
Both Products and Categories support JSON import and export for bulk data operations.
Export: Click Import / Export → Export JSON in the list header. A .json file is downloaded containing all records for the current store.
Import: Click Import / Export → Import JSON and select a JSON file. Records are upserted by slug — existing records are updated, new records are created.
Product import supports both JSON and CSV formats.
Customer Cart
The cart is server-side and persists across sessions for 30 days. It is managed via the public REST API:
| Endpoint | Description |
|---|---|
POST /api/public/front/sites/:slug/cart |
Create a new cart. Returns { cartId }. |
GET /api/public/front/sites/:slug/cart/:cartId |
Get cart contents with computed totals. |
POST ...cart/:cartId/items |
Add item { variantId, quantity }. |
PATCH ...cart/:cartId/items/:itemId |
Update item quantity. Set to 0 to remove. |
DELETE ...cart/:cartId/items/:itemId |
Remove a specific item. |
DELETE ...cart/:cartId |
Clear all items. |
POST ...cart/:cartId/checkout |
Create a Stripe Checkout session. |
The cart response includes computed subtotal, shippingAmount, discountAmount, and total fields (all in cents).
The Alpine.js storefront JavaScript manages the cart cookie and calls these endpoints automatically.
Checkout and Payments
Checkout uses Stripe Checkout (hosted redirect). When a customer clicks Checkout:
- The server creates a Stripe Checkout session with the cart's line items.
- The customer is redirected to Stripe's hosted checkout page.
- On payment, Stripe sends a webhook to
/api/public/front/stripe/webhook. - The server creates an
Orderrecord and sends confirmation emails. - The customer is redirected back to the success URL.
Requirements:
- Set
STRIPE_SECRET_KEYin your server environment variables. - Configure the Stripe webhook endpoint in your Stripe dashboard pointing to your server's public URL.
Stripe Connect is used so payouts go directly to the organisation's bank account. No platform fee is applied.
Stripe Connect — merchants connect their own Stripe account via OAuth. Payments go directly to the merchant with a 2% platform fee. Connect from Store Settings > Stripe Payments.
Store Settings
Navigate to Store › Settings to configure the store.
General
| Setting | Description |
|---|---|
| Store Name | Displayed in the storefront header and Stripe checkout. |
| Slug | URL-friendly identifier used in storefront URLs. Unique per organisation. |
| Description | Optional store description (shown on the store picker page). |
| Currency | ISO 4217 currency code (e.g., usd, eur, gbp). |
| Store Type | Consumer (default) or B2B. Switching to B2B unlocks wholesale settings. |
Shipping
| Setting | Description |
|---|---|
| Enable Shipping | Toggle whether shipping costs are applied at checkout. |
| Flat Rate | A fixed shipping cost in cents charged on all orders. |
| Free Shipping Above | Orders over this subtotal (in cents) qualify for free shipping. |
B2B Wholesale Settings
Available when Store Type is set to B2B.
| Setting | Description |
|---|---|
| Minimum Order Amount | Minimum cart subtotal (in cents) required before a customer can proceed to checkout. |
| Net Payment Terms | Display payment terms at checkout — None, Net 15, Net 30, Net 45, or Net 60. |
| Enable Quote Requests | Show a "Request a Quote" button alongside (or instead of) the Checkout button. Submitted quotes are logged as orders with status QUOTE. |
| Account-Based Pricing | Hide public prices and require the customer to be logged in and assigned an account price tier. |
Customer Groups — create named pricing tiers (e.g. "Wholesale", "VIP") with discount percentages. Assign customers to groups for automatic price adjustments at checkout.
PO Numbers — B2B customers can enter a purchase order number at checkout. PO numbers appear on order details and exports.
Customer Approval — when "Require approval for new customers" is enabled, new registrations are set to Pending status. Customers can browse but cannot place orders until approved by an admin.
Roles and Permissions
| Action | Required Role |
|---|---|
| View products and categories | CONTRIBUTOR |
| Create products | CONTRIBUTOR |
| Edit products / add variants | AUTHOR |
| Delete products / manage variants | AUTHOR |
| Manage categories | AUTHOR |
| Import / Export | EDITOR |
| Manage store settings | EDITOR |
| Create or delete store | ADMIN |
Orders
Navigate to Store › Orders to manage customer orders.
The order list shows all orders across all statuses with:
- Order number — sequential number within the store.
- Customer — name and email from the Stripe checkout.
- Status — current fulfilment status with colour coding.
- Total — order total in the store's currency.
Click View on any row to open the order detail page.
Order Detail
The order detail page shows:
- Line items — product name, variant, price, quantity, and subtotal.
- Order totals — subtotal, shipping, discounts, and grand total.
- Customer — name, email, phone, and shipping address captured at checkout.
- Status transitions — use the dropdown to advance the order through the fulfilment workflow:
| From | Valid next statuses |
|---|---|
| PENDING | PAID, CANCELLED |
| PAID | PROCESSING, CANCELLED |
| PROCESSING | SHIPPED, CANCELLED |
| SHIPPED | DELIVERED |
| CANCELLED | REFUNDED |
- Tracking number — enter and save a carrier tracking number. Setting status to SHIPPED automatically records a timestamp.
- Internal notes — private notes not visible to the customer.
Public Storefront
The Store module includes a fully server-rendered storefront. The canonical URL pattern includes the store slug:
| Path | Description |
|---|---|
/site/:siteSlug/shop |
Store picker (multiple stores) or store home (single store). |
/site/:siteSlug/shop/:storeSlug |
Store home — featured products and categories. |
/site/:siteSlug/shop/:storeSlug/products |
Product catalogue with search and category filter. |
/site/:siteSlug/shop/:storeSlug/products/:slug |
Product detail with variant selection and add-to-cart. |
/site/:siteSlug/shop/:storeSlug/categories/:slug |
Category page with filtered product grid. |
/site/:siteSlug/shop/:storeSlug/cart |
Shopping cart with live totals and checkout button. |
/site/:siteSlug/shop/:storeSlug/order-success |
Confirmation page after successful Stripe payment. |
Legacy routes (without :storeSlug) continue to work and resolve to the site's primary store.
Cart JavaScript
The storefront uses a self-contained vanilla JS module (/public/js/shop-cart.js) for cart interactivity. Cart state is persisted in localStorage and synced to the server-side cart API — no page reload is required to add or remove items.
The global window._shopCart object exposes helper methods used by the Twig templates. Cart interactions are wired via data-action attributes and delegated event listeners; no inline event handlers are needed.
Digital Downloads
Set productType to DIGITAL to sell downloadable files. For each variant, configure:
- Download URL — The direct URL to the file (e.g., an R2/S3 pre-signed URL).
- Download Limit — Optional cap on how many times a link can be used. Leave blank for unlimited.
After a successful Stripe checkout, the system automatically generates a secure download token (64-character hex, valid for 30 days) for each digital variant. Customers are redirected to /site/:siteSlug/shop/order-success which displays their download links.
Download tokens are accessible in the Hub under Store → Orders → [order] → Download Links.
Download API
| Route | Description |
|---|---|
GET /api/public/front/sites/:siteSlug/downloads/:token |
Validates token and redirects to file URL. Returns 410 Gone if expired, 403 Forbidden if limit reached. |
GET /api/front-downloads/order/:orderId |
List tokens for an order (authenticated, CONTRIBUTOR+). |
POST /api/front-downloads/tokens/:tokenId/regenerate |
Regenerate an expired/exhausted token (EDITOR+). |
Recurring Subscriptions
Set productType to SUBSCRIPTION to sell recurring billing plans. Once set, a Plans tab appears on the product edit page where you can create multiple billing tiers.
Subscription Plans
Each plan has:
| Field | Description |
|---|---|
| Name | Display name (e.g. "Monthly", "Pro Annual"). |
| Interval | MONTHLY, QUARTERLY (3-month), or ANNUAL. |
| Price | Amount in the store's currency (cents). |
| Trial Days | Optional free trial period before billing begins. |
Plans are lazily synced to Stripe: the first time a checkout is attempted for a given plan, a Stripe Price object is created and the stripePriceId is stored to avoid future duplication.
Subscriber Management
Navigate to Store → Subscriptions to see all subscribers with their status, plan, period dates, and trial end date. From this view you can cancel any active subscription (which also calls stripe.subscriptions.cancel() immediately).
Subscription lifecycle is kept in sync via Stripe webhooks (customer.subscription.created/updated/deleted), all routed through the existing /api/public/front/stripe/webhook endpoint.
Coupon Codes
Navigate to Store → Coupons to create and manage discount codes for your customers.
Coupon Types
| Type | Description |
|---|---|
| Percentage | Deducts a percentage of the subtotal (e.g., 10% off). Value is stored in basis points — 1000 = 10%. |
| Fixed Amount | Deducts a fixed amount in cents (e.g., 500 = $5.00 off). Capped at the order subtotal. |
| Free Shipping | Zeroes the shipping cost. |
Coupon Fields
| Field | Description |
|---|---|
| Code | Uppercase alphanumeric code shown to customers. Unique per store. |
| Min Order Amount | Minimum subtotal (in cents) required to use the coupon. |
| Max Uses | Total number of times this coupon can be used. Leave blank for unlimited. |
| Expires At | Date after which the coupon is no longer valid. |
| Active | Toggle to enable or disable the coupon without deleting it. |
Customers enter coupon codes on the cart page before checkout. The discount is reflected in the order summary before payment. Coupon usage counts are incremented when an order is fulfilled.
Tax Rules
Configure tax settings in Store → Settings → Tax.
| Setting | Description |
|---|---|
| Tax Rate (%) | Percentage applied to taxable products at checkout (e.g., 8.5 for 8.5%). Set to 0 to disable. |
| Prices Include Tax | When enabled, prices are treated as tax-inclusive and no tax is added at checkout. |
Tax is computed as: taxableSubtotal × taxRate ÷ 100, where taxableSubtotal is the total of all items where product.taxable = true, minus any coupon discount. The tax amount is shown as a separate line item on the cart summary.
For Stripe Checkout, tax is passed as an additional line item. For PayPal, it is included in the order breakdown.tax_total.
PayPal Checkout
Enable PayPal as an additional payment method in Store → Settings → PayPal.
- Create a PayPal Developer account at developer.paypal.com
- Create a REST API app to obtain a Client ID and Client Secret
- Enter these credentials in Store Settings
- Toggle Enable PayPal checkout — a "Pay with PayPal" button will appear on the cart page alongside the Stripe card button
Sandbox mode: Set PAYPAL_BASE_URL=https://api-m.sandbox.paypal.com in your server environment variables to use PayPal's sandbox. Remove this variable (or set it to https://api-m.paypal.com) for live payments.
PayPal orders are captured via the PayPal Orders API v2. On capture, an Order record is created with paymentMethod: PAYPAL. No Stripe involvement.
Upcoming Features
The following features are planned for future phases:
- Multi-currency display and automatic currency conversion
- Account-based pricing tiers for B2B customers (assignable price lists)
- Service and booking products with calendar integration