Tom Sailors
Brief · Anonymized case study

Blocking Mixed Subscription and One-Time Checkout

I'd build a cart-validation Function that runs at checkout and blocks orders when subscription items are mixed with one-time purchases above the threshold. The merchant would tag subscription products in their metafields, and I'd pair that with a client-side banner so customers understand why checkout failed and how to fix it.

A subscription-focused DTC merchant needed to enforce a fulfillment constraint: preventing customers from mixing subscription and one-time purchase items in the same cart when the one-time subtotal exceeds $500. The merchant's logistics couldn't handle fulfilling mixed subscriptions and high-value one-time orders together.
Four pieces
Checkout

Cart Validation Function

Blocks checkout and shows a message if the cart contains subscription items mixed with one-time purchases totaling over $500.

Shopify Function
extensions/checkout-validation/src/run.graphql function
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 tags each product metafield to flag whether it's a subscription item so the Function knows which to watch.

Theme extension
theme/snippets/subscription-flag.liquid liquid
{% # Add this to a product page section or app block to set/review subscription flags %}
{% if product.metafields.custom.is_subscription %}
  {% assign is_sub = product.metafields.custom.is_subscription.value %}
{% else %}
  {% assign is_sub = false %}
{% endif %}

<div class="subscription-flag" style="padding: 12px; background: #f5f5f5; border-radius: 4px; margin: 16px 0;">
  <p style="margin: 0; font-size: 14px; font-weight: 500;">
    Subscription Item: 
    {% if is_sub == 'true' or is_sub == true %}
      <span style="color: #0a7d3d;">✓ Yes</span>
    {% else %}
      <span style="color: #6b7280;">No</span>
    {% endif %}
  </p>
</div>
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

Shows a red banner at checkout explaining the rule if the cart validation Function detects a mixed-subscription violation.

Checkout UI extension
theme/snippets/cart-validation-banner.liquid liquid
{% # 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 small script to mark multiple products as subscriptions at once so you don't have to edit each one manually in the product editor.

Admin API script
Admin GraphQL explorer graphql
# 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. You can 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.

Sketch the build →