Start by marking subscription products in the admin with a custom metafield flag. Build a Shopify Function that reads this flag and rejects any cart containing both subscription and one-time items where the one-time total crosses $500. Layer a client-side banner in the cart to surface the rule before checkout. Use the Admin API to batch-set the subscription flag across the catalog.
A DTC merchant with a subscription-first product line cannot reliably fulfill orders that mix subscription and one-time purchases—especially when the one-time portion exceeds $500. The business needs a way to prevent these invalid mixed carts from reaching checkout so that customers understand the constraint upfront and orders ship on the intended cadence.
Four pieces
Checkout
Cart Validation Function
Blocks checkout and shows an error message if the cart contains subscription items mixed with one-time purchases totaling over $500.
query Input {
cart {
lines {
id
quantity
merchandise {
__typename
... on ProductVariant {
id
title
product {
id
title
handle
metafield(namespace: "custom", key: "is_subscription") {
value
}
}
}
}
cost {
totalAmount {
amount
}
}
attribute(key: "subscription_frequency") {
value
}
}
}
}
Metafield namespace is 'custom' and key is 'is_subscription'; set to true/false in product metafields. The attribute key 'subscription_frequency' is optional but helps identify subscription lines.
Products
Subscription Tagging Helper
A snippet that displays whether a product is flagged as a subscription item via its custom metafield, so the cart validation Function knows which products to watch.
This is read-only display; to actually set the metafield, use the Admin API mutation in piece four or edit directly in admin product editor.
Storefront
Checkout Error Banner
Renders a red warning banner at checkout explaining the mixed-cart constraint if the Function detects a violation, so customers know exactly why their order was blocked.
{% # Show this in your checkout or cart if mixed subscription/one-time detected %}
{% assign has_sub = false %}
{% assign has_onetime = false %}
{% assign onetime_total = 0 %}
{% for item in cart.items %}
{% if item.properties.subscription_frequency %}
{% assign has_sub = true %}
{% else %}
{% assign has_onetime = true %}
{% assign onetime_total = onetime_total | plus: item.price | times: item.quantity %}
{% endif %}
{% endfor %}
{% if has_sub and has_onetime and onetime_total > 50000 %}
<div style="background: #fee2e2; border: 1px solid #fecaca; padding: 12px 16px; border-radius: 6px; margin: 16px 0;">
<p style="margin: 0; color: #991b1b; font-weight: 500; font-size: 14px;">
⚠️ We can't mix subscription and one-time purchases above $500 in one order. Please split into separate carts or remove items.
</p>
</div>
{% endif %}
This detects the condition client-side; the Function is server-side enforcement. Use together for best UX.
Admin
Metafield Bulk Updater
A GraphQL mutation to batch-flag multiple products as subscription items at once, avoiding manual per-product edits in the admin.
Admin API script
Admin GraphQL explorergraphql
# Admin GraphQL
# Run this once per product or batch to set the subscription flag.
# Replace PRODUCT_ID with the actual product ID and set value to "true" or "false".
mutation SetSubscriptionFlag($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields {
id
namespace
key
value
}
userErrors {
field
message
}
}
}
# Variables (paste in Variables panel):
# {
# "metafields": [
# {
# "ownerId": "gid://shopify/Product/PRODUCT_ID_1",
# "namespace": "custom",
# "key": "is_subscription",
# "type": "boolean",
# "value": "true"
# },
# {
# "ownerId": "gid://shopify/Product/PRODUCT_ID_2",
# "namespace": "custom",
# "key": "is_subscription",
# "type": "boolean",
# "value": "true"
# }
# ]
# }
Replace PRODUCT_ID_1, PRODUCT_ID_2, etc. with real product IDs. Batch up to ~25 products per call to avoid rate limits.
Got a similar problem?
Sketch your build in 30 seconds — voice, type, or attach a screenshot.