Displaying subscriptions on your website
The new Subscriptions app requires code changes to your website's theme. Existing theme code for the legacy Subscriptions app uses subscription intervals and does not submit the plan details required by the new system.
Create the plans and assign their products or product variations before configuring your theme. Follow the guide to creating and managing plans.
Update product page templates
Update product page templates
Edit the theme template that displays the product purchase form. In many themes this is views/partials/product_form.twig, but the filename can differ in a customised theme. Replace the earlier subscription interval selector with a selector for the new plans.
Apply the subscription selector to products with subscription plans. Products without subscription plans should retain their normal purchase form.
Subscription values available in your theme
Use the following Twig values to build the purchase options and describe each plan.
In this table, plan represents an item in a subscription_plans list. variation represents an item in product.variations.
| Twig value | What it contains and how to use it |
|---|---|
product.has_subscription_plans |
Returns true when the product has plans in the new subscription system. Plans can be assigned to the base product SKU or its variations, so this does not mean every variation has the same plans. |
product.subscription_plans |
The plans assigned to the base product SKU. Loop through this list to display the available purchase options for that SKU. |
product.variations |
The product's variations. Each variation has its own SKU and may have different subscription plans. |
variation.subscription_plans |
The plans assigned to that variation's SKU. When a visitor selects a variation, display the plans from this list rather than the base product's list. |
product.one_off_purchasing |
Returns true when the product allows purchases without a subscription. Your theme must use this value to decide whether to display a one-off purchase option; it does not add that option automatically. |
plan.id |
The plan's unique ID. Submit this as subscription_plan_id when the visitor selects the plan. It is not a customer-facing label. |
plan.version |
The plan's version number. Submit this as subscription_plan_version alongside its ID. Always use the version supplied with the selected plan. |
plan.name |
The plan's name, such as Monthly or Fortnightly. Use this as the option's visible label. |
plan.interval |
The subscription interval's unit: day, month or year. Use it with plan.interval_count when describing how often the subscription renews. |
plan.interval_count |
The number of units between renewals. For example, day with 7 means every seven days; month with 2 means every two months. This is not the number of instalments or payments. |
plan.cycle_limit |
The total order limit, including the first order. A value of 0 means there is no set order limit. Use this value, rather than plan.interval_count, to describe a limited subscription. |
plan.trial_days |
The free trial length in days. A value of 0 means there is no free trial. |
plan.price_modifier_type |
The pricing rule: fixed_price sets a fixed unit price; percent_discount applies a percentage discount. A blank value means there is no plan-specific price adjustment. |
plan.price_modifier_value |
The amount associated with the pricing rule. For percent_discount, 10 means a 10% discount. For fixed_price, it is the fixed unit price before VAT, in your account's currency. Interpret it using plan.price_modifier_type and apply the appropriate tax and currency formatting when displaying a price. |
For a purchase-options dropdown, displaying plan.name on its own is sufficient. Do not append an instalment count using plan.interval_count.
For example, an interval count of 6 with an interval of month means the subscription renews every six months. An order limit of 6 means the first order and five renewal orders. Read more about subscription renewals.
The earlier product.subscription and product.subscription_intervals values belong to the legacy system and do not supply the new plan choices.
Offering a one-off purchase
If a subscription product allows visitors to buy once or subscribe, the theme must display both options.
Use Purchase options as the selector's label. Include:
- A blank
Please select…option - A
One-off purchaseoption whenproduct.one_off_purchasingistrue - The names of the plans available for the selected SKU
Do not display the one-off option when product.one_off_purchasing is false.
Selecting One-off purchase must explicitly tell ShopWired that the visitor is buying without a subscription. Leaving the plan fields blank is not a substitute for submitting the one-off flag.
The add-to-basket request must contain the following values:
| Visitor's choice | one_off_purchase |
subscription_plan_id |
subscription_plan_version |
|---|---|---|---|
| One-off purchase | 1 |
Blank or omitted | Blank or omitted |
| A subscription plan | Blank or omitted | The selected plan's id |
The selected plan's version |
| No option selected | Do not submit the request | Do not submit the request | Do not submit the request |
Update these fields whenever the visitor changes their purchase option. When they select a different variation, rebuild the available plan choices and clear the previous selection, including its stored plan ID and version.
Before adding a subscription product to the basket, require the visitor to select an available option. If nothing is selected, display:
Please choose a one-off purchase or a subscription option.
If one-off purchasing is unavailable, display:
Please choose a subscription option.
Apply this validation to both normal form submissions and AJAX add-to-basket buttons. Neither method should submit a blank selection or silently treat it as a one-off purchase.
Render only the plans assigned to the selected base product SKU or product variation. If the selected SKU has no available plans and one-off purchasing is not allowed, do not submit an unavailable purchase option.
Subscription plans for product variations
Subscription plans for product variations
Different variations of the same product can have different subscription plans. Update the purchase-options dropdown whenever the visitor selects a variation so it shows only the plans available for that variation's SKU.
The examples below use syntax compatible with Twig 1.x. They show how to pass plan information to JavaScript; they are not a complete replacement for your theme's variation-selection and add-to-basket code.
Build the plan list in Twig
Create an object containing the available plans for each SKU. Include the base product's plans and each variation's plans.
Prefix each key with sku:. This prevents Twig's merge filter from treating numeric SKU codes as numeric array indexes and renumbering them. The prefix is used only inside this object; it does not change the product's actual SKU code.
{% set plans_by_sku = {} %}
{% if product.sku %}
{% set plans_by_sku = plans_by_sku|merge({
('sku:' ~ product.sku): product.subscription_plans|default([])
}) %}
{% endif %}
{% for variation in product.variations %}
{% if variation.sku %}
{% set plans_by_sku = plans_by_sku|merge({
('sku:' ~ variation.sku): variation.subscription_plans|default([])
}) %}
{% endif %}
{% endfor %}
Include variations with a blank plan list. This tells the JavaScript that no subscription plans are available for that SKU.
Pass the data to JavaScript
Output the object as JSON in a data attribute on the purchase-options dropdown. Escape the JSON for use inside an HTML attribute.
<select
name="_subscription_purchase_choice"
data-plans-by-sku="{{ plans_by_sku|json_encode|escape('html_attr') }}"
data-one-off-purchasing="{{ product.one_off_purchasing ? '1' : '0' }}"
>
<option value="">Please select...</option>
</select>
The example uses _subscription_purchase_choice for the visible dropdown. This is a theme field, not a replacement for the fields ShopWired expects in the add-to-basket request.
Include these hidden fields inside the same product form:
<input type="hidden" name="subscription_plan_id" value="">
<input type="hidden" name="subscription_plan_version" value="">
<input type="hidden" name="one_off_purchase" value="">
This markup prepares the dropdown and its data. JavaScript must populate the options and keep the hidden fields up to date.
Update the dropdown when the variation changes
Connect the update to your theme's existing variation-selection code. Run it when the page first loads and whenever the selected variation changes.
Use the selected variation's SKU to retrieve its plans. For example, inside your variation handler:
var select = productForm.querySelector(
'[name="_subscription_purchase_choice"]'
);
var plansBySku = JSON.parse(
select.getAttribute('data-plans-by-sku')
);
var plans = plansBySku['sku:' + selectedSku] || [];
In this example, productForm is the relevant product form element and selectedSku is the SKU resolved by your theme's variation-selection code. For a product without variations, use the base product's SKU.
For each update:
- Remove the previous variation's options
- Add the plans from the selected SKU's list
- Display
plan.nameas each plan's label - Retain each plan's
idandversionso they can be submitted when selected - Include
One-off purchaseonly whenproduct.one_off_purchasingistrue - When the SKU changes, clear the previous purchase selection and all three hidden fields
Do not use the base product's plans as a fallback when a variation's list is blank. Those plans may not be available for the selected SKU.
If no variation has been selected, do not display another variation's plans or submit its plan details.
Keep the submitted fields in sync
Whenever the visitor selects a purchase option, update the hidden fields as follows:
| Selected option | subscription_plan_id |
subscription_plan_version |
one_off_purchase |
|---|---|---|---|
| A subscription plan | Selected plan's id |
Selected plan's version |
Blank |
| One-off purchase | Blank | Blank | 1 |
Leaving both plan fields blank does not, by itself, select a one-off purchase. Submit one_off_purchase=1 when the visitor selects that option.
Where a purchase option is required, prevent submission until the visitor selects one. Apply this validation to normal form submissions and AJAX add-to-basket buttons.
Never submit a plan ID or version left over from a previously selected variation.
Update the add-to-basket form
Update the add-to-basket form
When the visitor selects a plan, the product form must submit both of these fields with the add-to-basket request:
| Form field | Value |
|---|---|
subscription_plan_id |
The selected plan's id |
subscription_plan_version |
The selected plan's version |
If the visitor selects a one-off purchase, submit one_off_purchase=1 and clear both plan fields. If the visitor selects a plan, clear the one-off value. Update these fields whenever the plan or variation selection changes.
ShopWired checks the submitted plan ID and version against the selected product's exact SKU. It rejects a plan that is no longer available or has changed version. Do not submit the earlier subscription_interval value as a substitute for a plan.
Check every route your theme uses to add products to the basket, including normal forms, AJAX buttons and quick-view forms. If an AJAX request builds its own data rather than submitting the whole form, include the subscription fields in that request.
Update basket and order templates
Update basket and order templates
Use the basket item's subscription_plan value to show the selected plan in the basket. Use the order product's subscription_plan value to show it in order displays. These values provide the plan name, interval, trial and other recorded plan details.
Review any theme code that displays subscription_interval on basket items or ordered products. That value describes the earlier subscription system and does not identify a new plan.
Check the full basket page and any mini-basket, basket drawer or popup. Display the plan beside the relevant product, because different items can use different plans. Do not label a one-off purchase as a subscription.
Use the plan details recorded against the basket item or ordered product. Do not replace them with the product's currently available plans.
Customer subscription management pages
Customer subscription management pages
The customer subscription portal is hosted outside your website's theme. You do not need to build these pages in your theme.
Use Customer portal in the Subscriptions app to configure the portal's font, main colour and accent colour. These settings are separate from your website's theme.
The existing account_subscriptions.twig and account_subscription.twig templates belong to the legacy subscription system. Do not use them to display contracts from the new app.
Follow the customer subscription management guide to add a portal link to your website's customer account pages, configure sign-in emails and retain access to earlier subscriptions where needed.
Customers sign in to the portal using an emailed code. Signing in to their website account does not automatically sign them in to the portal.
Display subscription details in order confirmation emails and invoice PDFs
Display subscription details in order confirmation emails and invoice PDFs
Subscription details are available in the order object used by order confirmation emails and invoice PDF templates.
Each product in order.products has its own subscription information. Display the details for each item separately, because an order can contain products purchased using different plans.
There is no single order.subscription_plan value covering the whole order.
Available Twig values
In the table below, item represents a product in a loop over order.products. Your template may use a different variable name.
| Twig value | What it contains and how to use it |
|---|---|
order.has_subscription_products |
Returns true when the order is linked to a subscription, including subscriptions created through the earlier system. This does not identify the plan used by each item. |
item.subscription_plan |
The subscription plan details recorded against the ordered item. Check this value before displaying any of its properties. |
item.subscription_plan.id |
The recorded plan ID. This is an internal identifier rather than a customer-facing label. |
item.subscription_plan.version |
The recorded version of the plan. This identifies the plan terms used for the purchase. |
item.subscription_plan.name |
The plan name, such as Monthly or Fortnightly. Use this to identify the selected plan to the customer. |
item.subscription_plan.interval |
The subscription interval's unit: day, month or year. Use it with item.subscription_plan.interval_count when describing how often the subscription renews. |
item.subscription_plan.interval_count |
The number of units between renewals. For example, day with 14 means every fourteen days. This is not the number of instalments or payments. |
item.subscription_plan.cycle_limit |
The total order limit, including the first order. A value of 0 means there is no set order limit. |
item.subscription_plan.trial_days |
The free trial length in days. A value of 0 means there is no free trial. |
item.subscription_plan.price_modifier_type |
The plan's pricing rule: fixed_price, percent_discount, or a blank value when there is no plan-specific price adjustment. |
item.subscription_plan.price_modifier_value |
The value associated with the pricing rule. For percent_discount, 10 means a 10% discount. For fixed_price, it is the fixed unit price before VAT, in your account's currency. |
item.is_one_off_purchase |
Returns true when the item was recorded as a one-off purchase rather than a subscription purchase. |
A false item.is_one_off_purchase value does not, by itself, mean the item is a subscription. Check item.subscription_plan to determine whether new-system plan details are available.
Add the selected plan name
Locate the existing loop that displays the products in your order confirmation email or invoice PDF template.
Add the following code inside that loop, replacing item if the template uses a different variable name:
{% if item.subscription_plan %}
<p>
Subscription plan:
{{ item.subscription_plan.name }}
</p>
{% elseif item.is_one_off_purchase %}
<p>One-off purchase</p>
{% endif %}
For reference, an order-product loop normally begins with:
{% for item in order.products %}
Do not add another product loop inside an existing one.
Displaying the plan name alone is sufficient for an order summary. Do not append an instalment count using item.subscription_plan.interval_count.
Use the details recorded with the order
Use item.subscription_plan rather than looking up the product's currently available plans. The recorded values describe the plan used for that purchase, even if you subsequently edit the plan.
These values describe the order's recorded subscription terms. They do not provide the subscription's current status or next renewal date. Review that information in the app or customer portal. Read more about managing contracts.
Update emails and PDFs separately
Order confirmation emails and invoice PDFs have separate templates. Add the required subscription display code to each template.
Changing the website's product or basket templates does not automatically update either the email template or the invoice PDF template.
After updating them, preview an order containing a subscription item and check that its plan name appears alongside the correct product.
Check your theme and activation
Check your theme and activation
Prepare the theme code and all required plans before switching new subscription purchases to the new app.
Follow the migration guide for the activation steps and the effect on existing subscriptions.
One-off purchases
- Confirm that subscription products allowing one-off purchases display a
One-off purchaseoption - Confirm that products which do not allow one-off purchases do not display this option
- Check that selecting a one-off purchase submits
one_off_purchase=1and clears both plan fields - Check that selecting a subscription clears the one-off value and submits the selected plan's ID and version
- Confirm that products without subscription plans can still be purchased normally
Subscription options
- Confirm that all available plans assigned to the selected SKU appear in the dropdown
- Use
plan.namefor each plan's label - Check that any description of the subscription interval uses
plan.intervalandplan.interval_countcorrectly - Do not display
plan.interval_countas a number of instalments - Use
plan.cycle_limitwhen displaying an order limit and explain that it includes the first order - Select each product variation and confirm that its own plans are displayed
- Confirm that changing the variation clears the previous selection and its submitted values
- Confirm that a variation without plans does not display another SKU's plans
Selection warning
- Try adding a subscription product without selecting a purchase option
- Confirm that the request is blocked and an appropriate warning appears
- Where one-off purchases are available: “Please choose a one-off purchase or a subscription option.”
- Where one-off purchases are unavailable: “Please choose a subscription option.”
- Confirm that the warning clears after a valid selection
- Check normal form submissions and AJAX add-to-basket buttons
- Check quick-view forms if the theme uses them
Product and basket pages
- Use Purchase options as the selector's label when offering subscriptions and one-off purchases
- Add a product using each available plan and confirm that the selected plan name appears beside the basket item
- Confirm that one-off purchases are not labelled as subscriptions
- Check the full basket page and any mini-basket, basket drawer or popup
- Confirm that the correct plan remains displayed after changing quantities or refreshing the page
Customer portal
- Confirm that the
Manage subscriptionslink opens the correct customer portal - Confirm that sign-in emails are configured and a test customer can sign in using an emailed code
- Check that customers with earlier subscriptions can still reach the legacy subscription pages where needed
Order confirmation emails and invoice PDFs
- Check the order confirmation email and invoice PDF for a subscription purchase
- Confirm that the recorded plan name appears beside the correct product in both documents
- Check a one-off purchase and confirm that it is not labelled as a subscription
- Use each item's
subscription_planvalue fromorder.products - Update the email and invoice PDF templates separately from the website theme
Completed orders
- Confirm that a completed subscription purchase creates a contract in the Subscriptions app with the correct plan and items
- Include a payment requiring bank authentication, such as 3-D Secure
- Confirm that a completed one-off purchase does not create a subscription contract
Contact theme support for help implementing or checking these changes.