Skip to main content
A dynamic pricing bundle lets customers build their own selection from an eligible set, and applies a discount to the running total — either a percentage (e.g. “15% off when you bundle any 3”) or a fixed amount (e.g. “$10 off your build-your-own box”). Unlike a fixed pricing bundle, the total scales with what the customer picks; the discount is layered on top. This guide covers running a dynamic pricing bundle from your own storefront UI. You use the same metafield, cart-line attributes, and Shopify Function that Appstle’s built-in widget uses — only the UI is yours. Read the headless overview first for the automatic-discount model.
bundleTypeCLASSIC_BUILD_A_BOX
Read fromappstle_bundles.bundle_rules shop metafield
Discount applied byAppstle automatic discount (Shopify Function)

How pricing works

The rule defines a discountType and discountValue, plus optional selection gates:
discountTypeDiscount on the eligible subtotal
PERCENTAGEsubtotal × (discountValue / 100) off
FIXED_AMOUNTdiscountValue off
NO_DISCOUNTNo discount (the bundle exists purely for grouping).
The discount applies only when the selection satisfies the configured gates:
  • Item count between minProductCount and maxProductCount.
  • Selection value between minOrderAmount and maxOrderAmount.
So the customer-facing math is:
subtotal       = Σ (line price × quantity)        // eligible bundle lines
discount       = PERCENTAGE → subtotal × value/100
                 FIXED_AMOUNT → value
customer pays  = subtotal − discount               // when gates are satisfied
Appstle’s Function evaluates this and applies the discount automatically once the lines carry the bundle attributes.

Step 1 — Read the rule from the metafield

Read the active rules from the appstle_bundles.bundle_rules shop metafield — the Shopify-native source, with no call to Appstle’s servers (see Reading bundle configuration). Filter to dynamic pricing bundles by bundleType.
{% assign bundle_rules = shop.metafields.appstle_bundles.bundle_rules.value %}
<script>window.MY_BUNDLE_RULES = {{ bundle_rules | json }};</script>
const rules = window.MY_BUNDLE_RULES || [];

const dynamicBundles = rules.filter(
  r => r.bundleType === 'CLASSIC_BUILD_A_BOX' && r.status === 'ACTIVE'
);
Fields you need:
FieldUse
uniqueRefAttach to every cart line as _appstle-bb-id.
nameDisplay label; attach as __appstle-bb-name.
discountTypePERCENTAGE, FIXED_AMOUNT, or NO_DISCOUNT.
discountValueThe percentage or fixed amount.
minProductCount / maxProductCountItem-count gate.
minOrderAmount / maxOrderAmountSelection-value gate.
products / variantsEligible products and variants (JSON).

Step 2 — Render your selector

Build your own selector and gate the “add to cart” action on the rule:
  • Allow only eligible variants.
  • Track the running item count and subtotal; enable checkout only when both gates are satisfied.
  • Show the live discounted total using the preview formula.

Step 3 — Add the bundle to the cart

Add the selected variants with the bundle attributes. For a dynamic pricing bundle, _appstle_bundles_type is CLASSIC_BUILD_A_BOX.
const line = (variantId, quantity, sellingPlanId = null) => ({
  id: variantId,
  quantity,
  ...(sellingPlanId ? { selling_plan: sellingPlanId } : {}),
  properties: {
    '_appstle-bb-id': bundle.uniqueRef,
    '_appstle_bundles_type': 'CLASSIC_BUILD_A_BOX',
    '__appstle-bb-name': bundle.name,
  },
});

await fetch(`${Shopify.routes.root}cart/add.js`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ items: selection.map(s => line(s.variantId, s.quantity)) }),
});
mutation AddDynamicBundle($cartId: ID!) {
  cartLinesAdd(
    cartId: $cartId
    lines: [
      {
        merchandiseId: "gid://shopify/ProductVariant/123"
        quantity: 2
        attributes: [
          { key: "_appstle-bb-id", value: "byo-box-ref" }
          { key: "_appstle_bundles_type", value: "CLASSIC_BUILD_A_BOX" }
          { key: "__appstle-bb-name", value: "Build Your Own Box" }
        ]
      }
    ]
  ) {
    cart { id }
    userErrors { field message }
  }
}
Dynamic pricing bundles support subscriptions. If the selection uses a subscription purchase option, pass the chosen selling_plan on the line (Ajax) or sellingPlanId on the merchandise line (Storefront API), exactly as you would for any subscription line item.

Step 4 — Discount applies automatically

When the lines carrying the attributes satisfy the gates, Appstle’s automatic discount applies the percentage or fixed amount in cart and checkout. If the selection later drops below the gate (e.g. an item is removed), the Function stops applying the discount — your UI does not need to manage this.

Previewing the discount

Mirror the Function’s math for a live preview (cart prices are in cents):
function previewDynamicDiscount(bundle, selection) {
  const subtotalCents = selection.reduce((s, i) => s + i.priceCents * i.quantity, 0);
  const count = selection.reduce((n, i) => n + i.quantity, 0);

  // Gates — the discount only applies when these pass.
  const countOk =
    (bundle.minProductCount == null || count >= bundle.minProductCount) &&
    (bundle.maxProductCount == null || count <= bundle.maxProductCount);
  const amountOk =
    (bundle.minOrderAmount == null || subtotalCents >= bundle.minOrderAmount * 100) &&
    (bundle.maxOrderAmount == null || subtotalCents <= bundle.maxOrderAmount * 100);
  if (!countOk || !amountOk) return { subtotalCents, discountCents: 0, totalCents: subtotalCents };

  let discountCents = 0;
  if (bundle.discountType === 'PERCENTAGE') {
    discountCents = Math.round(subtotalCents * (bundle.discountValue / 100));
  } else if (bundle.discountType === 'FIXED_AMOUNT') {
    discountCents = Math.round(bundle.discountValue * 100);
  }
  discountCents = Math.min(discountCents, subtotalCents);
  return { subtotalCents, discountCents, totalCents: subtotalCents - discountCents };
}
This preview is display-only. The Shopify Function is authoritative at checkout; sourcing the preview from the same discountType / discountValue / gate fields keeps them in sync.

Checklist

1

Bundle is Active in the Appstle admin

2

Selection satisfies count and amount gates before checkout

3

Every line carries _appstle-bb-id and _appstle_bundles_type = CLASSIC_BUILD_A_BOX

4

Verify the discounted total in the cart matches your preview

Next steps

Volume discount bundle

Tiered “buy more, save more” discounts.

Fixed pricing bundle

Sell a curated set for one fixed price.