Kirby CMS
WireGum widget for a Kirby product page
Kirby can keep rendering the product page, while WireGum owns sellable variants, stock, checkout creation, order webhooks, shipping, and emails.
1. Add the widget script
Add this in the shared Kirby layout header. The storefront domain must also be authorized in WireGum Storefront.
<script
async
src="https://app.example.com/api/storefront/widget"
data-wiregum-organization="workspace-slug"
data-wiregum-locale="<?= kirby()->language()?->code() ?? 'en' ?>"
></script>2. Add a buy box to your product template
<div
data-wiregum-product="<?= $product->wiregumProductId()->esc() ?>"
lang="<?= kirby()->language()?->code() ?? 'en' ?>"
></div>The empty element lets WireGum render a default buy box. If the Kirby template needs full control over markup, add the data attributes to your own product card.
<article
data-wiregum-product="<?= $product->wiregumProductId()->esc() ?>"
lang="<?= kirby()->language()?->code() ?? 'en' ?>"
>
<h1><?= $product->title()->esc() ?></h1>
<?= $product->image()?->crop(900, 1100) ?>
<p><?= $product->description()->esc() ?></p>
<div data-wiregum-price></div>
<div data-wiregum-variant-selector></div>
<label>
<span>Quantity</span>
<input data-wiregum-quantity type="number" min="1" value="1">
</label>
<button data-wiregum-add-to-cart type="button">Add to cart</button>
</article>
<div data-wiregum-country-selector></div>
<div data-wiregum-cart></div>3. Render add-to-cart buttons in a product grid
A listing page can show multiple WireGum buttons before the site has internal product pages. Keep the WireGum public product ID in each Kirby product page and render it asdata-wiregum-product. Slugs are still supported, but the public ID survives slug changes.
<?php
$products = page('shop')
->children()
->listed()
->filterBy('wiregumProductId', '!=', '');
?>
<section class="product-grid" lang="<?= kirby()->language()?->code() ?? 'en' ?>">
<?php foreach ($products as $product): ?>
<article data-wiregum-product="<?= $product->wiregumProductId()->esc() ?>">
<?php if ($image = $product->image()): ?>
<img src="<?= $image->crop(600, 760)->url() ?>" alt="<?= $product->title()->esc() ?>">
<?php endif ?>
<h2><?= $product->title()->esc() ?></h2>
<div data-wiregum-price></div>
<button data-wiregum-add-to-cart type="button">Add to cart</button>
</article>
<?php endforeach ?>
</section>
<div data-wiregum-cart-count></div>
<div data-wiregum-country-selector></div>
<div data-wiregum-cart></div>For products with variants, keep data-wiregum-variant-selector in the card or pass a fixed WireGum public variant ID on the button when the Kirby field already selects one.
<button
data-wiregum-product="<?= $product->wiregumProductId()->esc() ?>"
data-wiregum-variant="<?= $product->wiregumVariantId()->esc() ?>"
data-wiregum-add-to-cart
type="button"
>
Add selected edition
</button>The cart, cart count, checkout button, and shipping country selector are shared across the page. Keep the country selector visible when shipping rates depend on country.
4. Test the API directly
curl https://app.example.com/api/storefront/checkout \
-H "Origin: https://shop.example.com" \
-H "Content-Type: application/json" \
-d '{
"organizationSlug": "workspace-slug",
"locale": "it",
"items": [
{ "variantId": "wg_variant_ref", "quantity": 1 }
]
}'Current API contract
GET /api/storefront/products returns public catalog data for authorized origins.POST /api/storefront/checkout creates a Stripe Checkout Session.POST /api/stripe/webhook receives Stripe fulfillment events.