Subscription Addons¶
Barberklingen\BasePlugin\Modules\SubscriptionAddons\SubscriptionAddonsLoader
Addon products are extra items attached directly to a subscription rather than purchased as a full order. They carry their own interval and count logic so they can be included in renewal orders on a different cadence than the main subscription.
Activating the module¶
The loader boots the following sub-modules:
| Sub-module | Responsibility |
|---|---|
AddonOrderItemTypesLoader | Registers the addon WC order item type |
AddonAdminMetaOutput | Renders the addon UI in the WP admin subscription screen |
AddonAdminAjax | AJAX handlers for admin-side addon management |
AddonAdminProductSettings | Adds _profile_addon_price / _profile_addon_oneoff_price fields to products |
AddonRenewals | Copies addon items into renewal orders at the correct interval |
AddonSubscriptionsBackend | Backend logic for attaching/detaching addons |
AjaxSetAddonProductsEndpoint | Customer-facing AJAX endpoint for updating addons |
AddonKlaviyoExtension | Fires Klaviyo events when addons change |
The addon order item type¶
Addon items are stored as WC_Order_Item records with order_item_type = 'addon'. The custom class SubscriptionItemAddon extends WC_Order_Item_Product with two extra properties:
| Property | Type | Description |
|---|---|---|
addon_interval | int | How often the item is included (0 = once, 1 = every renewal, N = every N renewals) |
addon_count | int | Countdown until the next inclusion; when it reaches 1 the item is added to the renewal order |
Adding an addon to a subscription¶
use Barberklingen\BasePlugin\Modules\SubscriptionAddons\OrderItemTypes\Types\SubscriptionItemAddon;
SubscriptionItemAddon::add_to_subscription(
$product, // WC_Product or product ID
$qty, // int
$subscription, // WC_Subscription
[
'addon_interval' => 2, // include every 2 renewals
'addon_count' => 1, // include on the very next renewal
]
);
The method calculates price, taxes, and saves the item, then fires subscription/addon-product/added.
Removing an addon¶
SubscriptionItemAddon::remove_from_subscription($subscription, $addonItem);
// Fires: subscription/addon-product/before-remove, subscription/addon-product/removed
Pricing¶
Addon prices are read from two product meta fields:
| Meta key | Used when |
|---|---|
_profile_addon_price | addon_interval > 0 (recurring) |
_profile_addon_oneoff_price | addon_interval === 0 (one-off) |
Both fall back to the product's regular price if the meta is absent. Override via filters:
add_filter('subscription/addon-product/recurring-price', fn($price, $product, $interval, $qty, $order) => $price);
add_filter('subscription/addon-product/one-off-price', fn($price, $product, $interval, $qty, $order) => $price);
Interval options¶
AddonOptionsHelper provides helpers for rendering interval/delivery dropdowns in admin and frontend forms:
AddonOptionsHelper::get_interval_options(); // [0 => 'Only once', 1 => 'Every renewal', ...]
AddonOptionsHelper::get_next_delivery_options($interval, $period, $dateFrom);
AddonOptionsHelper::print_interval_field($fieldName, $currentValue);
AddonOptionsHelper::print_next_delivery_field($fieldName, $currentValue, $interval, $period, $dateFrom);
Interval options are filterable via subscription-addons/interval-options.
Checking addon presence¶
use Barberklingen\BasePlugin\Modules\SubscriptionAddons\Helpers\AddonSubscriptionHelper;
AddonSubscriptionHelper::has_addon_product($subscription, $productId); // bool
AddonSubscriptionHelper::get_addon_product_item($subscription, $productId); // ?SubscriptionItemAddon
AddonSubscriptionHelper::get_estimated_next_shipment($addonItem); // ?string (formatted date)
Renewal behaviour¶
On each renewal, AddonRenewals decrements each addon's addon_count. When addon_count reaches 1, the item is added as a line_item to the renewal order. The count then resets to addon_interval (or the item is removed if addon_interval === 0).
Extending with GTM/Klaviyo¶
AddonGTMExtension and AddonKlaviyoExtension listen to the addon action hooks and push events to their respective systems. Consuming plugins can add their own extensions by hooking into:
subscription/addon-product/added—($item, $subscription, $qty)subscription/addon-product/removed—($subscription, $productId)subscription/addon-product/before-remove—($subscription, $item, $productId)