# Woo Template Integration Contract

> The code-owned template, page, option, order confirmation, taxonomy archive, and route smoke contract for Woo starters.

# Woo Template Integration Contract

Woo components are not enough by themselves. A starter also has to own the Woo
routes that WordPress and WooCommerce resolve: product archive, single product,
cart, checkout, order confirmation, and any product taxonomy archives the
starter exposes.

This page is the public contract for that ownership.

## Required Route Ownership

| Route surface | Starter-owned content | Required slug or option | Notes |
| --- | --- | --- | --- |
| Product archive | Active-theme block template | `archive-product` | Use `Template::new()` and render a product loop. The default Woo shop route is usually `/shop/`. |
| Single product | Active-theme block template | `single-product` | Use `Template::new()` and current-product dynamic data such as `{this.id}` and `{this.omewoo.*}`. |
| Cart | Page content plus optional page template | `woocommerce_cart_page_id`, optionally `page-cart` | Assign a real cart page. A `page-cart` template should pass through page content or render the Woo cart block. |
| Checkout | Page content plus optional page template | `woocommerce_checkout_page_id`, optionally `page-checkout` | Assign a real checkout page. A `page-checkout` template should pass through page content or render the Woo checkout block. |
| Order confirmation | Active-theme block template or Woo-owned page content | `order-confirmation` and `order-received` flow | Keep Woo's order received lifecycle intact. Render Woo core order confirmation output for completed orders. |
| Product taxonomies | Active-theme taxonomy templates when customized | `taxonomy-product_cat`, `taxonomy-product_tag` | Use product-loop-aware markup. Do not let taxonomy archives fall back to a non-product post layout if the starter presents them as store routes. |

## Builder Shape

The minimum code-owned content shape is:

<CodeExample title="Demo builder route ownership shape" language="php">
{`Template::new()
    ->slug( 'archive-product' )
    ->title( 'Product Archive' )
    ->overwrite()
    ->blocks_markup( product_archive_blocks() )
    ->register();

Template::new()
    ->slug( 'single-product' )
    ->title( 'Single Product' )
    ->overwrite()
    ->blocks_markup( single_product_blocks() )
    ->register();

$cart_page_id = Page::new()
    ->slug( 'cart' )
    ->title( 'Cart' )
    ->overwrite()
    ->blocks_markup( cart_page_blocks() )
    ->register();

$checkout_page_id = Page::new()
    ->slug( 'checkout' )
    ->title( 'Checkout' )
    ->overwrite()
    ->blocks_markup( checkout_page_blocks() )
    ->register();

if ( is_int( $cart_page_id ) ) {
    update_option( 'woocommerce_cart_page_id', $cart_page_id, false );
}

if ( is_int( $checkout_page_id ) ) {
    update_option( 'woocommerce_checkout_page_id', $checkout_page_id, false );
}`}
</CodeExample>

Use `Page::new()` for cart and checkout because those are Woo page
settings. A starter can additionally own `page-cart` and `page-checkout`
templates, but those templates must not hide the assigned page content.

## Product Archive And Single Product

Use `Template::new()` for `archive-product` and `single-product` so the active
theme owns those Woo block templates.

Archive templates should render a product loop. Inside each loop item, use
`{item.*}` and `{item.omewoo.*}` values. Single product templates should use
`{this.*}` and `{this.omewoo.*}` values from the current product.

Read [Product Archive Flow](/woo/flows/product-archive) and
[Single Product Flow](/woo/flows/single-product) for component composition.

## Cart And Checkout Page Assignment

Woo still needs assigned pages. The starter must create or update page content
and write the Woo options:

| Option | Meaning |
| --- | --- |
| `woocommerce_cart_page_id` | Page Woo uses for `/cart/`, cart links, and cart redirects. |
| `woocommerce_checkout_page_id` | Page Woo uses for `/checkout/`, checkout redirects, and payment return fallbacks. |

