Order Subscriptions

Order Subscriptions for WooCommerce allows your customers to turn any WooCommerce order or product into a subscription. Nothing to rebuild, no plans to maintain, every renewal reduces real stock, full customer self-service.

Installation

↑ Back to top

To start using a product from WooCommerce.com, you can use the โ€œAdd to storeโ€ functionality on the order confirmation page or the My subscriptions section in your account.

  1. Navigate to My subscriptions.
  2. Find the Add to store button next to the product youโ€™re planning to install.
  3. Follow the instructions on the screen, and the product will be automatically added to your store.

Alternative options and more information at:
Managing WooCommerce.com subscriptions.

Adding a WooCommerce.com subscription to your store

Upgrading from 1.0.x

↑ Back to top

Nothing needs to be done to upgrade, and no live storefront changes price because of it. Four things behave differently afterwards, and all four are deliberate.

Existing subscriptions keep renewing at their current price

↑ Back to top

Before this version the Subscribe & Save discount only ever applied to the first order. Subscriptions created then have no percentage stored on them, and keep renewing at full price until you set one on the subscription itself. Discounting them automatically would change what those customers are charged without them having agreed to it, so it does not happen on its own.

“Discount the first order” is set to match what your store already did

↑ Back to top

On upgrade this setting is switched on for a store that already has subscriptions, and off for a store that has none. An existing store therefore keeps charging exactly what it charged yesterday, while a new store starts with the discount on renewals only, which is the arrangement that gives customers a reason to stay.

The product page widget is on

↑ Back to top

It appears wherever a subscription could already be offered, so it does not widen what is eligible, it only moves the choice earlier. Turn it off under Front-end settings if you would rather customers decided at checkout.

Widget colours

↑ Back to top

Stores that have never saved the Front-end settings tab pick up the new green highlight. Stores that have saved it keep the colours they stored, because a saved settings page stores every field on it, not only the ones that were edited. The Reset appearance to defaults button on that tab clears them if you want the new defaults.

Setup

↑ Back to top

Everything is under WooCommerce โ†’ Settings โ†’ Order Subscriptions, on four tabs. A new install needs the first tab only.

Step 1: General settings

↑ Back to top

The tab is grouped into panels, each named for what it controls.

  • General settings: the master switch, off by default. Off means no new subscriptions can be taken out and existing ones stop renewing. Nothing is deleted, and renewals resume from the next due date when you switch it back on.
  • Renewal frequencies: which periods a customer may choose (days, weeks, months, years) and the minimum and maximum they may pick within each.
  • What customers can change themselves: four switches: change the schedule, skip a renewal, pause and resume, cancel. Each one off means the customer contacts you instead.
  • Reminders: whether to warn a customer before a renewal and how many days ahead, plus the two windows that govern unpaid payment-link renewals.
  • Order settings: what a renewal order carries over from the original: fees, shipping pricing, the Subscribe & Save discount, and any extra order meta you want copied.
  • Other settings: logging, and whether to delete the plugin’s settings when it is uninstalled.

Step 2: Front-end settings

↑ Back to top

Every word a customer reads, and how the subscribe widget looks. Wording is stored per language. Covered in full under Front-end wording and appearance below.

Step 3: Product & user restrictions

↑ Back to top

Controls which products may be subscribed to and which customers may subscribe.

  • Include or exclude by product category, tag or brand
  • Include or exclude by user role
  • Exclude an individual product or variation with a checkbox on its edit screen

Include users (whitelist – has priority)

↑ Back to top
  • Enable order subscriptions for user roles: Select one or more user roles. Only users with these roles will see the subscription widget. If left empty, all logged-in users can access subscriptions (subject to the exclusion rules below).

Exclude users

↑ Back to top
  • Disable order subscriptions for user roles: Select one or more user roles to prevent from using subscriptions. Only applies when the Include users field is empty.

Note: Include rules always take priority over Exclude rules. If you set both, only the Include rules are evaluated.

Include products (whitelist – has priority)

↑ Back to top
  • Allow only these product categories: Only products in these categories will be eligible.
  • Allow only these product tags: Only products with these tags will be eligible.
  • Allow only these product brands: Only products with these brands will be eligible (requires WooCommerce Brands or a compatible plugin).

Exclude products

↑ Back to top
  • Exclude product categories: Products in these categories cannot be purchased as subscriptions.
  • Exclude product tags: Products with these tags cannot be purchased as subscriptions.
  • Exclude product brands: Products with these brands cannot be purchased as subscriptions.

Product-level exclusion

↑ Back to top
  • Enable exclusion at product level: When enabled, a checkbox appears on the product edit screen (for simple products) and on each variation (for variable products) that lets you individually exclude a product or variation from order subscriptions. The meta key is _osfwc_exclude_from_order_subscriptions with values yes or no.

Step 4: Gateway compatibility

↑ Back to top

Which of your payment methods may be used for subscriptions, and how each one renews. Covered in full under The Gateway compatibility screen below.

Front-end wording and appearance

↑ Back to top

Under WooCommerce โ†’ Settings โ†’ Order Subscriptions โ†’ Front-end settings. Everything on this tab is optional; leave a field empty and the plugin’s own wording is used.

Wording, per language

↑ Back to top

If your store runs more than one language, the tab shows a language switcher and every string is stored separately for each one. Switching language changes which set you are editing; one Save writes them all.

The strings cover the checkout widget (the table cell title, the subscribe label, the interval label, the frequency prefix), the Subscribe & Save wording (the label on the totals line and the badge text, both of which accept {percent}), the message shown to logged-out customers, and every label on the product widget including its list of benefits.

The product widget’s appearance

