Subscriptions Guide
Configuring and managing recurring billing, payment collection deadlines, and subscription lifecycles
WooNooW's Subscriptions module seamlessly integrates with WooCommerce, enabling robust recurring revenue models without requiring external SaaS billing providers or bloated third-party extensions.
Setup Requirements
To use Subscriptions, verify the following prerequisites:
- The Subscriptions module is toggled ON under WooNooW > Settings > Modules.
- Your payment gateway supports tokenization / recurring payments (e.g., Stripe, PayPal, Midtrans, or manual BACS/Bank Transfer).
- WordPress Cron (or a system crontab executing
wp-cron.php) is active to run scheduled renewal, reminder, and expiration tasks.
Module Configuration Settings
Subscription behavior is configured globally under WooNooW > Settings > Modules > Subscription:
Creating a Subscription Product
WooNooW integrates recurring billing options directly into standard WooCommerce products:
- Navigate to Products in the Admin SPA and create or edit a product.
- In the General tab, check Enable subscription for this product.
- Configure the recurring parameters:
- Billing Period & Interval: Billing cadence (e.g., Every
1Monthor Every1Year). - Trial Days: Optional introductory trial duration before the first recurring charge (e.g.,
14days). - Signup Fee: Optional one-time fee added to the initial checkout payment.
- Billing Period & Interval: Billing cadence (e.g., Every
Renewal Billing & Immutable Collection Deadlines
1. Configurable payment collection window (default 14 days)
To prevent stale invoices and maintain billing hygiene, WooNooW enforces a strict, merchant-configurable payment window (unpaid_renewal_max_age_days, default 14 days):
- When a renewal order is generated (by cron, customer early renewal, or admin trigger), WooNooW calculates an immutable deadline:
Deadline = Anchor Date + Payment Collection Window - This deadline is permanently stamped onto the WooCommerce renewal order as post meta:
_woonoow_renewal_collection_deadline(site-local SQL datetimeY-m-d H:i:s). - For standard renewals, the anchor is
subscription.next_payment_date. For legacy renewal orders without an explicit deadline, WooNooW falls back toorder_created_timestamp + (unpaid_renewal_max_age_days * 86400).
2. Guarding checkout and payment execution
As long as current_time('mysql') <= _woonoow_renewal_collection_deadline:
- The renewal order remains open and payable at its protected recurring price.
- If the customer attempts to pay after the deadline has passed, payment guards in both the WooNooW checkout API (
/checkout/pay-order/{id}) and native WooCommerce checkout reject the transaction with an error stating the collection window has expired.
3. Unpaid renewal order reuse (zero duplicate invoices)
WooNooW prevents customers from accumulating multiple pending invoices for the same subscription:
- Before generating a renewal order,
SubscriptionManager::renew()inspects the subscription's linked orders. - If an existing unpaid renewal order (
pending,on-hold, orfailed) is found:- If its collection window is still open, WooNooW reuses that existing order.
- If the order was previously marked failed from an auto-debit attempt, WooNooW clears the failure flag and re-attempts payment on the same order.
- If the collection window has closed, the expired order is automatically cancelled before proceeding.
- Customers never receive duplicate or competing invoices for the same renewal cycle.
4. Automatic cancellation of expired invoices
The scheduler cron woonoow_retry_unpaid_renewals runs twice daily:
- It locates
on-holdsubscriptions waiting for manual renewal payment (paused_at IS NULL). - For orders within the collection window, it sends a daily payment reminder (
woonoow/subscription/renewal_payment_due). - When an order's collection deadline is exceeded, WooNooW calls
SubscriptionManager::cancel_expired_renewal_orders(), updates the order status tocancelledwith the note"Payment collection window expired.", and terminates the collection cycle.
Protected-Price Reactivation (Admin-Only Exception)
Once a subscription passes its collection deadline or reaches a terminal status (expired or cancelled), regular customers cannot self-reactivate at the protected recurring price via the customer portal. The customer must start over and purchase a new subscription at the current catalog price.
However, store administrators can grant a discretionary protected-price exception directly from the Admin SPA:
Reactivation workflow
When an administrator clicks Reactivate on an expired or cancelled subscription:
- Permission check: Enforces
manage_woocommercecapability (admin only). - Terminal state validation: Only subscriptions with status
expiredorcancelledcan be reactivated. - Catalog availability check: Verifies the underlying product and variation are not permanently deleted or in the trash.
- Order cleanup: Any prior pending, on-hold, or failed renewal orders are marked as superseded (
_woonoow_renewal_superseded = '1') and cancelled. - Subscription reset: The subscription is moved to
on-hold,failed_payment_countis reset to0, andpaused_at,reminder_sent_at, andcancel_reasonare cleared. - Fresh invoice generation: A new renewal order is created at the grandfathered recurring amount with a new collection deadline anchored from the moment of reactivation based on the configured collection window (
current_time('mysql') + (unpaid_renewal_max_age_days * 86400)). - Access remains disabled: The subscription remains
on-hold(licenses inactive) until the customer pays the newly issued invoice.
Customer Pause Controls & Automatic Resumption
Store owners can allow customers to pause their subscriptions to prevent churn while enforcing protective limits.
1. Pause count limits
- Configured via
max_pause_count(default: 3). - Each pause increments the subscription's
pause_countin the database. - When
pause_count >= max_pause_count, the customer cannot pause the subscription again from the account dashboard. The UI indicates how many pauses remain.
2. Auto-resume scheduler
If customers forget to resume a paused subscription, store revenue is protected by the maximum pause duration setting:
- Setting:
max_pause_duration_days(default0= disabled/unlimited). - When set to a positive integer (e.g.,
30days), the daily cronwoonoow_check_pause_expirationssearches for subscriptions where:status = 'on-hold' AND paused_at <= Now - max_pause_duration_days - WooNooW automatically calls
SubscriptionManager::resume(), recalculatesnext_payment_datefrom the resumption moment, resetspaused_at, transitions status back toactive, and fireswoonoow/subscription/auto_resumed.
Admin Operations: Detail View Actions
From Store > Subscriptions in the Admin SPA, selecting any subscription reveals its management actions:
- Pause / Resume: Manually toggles subscription status between
activeandon-hold. - Cancel: Cancels the subscription. If there is prepaid time remaining before
next_payment_date, defaults topending-cancel(active until the billing cycle ends) unless immediate cancellation is requested. - Renew Now: Initiates the standard renewal sequence honoring the gateway capability matrix. If the gateway supports recurring charges, auto-debit is executed; otherwise, a manual renewal invoice is generated.
- Charge Now: Admin bypass flag (
$charge_now = true). Forces an immediate auto-debit charge against the customer's payment token, bypassing gateway capability gates. If the charge fails or the gateway cannot process tokenized debits, the order is immediately markedfailed(without creating a fallback manual invoice), providing direct error feedback. - Reactivate: Available only on terminal subscriptions (
expired,cancelled) to issue an administrative protected-price invoice.
Automated Schedulers (Cron Jobs)
WooNooW registers five distinct cron hooks to manage the complete recurring billing lifecycle:
Last updated Sep 11, 2026