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.

text
┌────────────────┐     Due Date     ┌─────────────────────┐    Collection Window    ┌────────────────────┐
│ Active Billing │ ───────────────> │  Renewal Generated  │ ──────────────────────> │  Window Closes     │
│ Subscription   │                  │  (Protected Price)  │    (Default 14 Days)    │  (Invoice Expired) │
└────────────────┘                  └─────────────────────┘                         └────────────────────┘
        ▲                                      │                                           │
        │               Payment Paid           │                       Unpaid              ▼
        └──────────────────────────────────────┘                                  ┌────────────────────┐
                                                                                  │ Expired/Cancelled  │
                                                                                  │ (Admin Reactivate) │
                                                                                  └────────────────────┘

Setup Requirements

To use Subscriptions, verify the following prerequisites:

  1. The Subscriptions module is toggled ON under WooNooW > Settings > Modules.
  2. Your payment gateway supports tokenization / recurring payments (e.g., Stripe, PayPal, Midtrans, or manual BACS/Bank Transfer).
  3. 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:

SettingDefaultRange / OptionsDescription
default_statusactiveactive, pendingInitial status assigned to new subscriptions immediately after initial checkout payment.
allow_customer_canceltrueBoolean toggleAllows customers to cancel their subscriptions from the My Account dashboard.
allow_customer_pausetrueBoolean toggleAllows customers to pause and resume their subscriptions from My Account.
max_pause_count30 to 10 (0 = unlimited)Maximum lifetime pause operations permitted per subscription.
max_pause_duration_days00 to 365 (0 = unlimited)Maximum consecutive days a subscription may remain paused before WooNooW automatically resumes it.
price_sync_on_renewaluse_storeduse_stored, use_current_product_pricePrice applied when renewing. use_stored grandfathers the customer at their original price; use_current_product_price updates renewals to match current catalog pricing.
unpaid_renewal_max_age_days141 to 90 daysPayment collection window. Number of days after the due date that an unpaid renewal invoice remains payable at the protected recurring price.
renewal_retry_enabledtrueBoolean toggleAutomatically retry failed renewal payments using stepped delay intervals.
renewal_retry_days1,3,5Comma-separated integersDays after payment failure to attempt subsequent charges.
expire_after_failed_attempts31 to 10Number of failed payment attempts before the subscription automatically transitions to expired.
send_renewal_remindertrueBoolean toggleSends advance renewal reminder emails before the subscription renewal date.
reminder_days_before31 to 14 daysDays prior to the renewal date that reminder emails are sent.
force_manual_renewalfalseBoolean toggleEmergency kill switch. Treats all gateways as manual renewal only, bypassing automatic debit attempts regardless of gateway capabilities.

Creating a Subscription Product

WooNooW integrates recurring billing options directly into standard WooCommerce products:

  1. Navigate to Products in the Admin SPA and create or edit a product.
  2. In the General tab, check Enable subscription for this product.
  3. Configure the recurring parameters:
    • Billing Period & Interval: Billing cadence (e.g., Every 1 Month or Every 1 Year).
    • Trial Days: Optional introductory trial duration before the first recurring charge (e.g., 14 days).
    • Signup Fee: Optional one-time fee added to the initial checkout payment.

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 datetime Y-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 to order_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, or failed) 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-hold subscriptions 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 to cancelled with 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:

http
POST /wp-json/woonoow/v1/subscriptions/{id}/reactivate
Authorization: Required (manage_woocommerce capability)

Reactivation workflow

When an administrator clicks Reactivate on an expired or cancelled subscription:

  1. Permission check: Enforces manage_woocommerce capability (admin only).
  2. Terminal state validation: Only subscriptions with status expired or cancelled can be reactivated.
  3. Catalog availability check: Verifies the underlying product and variation are not permanently deleted or in the trash.
  4. Order cleanup: Any prior pending, on-hold, or failed renewal orders are marked as superseded (_woonoow_renewal_superseded = '1') and cancelled.
  5. Subscription reset: The subscription is moved to on-hold, failed_payment_count is reset to 0, and paused_at, reminder_sent_at, and cancel_reason are cleared.
  6. 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)).
  7. 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_count in 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 (default 0 = disabled/unlimited).
  • When set to a positive integer (e.g., 30 days), the daily cron woonoow_check_pause_expirations searches for subscriptions where: status = 'on-hold' AND paused_at <= Now - max_pause_duration_days
  • WooNooW automatically calls SubscriptionManager::resume(), recalculates next_payment_date from the resumption moment, resets paused_at, transitions status back to active, and fires woonoow/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 active and on-hold.
  • Cancel: Cancels the subscription. If there is prepaid time remaining before next_payment_date, defaults to pending-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 marked failed (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:

Cron HookFrequencyHandlerResponsibility
woonoow_process_subscription_renewalsHourlySubscriptionScheduler::process_renewalsDispatches renewals for all active subscriptions whose next_payment_date <= Now.
woonoow_check_expired_subscriptionsDailySubscriptionScheduler::check_expirationsTransitions end-dated subscriptions to expired and finalizes pending-cancel subscriptions whose cycle has ended.
woonoow_send_renewal_remindersDailySubscriptionScheduler::send_remindersSends advance email notices to customers reminder_days_before their next billing date.
woonoow_retry_unpaid_renewalsTwice DailySubscriptionScheduler::retry_unpaid_renewalsSends daily reminders for open renewal invoices; cancels expired invoices whose collection deadline has passed.
woonoow_check_pause_expirationsDailySubscriptionScheduler::check_pause_expirationsAutomatically resumes subscriptions that have remained paused beyond max_pause_duration_days.

Last updated Sep 11, 2026