↑ Back to top
  • Show the widget on product pages: off returns you to the pre-1.1.0 behaviour, where the choice is only made at checkout.
  • Pre-selected option: whether the page opens on one-time purchase or on subscribe. One-time is the default.
  • Layout: the two options stacked, or side by side. Stacked is the default; side by side falls back to stacked on small screens.
  • Show the discount badgeShow benefitsShow the discounted price: three independent switches.
  • Highlight colour: marks the option the customer has chosen: its border, its tick, and the ticks beside the benefits. Leave it empty to use your theme’s own colour.
  • Saving colour: used only for the “Save X%” badge, drawn as dark text on a pale tint of the colour you pick so it stays readable whatever you choose.

Resetting the appearance

↑ Back to top

Reset appearance to defaults clears the stored layout, badge, benefit, price and colour settings so they follow the plugin again. It is there because saving a settings page stores every field on it, not only the ones you edited, so a value saved on an older version keeps that version’s default until it is cleared. Your wording and the widget’s on/off switch are not affected.

Subscribe & Save

↑ Back to top

A percentage off every order the subscription places. Configured under General โ†’ Order settings.

The settings

↑ Back to top
  • Enable Subscribe & Save discount: the master switch for the feature.
  • Discount percentage: between 1 and 50. Calculated on the subtotal after any coupons.
  • Discount the first order: whether the order that starts the subscription carries the discount too. Switch it off and the discount begins at the first renewal, so there is nothing to gain from subscribing and cancelling immediately.
  • Discounted orders: how many orders carry the discount before the subscription returns to full price. Zero means every order, forever.

How the cap counts

↑ Back to top

The cap counts orders that have actually been paid, not renewals attempted. Whether the first order is one of them depends on the setting above: with Discount the first order on, the order that started the subscription uses one of the capped orders; with it off, it does not, and the count begins at the first renewal.

So a cap of 3 with first-order discounting on means the signup order and two renewals. The same cap with it off means three renewals.

Terms are fixed when the customer subscribes

↑ Back to top

The percentage, the first-order rule and the cap are all written onto the subscription at signup. Changing any of them afterwards affects new subscriptions only. Nobody who has already subscribed is ever repriced, in either direction.

This is also why a subscription created before 1.1.0 renews at full price: it has no percentage written on it. Set one on the subscription itself if you want it discounted.

The product page widget

↑ Back to top

On any product that could be subscribed to, the customer is offered a choice before adding to the basket: buy once, or subscribe. It renders above the add-to-cart button.

Where it appears

↑ Back to top

On simple and variable products that pass your product and user restrictions. On a variable product the choice resolves per variation, so a product where only some variations are eligible offers the choice on those and not on the others.

It does not widen what may be subscribed to. A product that could not be subscribed to at checkout does not gain a widget.

What the customer sees

↑ Back to top
  • Two options, one pre-selected according to your setting
  • The subscribed price, with the one-time price struck through, when a discount applies
  • A “Save X%” badge, if you have one configured and have left it switched on
  • A short list of what subscribing gets them, built from what your settings actually allow so it never promises an action you have switched off
  • A note that the delivery frequency is chosen at checkout

If the customer is already subscribing

↑ Back to top

When the basket already contains a subscription, the widget says so and offers to add the product to that subscription rather than starting a second one.

Turning it off

↑ Back to top

Under Front-end settings, switch off Show the widget on product pages. Subscriptions still work exactly as before; the choice is simply made at checkout instead.

Checkout experience

↑ Back to top

In the cart

↑ Back to top

Each eligible line offers a tick to subscribe to that item. Ticking one shows what it costs subscribed against what it costs once, with the one-time price struck through, and a Subscribe & Save line appears in the cart totals showing the saving on the items ticked.

A customer does not have to subscribe to everything. Subscribe to the coffee and buy the grinder once, in the same order: the ticked items renew on the customer’s schedule and the rest is an ordinary one-time purchase.

At checkout

↑ Back to top

A logged-in customer sees the subscription widget above the order totals:

  • The subscribe option, with your wording
  • A frequency and interval picker, limited to the periods and the minimum and maximum you allow
  • The Subscribe & Save saving, if one applies
  • What the next renewal will cost, shown whenever it differs from the order being placed, a mixed basket, a first order that carries no discount, or shipping that will be recalculated

Payment methods

↑ Back to top

Each payment method carries a short line saying how it will renew, so a customer knows before choosing whether renewals arrive automatically or as an email to pay. Any method you have disallowed for subscriptions disappears from checkout while the basket contains one.

Customers who are not logged in

↑ Back to top

Subscriptions need an account, so a logged-out customer sees a message inviting them to log in or register instead of the subscribe option. The wording is yours, on the Front-end settings tab.

After the order is placed

↑ Back to top

The order confirmation page shows a summary of the subscription that was created: what renews, how often, and when the next payment is due.

Customer self-service (My Account)

↑ Back to top

Customers get a Subscriptions section in My Account. Everything there is optional: each action can be switched off under General โ†’ What customers can change themselves, and off means the customer contacts you instead.

The list

↑ Back to top

Every subscription the customer has, with its status, what it contains, how often it renews and when the next payment is due.

A single subscription

↑ Back to top

Opening one shows its details, the items on it, the billing and delivery addresses, and the orders it has already placed. From here the customer can:

  • Pause and resume: useful when they are overstocked
  • Skip the next renewal, instead of cancelling
  • Cancel
  • Change the schedule: within the frequencies and limits you allow
  • Update quantities
  • Edit the billing and delivery addresses: before the next shipment goes out

Every action is confirmed by email to the customer and to you.

Available actions by status

↑ Back to top
StatusAvailable actions
ActivePause, Skip Next, Cancel
PausedResume, Cancel
CancelledNone (final state)

My Account endpoint configuration

