Appearance
Subscriptions
Action hooks for Subscription lifecycle management, status transitions, and scheduled reminder notifications. These hooks let you react to subscription state changes, send custom notifications, and integrate with external systems. Most hooks pass the related Order and Customer models as well.
Subscription Status Changes
subscription_status_changed
fluent_cart/payments/subscription_status_changed — Fires on any subscription status transition
When it runs: This action fires whenever a Subscription's status changes from one value to another (e.g., pending to active, active to cancelled, etc.). It does not fire when other subscription data changes without a status transition. This is the generic handler -- for status-specific hooks, see fluent_cart/payments/subscription_{$status} below.
Parameters:
$data(array): Subscription status change dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'pending', // string — previous status 'new_status' => 'active', // string — current status after update ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_status_changed', function ($data) {
$subscription = $data['subscription'];
$oldStatus = $data['old_status'];
$newStatus = $data['new_status'];
// Log every status transition
fluent_cart_add_log(
'Subscription Status Changed',
sprintf('Subscription #%d changed from %s to %s', $subscription->id, $oldStatus, $newStatus),
'info'
);
}, 10, 1); subscription_active
fluent_cart/payments/subscription_active — Fires when a subscription becomes active
When it runs: This action fires when a subscription's status transitions to active. This may occur after initial payment, after reactivation, or when moving from trialing to active.
Parameters:
$data(array): Subscription dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'pending', // string — previous status 'new_status' => 'active', // string — always 'active' ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_active', function ($data) {
$customer = $data['customer'];
// Grant premium access when subscription activates
update_user_meta($customer->user_id, 'premium_member', true);
}, 10, 1); subscription_canceled
fluent_cart/payments/subscription_canceled — Fires when a subscription is cancelled
When it runs: This action fires when a subscription's status transitions to canceled. The canceled_at timestamp is automatically set if not already provided.
Parameters:
$data(array): Subscription dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'active', // string — previous status 'new_status' => 'canceled', // string — always 'canceled' ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_canceled', function ($data) {
$subscription = $data['subscription'];
$customer = $data['customer'];
// Revoke premium access on cancellation
update_user_meta($customer->user_id, 'premium_member', false);
// Notify admin
wp_mail(
get_option('admin_email'),
'Subscription Cancelled',
sprintf('Subscription #%d for %s has been cancelled.', $subscription->id, $customer->email)
);
}, 10, 1); subscription_paused
fluent_cart/payments/subscription_paused — Fires when a subscription is paused
When it runs: This action fires when a subscription's status transitions to paused. The subscription remains in the system but billing is temporarily halted.
Parameters:
$data(array): Subscription dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'active', // string — previous status 'new_status' => 'paused', // string — always 'paused' ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_paused', function ($data) {
$subscription = $data['subscription'];
// Temporarily suspend feature access
update_user_meta($data['customer']->user_id, 'subscription_paused', true);
}, 10, 1); subscription_expired
fluent_cart/payments/subscription_expired — Fires when a subscription expires
When it runs: This action fires when a subscription's status transitions to expired. After this hook fires, the system also stores a validity_expired_at meta value and dispatches the SubscriptionValidityExpired event.
Parameters:
$data(array): Subscription dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'active', // string — previous status 'new_status' => 'expired', // string — always 'expired' ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_expired', function ($data) {
$customer = $data['customer'];
// Remove premium access
update_user_meta($customer->user_id, 'premium_member', false);
// Notify the customer
wp_mail(
$customer->email,
'Your Subscription Has Expired',
'Your subscription has expired. Please renew to continue using premium features.'
);
}, 10, 1); subscription_failing
fluent_cart/payments/subscription_failing — Fires when a subscription payment is failing
When it runs: This action fires when a subscription's status transitions to failing, indicating that a renewal payment attempt has failed. The subscription is still technically active but requires payment attention.
Parameters:
$data(array): Subscription dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'active', // string — previous status 'new_status' => 'failing', // string — always 'failing' ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_failing', function ($data) {
$subscription = $data['subscription'];
$customer = $data['customer'];
// Alert the customer about the payment failure
wp_mail(
$customer->email,
'Payment Failed for Your Subscription',
sprintf('We were unable to process payment for subscription #%d. Please update your payment method.', $subscription->id)
);
}, 10, 1); subscription_expiring
fluent_cart/payments/subscription_expiring — Fires when a subscription is marked as expiring soon
When it runs: This action fires when a subscription's status transitions to expiring, indicating that the subscription is approaching its end-of-term and will not be renewed.
Parameters:
$data(array): Subscription dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'active', // string — previous status 'new_status' => 'expiring', // string — always 'expiring' ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_expiring', function ($data) {
$subscription = $data['subscription'];
$customer = $data['customer'];
// Send a win-back offer before the subscription fully expires
wp_mail(
$customer->email,
'Your Subscription is About to Expire',
'Renew now and get 10% off your next billing cycle!'
);
}, 10, 1); subscription_completed
fluent_cart/payments/subscription_completed — Fires when a subscription completes all billing cycles
When it runs: This action fires when a subscription's status transitions to completed. This occurs when the subscription has reached its end-of-term (EOT) -- i.e., the bill_count has met or exceeded bill_times. The next_billing_date is set to NULL and canceled_at is cleared.
Parameters:
$data(array): Subscription dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'active', // string — previous status 'new_status' => 'completed', // string — always 'completed' ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_completed', function ($data) {
$subscription = $data['subscription'];
$customer = $data['customer'];
// Thank the customer for completing their subscription term
wp_mail(
$customer->email,
'Subscription Complete',
sprintf('Your subscription #%d has completed all %d billing cycles. Thank you!', $subscription->id, $subscription->bill_times)
);
}, 10, 1); subscription_trialing
fluent_cart/payments/subscription_trialing — Fires when a subscription enters trial status
When it runs: This action fires when a subscription's status transitions to trialing. The subscription is in a free trial period and will transition to active (with billing) when the trial ends.
Parameters:
$data(array): Subscription dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription 'order' => $subscriptionModel->order, // \FluentCart\App\Models\Order 'customer' => $subscriptionModel->customer, // \FluentCart\App\Models\Customer 'old_status' => 'pending', // string — previous status 'new_status' => 'trialing', // string — always 'trialing' ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/payments/subscription_trialing', function ($data) {
$subscription = $data['subscription'];
$customer = $data['customer'];
// Grant trial access
update_user_meta($customer->user_id, 'trial_active', true);
// Schedule a welcome email
wp_mail(
$customer->email,
'Your Free Trial Has Started',
sprintf('Enjoy your trial! Your first payment will be on %s.', $subscription->next_billing_date)
);
}, 10, 1);Subscription Data Updates
subscription_data_updated
fluent_cart/subscription/data_updated — Fires when subscription data changes without a status transition
When it runs: This action fires when Subscription attributes are modified but the status remains the same. Examples include billing amount changes, next payment date adjustments, or metadata updates. It only fires when there are actual dirty (changed) fields on the model.
Parameters:
$data(array): Subscription update dataphp$data = [ 'subscription' => $subscriptionModel, // \FluentCart\App\Models\Subscription (already saved) 'updated_data' => [ // Only the fields that actually changed (dirty attributes), e.g.: 'recurring_total' => 2999, // int — new amount in cents 'next_billing_date' => '2025-03-15 00:00:00', // string — updated billing date ], ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php
Usage:
php
add_action('fluent_cart/subscription/data_updated', function ($data) {
$subscription = $data['subscription'];
$updatedData = $data['updated_data'];
// Log billing amount changes
if (isset($updatedData['recurring_total'])) {
fluent_cart_add_log(
'Subscription Amount Changed',
sprintf(
'Subscription #%d recurring total changed to %s',
$subscription->id,
number_format($updatedData['recurring_total'] / 100, 2)
),
'info'
);
}
// Sync next billing date with external calendar
if (isset($updatedData['next_billing_date'])) {
do_action('my_plugin/sync_billing_date', $subscription->id, $updatedData['next_billing_date']);
}
}, 10, 1);Reminders & Notifications
subscription_renewal_reminder
fluent_cart/subscription_renewal_reminder — Fires when a subscription renewal reminder is due
When it runs: This action fires on a scheduled basis (via Action Scheduler) when a subscription's next billing date is approaching. The reminder system supports multiple billing cycles (yearly, monthly, quarterly, half-yearly) and configurable "days before" thresholds. Only fires for subscriptions with active or trialing status. The stage name indicates how many days before renewal (e.g., before_30, before_7).
Parameters:
$data(array): Renewal reminder dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'order' => $subscription->order, // \FluentCart\App\Models\Order 'customer' => $subscription->customer, // \FluentCart\App\Models\Customer 'reminder' => [ 'stage' => 'before_30', // string — e.g., 'before_30', 'before_7' 'billing_cycle' => 'yearly', // string — 'yearly', 'monthly', 'quarterly', 'half_yearly' 'billing_date' => '2025-03-15 00:00:00', // string — GMT formatted next billing date ], ];
Source: app/Services/Reminders/SubscriptionReminderService.php
Usage:
php
add_action('fluent_cart/subscription_renewal_reminder', function ($data) {
$subscription = $data['subscription'];
$customer = $data['customer'];
$reminder = $data['reminder'];
// Send a custom renewal reminder email
wp_mail(
$customer->email,
'Subscription Renewal Coming Up',
sprintf(
'Your %s subscription #%d will renew on %s.',
$reminder['billing_cycle'],
$subscription->id,
date('F j, Y', strtotime($reminder['billing_date']))
)
);
}, 10, 1); subscription_trial_end_reminder
fluent_cart/subscription_trial_end_reminder — Fires when a trial ending reminder is due
When it runs: This action fires on a scheduled basis when a trialing subscription's trial period is about to end. Only fires for subscriptions with trialing status (excluding simulated trials). The stage name indicates how many days before the trial ends (e.g., trial_end_3, trial_end_1). Configurable via the trial_end_reminder_days store setting.
Parameters:
$data(array): Trial end reminder dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'order' => $subscription->order, // \FluentCart\App\Models\Order 'customer' => $subscription->customer, // \FluentCart\App\Models\Customer 'reminder' => [ 'stage' => 'trial_end_3', // string — e.g., 'trial_end_3', 'trial_end_1' 'trial_end_date' => '2025-02-01 00:00:00', // string — GMT formatted trial end date ], ];
Source: app/Services/Reminders/SubscriptionReminderService.php
Usage:
php
add_action('fluent_cart/subscription_trial_end_reminder', function ($data) {
$subscription = $data['subscription'];
$customer = $data['customer'];
$reminder = $data['reminder'];
// Notify customer that their trial is ending soon
wp_mail(
$customer->email,
'Your Free Trial is Ending Soon',
sprintf(
'Your trial for subscription #%d ends on %s. After that, you will be billed %s.',
$subscription->id,
date('F j, Y', strtotime($reminder['trial_end_date'])),
number_format($subscription->recurring_total / 100, 2)
)
);
}, 10, 1); renewal_reminder_overdue
fluent_cart/renewal_reminder_overdue — Fires when an overdue renewal reminder is triggered
When it runs: This action fires on a scheduled basis when a renewal Order with an outstanding balance has passed its due date by a configured number of days. The stage name indicates how many days overdue (e.g., overdue_1, overdue_3, overdue_7). The overdue offsets are configurable via the renewal_reminder_overdue_days store setting (defaults to 1,3,7) and the fluent_cart/reminders/renewal_overdue_days filter. Reminders as a whole are gated by the renewal_reminders_enabled setting.
Parameters:
$data(array): Renewal overdue reminder dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order 'customer' => $order->customer, // \FluentCart\App\Models\Customer 'reminder' => [ 'stage' => 'overdue_3', // string — e.g., 'overdue_1', 'overdue_3', 'overdue_7' 'order_id' => 123, // int — order ID 'order_ref' => 'INV-00123', // string — invoice number or '#123' fallback 'due_at' => '2025-01-15 00:00:00', // string — GMT formatted due date 'due_amount' => 5000, // int — outstanding amount in cents 'payment_link' => 'https://example.com/checkout/pay/uuid', // string — customer payment URL ], ];
Source: app/Services/Reminders/RenewalReminderService.php
Usage:
php
add_action('fluent_cart/renewal_reminder_overdue', function ($data) {
$order = $data['order'];
$customer = $data['customer'];
$reminder = $data['reminder'];
// Send a payment reminder with a direct payment link
wp_mail(
$customer->email,
sprintf('Payment Overdue for %s', $reminder['order_ref']),
sprintf(
"Your payment of %s for order %s is overdue.\n\nPay now: %s",
number_format($reminder['due_amount'] / 100, 2),
$reminder['order_ref'],
$reminder['payment_link']
)
);
}, 10, 1); renewal_reminder_due
fluent_cart/renewal_reminder_due — Fires when a renewal due-date reminder is triggered
When it runs: This action fires on a scheduled basis when a renewal order with an outstanding balance reaches its due-date reminder window. This is the pre-/on-due-date notification, as opposed to the overdue reminders that fire after the due date has passed. Shares the same parameter structure as the overdue reminder. Gated by the renewal_reminder_due_days store setting (default 0 — no due reminder until set) and the fluent_cart/reminders/renewal_due_days filter.
Parameters:
$data(array): Renewal due reminder dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order 'customer' => $order->customer, // \FluentCart\App\Models\Customer 'reminder' => [ 'stage' => 'before_0', // string — due-date reminder stage 'order_id' => 123, // int — order ID 'order_ref' => 'INV-00123', // string — invoice number or '#123' fallback 'due_at' => '2025-01-15 00:00:00', // string — GMT formatted due date 'due_amount' => 5000, // int — outstanding amount in cents 'payment_link' => 'https://example.com/checkout/pay/uuid', // string — customer payment URL ], ];
Source: app/Services/Reminders/RenewalReminderService.php
Usage:
php
add_action('fluent_cart/renewal_reminder_due', function ($data) {
$order = $data['order'];
$customer = $data['customer'];
$reminder = $data['reminder'];
// Notify customer that their renewal is due
wp_mail(
$customer->email,
sprintf('Payment Due for %s', $reminder['order_ref']),
sprintf(
"Your payment of %s for order %s is due.\n\nPay now: %s",
number_format($reminder['due_amount'] / 100, 2),
$reminder['order_ref'],
$reminder['payment_link']
)
);
}, 10, 1);Renewal Lifecycle Store-managed
These fire for store-managed renewals (the manual / system collection methods), where FluentCart generates renewal orders on a schedule rather than mirroring a gateway subscription. See the Subscriptions Module for the full engine.
renewal_created
fluent_cart/renewal_created — Fires when a renewal order is generated
When it runs: This action fires whenever the renewal engine creates a renewal Order for a store-managed Subscription — from the advance-window cron (system/manual), from a system auto-charge cycle, and from the admin "Create Renewal Now" action.
Parameters:
$data(array): Renewal creation dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'order' => $order, // \FluentCart\App\Models\Order — the new renewal order 'parent_order' => $parentOrder, // \FluentCart\App\Models\Order — the original order 'customer' => $customer, // \FluentCart\App\Models\Customer 'transaction' => $transaction, // \FluentCart\App\Models\OrderTransaction ];
Source: app/Http/Controllers/RenewalController.php:155, app/Modules/Subscriptions/Services/SystemChargeService.php:310, app/Modules/StoreManagedRenewal/Services/RenewalService.php:237
Usage:
php
add_action('fluent_cart/renewal_created', function ($data) {
$order = $data['order'];
// Sync the freshly generated renewal order to an external ledger
}, 10, 1); renewal_paid
fluent_cart/renewal_paid — Fires once when a renewal order is paid
When it runs: This action fires exactly once when a renewal Order transitions to paid — the single edge-triggered "a renewal was collected" signal, whether the payment came from a system off-session charge, a customer Pay-Now, or a gateway-managed renewal webhook. It is the canonical place to grant/extend access on renewal.
Parameters:
$data(array): Renewal payment dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order — the paid renewal order ];
Source: app/Helpers/StatusHelper.php:208
Usage:
php
add_action('fluent_cart/renewal_paid', function ($data) {
$order = $data['order'];
// Extend the customer's access period for this renewal
}, 10, 1); renewal_payment_scheduled
fluent_cart/renewal_payment_scheduled — Fires when a gateway renewal payment is scheduled (positional args)
When it runs: This action fires when a gateway confirms a renewal payment is scheduled at the vendor (Stripe/PayPal confirmation paths). Fires alongside fluent_cart/renewal/payment_scheduled (array shape) — this is the legacy positional-argument variant.
Positional arguments
This hook passes two positional arguments, not a single array. Register with , 10, 2.
Parameters:
$order(Order): The renewal order$subscription(Subscription): The subscription
Source: app/Modules/PaymentMethods/PayPalGateway/Processor.php:476, app/Modules/PaymentMethods/StripeGateway/Confirmations.php:485
Usage:
php
add_action('fluent_cart/renewal_payment_scheduled', function ($order, $subscription) {
// React to a scheduled gateway renewal payment
}, 10, 2); renewal/payment_scheduled
fluent_cart/renewal/payment_scheduled — Fires when a gateway renewal payment is scheduled (array payload)
When it runs: The array-payload variant of the hook above, fired back-to-back with fluent_cart/renewal_payment_scheduled. Prefer this one for new integrations.
Parameters:
$data(array): Scheduled renewal payment dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order 'subscription' => $subscription, // \FluentCart\App\Models\Subscription ];
Source: app/Modules/PaymentMethods/PayPalGateway/Processor.php:477, app/Modules/PaymentMethods/StripeGateway/Confirmations.php:486
Usage:
php
add_action('fluent_cart/renewal/payment_scheduled', function ($data) {
$order = $data['order'];
}, 10, 1); renewal_voided
fluent_cart/renewal_voided — Fires when a renewal order is voided
When it runs: This action fires when an open renewal order is voided (e.g., superseded or cancelled before payment).
Parameters:
$data(array): Voided renewal dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order — the voided renewal order 'customer' => $customer, // \FluentCart\App\Models\Customer ];
Source: app/Http/Controllers/RenewalController.php:126
Usage:
php
add_action('fluent_cart/renewal_voided', function ($data) {
// Clean up any external state tied to the voided renewal
}, 10, 1); subscriptions/system_renewal_scheduled
fluent_cart/subscriptions/system_renewal_scheduled — Fires when a store-managed renewal is scheduled for auto-charge
When it runs: This action fires when the renewal engine schedules a store-managed renewal order for its due-date off-session charge attempt.
Parameters:
$data(array): Scheduled renewal dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'order' => $order, // \FluentCart\App\Models\Order 'parent_order' => $parentOrder, // \FluentCart\App\Models\Order 'customer' => $customer, // \FluentCart\App\Models\Customer 'transaction' => $transaction, // \FluentCart\App\Models\OrderTransaction ];
Source: app/Modules/StoreManagedRenewal/Services/RenewalService.php:227
Usage:
php
add_action('fluent_cart/subscriptions/system_renewal_scheduled', function ($data) {
// Notify the customer that an automatic charge is upcoming
}, 10, 1);Collection Method & Status Store-managed
subscription_converted_to_automatic
fluent_cart/subscription_converted_to_automatic — Fires when a subscription becomes gateway-managed
When it runs: This action fires when a subscription's collection method is converted to automatic — a live vendor subscription now owns the billing schedule (legacy manual→automatic conversion at a subscription-capable gateway).
Parameters:
$data(array): Conversion dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'payment_method' => $paymentMethod, // string — gateway slug now managing billing ];
Source: app/Modules/PaymentMethods/PayPalGateway/PayPal.php:128, app/Modules/PaymentMethods/StripeGateway/Stripe.php:140
Usage:
php
add_action('fluent_cart/subscription_converted_to_automatic', function ($data) {
$subscription = $data['subscription'];
}, 10, 1); subscription_converted_to_manual
fluent_cart/subscription_converted_to_manual — Fires when a subscription falls back to manual invoicing
When it runs: This action fires when a subscription is demoted to the manual collection method — for example, a system subscription whose gateway can no longer token-charge (see demotion).
Parameters:
$data(array): Conversion dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'payment_method' => $paymentMethod, // string — current payment method slug ];
Source: app/Modules/PaymentMethods/Core/AbstractPaymentGateway.php:340
Usage:
php
add_action('fluent_cart/subscription_converted_to_manual', function ($data) {
// Alert staff that automatic billing was lost for this subscription
}, 10, 1); subscription_past_due
fluent_cart/subscription_past_due — Fires when a store-managed subscription enters dunning
When it runs: This action fires when a store-managed subscription's renewal passes its grace anchor and the subscription enters the past-due (dunning) window, before eventual expiry.
Parameters:
$data(array): Past-due dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'order' => $order, // \FluentCart\App\Models\Order — the unpaid renewal 'customer' => $customer, // \FluentCart\App\Models\Customer ];
Source: app/Modules/StoreManagedRenewal/Services/RenewalService.php:696
Usage:
php
add_action('fluent_cart/subscription_past_due', function ($data) {
// Trigger a custom dunning sequence
}, 10, 1); subscription/reactivated_locally
fluent_cart/subscription/reactivated_locally — Fires when a store-managed subscription is reactivated locally (positional arg)
When it runs: This action fires when a store-managed subscription is reactivated by FluentCart itself (no vendor call), e.g., after a successful late payment.
Positional argument
This hook passes the Subscription model directly, not an array. Register with , 10, 1 and type-hint the model.
Parameters:
$subscription(Subscription): The reactivated subscription
Source: app/Modules/Subscriptions/Services/SubscriptionService.php:814
Usage:
php
add_action('fluent_cart/subscription/reactivated_locally', function ($subscription) {
// Restore access for the reactivated subscription
}, 10, 1);Native Subscription Events Store-managed
Distinct from the payments/subscription_{status} bus
These fluent_cart/subscription_* events carry richer payloads (reason, updates, changes) and are separate hooks from the like-named status bus (fluent_cart/payments/subscription_paused, etc.). Both may fire for the same transition.
subscription_paused
fluent_cart/subscription_paused — Native event: subscription paused
When it runs: Dispatched when a subscription is paused, via the subscription event dispatcher.
Parameters:
$data(array): Pause event dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'reason' => 'user_request', // string — pause reason 'order' => $order, // \FluentCart\App\Models\Order 'customer' => $customer, // \FluentCart\App\Models\Customer 'old_status' => 'active', // string — status before pause ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php:527 (via EventDispatcher)
Usage:
php
add_action('fluent_cart/subscription_paused', function ($data) {
$reason = $data['reason'];
}, 10, 1); subscription_resumed
fluent_cart/subscription_resumed — Native event: subscription resumed
When it runs: Dispatched when a paused subscription is resumed.
Parameters:
$data(array): Resume event dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'reason' => 'user_request', // string — resume reason 'order' => $order, // \FluentCart\App\Models\Order 'customer' => $customer, // \FluentCart\App\Models\Customer 'old_status' => 'paused', // string — status before resume ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php:530 (via EventDispatcher)
Usage:
php
add_action('fluent_cart/subscription_resumed', function ($data) {
$subscription = $data['subscription'];
}, 10, 1); subscription_updated
fluent_cart/subscription_updated — Native event: subscription terms updated
When it runs: Dispatched when a store-managed subscription's editable terms change (amount, interval, next billing date, etc.).
Parameters:
$data(array): Update event dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'updates' => [ /* ... */ ], // array — fields submitted for update 'changes' => [ /* ... */ ], // array — fields that actually changed 'order' => $order, // \FluentCart\App\Models\Order 'customer' => $customer, // \FluentCart\App\Models\Customer ];
Source: app/Modules/Subscriptions/Services/SubscriptionService.php:533 (via EventDispatcher)
Usage:
php
add_action('fluent_cart/subscription_updated', function ($data) {
$changes = $data['changes'];
}, 10, 1);System Auto-Charge system
These fire only for system subscriptions — store-managed subscriptions that auto-charge a saved token each renewal. See Store-managed + auto-charge.
subscriptions/system_charge_succeeded
fluent_cart/subscriptions/system_charge_succeeded — Fires when an off-session renewal charge succeeds
When it runs: This action fires when a system subscription's off-session renewal charge is confirmed successful.
Parameters:
$data(array): Successful charge dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order — the renewal order 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'attempt' => 1, // int — which retry attempt succeeded ];
Source: app/Modules/Subscriptions/Services/SystemChargeService.php:584
Usage:
php
add_action('fluent_cart/subscriptions/system_charge_succeeded', function ($data) {
$order = $data['order'];
}, 10, 1); subscriptions/system_charge_failed
fluent_cart/subscriptions/system_charge_failed — Fires when an off-session renewal charge fails
When it runs: This action fires when a system renewal charge attempt fails. The retry ladder may schedule further attempts within the grace window before the subscription is marked past due.
Parameters:
$data(array): Failed charge dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'attempt' => 2, // int — attempt number that failed 'error' => 'card_declined', // string — gateway error 'next_retry_at' => '2025-01-18 00:00:00', // string|null — GMT time of next attempt, null if exhausted ];
Source: app/Modules/Subscriptions/Services/SystemChargeService.php:730
Usage:
php
add_action('fluent_cart/subscriptions/system_charge_failed', function ($data) {
if (empty($data['next_retry_at'])) {
// Retries exhausted — escalate
}
}, 10, 1); subscriptions/system_charge_failed_notification
fluent_cart/subscriptions/system_charge_failed_notification — Fires when a charge-failure notification should be sent
When it runs: This action fires when a system charge failure warrants notifying the customer (gated by fluent_cart/subscriptions/system_charge_failure_notify). Carries the full context needed to render an email.
Parameters:
$data(array): Failure notification dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'parent_order' => $parentOrder, // \FluentCart\App\Models\Order 'customer' => $customer, // \FluentCart\App\Models\Customer 'transaction' => $transaction, // \FluentCart\App\Models\OrderTransaction 'error' => 'card_declined', // string — gateway error 'attempt' => 2, // int — attempt number 'next_retry_at' => '2025-01-18 00:00:00', // string|null — GMT time of next attempt ];
Source: app/Modules/Subscriptions/Services/SystemChargeService.php:746
Usage:
php
add_action('fluent_cart/subscriptions/system_charge_failed_notification', function ($data) {
// Send a custom "payment failed" email
}, 10, 1); subscriptions/system_charge_disabled
fluent_cart/subscriptions/system_charge_disabled — Fires when auto-charge is turned off for a subscription
When it runs: This action fires when a subscription loses system auto-charge and is handed back to plain manual invoicing (demoteToManual()), e.g., its gateway can no longer token-charge.
Parameters:
$data(array): Disable dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'reason' => 'gateway_incapable', // string — why auto-charge was disabled ];
Source: app/Modules/Subscriptions/Services/SystemChargeService.php:325
Usage:
php
add_action('fluent_cart/subscriptions/system_charge_disabled', function ($data) {
$reason = $data['reason'];
}, 10, 1); subscriptions/system_charge_manual_triggered
fluent_cart/subscriptions/system_charge_manual_triggered — Fires when an admin triggers an off-session charge
When it runs: This action fires when a store admin manually triggers a system charge (the "Charge Now" / "Charge Next Renewal Now" actions).
Parameters:
$data(array): Manual trigger dataphp$data = [ 'order' => $order, // \FluentCart\App\Models\Order 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'attempt' => 1, // int — attempt number 'actor_id' => 5, // int — admin user ID who triggered it 'result' => 'success', // mixed — charge result ];
Source: app/Modules/Subscriptions/Services/SystemChargeService.php:444
Usage:
php
add_action('fluent_cart/subscriptions/system_charge_manual_triggered', function ($data) {
// Audit-log the admin-initiated charge
}, 10, 1); subscriptions/system_payment_method_updated
fluent_cart/subscriptions/system_payment_method_updated — Fires when the saved token for a system subscription changes
When it runs: This action fires when the vaulted payment method (token) backing a system subscription is updated — e.g., the customer updates the card on file.
Parameters:
$data(array): Payment method update dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'payment_method' => $paymentMethod, // array|string — the new vaulted method reference ];
Source: app/Modules/PaymentMethods/StripeGateway/UpdateCustomerPaymentMethod.php:129
Usage:
php
add_action('fluent_cart/subscriptions/system_payment_method_updated', function ($data) {
// Confirm the new card to the customer
}, 10, 1);Subscription Upgrades & Early Payments Pro
early_payment_completed
fluent_cart/subscription/early_payment_completed Pro — Fires when an early installment payment is completed
When it runs: This action fires when a customer makes an early installment payment on their subscription, paying for one or more future billing cycles ahead of schedule.
Parameters:
$data(array): Early payment completion dataphp$data = [ 'subscription' => $subscription, // \FluentCart\App\Models\Subscription 'order' => $order, // \FluentCart\App\Models\Order 'installment_count' => $installmentCount, // int — number of installments paid early ];
Source: fluent-cart-pro/app/Hooks/Handlers/EarlyInstallmentPaymentHandler.php:279
Usage:
php
add_action('fluent_cart/subscription/early_payment_completed', function ($data) {
$subscription = $data['subscription'];
$order = $data['order'];
$installmentCount = $data['installment_count'];
fluent_cart_add_log(
'Early Payment Completed',
sprintf('Subscription #%d received %d early installment(s) via order #%d', $subscription->id, $installmentCount, $order->id),
'info'
);
}, 10, 1); order_upgraded
fluent_cart/order/upgraded Pro — Fires when a plan upgrade is completed
When it runs: This action fires when a customer completes a plan upgrade, transitioning from one product variant to another. The upgrade creates a new order and transaction record.
Parameters:
$data(array): Upgrade completion dataphp$data = [ 'order' => $newOrder, // \FluentCart\App\Models\Order — the new upgrade order 'from_order' => $upgradeFromOrder, // \FluentCart\App\Models\Order — the original order 'cart' => $cartModel, // \FluentCart\App\Models\Cart — the cart used for the upgrade 'from_variant_id' => $fromVariantId, // int — ID of the original product variant 'transaction' => $transaction, // \FluentCart\App\Models\OrderTransaction — the upgrade payment transaction ];
Source: fluent-cart-pro/app/Hooks/Handlers/UpgradeHandler.php:242
Usage:
php
add_action('fluent_cart/order/upgraded', function ($data) {
$order = $data['order'];
$fromOrder = $data['from_order'];
$fromVariantId = $data['from_variant_id'];
$transaction = $data['transaction'];
fluent_cart_add_log(
'Plan Upgraded',
sprintf(
'Order #%d upgraded from order #%d (variant %d). Transaction: %s',
$order->id,
$fromOrder->id,
$fromVariantId,
$transaction->charge_id
),
'info'
);
}, 10, 1);