XC WooCommerce Subscriptions Lite
A complete subscription engine for WooCommerce. This guide covers everything you need to install, configure, operate, and extend the plugin — from the first license activation to advanced developer integrations.
Introduction
XC WooCommerce Subscriptions Lite turns any WooCommerce store into a fully featured subscription business. It ships every operational tool a recurring-revenue business needs — billing automation, customer self-service, content gating, coupons, analytics, and six branded email templates — for one lifetime payment with six months of direct developer support included.
This documentation is structured top-to-bottom in the order you will need it. Read the first three sections to install and license the plugin. Skim the configuration sections to set defaults that match your business. Then dive into whichever feature area you need to use. The Developer reference and Troubleshooting sections are for when you need them.
What you can build with this plugin
- Recurring digital products and memberships (newsletters, courses, communities)
- Streaming and content services with €0-checkout free trials
- Subscription boxes with weekly, monthly, or quarterly fulfillment cadences
- Recurring services (gym, coaching, SaaS, donations) with sign-up fees
- Premium content gated by an active subscription, page-by-page or inline
- B2B annual subscriptions with extended trials and renewal reminders
What this plugin deliberately does not do
To stay focused, fast, and easy to support, the following functionality is intentionally excluded:
- Variable / variation subscriptions (one billing schema per product)
- Plan switching between subscriptions mid-cycle
- Pro-rata billing calculations
- Multiple subscriptions inside a single cart (one subscription per checkout)
If you require any of the above, this plugin is not the right fit and we will tell you so before purchase.
Server requirements
Before installing, verify your hosting environment meets the minimum baseline below. Most modern WordPress hosts comfortably exceed these requirements.
| Requirement | Minimum | Recommended |
|---|---|---|
| WordPress | 6.0 | 6.7 or newer |
| WooCommerce | 8.0 | 9.0 or newer |
| PHP | 8.0 | 8.2 or 8.3 |
| MySQL | 5.7 | 8.0+ |
| MariaDB (alternative) | 10.4 | 10.6+ |
| PHP memory limit | 128 MB | 256 MB |
PHP max_execution_time | 30 s | 60 s |
| Disk space (plugin) | ~5 MB | — |
| HTTPS for license validation | Required | Required |
Required PHP extensions
These extensions ship with most PHP installations. Verify they are enabled by viewing Tools → Site Health → Info → Server in WordPress admin.
openssl— required for license-key encryption at restcurl— required for license API and update checksjson— required for REST API endpointsmbstring— required for safe string handling across localesmysqliorpdo_mysql— required for database access
Cron requirements
The plugin uses two scheduled tasks: an hourly check for due renewals and trial-ending notices, and a daily activity-log cleanup. WordPress runs these via WP-Cron on every page load. For low-traffic sites, configure a real system cron instead so renewals fire on time:
*/15 * * * * curl -s https://your-site.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
DISABLE_WP_CRON set to true), set up a real cron as shown above — otherwise renewals will never run.
Outbound network access
The plugin contacts xaniacode.com for two purposes only — license validation and update checks. There is no analytics, no telemetry, no third-party CDN. Allowlist these endpoints if your firewall blocks outbound HTTPS:
https://xaniacode.com/api/v1/license/*(license activate / verify / deactivate)https://xaniacode.com/updates/wordpress(update info and download URL)
Compatibility matrix
| HPOS (High-Performance Order Storage) | ✅ Full support, declared via FeaturesUtil |
| Cart & Checkout Blocks | ✅ Full compatibility declared |
| Multisite | ✅ Network or per-site activation |
| Page builders | ✅ Gutenberg, Elementor, Divi, Beaver Builder, Bricks |
| Themes | ✅ Theme-independent — works with any well-coded theme |
| WPML / Polylang | ⚠️ Strings are translatable; multi-currency on subscriptions requires a third-party multi-currency plugin |
| GDPR | ✅ No tracking, no third-party CDN, no analytics |
Installation
The plugin is distributed as a standard WordPress ZIP. There are two ways to install it.
Method 1 — Upload through WordPress admin (recommended)
- Download
xc-woocommerce-subscriptions-lite.zipfrom your XaniaCode account dashboard. - In WordPress admin, go to Plugins → Add New → Upload Plugin.
- Click Choose File, select the ZIP, click Install Now.
- After installation completes, click Activate Plugin.
- If WooCommerce is not installed, the plugin shows a notice and refuses to load further. Install and activate WooCommerce first, then reactivate.
Method 2 — FTP / SFTP upload
- Unzip the plugin file locally.
- Upload the resulting
xc-woocommerce-subscriptions-litefolder to/wp-content/plugins/on your server using FTP or SFTP. - In WordPress admin, go to Plugins and click Activate next to XC WooCommerce Subscriptions Lite.
xc-woocommerce-subscriptions-lite/xc-woocommerce-subscriptions-lite/ — WordPress will not recognise it. The plugin's main .php file must sit one level deep inside /wp-content/plugins/.
What happens on activation
The first time the plugin activates it performs the following one-time setup tasks automatically:
- Creates three custom database tables:
{prefix}xc_wcsl_subscription_meta,{prefix}xc_wcsl_log, and{prefix}xc_wcsl_renewals - Adds 14 capabilities to the administrator and shop_manager roles for managing the subscription post type
- Schedules two recurring cron events (
xc_wcsl_check_subscriptionshourly,xc_wcsl_cleanup_logsdaily) - Creates default settings under the
xc_wcsl_settingsoption - Marks the rewrite rules for a one-shot flush, which happens automatically on the next admin page load
- Starts the 14-day free trial countdown
Reactivation is idempotent — settings, capabilities, and database tables are preserved across deactivate / reactivate cycles.
License activation
The plugin runs in trial mode for 14 days from first activation, no license key required. Within that window, every feature is fully functional. To continue past the trial — and to receive automatic updates — activate your license.
Activate your license
- Open WooCommerce → XC Subscriptions Lite (License) in WordPress admin.
- Locate your license key in the order confirmation email from XaniaCode, or under My Account → My Licenses on xaniacode.com.
- Paste the key into the License key field.
- Click Activate. A success message confirms activation; the page now shows your activation status, expiry (if applicable), and remaining domain seats.
Deactivating the license on a site
If you are migrating the plugin to a different domain, deactivate the license on the old site first to free up the seat:
- Open WooCommerce → XC Subscriptions Lite (License).
- Click Deactivate license.
- Wait for the confirmation message before changing domains.
What happens after the license expires
The plugin is designed to never break paying customers. When the license expires:
- ✅ Existing subscriptions continue running
- ✅ Renewal orders are still generated automatically
- ✅ Customer self-service (cancel, pause, reactivate) keeps working
- ✅ All admin tooling stays available
- ❌ New subscription sign-ups on your storefront are paused — the Subscribe button stops working until the license is renewed
A persistent admin notice on plugin pages reminds you to reactivate.
First-time setup
After activation, walk through the four settings tabs in order. Each one takes about a minute to configure for a typical store. Defaults are sensible — most stores can leave the General and Renewals tabs unchanged.
- Open WooCommerce → Subscriptions Settings.
- Review the General tab. Decide whether customers can self-serve cancel and pause, and pick the active and inactive subscriber roles.
- Review the Renewals tab. The defaults (3 retries, 2 days apart) work for most gateways.
- Review the Emails tab. Enable renewal reminders and set how many days before renewal the reminder is sent.
- Review the Advanced tab. Set log retention (default 60 days). Use the Run scheduler now button to verify cron tasks fire correctly.
- Create a test subscription product and complete a test checkout (see Creating products).
That's it — the plugin is now ready for production.
General settings
Found under WooCommerce → Subscriptions Settings → General.
enable_subscriptionsyesallow_cancelyesallow_pauseyesmixed_checkoutnorole_activesubscriberrole_inactivecustomeradministrator or shop_manager roles, regardless of their subscription status. This protects against accidentally demoting your own staff accounts.
Renewal settings
Found under WooCommerce → Subscriptions Settings → Renewals. These options control what happens when a renewal payment fails.
failed_payment_retries3 · Range: 0–10failed_payment_retry_days2 · Range: 1–30auto_complete_renewalyescancel_grace_days0 · Range: 0–365Email settings
Found under WooCommerce → Subscriptions Settings → Emails. Per-email customization (subject, heading, enable / disable each email individually) is under WooCommerce → Settings → Emails.
send_renewal_reminderyesrenewal_reminder_days3 · Range: 1–30Advanced settings
Found under WooCommerce → Subscriptions Settings → Advanced. These are operational and developer-facing options.
log_retention_days60 · Range: 1–3650delete_data_on_uninstallnoCreating subscription products
Subscription products are regular WooCommerce products with a different Product data type. Once you've configured the plugin, creating a subscription is a 60-second job.
Step-by-step
- Go to Products → Add New.
- Add a title, description, image, and category as you would for any product.
- Set the Regular price in the Product data box. This is the recurring price (the amount charged per billing cycle).
- In the Product data dropdown, select Simple subscription.
- Click the Subscription tab that appears.
- Configure the subscription fields (see reference below).
- Click Publish.
Subscription tab fields
00 means unlimited (until cancelled). Set to 12 with monthly billing to create an annual commitment that expires after a year. Set to 6 with monthly billing for a 6-month course or program.0.000dayWhat gets stored
When you save the product, all these fields are persisted as post meta on the product. The plugin's WC_Product_XC_Subscription class exposes them via getters that match WooCommerce conventions, so any theme or extension built on the standard WC_Product API can read them.
Product examples
Ten concrete subscription configurations covering common business models. Use these as starting points.
| Use case | Price | Billing | Length | Trial | Sign-up fee |
|---|---|---|---|---|---|
| Newsletter premium | €5 | 1 month | 0 (unlimited) | — | — |
| SaaS annual | €99 | 1 year | 0 (unlimited) | — | — |
| Streaming with trial | €12 | 1 month | 0 | 14 day | — |
| Subscription box quarterly | €45 | 3 months | 0 | — | — |
| Coffee weekly | €15 | 1 week | 0 | — | — |
| Gym membership | €40 | 1 month | 0 | — | €100 |
| 6-month course | €99 | 1 month | 6 | — | — |
| Insurance bi-annual | €250 | 6 months | 0 | — | — |
| B2B SaaS with trial | €500 | 1 year | 0 | 30 day | — |
| Recurring donation | €10 | 1 month | 0 | — | — |
Customer experience
From the customer's point of view, buying and managing a subscription should feel exactly like buying any other WooCommerce product — only with a few clearly-marked recurring elements.
Single product page
The product page renders with three subscription-aware elements:
- The price displays as a recurring schedule, e.g. "€28 / month" or "€45 every 3 months"
- If a free trial is configured, a small label is added: "with 14-day free trial"
- If a sign-up fee is set, a small label is added: "and a €100 sign-up fee"
- The Add to cart button reads Subscribe instead of Add to cart
Cart page
In the cart, the subscription line item shows the recurring schedule. During an active free trial, the line price displays as Free trial and the cart subtotal is €0 (plus any sign-up fee). Below the cart totals, a small box explains the recurring billing:
Recurring totals
─────────────────
Subtotal: €28.00 / month
Total: €28.00 / month
First payment will be processed on May 19, 2026.
Checkout
Checkout filters available payment gateways automatically — only gateways with tokenization support or manual gateways (BACS, cheque) are offered for subscription carts. Guest checkout is disabled for subscription orders; the customer is required to create an account.
My Account portal
After purchase, the customer sees a new Subscriptions tab in their My Account page. The tab lists all their subscriptions with status, schedule, and next payment date. Clicking a row opens the single-subscription view with three actions:
- Cancel — ends the subscription (or moves to grace-period state if configured)
- Pause — temporarily stops renewals; can be reactivated anytime
- Reactivate — visible only when the subscription is paused
All three actions are nonce-protected and only available to the subscription's owner.
Free trials and sign-up fees
Trials and sign-up fees are independent and can be combined in any way.
Free trial only
Configure a trial length on the product. The customer pays €0 at sign-up. The first recurring charge fires on the trial end date. A Trial ending email is sent automatically one day before the trial ends, giving the customer a chance to cancel if the service isn't for them.
Sign-up fee only
Configure a sign-up fee on the product. The customer pays the recurring price plus the fee at sign-up. Future renewals only charge the recurring price.
Trial plus sign-up fee
Both can coexist. The customer pays the sign-up fee at checkout (cart total = sign-up fee, recurring price = €0 during trial). The first recurring charge fires when the trial ends.
| Configuration | Customer pays at checkout | Customer pays at first renewal |
|---|---|---|
| Recurring €28/mo | €28 | €28 (1 month later) |
| Recurring €28/mo + 14-day trial | €0 | €28 (14 days later) |
| Recurring €40/mo + €100 sign-up fee | €140 | €40 (1 month later) |
| Recurring €40/mo + €100 sign-up fee + 14-day trial | €100 | €40 (14 days later) |
Subscription list
Found under WooCommerce → Subscriptions. This is the operational hub — every subscription on your store appears here.
What you see
Each row shows: subscription ID and title, customer name, status, recurring schedule, next payment date, and action links. The list is sortable by date and filterable by status (Pending, Active, On hold, Paused, Cancelled, Expired).
Status filter views
Above the list, status views show the count of subscriptions in each state:
All (412) | Pending (3) | Active (385) | On hold (12) | Paused (4) | Cancelled (8) | Expired (0)
Click any view to filter the list. The view "All" matches subscriptions in any status.
Search
The search box at the top of the list searches subscription IDs, titles, and now also customer email, login, and display name. Type any of those to find a customer's subscriptions instantly.
Custom filter dropdown
A custom Filter by status dropdown appears above the list with all six subscription states. Use it to narrow the visible list before applying a bulk action or exporting.
Bulk actions
Manage many subscriptions at once with the bulk-action dropdown above the list.
Available actions
- Mark as Active — moves selected subscriptions to Active status
- Pause — moves selected subscriptions to Paused
- Put on hold — moves selected subscriptions to On hold
- Cancel — moves selected subscriptions to Cancelled
How to use
- Tick the checkbox on each row you want to affect, or tick the header checkbox to select all visible rows.
- Open the Bulk actions dropdown and choose the action.
- Click Apply.
- A confirmation notice shows the number of subscriptions updated.
CSV export
Export the current filtered list of subscriptions to CSV with one click. Useful for reporting, accounting handoff, and spreadsheet analysis.
How to export
- Apply any filter (status, search) to scope the list to what you want to export.
- Click Export to CSV (button next to the bulk-action dropdown).
- A CSV file downloads immediately with filename
xc-subscriptions-YYYY-MM-DD-HHMMSS.csv.
Exported columns
The CSV contains 17 columns:
- ID
- Status
- Customer ID
- Customer email
- Product ID
- Product name
- Recurring total
- Currency
- Period
- Interval
- Start date
- Next payment
- End date
- Parent order
- Completed payments
- Failed payments
- Payment method
Single subscription view
Click any subscription in the list to open the single-subscription edit screen.
Subscription details panel
The main panel summarises the subscription:
- Status (with dropdown to change manually)
- Customer name and link to user profile
- Linked product
- Recurring schedule (price + period)
- Start date, next payment, end date
- Parent order link
- Payment method
- Payments counter (completed / failed)
- Recent activity log
Manual actions available
- Change status — update the status dropdown and click Update
- Create renewal order — generates the next renewal order immediately, bypassing the schedule (useful for testing or force-billing)
- Update next payment date — change the meta directly to reschedule
Activity log
Every status change, every renewal order created, every failed payment, and every customer self-service action is logged in the activity feed at the bottom of the screen. The log is searchable by event type and stored in a custom database table.
Subscription notes
The Subscription notes meta box on the right side of the single-subscription screen lets you write two types of notes.
Private (admin only)
Visible only to administrators and shop managers in the meta box. Useful for internal reminders ("call back about pricing", "VIP — escalate any issue").
Customer-visible
Shown to the customer in their My Account portal under that subscription. When you submit a customer-visible note, the customer is also notified by email with the note content and a link to view the subscription.
How to add a note
- Type the note text in the textarea inside the Subscription notes meta box.
- Choose the visibility from the dropdown — Private (admin only) or Send to customer.
- Click Add note.
- The note appears at the top of the notes list. Customer-visible notes have a green left border; private notes have a blue one.
Dashboard KPI widget
The plugin adds a widget to the WordPress dashboard (Dashboard → Home) summarising your subscription business.
What it shows
- Active subscribers — total count of subscriptions in Active status
- On hold count — subscriptions waiting on a failed payment retry
- Monthly recurring revenue (MRR) — sum of all active subscriptions normalised to monthly
- New (last 30d) — count of subscriptions activated in the past 30 days
- New (last 7d) — same metric over a 7-day window
- Cancelled (last 30d) — count of cancellations in the past 30 days
- Cancelled (last 7d) — same over 7 days
How MRR is calculated
The widget queries every active subscription's recurring total and billing cadence, then normalises to a monthly amount. Daily billing × 30, weekly × 4.333, monthly × 1, yearly ÷ 12. Currencies are summed per active currency without conversion. The result is an estimate, not a financial report — for accounting use the CSV export and your accounting software.
Caching
KPIs are cached in a transient for 5 minutes to keep the dashboard fast. After bulk actions or major changes, KPIs may take up to 5 minutes to update.
Content gating
Restrict pages, posts, or specific content blocks to active subscribers. Three integration points are provided — pick whichever fits your use case.
Method 1 — Page-level meta box
The simplest method. Restricts an entire post or page.
- Edit any post or page.
- In the right sidebar, find the XC Subscription access meta box.
- Tick Restrict to active subscribers.
- Optionally pick a specific subscription product from the dropdown — only customers subscribed to that product will see this page.
- Optionally enter a redirect URL — non-subscribers will be sent there. Leave empty to show a deny message instead.
- Optionally enter a custom deny message. Falls back to a generic message with a login / shop link.
- Click Update on the post.
Method 2 — Shortcode
For inline content gating inside any post, page, or widget.
Basic usage
[xc_wcsl_member]
This text is only visible to active subscribers.
[/xc_wcsl_member]
Restrict to a specific product
[xc_wcsl_member product_id="123"]
Only customers with an active subscription to product 123 see this.
[/xc_wcsl_member]
Custom deny message
[xc_wcsl_member message="Become a member to read the full article"]
Premium content here…
[/xc_wcsl_member]
Method 3 — Helper functions (PHP)
For theme integration. Use these in header.php, single.php, custom templates, or any plugin code.
// Returns true if the current user has at least one active subscription
if ( xc_wcsl_user_has_active_subscription() ) {
// Show member-only menu, button, or section
}
// Returns true if the current user has an active subscription for product 123
if ( xc_wcsl_user_has_active_subscription_for_product( 123 ) ) {
// Show product-specific gated content
}
// Both functions accept an optional user_id argument
xc_wcsl_user_has_active_subscription( $other_user_id );
xc_wcsl_user_has_active_subscription_for_product( 123, $other_user_id );
Capability bypass
Users with the manage_woocommerce capability (administrators and shop managers) can always view restricted content, regardless of subscription status. This prevents you from locking yourself out of pages while editing them.
Subscription-aware coupons
The plugin extends WooCommerce's standard coupon system with three subscription-specific scopes. Configure on each coupon individually under Marketing → Coupons → Edit coupon.
The three scopes
How to set the scope
- Edit a coupon under Marketing → Coupons.
- Scroll to the General section of the coupon data tabs.
- Find the XC Subscription scope dropdown.
- Select the desired scope.
- Save the coupon.
How recurring re-application works
When a renewal order is generated, the plugin checks every coupon used on the parent order. For each coupon with scope Every renewal payment, it calls WooCommerce's standard apply_coupon() on the renewal order before totals are calculated. Coupons with other scopes are ignored on renewals.
Email system
Six branded email templates ship with the plugin. Each is a full WC_Email subclass and integrates with the standard WooCommerce email system.
The six emails
| Trigger | Audience | |
|---|---|---|
| Subscription Activated | Status changes to Active | Customer |
| Subscription Paused | Status changes to Paused | Customer |
| Subscription Cancelled | Status changes to Cancelled | Customer |
| Subscription Expired | Status changes to Expired | Customer |
| Renewal Reminder | N days before next payment (configurable) | Customer |
| Trial Ending | 1 day before trial end date | Customer |
Customising emails
Each email can be customised at WooCommerce → Settings → Emails. Click Manage on the email row to:
- Enable or disable the email
- Change the subject line (with placeholders like
{site_title},{subscription_id}) - Change the heading shown at the top of the email body
- Override the HTML and plain-text templates
Template files
To override the HTML or plain-text template, copy the relevant file from:
/wp-content/plugins/xc-woocommerce-subscriptions-lite/templates/emails/
To your active theme:
/wp-content/themes/your-theme/woocommerce/emails/
WooCommerce's standard template loader will pick up your override automatically.
Renewal engine
The plugin's renewal engine generates renewal orders on schedule and handles payment retries, race conditions, and end-of-life transitions automatically.
How renewals are processed
Once an hour, the xc_wcsl_check_subscriptions cron runs XC_WCSL_Renewal::process_due(), which:
- Acquires a global transient lock
xc_wcsl_renewal_running(5-minute TTL) to prevent concurrent runs. - Queries up to 50 subscriptions in Active status with
_next_payment_date <= now. - For each subscription, acquires a per-subscription lock (10-minute TTL) to prevent the same row being processed twice.
- If the subscription has reached its end date, transitions it to Expired and skips renewal.
- Otherwise, creates a new WooCommerce order linked back to the subscription via meta, and triggers the gateway-specific renewal-payment hook.
- Releases per-subscription lock and moves to the next.
- Releases the global lock at the end of the run.
Payment retry logic
When a renewal order is marked Failed, the plugin increments the failed-payments counter on the subscription and reschedules the next attempt by the configured retry interval. After the configured retry count is exceeded, the subscription is moved to On hold and the customer receives no further automatic charges until they update their payment method through customer support or self-service flows.
Manual scheduler runner
For testing — or to force the queue to flush immediately — use the Run scheduler now button under WooCommerce → Subscriptions Settings → Advanced. This fires process_due() synchronously and reports back the number of renewals processed.
Action hooks for gateway integration
Custom payment gateways can subscribe to the following actions to handle automatic re-charging of saved payment methods on renewals:
// Fired when a new renewal order is created
do_action( 'xc_wcsl_renewal_order_created', $renewal_order, $subscription );
// Generic gateway-aware hook (replaces {gateway} with the gateway slug)
do_action( "xc_wcsl_process_renewal_payment_{$gateway}", $renewal_order, $subscription );
Most modern gateways (Stripe, PayPal Payments, Mollie) ship with subscription-friendly tokenization and re-charge saved payment methods automatically when these actions fire. Manual gateways (BACS, cheque) leave the order in Pending payment and the customer pays the renewal manually.
REST API
The plugin exposes a small REST API under namespace xc-wcsl/v1 for automation, dashboards, and external integrations. All endpoints require authentication via standard WP REST authentication (cookies, application passwords, or JWT) and capability checks.
Endpoints
| Method | Endpoint | Capability required | Purpose |
|---|---|---|---|
| GET | /subscriptions | edit_xc_wcsl_subscriptions | List subscriptions, filterable by status |
| GET | /subscriptions/<id> | edit_xc_wcsl_subscription | Get a single subscription |
| POST | /subscriptions/<id>/status | edit_xc_wcsl_subscription | Update status |
| POST | /subscriptions/<id>/renew | edit_xc_wcsl_subscription | Manually trigger a renewal order |
List subscriptions
GET /wp-json/xc-wcsl/v1/subscriptions?status=active&per_page=20
Query parameters:
status : pending | active | on-hold | paused | cancelled | expired | any
per_page : 1–100 (default 20)
page : 1+ (default 1)
customer : user ID (optional, filter by customer)
Response (excerpt):
{
"subscriptions": [
{
"id": 412,
"status": "xcsl-active",
"customer_id": 89,
"product_id": 17,
"recurring_total": "28.00",
"currency": "EUR",
"period": "month",
"interval": 1,
"start_date": "2026-04-15 12:30:00",
"next_payment": "2026-05-15 12:30:00",
"completed_payments": 1,
"failed_payments": 0
}
],
"total": 412,
"pages": 21
}
Update status
POST /wp-json/xc-wcsl/v1/subscriptions/412/status
Content-Type: application/json
X-WP-Nonce: <nonce>
{ "status": "xcsl-cancelled", "note": "Cancelled by API" }
Trigger renewal
POST /wp-json/xc-wcsl/v1/subscriptions/412/renew
X-WP-Nonce: <nonce>
Returns the new renewal order ID on success, or a WP_Error with HTTP 4xx on failure.
Hooks reference
The plugin provides extensive action and filter hooks for developers. Below are the most useful ones for theme and plugin integration.
Action hooks (events)
| Hook | Fired when | Args |
|---|---|---|
xc_wcsl_subscription_created | A new subscription is created (typically at checkout) | $subscription, $args |
xc_wcsl_subscription_status_changed | Status changes (any direction) | $subscription, $old, $new |
xc_wcsl_subscription_status_xcsl-active | Subscription transitions to Active | $subscription, $old |
xc_wcsl_subscription_status_xcsl-cancelled | Subscription transitions to Cancelled | $subscription, $old |
xc_wcsl_subscription_status_xcsl-paused | Subscription transitions to Paused | $subscription, $old |
xc_wcsl_subscription_status_xcsl-expired | Subscription transitions to Expired | $subscription, $old |
xc_wcsl_renewal_order_created | A renewal order is generated | $order, $subscription |
xc_wcsl_send_renewal_reminder | Renewal reminder email is being sent | $subscription |
xc_wcsl_send_trial_ending | Trial-ending email is being sent | $subscription |
Filter hooks (modify values)
| Hook | Modifies |
|---|---|
xc_wcsl_add_to_cart_text | The "Subscribe" button label on subscription products |
xc_wcsl_restrict_post_types | List of post types where the access meta box is shown (default: post, page) |
Helper functions
// Subscription access checks
xc_wcsl_user_has_active_subscription( $user_id = 0 ): bool
xc_wcsl_user_has_active_subscription_for_product( $product_id, $user_id = 0 ): bool
// Working with the subscription object
$subscription = new XC_WCSL_Subscription( $post_id );
$subscription->get_status(); // 'xcsl-active' (without wc- prefix)
$subscription->get_customer_id();
$subscription->get_product_id();
$subscription->get_recurring_total();
$subscription->get_next_payment_date();
$subscription->update_status( 'xcsl-cancelled', 'Reason' );
$subscription->can_be_cancelled();
$subscription->can_be_paused();
$subscription->can_be_reactivated();
$subscription->get_formatted_schedule(); // "€28 every month"
Database tables
The plugin creates three custom tables on activation. All names are prefixed with the WordPress table prefix (default wp_).
{prefix}xc_wcsl_subscription_meta
Reserved for subscription-specific metadata that should not live in postmeta. Currently used sparingly; most subscription data lives in standard postmeta on the subscription post type.
meta_id BIGINT UNSIGNED PK AUTO_INCREMENT
subscription_id BIGINT UNSIGNED -- foreign reference to wp_posts.ID
meta_key VARCHAR(255)
meta_value LONGTEXT
{prefix}xc_wcsl_log
Activity log. Every status change, every payment success or failure, every customer-initiated action, every admin note is recorded here. Pruned daily by the xc_wcsl_cleanup_logs cron based on Log retention (days) setting.
log_id BIGINT UNSIGNED PK AUTO_INCREMENT
subscription_id BIGINT UNSIGNED
user_id BIGINT UNSIGNED
level VARCHAR(20) -- info, warning, error
event VARCHAR(100) -- status_change, renewal_created, admin_note, customer_note, ...
message TEXT
context LONGTEXT -- JSON-encoded extra data
created_at DATETIME
{prefix}xc_wcsl_renewals
Tracks every renewal order created, scheduled, and processed. Useful for reporting and reconciliation.
renewal_id BIGINT UNSIGNED PK AUTO_INCREMENT
subscription_id BIGINT UNSIGNED
order_id BIGINT UNSIGNED -- linked WC order
status VARCHAR(50)
scheduled_for DATETIME
processed_at DATETIME
amount DECIMAL(19,4)
currency VARCHAR(10)
Troubleshooting
Solutions to the most common issues, in order of frequency.
"Subscribe" button is missing on the product page
The Subscribe button only appears for products configured as Simple subscription. Verify:
- Open the product edit screen and confirm the Product data dropdown shows Simple subscription, not Simple product.
- Confirm the product status is Published, not Draft.
- Confirm a price is set under Regular price.
- Clear any caching plugin (cache, opcode cache, CDN) — old HTML may be served.
If the product type dropdown does not show Simple subscription as an option, the plugin is not active. Check Plugins and reactivate.
Renewals are not running
Most renewal issues come down to WP-Cron not firing. Verify in this order:
- Open WooCommerce → Subscriptions Settings → Advanced and click Run scheduler now. If renewals process, the plugin works correctly — the issue is cron.
- Check that
DISABLE_WP_CRONis not set totrueinwp-config.php. If it is, configure a real system cron (see Server requirements). - For low-traffic sites, WP-Cron only runs when a page is loaded. Set up a real cron pinging
wp-cron.phpevery 15 minutes. - Verify the cron event exists: install the WP Crontrol plugin and look for
xc_wcsl_check_subscriptions. If missing, deactivate and reactivate the subscription plugin.
Emails are not being sent
If transactional emails are missing, check:
- Open WooCommerce → Settings → Emails and confirm the relevant subscription email is Enabled in its settings.
- Send a test of any standard WooCommerce email (e.g. New Order). If that also fails, the issue is your server's mail configuration, not the plugin.
- Install an SMTP plugin (FluentSMTP, WP Mail SMTP) and configure a real SMTP server.
wp_mail()via PHP'smail()is unreliable on most hosts. - For Renewal Reminders specifically, check that Send renewal reminders is enabled under Subscription Settings → Emails.
License activation fails with a generic error
For security reasons, the activation endpoint returns a generic error rather than disclosing whether the key is invalid, expired, revoked, or out of seats. Try:
- Verify the key in your XaniaCode account dashboard under My Licenses.
- If you previously activated this key on another domain, deactivate it there first.
- Confirm the site has outbound HTTPS access to
xaniacode.com— some firewalls block outgoing connections. - If you've hit the rate limit (5 failed attempts per 15 minutes), wait 15 minutes before retrying.
- Open a support ticket from your XaniaCode dashboard if the key still fails after the above checks.
Cart shows the wrong total during a free trial
During an active trial, the cart line price should display as Free trial and the subtotal should reflect only the sign-up fee, if any. If the cart shows the full recurring price:
- Confirm the trial length on the product is greater than zero.
- Empty the cart and re-add the product (the trial pricing applies on add-to-cart, not retroactively).
- Clear any cart-level caching (page builders, fragment caches).
- Check for theme overrides of the cart template that might bypass the
woocommerce_cart_item_pricefilter.
HPOS-related conflicts
The plugin declares full HPOS compatibility. If you see warnings about plugin compatibility under WooCommerce → Status → Logs, the issue is usually a different plugin in your stack — not this one. Disable other plugins one by one to identify the conflict.
Plugin conflict — site shows critical error after activation
The plugin's bootstrap is wrapped in a try/catch block — a critical-error page typically means a fatal PHP error from a different plugin or theme. Diagnose:
- Enable WP_DEBUG in
wp-config.php:define( 'WP_DEBUG', true );anddefine( 'WP_DEBUG_LOG', true );. - Reproduce the error and inspect
/wp-content/debug.logfor the actual fatal message and file/line. - If the file is in
xc-woocommerce-subscriptions-lite, contact support with the log excerpt. - If the file is in another plugin or theme, that's where the conflict lies.
Customer cannot cancel from My Account
Verify:
- Under WooCommerce → Subscriptions Settings → General, Allow customer cancellation is set to Yes.
- The customer is logged in to the same account that owns the subscription.
- The subscription is in a state that can be cancelled (Active, On hold, Paused, Pending). Already-cancelled or Expired subscriptions cannot be re-cancelled.
"Translation loading triggered too early" notice in WordPress 6.7+
This notice appears when a plugin loads its textdomain too early. The plugin loads its textdomain on init priority 1 (the WP 6.7+ best practice), so this notice should never come from this plugin. If you see it pointing at xc-wcs-lite textdomain, contact support — and confirm you're on the latest plugin version.
"Subscribe" button POSTs but nothing happens
Usually a JavaScript error blocking form submission. Open the browser console (F12) and look for errors. If errors point to a different plugin or theme, that's where to investigate. Also check that the AJAX add-to-cart isn't failing silently — disable AJAX add-to-cart temporarily under WooCommerce → Settings → Products → General.
Restored subscriptions don't trigger emails
By design, manually re-activating a subscription via the admin Status dropdown does fire the activation email. If it doesn't, see the email troubleshooting section above. Bulk-activating large batches of subscriptions will send one email per subscription — confirm your mail server can handle the volume.
Technical FAQ
Can I change the subscription post type slug?
No — the slug xc_wcsl_subscription is hard-coded by design. Renaming it would invalidate every existing subscription record. If you need to integrate with a different system, use the REST API or the action hooks rather than altering the post type.
Does the plugin work with Multisite?
Yes. Activate per-site or network-wide. Each site has its own custom tables, options, and subscription records. License activation is per-site (one license seat per site) unless you have a multi-domain license.
Can I bulk-import subscriptions from another system?
The plugin does not ship with an import UI. For migrations, use the helper class:
$subscription = XC_WCSL_Subscription::create([
'customer_id' => 89,
'parent_order' => 0, // 0 if no original order
'product_id' => 17,
'period' => 'month',
'interval' => 1,
'recurring_total' => 28.00,
'currency' => 'EUR',
'status' => 'xcsl-active',
]);
Loop this in a small migration script. The created subscription will behave exactly as if it had been created through the normal checkout flow.
Are there any deprecated PHP functions used?
No. The plugin requires PHP 8.0+ and uses modern syntax throughout — typed properties, return type declarations, match expressions, named arguments where appropriate. No create_function, no implicit nullable types.
How does the plugin handle currency switches?
Currency is stored on each subscription at creation time. If your store later switches base currency, existing subscriptions keep billing in their original currency. New subscriptions use the new base currency. The plugin does not auto-convert — for multi-currency stores, use a third-party multi-currency plugin and ensure it stores the currency at order creation.
What happens if WooCommerce is deactivated while the plugin is active?
The plugin's bootstrap detects WooCommerce is missing and shows an admin notice; no further code runs. Existing subscription data is preserved. Reactivating WooCommerce restores normal operation.
Can I extend the subscription model with custom fields?
Yes. The subscription is a regular WordPress post type — use add_post_meta() / update_post_meta() to attach arbitrary fields. To expose them in the admin, add a meta box on the xc_wcsl_subscription post type.
How are timezones handled?
All dates are stored in UTC (gmdate) and displayed in the site's configured timezone via date_i18n. Customer-facing emails show the customer's local time when WooCommerce email helpers are used.
What is the maximum subscription throughput?
The hourly cron processes up to 50 due renewals per run (configurable via the xc_wcsl_process_due_batch_size filter). At one run per hour, that's 1,200 renewals per day per site — well within WordPress's typical capacity. For higher throughput, increase the batch size and run cron more frequently.
Does the plugin modify core WordPress or WooCommerce files?
No. All integration is through public hooks and APIs. The plugin can be deactivated cleanly without leaving residue (other than the database tables and post records, which are preserved unless Delete data on uninstall is enabled).
Are there any security audits available?
The plugin follows WordPress and WooCommerce security best practices: prepared statements throughout, capability checks on every admin action, nonce verification on every form, encrypted license storage, rate-limited license activation. The codebase has been reviewed for $_GET / $_POST sanitization, SQL injection vectors, and XSS in template output. Independent third-party audits are not currently published.
Can I disable the dashboard widget?
Yes. Add this to your theme's functions.php or a small mu-plugin:
add_action( 'wp_dashboard_setup', function() {
remove_meta_box( 'xc_wcsl_kpi', 'dashboard', 'normal' );
}, 100 );
Where do I find logs for debugging?
Three places:
- WooCommerce → Status → Logs — the plugin writes critical events to a log source named
xc-wcs-lite - Per-subscription activity log — visible at the bottom of the single subscription edit screen
- WordPress debug log — when
WP_DEBUG_LOGis enabled, fatal errors and stack traces land here
Support
Every license includes 6 months of direct developer support via your XaniaCode account dashboard. After the initial 6 months, support can be renewed for a small annual fee.
Before you contact support
Most issues are resolved by checking the relevant troubleshooting entry. When you do contact support, include:
- WordPress version
- WooCommerce version
- PHP version
- Active theme name
- List of active plugins (a screenshot of the Plugins page is fine)
- Steps to reproduce the issue
- Any error messages from
/wp-content/debug.log - If the issue is renewal-related, the subscription ID
The WooCommerce → Status → System Info screen has all the technical details — copy and paste it into your ticket.
How to open a ticket
- Sign in to your account at xaniacode.com.
- Go to My Account → Support.
- Click Open new ticket, choose XC WooCommerce Subscriptions Lite from the product list, and describe the issue.
- Tickets receive a first response within 1–2 business days, typically faster.
Bug reports and feature requests
Both are welcome through the same support ticket system. Bug reports with reproducible steps get priority. Feature requests are reviewed monthly — popular requests are added to the roadmap and the new release notifies all license holders.