↑ Back to top

The endpoint slugs for the subscriptions pages can be customized in WooCommerce โ†’ Settings โ†’ Advanced โ†’ Account endpoints:

  • Order Subscriptions: The endpoint for the subscriptions list page (default: order-subscriptions).
  • View order subscription: The endpoint for the single subscription detail page (default: view-order-subscription).

These endpoints are also registered with WPML and Polylang for translation.

Admin subscription management

↑ Back to top

Subscriptions list

↑ Back to top

Navigate to WooCommerce โ†’ Subscriptions (or WooCommerce โ†’ Order Subscriptions when WooCommerce Subscriptions is also active).

The subscriptions list shows all subscriptions in a sortable, filterable table with columns for:

  • Subscription ID
  • Customer name
  • Status (Active / Paused / Cancelled)
  • Frequency (e.g., “Every 2 Weeks”)
  • Next payment date
  • Order count
  • Created date

Bulk actions are available: Activate, Pause, Cancel, and Delete.

Filters: Filter by status (Active, Paused, Cancelled, All).

Search: Search subscriptions by ID, customer name, or email.

Subscription detail / edit view

↑ Back to top

Click on a subscription ID or the View action to open the subscription detail page. Click Edit to enter edit mode.

The detail page shows:

  • Subscription details: Status, subscription ID, base order link, customer, created date.
  • Schedule: Current interval and frequency (editable in edit mode), next payment date (editable via date picker in edit mode), last payment date.
  • Payment info: Payment gateway, gateway title, provider name, provider customer ID, failed payment retry count.
  • Subscribe & Save: Discount percentage locked at subscription creation (if applicable).
  • Items: Products from the base order with quantities. In edit mode, quantities can be changed via AJAX.
  • Addresses: Billing and shipping addresses stored on the subscription.
  • Related orders: All orders linked to this subscription, with links to each order.
View mode

Order metabox

↑ Back to top

On any WooCommerce order that is linked to a subscription (either the base order or a renewal order), an Order Subscriptions metabox appears in the order edit screen.

The metabox shows:

  • Whether the order is the initial subscription order or a recurring renewal order
  • Subscription ID (linked)
  • Status
  • Frequency
  • Next payment date
  • Failed payment count (if any)
  • Total order count
  • A “Related Orders” table showing the most recent orders linked to this subscription (up to 5, with a “View all” link)
  • A Manage subscription button linking to the subscription detail page

Orders table – subscription icon column

↑ Back to top

A subscription icon column is added to the WooCommerce orders list table (both HPOS and legacy). Orders linked to a subscription show a colored icon indicating the subscription status:

  • Green = Active subscription
  • Purple = Paused subscription
  • Red = Cancelled subscription

Clicking the icon opens the subscription detail page. You can also filter the orders list by subscription ID using the ?osfwc_subscription_id= URL parameter.

Subscription analytics

↑ Back to top

Under WooCommerce โ†’ Subscription analytics. A visual overview of the subscription side of the business: how many are active, what they are worth, how they trend over time, and which products are subscribed to most.

CSV export

↑ Back to top

From the subscriptions list page, click the Export CSV button to download a spreadsheet of your subscriptions. The export includes all subscription data: ID, status, customer info, schedule, payment details, next payment date, order count, and created date.

Email notifications

↑ Back to top

Eighteen templates in total: seven to you, eleven to the customer. All of them are managed under WooCommerce โ†’ Settings โ†’ Emails like any other WooCommerce email, and all of them can be overridden from your theme.

Sent whenTo youTo the customer
A subscription is createdyesyes
A subscription becomes activeyesyes
A subscription is pausedyesyes
A subscription is cancelledyesyes
A subscription is cancelled automatically after repeated failuresyesyes
A subscription is changedyesyes
A renewal payment failsyesyes
A renewal is skippedโ€”yes
A renewal is coming upโ€”yes
A renewal needs paying by linkโ€”yes
A payment-link renewal is still unpaidโ€”yes

Each one comes in HTML and plain text. The last two are new in 1.1.0 and are only ever sent for a payment method that cannot charge on its own; the timing of both is under General โ†’ Reminders, and setting either window to zero switches that email off.

Template overrides

↑ Back to top

All email templates can be overridden by copying them from plugins/order-subscriptions/templates/emails/ to your theme at yourtheme/woocommerce/emails/. Both HTML and plain text templates are provided.

Automatic recurring payments

↑ Back to top

A background check runs once an hour, looks for subscriptions whose next payment date has arrived, and processes them. It is scheduled through Action Scheduler, so you can watch it under WooCommerce โ†’ Status โ†’ Scheduled Actions.

What a renewal order contains

↑ Back to top

Each renewal is a new WooCommerce order built from the subscription’s current items, not a copy of the original order:

  • The products and quantities currently on the subscription, priced at today’s prices
  • Stock reduced, exactly as a normal sale reduces it
  • The billing and delivery addresses currently on the subscription
  • Coupons and fees, if you have chosen to carry them over
  • Shipping, priced according to your recurring shipping setting
  • The Subscribe & Save discount, if the subscription has one

Because it is built fresh each time, an edit the customer makes: a quantity, an address, the schedule, is reflected in the next renewal without anything else being touched.

Shipping on renewals

↑ Back to top

Under General โ†’ Order settingsRecurring shipping cost has two options:

  • Keep the cost from the original order. The renewal ships at the rate the customer agreed to at signup, whatever your rates do afterwards. This is the default, and it is what existing subscriptions already do.
  • Recalculate at current rates on every renewal. Shipping is priced fresh each time, so rate changes reach subscribers.

One exception applies whichever you choose: a subscription renewing only part of its original order always recalculates, because the stored cost covered items it no longer ships.