The page slug can be `cart` and `checkout` for public starter sites.

If the starter also registers block templates for `page-cart` or
`page-checkout`, those templates should preserve page content:

<CodeExample title="Pass assigned page content through page templates" language="php">
{`Template::new()
    ->slug( 'page-cart' )
    ->title( 'Cart Page Template' )
    ->overwrite()
    ->blocks_markup( '<!-- wp:post-content /-->' )
    ->register();

Template::new()
    ->slug( 'page-checkout' )
    ->title( 'Checkout Page Template' )
    ->overwrite()
    ->blocks_markup( '<!-- wp:post-content /-->' )
    ->register();`}
</CodeExample>

If your starter has a helper such as `CoreBlock::post_content()`, use that for
the same pass-through behavior. If it does not, the raw core block markup is:
`<!-- wp:post-content /-->`.

## Woo Core Block Fallbacks

When a starter is not composing the cart or checkout with OhMyEtch Woo
components, use Woo core blocks inside the assigned page content:

<CodeExample title="Woo core fallback blocks" language="php">
{`$cart_blocks = '<!-- wp:woocommerce/cart /-->';

$checkout_blocks = '<!-- wp:woocommerce/checkout /-->';

$order_confirmation_blocks = '<!-- wp:woocommerce/order-confirmation /-->';`}
</CodeExample>

Do not mix a full OhMyEtch checkout form and the Woo checkout block in the same
visible checkout surface. Pick one owner for the checkout UI.

## Order Confirmation

The checkout Store API success path redirects into Woo's order received flow.
The route can include `order-received`, and payment gateways may also use
Woo-owned return URLs.

If the starter owns `order-confirmation`, render the Woo core order confirmation
block or pass through Woo-managed page content:

<CodeExample title="Order confirmation template" language="php">
{`Template::new()
    ->slug( 'order-confirmation' )
    ->title( 'Order Confirmation' )
    ->overwrite()
    ->blocks_markup( '<!-- wp:woocommerce/order-confirmation /-->' )
    ->register();`}
</CodeExample>

Do not replace this route with a generic static thank-you page. The customer
must land on a Woo-aware surface that can read the completed order context.

## Taxonomy Archive Considerations

Product taxonomy routes are store routes too. If the starter links to product
categories or tags, add product-aware taxonomy templates for `product_cat` and
`product_tag`.

The normal WordPress block template slugs are:

| Taxonomy | Template slug |
| --- | --- |
| Product categories | `taxonomy-product_cat` |
| Product tags | `taxonomy-product_tag` |

The template content should reuse the product archive loop shape rather than a
generic post archive card. It should render product purchase components only
inside a product loop context where `{item.omewoo.*}` is available.

## Starter Skill Smoke Recipe

A starter-building skill or generated recipe should explicitly mention every
required ownership slug before it claims Woo route integration is complete:

```text
Required starter route slugs:
- archive-product
- single-product
- page-cart
- page-checkout
- order-confirmation

Required assigned Woo page options:
- woocommerce_cart_page_id
- woocommerce_checkout_page_id
```

The smoke should fail if any slug is missing from the generated starter plan or
if cart and checkout pages are not assigned to Woo options.

## Route Smoke Checklist

After building a starter, run browser smoke checks against all customer-facing
routes:

| Route | What to verify |
| --- | --- |
| `/shop/` | Product archive renders product cards and add-to-cart controls. |
| `/product/.../` | Single product renders current product data, variation selectors when needed, and purchase controls. |
| `/cart/` | Cart page is the page stored in `woocommerce_cart_page_id`; cart rows and empty state render. |
| `/checkout/` | Checkout page is the page stored in `woocommerce_checkout_page_id`; checkout fields, payment selector, notices, order summary, and place-order flow render. |
| `order-received` | A completed checkout reaches a Woo order received or order confirmation surface with order-aware content. |

A production starter should expose equivalent route checks through its own smoke workflow.
