Show offers on product pages

Wire Mulberry warranty offers into your Shopify product pages — fetch an offer with the SDK, render it, and attach the chosen plan to the cart.

This guide gets a Mulberry warranty offer onto your Shopify product detail pages (PDPs), so customers can add protection alongside the product they're buying.

On Shopify this is a wiring job in your theme: you load the Mulberry SDK, fetch an offer for the current product, render it, and attach the chosen plan to the cart. That's how nearly every store running Mulberry on Shopify does it, and it's what this page walks through. (An automatic placement mode exists for eligible accounts — see the note at the end.)

Prerequisites

  • The Mulberry Product Protection app is installed and Mulberry has configured pricing for your account. See Install the Shopify app.
  • Your public token (Get your credentials).
  • Edit access to the storefront's <head> and PDP template.

Wire the offer into your theme

You'll load the SDK, fetch an offer, render it, and attach the chosen plan to the Shopify cart.

One idea drives every step here: the warranty must land in the Shopify cart as a line item carrying the right properties (_warranty_offer_id, _variantId, and friends) — that's what the app's orders/create webhook reads to register the sale automatically. Get the cart attach right and registration takes care of itself. See Register the sale.

This page covers only the Shopify-specific glue — scripts, events, and the mulberryShop.* cart helpers. The SDK contract itself (signatures, the offer object, rendering options) lives in the Mulberry SDK reference.

🚧

Pass Shopify's price through unchanged

Give mulberry.core.getWarrantyOffer Shopify's price value exactly as Shopify provides it (a decimal string like "2999.00") — do not convert it to cents.

1. Load and initialize

Add the adapter and SDK scripts to your storefront's <head>, set your public token on a global before they run, and hang all Mulberry code off the mulberry-shopify:loaded event — calling mulberry.* earlier is not safe.

<!-- Production (staging: ottawa-staging.getmulberry.com / staging.getmulberry.com) -->
<script src="https://ottawa.getmulberry.com/app/adapter.js"></script>
<script src="https://getmulberry.com/plugin/static/js/mulberry.js"></script>
window.mulberryConfig = {
  publicToken: 'YOUR_PUBLIC_TOKEN', // required on headless; app-installed stores can fall back to the app proxy
}

document.addEventListener('mulberry-shopify:loaded', () => {
  init() // your setup, below
})

You should now be able to read mulberry.core.settings and call the SDK inside your handler. Confirm the script hosts against your dashboard configuration before publishing — accounts can be provisioned on different hosts.

2. Fetch an offer for the current product

Use the variant id as id, Shopify's price unchanged, and the raw Shopify product JSON as detail — the helper mulberryShop.getProductData(url) fetches it for you.

async function init() {
  const { product } = await mulberryShop.getProductData(mulberryShop.getProductUrl())

  const offers = await mulberry.core.getWarrantyOffer({
    id: product.variants[0].id,        // the VARIANT id, not the product id
    title: product.title,
    price: product.variants[0].price,  // Shopify's value, passed through unchanged
    detail: product,                   // the raw Shopify product JSON
  })
  // offers = array of offer objects; [] when there's no coverage
}

An empty array usually means the product isn't covered — or, on a new account, that pricing isn't configured yet (see Install the Shopify app).

3. Render the offer

Render with the SDK's inline and/or modal namespaces, passing mulberry.core.settings. A common pattern: an inline selector on the PDP, and an intercepted "Add to cart" submit that opens the modal when no plan is selected.

// Add <div id="mulberry-inline"></div> to your PDP template first
const inline = mulberry.inline.init({
  offers,
  settings: mulberry.core.settings,
  selector: '#mulberry-inline',
  onWarrantyToggle: ({ offer, isSelected }) => {
    selectedOffer = isSelected ? offer : null
  },
})

Full config — callbacks, layout, and instance methods like updateOffer — is in the SDK reference.

4. Attach the selected plan to the cart

This is the load-bearing step. Resolve the warranty's Shopify variant, then add it to the cart tied to the product it protects:

// 1. Resolve the Shopify variant + selling plan for the chosen offer
const { shopify_variant_id, selling_plan_id } =
  await mulberryShop.getWarrantyVariant(selectedOffer)

// 2. Add the warranty to the Shopify cart
await mulberryShop.addWarrantyToCart(
  shopify_variant_id,
  { title: product.title, variantId: product.variants[0].id }, // the PROTECTED product
  selectedOffer,
  selling_plan_id,  // pass this for selling-plan / subscription plans
)

addWarrantyToCart POSTs the warranty variant to /cart/add.js with the line-item properties Mulberry's registration reads — _warranty_offer_id, _variantId, _product, _product_price, and _warranty_duration_months. You should now see the plan in the cart as a line item carrying those properties.

🚧

Always pass the protected product's variantId

addWarrantyToCart records it as the _variantId property (camelCase, leading underscore). Without it, the plan can't be matched back to its product — and cart reconciliation below can't work. If you build the line item by hand, use the exact keys _variantId and _warranty_offer_id.

5. Keep the cart in sync

Two situations to handle after the initial render:

Variant changes — the adapter dispatches mulberry-shopify:variant-change on document. Re-fetch (the price may differ) and update your components:

document.addEventListener('mulberry-shopify:variant-change', async (e) => {
  const { product } = await mulberryShop.getProductData(mulberryShop.getProductUrl())
  const variant = product.variants.find((v) => String(v.id) === String(e.detail.variantId)) || product.variants[0]

  const offers = await mulberry.core.getWarrantyOffer({
    id: variant.id, title: product.title, price: variant.price, detail: product,
  })
  inline.updateOffer(offers) // and/or modal.updateOffer(offers)
})

Cart mutations — if a customer removes a covered product, its warranty is left orphaned. After any cart change, pass the current cart (from /cart.js) to the cleanup helper:

const updatedCart = await mulberryShop.removeDetachedWarranty(cart)
// drops orphaned/duplicate warranties and caps quantities to the matched product

Automatic placement (by arrangement)

Mulberry also has an automatic placement mode, where the app's injected script finds your theme's "Add to cart" button and places the offer with no theme edits — plus a variant that uses CSS selectors you supply instead of auto-detection. It's enabled per account by Mulberry and suits a limited set of themes. If you'd rather try it than wire the SDK, ask your Partner Success manager whether your store qualifies; offers, carts, and registration behave the same either way.

Next steps