Subscribe & Save on renewals

↑ Back to top

The discount applies to every order the subscription places, including renewals, at the percentage locked to that subscription when it was created. Changing the setting later never reprices anyone who has already subscribed.

When the gateway charges automatically

↑ Back to top

The order is created, the customer’s saved payment method is charged, and the order completes. They receive your usual order emails. Nothing else happens.

When the gateway renews by payment link

↑ Back to top
  1. The renewal order is created with a status of pending payment.
  2. The customer is emailed a link to pay that order.
  3. If it is still unpaid after the number of days set in Manual renewal reminder (days), a reminder goes out.
  4. If it is still unpaid after Manual renewal escalation (days), the renewal is treated as failed and the subscription follows the retry behaviour below.

Both windows are under General โ†’ Reminders. Set either to zero to switch that step off.

The escalation window applies to payment-link renewals only. A renewal on an automatic gateway is given a fixed fourteen days before being treated as abandoned, because some methods confirm slowly by design a SEPA Direct Debit can legitimately take a week to settle. Shortening the setting will not make those fail sooner, and it is not meant to.

When a payment fails

↑ Back to top

Failed renewals are retried on a backing-off schedule: after 12 hours, then 24, then 48. After the third failure the subscription is cancelled, and both you and the customer are notified at every step.

Some failures are not worth retrying a deleted product, a deleted customer, an order that can no longer be built. Those cancel the subscription immediately rather than spending three attempts arriving at the same answer.

Protections you do not have to configure

↑ Back to top
  • A subscription cannot be charged twice for the same cycle, even if two scheduler runs overlap.
  • A renewal left pending or on hold for an unusually long time is escalated rather than left open indefinitely.
  • If the subscription’s original order has been deleted, the subscription is cancelled and logged instead of retrying every hour.

Supported payment gateways

↑ Back to top

Every payment method you have enabled in WooCommerce can take a subscription. What differs is how the renewal is collected, and there are only two answers.

Renewed automatically

↑ Back to top

When the renewal falls due, the plugin charges the customer’s saved payment method and creates the order. Nobody is asked to do anything. This applies to the gateways with a built-in integration:

  • Mollie (iDEAL, Bancontact, credit cards, SEPA Direct Debit, and the other methods Mollie offers)
  • Stripe (via WooCommerce Stripe Gateway)
  • WooPayments

The payment method is saved during the first checkout, so nothing extra is asked of the customer at signup either.

Renewed by payment link

↑ Back to top

Every other enabled method, including Cash on Delivery and Bank Transfer, renews by email. The renewal order is created as pending payment and the customer is emailed a link to pay it, with a reminder if it goes unpaid.

Nothing is lost by using a payment-link gateway. The subscription behaves identically; the customer just clicks once per renewal.

What the customer is told at checkout

↑ Back to top

Each payment method carries a short line saying how it will renew, so a customer choosing Bank Transfer knows an email is coming and a customer choosing Mollie knows one is not. The wording is filterable, see osfwc_renewal_method_label and osfwc_renewal_expectation_message in the developer reference.

If a gateway stops being available

↑ Back to top

A gateway is only treated as automatic while its own plugin is properly configured. If the integration is present but the gateway reports itself unavailable, a missing API key, credentials changed mid-configuration, the method drops to renewing by payment link instead of the renewal failing. Subscriptions keep running; they just collect differently until the gateway is working again.

The Gateway compatibility screen

↑ Back to top

Go to WooCommerce โ†’ Settings โ†’ Order Subscriptions โ†’ Gateway compatibility. The table lists every payment method you currently have enabled and how each one will behave.

ColumnWhat it tells you
Payment methodThe method as your customers see it, with its gateway ID underneath.
Provided byWhich plugin supplies it, and that plugin’s version.
RenewalsAutomatic or Payment link. This is the only distinction that affects your customers.
Enabled for subscriptionsWhether the method may be used for a subscription at all. Automatic methods read Always on; everything else has a switch.

Choosing which methods may be used

↑ Back to top

Switch off any method you do not want used for subscriptions. It stays available for ordinary orders and disappears from checkout only when the basket contains a subscription.

The automatic gateways cannot be switched off here. They are the methods that renew without troubling the customer, so disallowing them would leave subscribers worse off. If you do not want one of them at all, disable the gateway in WooCommerce itself.

“Supports recurring natively”

↑ Back to top

Some payment-link methods carry this note. It means the gateway plugin tells WooCommerce it can store payment tokens, but there is no integration for it here yet, so this plugin does not attempt to charge it without the customer present.

The reason is deliberate. A stored token is not the same thing as permission to charge off-session, under strong customer authentication rules those are separate permissions, and charging on a token that was never authorised for it produces declines and chargebacks rather than payments. Until an integration exists that asks for the right permission at signup, the safe behaviour is a payment link. The note tells you an integration is plausible for that gateway, not that you should expect one today.

Verified against

↑ Back to top

Each built-in integration records the version of the gateway plugin it was last checked against. If you are running a much newer version of Mollie, Stripe or WooPayments than the one listed, renewals may well be fine, but it is the first place to look if automatic renewals start failing after a gateway update.

Request implementation

↑ Back to top

Every payment-link method carries a Request implementation link, which opens a short form asking which gateway you use. That is how the list of automatic gateways grows, and it is the most useful thing you can do if you want your own gateway renewing automatically.

Multilingual support

↑ Back to top

Compatible with WPML, Polylang, Weglot and GTranslate.

Every customer-facing string on the Front-end settings tab is stored per language, so the subscribe wording, the discount labels and the product widget’s benefits can differ between languages rather than being translated once for the whole store. See Front-end wording and appearance above.

Renewals processed in the background respect the customer’s language, so a renewal email arrives in the language they bought in rather than the site default.

Translations are bundled for English, Dutch (NL, BE), French (FR, BE, LU) and German (DE, AT, CH, BE, LU), including formal variants.

HPOS compatibility

↑ Back to top

Fully compatible with High-Performance Order Storage. Every order query runs dual-path logic, so the plugin works whether your store uses HPOS or the legacy post-based storage, and keeps working if you switch between them.

WooCommerce Cart and Checkout blocks

↑ Back to top

Both blocks carry the full experience: the subscribe option, the per-item choice for mixed baskets, the frequency picker, the Subscribe & Save saving and the renewal total.

The integration is built on WooCommerce’s own extension points, the official slot fills and the Store API, rather than on the block markup, so it does not break when the blocks change internally.

Developer reference

↑ Back to top

Sixty-seven documented hooks: 34 actions and 33 filters. Every one carries the version it arrived in and what it is passed.

Templates can be overridden from your theme in the usual WooCommerce way, by copying the file into your-theme/order-subscriptions/. This covers the product widget, the cart and checkout widgets, the My Account screens and every email.

Actions

↑ Back to top
HookWhat it doesParametersSince
osfwc_after_account_subscriptionFires at the very end of the single subscription page.$subscription_id The subscription being viewed.1.0.0
osfwc_after_account_subscriptionsFires after the subscriptions list in My Account.$subscriptions The customer’s subscriptions, possibly empty.1.0.0
osfwc_after_renewal_paymentFires after a renewal payment attempt.$subscription_id The subscription ID.
$renewal_order The renewal order object.
$success Whether payment was successful.
$gateway_id The payment gateway ID.
1.0.0
osfwc_before_account_subscriptionFires at the top of the single subscription page, before the summary line.$subscription_id The subscription being viewed.1.0.0
osfwc_before_account_subscriptionsFires before the subscriptions list in My Account. Also fires when the customer has none, so the empty state can be replaced.$subscriptions The customer’s subscriptions, possibly empty.1.0.0
osfwc_before_renewal_paymentFires before a renewal payment is processed.$subscription_id The subscription ID.
$renewal_order The renewal order object.
$gateway_id The payment gateway ID.
1.0.0
osfwc_email_subscription_detailsFires where the subscription details table is rendered in a subscription email. Fired by every subscription email template, HTML and plain text alike. Most pass the base WC_Order; the “subscription changed” emails pass the subscription WP_Post instead, so a callback must accept both. The default callback normalises them.$order The base order, or the subscription post.
$sent_to_admin Whether this email goes to the admin.
$plain_text Whether this is the plain text email.
$email The email object, null when sent outside one.
1.0.0
osfwc_manual_renewal_createdFires when a renewal order has been raised for the customer to pay. The order is on-hold and carries a pay-by-link. Used for gateways that cannot be charged off-session; COD renewals do not fire this.$renewal_order_id The renewal order awaiting payment.
$subscription_id The subscription ID.
1.0.0
osfwc_manual_renewal_reminderFires when an unpaid renewal order is due a reminder. The reminder email is hooked to this. Only fires while the subscription is active and the order is still unpaid.$renewal_order_id The unpaid renewal order.
$subscription_id The subscription ID.
1.1.0
osfwc_payment_failedFires when a renewal payment failed and a retry has been scheduled. Only fires while retries remain; the final failure fires osfwc_subscription_auto_cancelled instead. The retry date is already stored on the subscription.$subscription_id The subscription ID.
$order_id The renewal order that failed.
$retries How many attempts have now failed.
1.0.0
osfwc_payment_meta_copiedFires after payment meta has been copied between two orders. The target order is not saved yet, so meta added here is written with the rest.$source_order The order the meta came from.
$target_order The order the meta was copied to.
$payment_meta The meta keys and values copied.
1.0.0
osfwc_plugin_activatedFires at the end of plugin activation. Runs after the post type is registered, defaults are seeded and the recurring checks are scheduled.none1.0.0
osfwc_plugin_deactivatedFires at the end of plugin deactivation. Scheduled actions are already unscheduled; subscription data is left intact.none1.0.0
osfwc_plugin_uninstalledFires at the end of plugin uninstall. Settings have been removed if the merchant opted into that; use this to clear data the plugin does not own.none1.0.0
osfwc_recurring_order_createdFires once a renewal order has been built and saved. The order is still pending; payment has not been attempted. Call save() yourself if you change the order here.$recurring_order_id The new renewal order ID.
$recurring_order The renewal order object.
$subscription The subscription data it was built from.
1.0.0
osfwc_recurring_order_validation_failedFires when a renewal order could not be built from the base order. The reason decides what happens next: an unrecoverable one such as a deleted product cancels the subscription, anything else is retried. The reason is already stored as _osfwc_last_validation_failure.$subscription_id The subscription that failed to renew.
$reason Why validation failed.
1.0.0
osfwc_register_gateway_integrationsFires so third-party plugins can register their own gateway integrations. The IntegrationManager class name is passed as a string (not an instance), because registration happens through the static register_integration(). A listener extends the abstract GatewayIntegration base and registers an instance of it: add_action( ‘osfwc_register_gateway_integrations’, function ( $manager ) { // $manager === ‘OSFWC\Gateways\Integrations\IntegrationManager’ $manager::register_integration( new My_Gateway_Integration() ); } ); $manager::register_integration( $integration ) on it.$manager Fully-qualified IntegrationManager class name; call1.0.0
osfwc_subscription_auto_cancelledFires when a subscription is cancelled by the plugin rather than by a person. The status is already cancelled by this point. The reason is one of ‘invalid_base_order_id’, ‘payment_failures’, or an unrecoverable validation failure such as a deleted product.$subscription_id The subscription ID.
$reason Why the subscription was cancelled.
1.0.0
osfwc_subscription_billing_address_updatedFires when the billing address stored on a subscription changed. Applies to future renewals only. The address is stored per field as _osfwc_billing_* meta and the search index has been refreshed.$subscription_id The subscription ID.
$address The submitted address, keyed without the billing_ prefix.
$changed_by Who made the change: ‘admin’, ‘customer’ or ‘system’.
1.0.0
osfwc_subscription_cancelledFires when a subscription has been cancelled. Scheduled renewals are already unscheduled. Cancellation is final; there is no reactivation from this status.$subscription_id The subscription ID.1.0.0
osfwc_subscription_changedFires when something about a subscription other than its status changed. The change notification email is hooked to this. Keys present in $changes depend on what moved: ‘interval’, ‘frequency’, ‘next_payment_date’, ‘quantity_changes’, and the address fields. Each carries an ‘old’ and ‘new’ value, except ‘quantity_changes’ which is a list of per-product entries.$subscription_id The subscription ID.
$changes What changed, keyed by field.
$changed_by Who made the change: ‘admin’, ‘customer’ or ‘system’.
1.0.0
osfwc_subscription_createdFires once a subscription has been created from a paid order. All meta is written and the first renewal scheduled by this point.$subscription_id The new subscription ID.
$order The order it was created from.
1.0.0
osfwc_subscription_details_after_order_tableFires after the subscription items table, before the addresses.$subscription_id The subscription being viewed.1.0.0
osfwc_subscription_details_after_subscription_tableFires after the subscription details table, before the items table.$subscription_id The subscription being viewed.1.0.0
osfwc_subscription_details_before_subscription_tableFires before the subscription details table on the single subscription page.$subscription_id The subscription being viewed.1.0.0
osfwc_subscription_pausedFires when a subscription has been paused. Scheduled renewals are already unscheduled. The next payment date is left as it was and revalidated on reactivation.$subscription_id The subscription ID.1.0.0
osfwc_subscription_payment_skippedFires when a customer or admin skipped one renewal cycle. The new date is already stored and any renewal queued for the skipped cycle has been unscheduled.$subscription_id The subscription ID.
$current_next_payment The date that was skipped, UTC ‘Y-m-d H:i:s’.
$new_next_payment The date it moved to, UTC ‘Y-m-d H:i:s’.
1.0.0
osfwc_subscription_quantity_updatedFires when the quantity of one product in a subscription changed. The override is stored against the subscription; the base order is untouched. Renewals from here on use the new quantity.$subscription_id The subscription ID.
$product_id Product or variation ID.
$quantity The new quantity.
1.0.0
osfwc_subscription_reactivatedFires when a subscription becomes active again after being paused. The next payment date has been revalidated and moved forward if it had fallen into the past while paused.$subscription_id The subscription ID.1.0.0
osfwc_subscription_renewedFires after a renewal has been paid and the subscription rolled forward. The order count is incremented and the next payment date stored before this runs.$subscription_id The subscription ID.
$order_id The renewal order that was paid.
$next_payment_date Next payment date, UTC ‘Y-m-d H:i:s’.
1.0.0
osfwc_subscription_shipping_address_updatedFires when the shipping address stored on a subscription changed. Applies to future renewals only. The address is stored per field as _osfwc_shipping_* meta.$subscription_id The subscription ID.
$address The submitted address, keyed without the shipping_ prefix.
$changed_by Who made the change: ‘admin’, ‘customer’ or ‘system’.
1.0.0
osfwc_subscription_shipping_method_updatedFires when the shipping method stored on a subscription changed. Applies to future renewals only; orders already placed keep the method they were created with.$subscription_id The subscription ID.
$shipping_data The new method, with method_id, method_title and total.
$old_data The previous method, or null if none was stored.
$changed_by Who made the change: ‘admin’, ‘customer’ or ‘system’.
1.0.0
osfwc_subscription_status_changedFires after a subscription status change has been saved. Side effects (unscheduling, rescheduling) have already run. Statuses are the full slugs: osfwc_sub_active, osfwc_sub_paused, osfwc_sub_cancelled.$subscription_id The subscription ID.
$old_status Status before the change.
$new_status Status after the change.
$note Reason recorded with the change, may be empty.
1.0.0
osfwc_upcoming_renewal_reminderFires ahead of a renewal, to warn the customer it is coming. Runs inside the customer’s language context, which is restored afterwards. Fires at most once per renewal.$subscription_id The subscription about to renew.1.0.0

Filters

↑ Back to top
HookWhat it doesParametersSince
osfwc_allowed_subscription_gatewaysFilter the gateway IDs offered at checkout when a subscription is selected. This is the allow-list the checkout filters against. Removing an ID hides that method from subscription orders only.$allowed Gateway IDs allowed for subscriptions.1.0.0
osfwc_async_confirmation_gatewaysFilter whether a gateway confirms payment asynchronously. An async method is not known to have succeeded when the request returns, so the renewal waits for the webhook instead of being treated as failed.$is_async Whether confirmation arrives later, by webhook.
$gateway_id The gateway ID.
1.0.0
osfwc_certification_request_urlFilter the certification request form URL. Returning an empty string hides the request button entirely.$base The hosted form URL, before gateway details are appended.1.1.0
osfwc_checkout_guest_cta_enabledFilter whether the checkout guest CTA should be displayed.$display Whether to display the guest CTA. Default true.1.0.0
osfwc_checkout_widget_enabledFilter whether the checkout subscription widget should be displayed.$display Whether to display the widget. Default true.1.0.0
osfwc_custom_recurring_order_metaFilter the extra order meta keys copied onto each renewal order. Starts from the merchant’s comma-separated setting. Payment meta is handled separately by osfwc_payment_meta_from_order.$order_meta_keys Order meta keys to copy from the base order.1.0.0
osfwc_customer_id_from_orderFilter the provider customer or token ID for an order. Fires only when no known meta key held one, so the incoming value is always an empty string. Use it to supply the ID for a gateway the plugin does not map.$customer_id Provider-side customer ID, empty at this point.
$order The order being inspected.
$provider_name The resolved provider name.
1.0.0
osfwc_gateway_hooks_enabledFilter the gateway IDs that get the generic subscription support hooks. These declare tokenization support to WooCommerce on the gateway’s behalf; they do not by themselves make a gateway able to auto-renew.$enabled_gateways Gateway IDs hooked for subscription support.1.0.0
osfwc_gateway_tierFilter the resolved subscription tier for a gateway.$tier One of ‘certified’, ‘tokenized’, ‘manual’.
$gateway_id The gateway ID.
1.1.0
osfwc_manual_payment_gatewaysFilter which gateway IDs count as manual payment methods. A manual method is one the customer settles themselves, so its renewals are raised as an unpaid order rather than charged.$manual_gateways Gateway IDs treated as manual.1.0.0
osfwc_mollie_payment_dataFilter the Mollie payment data before creating the payment.$payment_data The payment data.
$order The order object.
1.0.0
osfwc_next_payment_dateFilter the next payment date before saving.$next_payment_date The next payment date (MySQL format).
$subscription_id The subscription ID.
$interval The subscription interval.
$frequency The subscription frequency code.
1.0.0
osfwc_payment_meta_from_orderFilter the payment meta copied onto a renewal order. This is what lets a renewal charge against the original mandate, so removing a key here can stop that gateway renewing.$payment_meta Meta keys and values to carry over.
$order The source order.
$gateway_id The gateway ID, empty when not narrowed to one.
1.0.0
osfwc_payment_provider_infoFilter the payment provider details resolved from an order. Provider details. }$info {
$order The order the details came from.
1.0.0
osfwc_product_is_subscription_eligibleFilter whether a product may be part of a subscription.$eligible Whether the product qualifies.
$product_id Parent product ID.
$variation_id Variation ID, or 0 when there is none.
$product The product, or null if it could not be loaded.
1.1.0
osfwc_product_widget_benefitsFilter the derived benefit bullets.$benefits The bullet lines.1.1.0
osfwc_product_widget_enabledFilter whether the product widget is displayed.$display Whether to display the widget.
$product The product being viewed.
1.1.0
osfwc_product_widget_hookFilter where the product widget is rendered. Any action that fires inside the add-to-cart form works; the choice has to be submitted with the form for it to reach the cart.$hook The action the widget renders on.1.1.0
osfwc_provider_display_nameFilter the human-readable name shown for a payment provider. the internal name when it is not a known one.$display_name Provider name for display, title-cased from
$provider_name The internal provider name.
1.0.0
osfwc_provider_from_gateway_idFilter the provider resolved from a gateway ID. Fires only when no built-in pattern matched, so the incoming value is the unrecognised gateway ID itself.$provider Resolved provider name, defaulting to the gateway ID.
$gateway_id The gateway ID being resolved.
1.0.0
osfwc_recurring_order_couponsFilter coupons applied to recurring order.$coupon_codes Array of coupon codes from base order.
$base_order The base order object.
$recurring_order The recurring order object.
1.0.0
osfwc_recurring_order_feesFilter the fees copied from the base order onto a renewal order. The Subscribe & Save fee is deliberately absent when the subscription calculates its own discount, so adding it back here charges it twice.$fees Fee lines, each with name, amount and total.
$base_order_id The order the fees were read from.
1.0.0
osfwc_recurring_order_productsFilter the line items placed on a renewal order. Already narrowed to the items that renew, with any per-subscription quantity overrides applied. Removing every item leaves an empty order.$products Line item data for the renewal order.
$base_order_id The order the subscription was created from.
1.0.0
osfwc_recurring_order_shippingFilter the shipping line placed on a renewal order. The cost is the stored one, or the recalculated one when the merchant chose current rates. Setting ‘cancel’ to true cancels the renewal order, and cancels the subscription too unless ‘keep_subscription_active’ is also true. The shipping line. }$shipping_data {
$recurring_order The renewal order being built.
$base_order The order the subscription came from.
$subscription_id The subscription ID.
1.0.0
osfwc_recurring_shipping_recalculateFilter whether a renewal recalculates its shipping cost.$recalculate Whether to recalculate.
$subscription_id The subscription ID.
$partial Whether the renewal ships less than the base order.
1.1.0
osfwc_renewal_expectation_messageFilter the customer-facing renewal expectation message for a gateway.$message The message, or ” for none.
$gateway_id The gateway ID.
$tier The resolved gateway tier.
1.1.0
osfwc_renewal_method_labelFilter the short renewal-method label shown under a gateway at checkout.$label The label, or ” for none.
$gateway_id The gateway ID.
$tier The resolved gateway tier.
1.1.0
osfwc_show_recurring_totalsFilter whether the recurring total is shown at checkout. Shown by default only when it differs from the order total, so a customer subscribing to their whole cart is not told the same number twice. Return true to always show it.$show Whether to show the recurring total.
$recurring_total The recurring total.
1.1.0
osfwc_stripe_payment_intent_requestFilter the Stripe payment intent request before creating the payment.$request The payment intent request.
$order The order object.
1.0.0
osfwc_subscribe_save_discountFilter the Subscribe & Save discount percentage.$discount_percent The discount percentage (0-100).
$subscription_id The subscription ID.
$order The recurring order object.
1.0.0
osfwc_subscription_actionsFilter the actions a customer is offered on a subscription. Display only. An action added here still has to be handled, and the nonce and ownership checks in handle_subscription_actions() reject anything the plugin does not know. Actions keyed by slug: pause, activate, cancel, skip. }$actions {
$subscription The subscription data.
1.0.0
osfwc_subscription_capable_gatewaysFilter the gateways considered capable of taking a renewal payment. Only gateways with a direct integration are added here. Adding one without an integration makes it selectable but unable to auto-renew.$capable_gateways Gateway objects keyed by gateway ID.1.0.0
osfwc_subscription_cpt_argsFilter the arguments the osfwc_subscription post type is registered with. The post type is deliberately private; the plugin renders its own admin screens. Making it public exposes subscriptions on the front end.$args Arguments passed to register_post_type().1.0.0

Template overrides

↑ Back to top

Front-end and email templates can be overridden by copying them from the plugin’s templates/ directory into your theme. There are two override locations, depending on the template.

Front-end templates

↑ Back to top

Override directory: yourtheme/osfwc/

TemplateOverride path
Product page widgetyourtheme/osfwc/frontend/product/subscription-widget.php
Cart item subscribe rowyourtheme/osfwc/frontend/cart/item-subscribe.php
Cart repeat noteyourtheme/osfwc/frontend/cart/repeat-note.php
Checkout recurring totalsyourtheme/osfwc/frontend/checkout/recurring-totals.php
Thank you page subscription summaryyourtheme/osfwc/frontend/checkout/thankyou-subscription.php
My Account subscriptions listyourtheme/osfwc/frontend/myaccount/subscriptions.php
My Account subscription detailyourtheme/osfwc/frontend/myaccount/view-subscription.php

Note the frontend/ segment, it is part of the path inside your theme, not just inside the plugin.

Email templates

↑ Back to top

Override directory: yourtheme/woocommerce/, the same place WooCommerce’s own emails are overridden.

  • HTML emails: yourtheme/woocommerce/emails/osfwc-subscription-*.php
  • Plain text emails: yourtheme/woocommerce/emails/plain/osfwc-subscription-*.php

Not overridable

↑ Back to top

The checkout widget and the guest call-to-action are loaded directly rather than through the template system, so they cannot be replaced from a theme. Use the Front-end settings tab to change their wording, and the osfwc_* filters for anything further. The admin templates: subscription details, the gateway compatibility table and the analytics dashboard are not overridable either.

Adding your own gateway integration

↑ Back to top

A gateway renews automatically when an integration exists that can charge it without the customer present. Three ship with the plugin. You can add your own.

Extend the base integration class and register it on osfwc_register_gateway_integrations:

add_action( 'osfwc_register_gateway_integrations', function ( $manager ) {
    $manager::register_integration( new My_Gateway_Integration() );
} );

The manager is passed as a class name, not an instance, because registration happens through a static method.

Your class extends OSFWC\Gateways\Integrations\GatewayIntegration and tells the plugin which gateway it handles, whether it is currently able to charge, and how to take a payment for a renewal order. Set $verified_against to the version of the gateway plugin you tested against; it is shown on the Gateway compatibility screen so drift is visible rather than discovered by a failed renewal.

If your integration reports itself unavailable, missing credentials, for instance, the gateway falls back to renewing by payment link rather than the renewal failing.

Internal tiers

↑ Back to top

The osfwc_gateway_tier filter reports one of three values. certified means an integration is present and able to charge. tokenized means the gateway stores payment tokens but has no integration here; it renews by payment link, because a stored token is not permission to charge off-session. manual is everything else. Only the first behaves differently from the customer’s point of view.

Logging and troubleshooting

↑ Back to top

When logging is enabled, the plugin writes to WooCommerce’s built-in log system. Navigate to WooCommerce โ†’ Status โ†’ Logs to view log files.

Log files are organized by category:

Log fileContents
osfwc-subscriptionsSubscription creation, status changes, schedule updates
osfwc-ordersRecurring order creation, validation, shipping
osfwc-gatewaysGeneral gateway processing
osfwc-gateway-mollieMollie-specific API calls and responses
osfwc-gateway-stripeStripe-specific API calls and responses
osfwc-gateway-woocommerce_paymentsWooPayments-specific API calls and responses
osfwc-generalPlugin activation, scheduler, and general events

Common questions

↑ Back to top

A customer received an email asking them to pay for a renewal. Their payment method renews by payment link rather than automatically. Check the Gateway compatibility screen to see which of your methods do which.

A payment method has disappeared from checkout. It is disallowed for subscriptions and the basket contains one. Re-enable it on the Gateway compatibility screen, or leave it off if that was deliberate, it still appears for ordinary orders.

An existing subscriber is not getting the discount. Subscriptions created before 1.1.0 have no percentage stored on them. Set one on the subscription itself.

The widget looks wrong after an update. A saved settings page stores every field on it, so colours saved on an older version keep that version’s defaults. Use Reset appearance to defaults on the Front-end settings tab.

Renewals are not running at all. Check WooCommerce โ†’ Status โ†’ Scheduled Actions for the hourly check, and confirm the master switch under General settings is on.

Minimum requirements

↑ Back to top
  • WordPress 5.6 or greater
  • WooCommerce 4.0.1 or greater
  • PHP 7.4 or greater

Related Products

Offer add-ons like gift wrapping, special messages or other special options for your products.

WooCommerce Subscriptions is a WooCommerce extension that lets customers subscribe to your products or services and pay on a weekly,...

Use of your personal data
We and our partners process your personal data (such as browsing data, IP Addresses, cookie information, and other unique identifiers) based on your consent and/or our legitimate interest to optimize our website, marketing activities, and your user experience.