WooCommerce Subscriptions: data model, switching, gifting
Use this when exact storage names or switch/gift internals matter. Prefer WCS CRUD functions and WC_Subscription methods for writes; use raw keys only for audits, migrations, debugging, or compatibility glue.
Core names
| Entity |
Name/key |
Notes |
| Subscription order type |
shop_subscription |
Registered with wc_register_order_type(), class WC_Subscription. In CPT storage it is a post type; in HPOS it is an order type. |
| Simple subscription product type |
subscription |
WC_Product_Subscription::get_type(). |
| Variable subscription product type |
variable-subscription |
WC_Product_Variable_Subscription::get_type(). |
| Subscription variation product type |
subscription_variation |
WC_Product_Subscription_Variation::get_type(). |
| Action Scheduler group |
wc_subscription_scheduled_event |
Used for subscription scheduled events. |
Subscription statuses are WooCommerce order statuses with wc- prefix in storage: wc-pending, wc-active, wc-on-hold, wc-cancelled, wc-switched, wc-expired, and wc-pending-cancel. Object APIs usually use unprefixed values.
Subscription meta keys
These keys map to WC_Subscription props in both CPT and HPOS subscription data stores.
| Meta key |
Prop/date key |
Purpose |
_billing_period |
billing_period |
day, week, month, or year. |
_billing_interval |
billing_interval |
Billing interval integer. |
_suspension_count |
suspension_count |
Customer/admin suspension count. |
_cancelled_email_sent |
cancelled_email_sent |
Cancellation email guard. |
_requires_manual_renewal |
requires_manual_renewal |
Manual renewal flag. |
_trial_period |
trial_period |
Trial period unit. |
_last_order_date_created |
last_order_date_created |
Last related parent/renewal order created date used by WCS. |
_schedule_start |
start / schedule_start |
Subscription start datetime. |
_schedule_trial_end |
trial_end / schedule_trial_end |
Trial end datetime. |
_schedule_next_payment |
next_payment / schedule_next_payment |
Next renewal datetime. |
_schedule_cancelled |
cancelled / schedule_cancelled |
Cancellation datetime. |
_schedule_end |
end / schedule_end |
End/expiration datetime. |
_schedule_payment_retry |
payment_retry / schedule_payment_retry |
Retry datetime. |
_subscription_switch_data |
switch_data |
Switch order execution payload. |
Use WC_Subscription::update_dates(), delete_date(), setters like set_billing_period(), and save(). Directly updating these meta keys can desync validation and scheduled actions.
Product subscription meta
Subscription product data lives on product or product_variation posts.
| Meta key |
Purpose |
_subscription_price |
Recurring price. |
_subscription_sign_up_fee |
Sign-up fee. |
_subscription_period |
Billing period. |
_subscription_period_interval |
Billing interval. |
_subscription_length |
Subscription length. |
_subscription_trial_period |
Trial period unit. |
_subscription_trial_length |
Trial length. |
_subscription_gifting |
Product-level gifting override: enabled, disabled, or empty for global setting. |
_subscription_one_time_shipping |
One-time shipping flag. |
_subscription_payment_sync_date |
Renewal synchronization setting. |
Use WC_Subscriptions_Product helpers such as get_price(), get_period(), get_interval(), get_length(), get_trial_length(), get_sign_up_fee(), and get_gifting().
Related order relation meta
WCS relates ordinary orders to subscriptions with order meta and relation stores. Do not infer relation from parent ID alone.
| Meta key |
Relation |
_subscription_renewal |
Renewal order to subscription. |
_subscription_switch |
Switch order to subscription. |
_subscription_resubscribe |
Resubscribe order to subscription. |
Use wcs_get_subscription_ids_for_order(), wcs_get_subscriptions_for_order(), wcs_get_subscriptions_for_renewal_order(), wcs_get_subscriptions_for_switch_order(), and $subscription->get_related_orders().
Switcher flow
Switching replaces or adds subscription line items through checkout. It is not a simple product ID update.
- A user clicks the switch link printed in My Account by
WC_Subscriptions_Switcher::print_switch_link().
- The link points to the product/grouped product URL with
switch-subscription, item, and _wcsnonce query args.
subscription_switch_handler() validates ownership, nonce, and item switchability.
validate_switch_request() blocks non-subscription products and identical product/variation/quantity switches.
set_switch_details_in_cart() adds cart item data under subscription_switch.
WCS_Switch_Totals_Calculator calculates proration, switch direction, first payment timestamp, and possible prepaid-term changes.
- Checkout line item meta records prorated amounts on the switch order and switched item links on subscription items.
process_checkout() creates _subscription_switch_data on the switch order and may create pending switch items on the existing subscription.
- When the switch order is paid/completed,
complete_subscription_switches() applies the payload to the subscription.
- WCS fires
woocommerce_subscriptions_switch_completed and item-level switch actions after completion.
Custom switch flows
WCS does not expose a simple customer REST endpoint for "switch this subscription item to that product". The built-in switcher is a cart/checkout/order-completion flow. Custom account UI, AJAX, or REST layers must wrap that flow or deliberately reproduce its order payload.
Safe pattern:
- Verify the current user can switch the exact subscription item with
WC_Subscriptions_Switcher::can_item_be_switched_by_user().
- Build a temporary cart/session context for the current user and add the replacement product with
subscription_switch cart item data.
- Let
WCS_Switch_Totals_Calculator calculate prorations, first_payment_timestamp, end_timestamp, force_payment, and switch direction.
- Return preview totals from the calculated cart/order data, not from hand-written price math.
- On confirmation, create the switch order with the same
_subscription_switch_data shape WCS checkout writes.
- If an immediate payment is due, process it through the gateway/order payment flow; if no payment is due, complete/apply the switch through WCS completion logic.
- Let
complete_subscription_switches() apply the change and let WCS fire its normal hooks.
Do not implement switching by directly calling $subscription->remove_item() and $subscription->add_product() from a customer action. That bypasses switch orders, prorations, tax/fee/coupon handling, old item archival, pending switch item types, cancellation of older unpaid switch orders, and completion hooks.
For read-only previews or eligibility checks, it is fine to expose custom endpoints that return allowed products, switchable item IDs, and WCS-calculated preview totals. For writes, prefer a service that uses WCS cart/checkout objects internally.
Switch cart data
The cart item key is subscription_switch. Typical shape:
$cart_item['subscription_switch'] = array(
'subscription_id' => 123,
'item_id' => 456,
'next_payment_timestamp' => 1770000000,
'upgraded_or_downgraded' => 'upgraded', // or downgraded/crossgraded, after calculation
'first_payment_timestamp' => 1771000000, // after proration calculation
'end_timestamp' => 1780000000, // when length changes
'recurring_payment_prorated' => true,
'force_payment' => true,
);
item_id is the subscription line item being replaced. When adding an item to a subscription without replacing one, item_id can be empty and wcs_cart_contains_switches( 'add' ) is relevant.
Switch order data
Switch orders store subscription_switch_data, persisted as _subscription_switch_data on the order. Shape by subscription ID:
$switch_data[ $subscription_id ] = array(
'switches' => array(
$switch_order_item_id => array(
'remove_line_item' => 456,
'add_line_item' => 789,
'switch_direction' => 'upgrade',
),
),
'billing_schedule' => array(
'_billing_period' => 'month',
'_billing_interval' => 1,
),
'dates' => array(
'update' => array(
'next_payment' => '2026-06-01 00:00:00',
'trial_end' => 0,
'end' => '2027-06-01 00:00:00',
),
'delete' => array( 'trial_end' ),
),
'coupons' => array( 111 ),
'fee_items' => array( 222 ),
'shipping_line_items' => array( 333 ),
);
WCS can cancel older unpaid switch orders for the same subscription when a new switch order is created.
Switched item types and meta
WCS uses custom order item types during and after switch execution.
| Item type |
Meaning |
line_item_pending_switch |
New line item staged on a subscription before switch completion. |
line_item_switched |
Old subscription line item archived after replacement. |
line_item_removed |
Removed subscription line item. |
coupon_pending_switch, fee_pending_switch, shipping_pending_switch |
Staged recurring coupon/fee/shipping items. |
coupon_switched, fee_switched, shipping_switched |
Archived old recurring coupon/fee/shipping items. |
Important item meta:
| Item meta |
Stored on |
Purpose |
_switched_subscription_item_id |
New subscription line item |
Old subscription item ID. |
_switched_subscription_new_item_id |
Old subscription line item |
New subscription/order line item ID. |
_switched_subscription_sign_up_fee_prorated |
Switch order line item |
Portion of order line total from prorated sign-up fee. |
_switched_subscription_price_prorated |
Switch order line item |
Portion of order line total from prorated recurring price. |
_has_trial |
Pending switch item |
Marks new item with trial. |
Switch extension points
| Need |
Hook/filter |
Args |
| Check product switchability |
wcs_is_product_switchable |
$is_switchable, $product, $variation |
| Check item switchability |
woocommerce_subscriptions_can_item_be_switched |
$can, $item, $subscription |
| Check user can switch item |
woocommerce_subscriptions_can_item_be_switched_by_user |
$can, $item, $subscription |
| Switch URL |
woocommerce_subscriptions_switch_url |
$url, $item_id, $item, $subscription |
| Switch link markup/text/classes |
woocommerce_subscriptions_switch_link, woocommerce_subscriptions_switch_link_text, woocommerce_subscriptions_switch_link_classes |
Link context. |
| Retain coupons |
woocommerce_subscriptions_retain_coupon_on_switch |
$retain, $coupon_code, $coupon, $subscription |
| Switch added to cart |
woocommerce_subscriptions_switch_added_to_cart |
$subscription, $existing_item, $cart_item_key, $cart_item |
| Proration price/day |
wcs_switch_proration_old_price_per_day, wcs_switch_proration_new_price_per_day |
Calculator context. |
| Switch type |
wcs_switch_proration_switch_type |
$type, $subscription, $cart_item, $old_price_per_day, $new_price_per_day |
| Prorate recurring/sign-up fee |
wcs_switch_should_prorate_recurring_price, wcs_switch_should_prorate_sign_up_fee |
$bool, $switch_item |
| Extra amount |
wcs_switch_proration_extra_to_pay |
$extra, $subscription, $cart_item, $days_in_old_cycle |
| Completion |
woocommerce_subscriptions_switch_completed |
$order |
| Item switched |
woocommerce_subscriptions_switched_item |
$subscription, $new_order_item, $old_subscription_item |
| Subscription item switched |
woocommerce_subscription_item_switched |
$order, $subscription, $new_item_id, $old_item_id |
Gifting storage
Gifting is included in Subscriptions. It lets purchaser and recipient differ.
| Storage |
Key |
Purpose |
| Cart item data |
wcsg_gift_recipients_email |
Recipient email before checkout/subscription creation. |
| Subscription meta |
_recipient_user_email_address |
Recipient email captured at subscription creation before a user is resolved. |
| Subscription meta |
_recipient_user |
Recipient user ID after account lookup/creation. Primary gifted-subscription marker. |
| Parent order item meta |
_wcsg_cart_key |
Links checkout order item to the recurring cart/subscription item. |
| Parent order item meta |
wcsg_recipient |
Value format wcsg_recipient_id_{user_id}. |
| Parent order item meta |
wcsg_deleted_recipient_data |
JSON snapshot used after recipient deletion. |
| User meta |
wcsg_update_account |
Recipient account setup/update flag. |
| User meta |
wcsg_recipient_just_reset_password |
Recipient onboarding flag. |
Gifted subscriptions are detected by WCS_Gifting::is_gifted_subscription(): true when _recipient_user exists or _recipient_user_email_address is present.
Gifting flow
- Product page/cart/checkout collects recipient email when
WCSG_Product::is_giftable() is true.
- Cart item stores
wcsg_gift_recipients_email; gifting is blocked for renewal and switch cart items.
- Checkout uses recipient email in recurring cart keys so different recipients produce separate subscriptions.
woocommerce_checkout_subscription_created writes _recipient_user_email_address to the subscription.
- On parent order processing/completion, recipient management finds or creates the recipient user, writes
_recipient_user, adds wcsg_recipient to the matching parent order item, and updates shipping fields from recipient user meta.
- Recipients are granted view/pay/suspend/cancel capabilities for gifted subscriptions, but cannot change payment method.
_recipient_user is not copied to renewal orders.
Product giftability:
- Global enablement comes from WCSG admin settings.
- Product-level override is
_subscription_gifting: enabled, disabled, or empty for global.
- Variable subscription parent returns giftable to render UI; each variation decides final
gifting variation data.
- Product page gifting UI is suppressed during switching (
switch-subscription query arg).
Memberships integration for gifts
If WooCommerce Memberships is active and a giftable subscription product grants a plan:
- Membership access is granted to the recipient when order item meta has
wcsg_recipient.
- Purchaser access is skipped unless the same product was also purchased for the purchaser.
- The created
wc_user_membership still stores _subscription_id.
- Because one order can contain the same product for multiple recipients, link the membership to the recipient's subscription, not just the first subscription in the order.
Common mistakes
// WRONG: direct date meta write can desync Action Scheduler.
update_post_meta( $subscription_id, '_schedule_next_payment', '2026-06-01 00:00:00' );
// RIGHT:
$subscription = wcs_get_subscription( $subscription_id );
$subscription->update_dates( array( 'next_payment' => '2026-06-01 00:00:00' ), 'gmt' );
// WRONG: treating a switch as a product meta update.
$subscription->remove_item( $old_item_id );
$subscription->add_product( wc_get_product( $new_product_id ) );
// RIGHT: use WCS switch flow or reproduce its order payload deliberately.
$is_switch = wcs_order_contains_switch( $order );
// WRONG: gifted subscription recipient is not the subscription customer.
$recipient_id = $subscription->get_user_id();
// RIGHT:
$recipient_id = WCS_Gifting::get_recipient_user( $subscription );
Cross-references
- Use
wcs-subscription-hooks for general lifecycle hook selection.
- Use
wcs-renewal-scheduler for renewal dates, Action Scheduler, and payment retry timing.
- Use
wcm-data-model-subscriptions-link for Memberships CPT/meta and the Memberships-to-Subscriptions relation.
1---2name: wcs-data-model-switching-gifting3description: WooCommerce Subscriptions data model, switcher, and gifting reference for exact order type names, product type slugs, subscription meta keys, schedule/date keys, related-order relation meta, switch cart data, switch order data, switched item types/meta, proration hooks, and WCS Gifting recipient storage. Use when code reads or writes shop_subscription, subscription, variable-subscription, subscription_variation, _billing_period, _schedule_next_payment, _subscription_switch_data, _subscription_switch, subscription_switch, _switched_subscription_item_id, wcsg_gift_recipients_email, _recipient_user, _recipient_user_email_address, wcsg_recipient, or when an agent needs the full WooCommerce Subscriptions switcher/gifting flow.4---56# WooCommerce Subscriptions: data model, switching, gifting78Use this when exact storage names or switch/gift internals matter. Prefer WCS CRUD functions and `WC_Subscription` methods for writes; use raw keys only for audits, migrations, debugging, or compatibility glue.910## Core names1112| Entity | Name/key | Notes |13|---|---|---|14| Subscription order type | `shop_subscription` | Registered with `wc_register_order_type()`, class `WC_Subscription`. In CPT storage it is a post type; in HPOS it is an order type. |15| Simple subscription product type | `subscription` | `WC_Product_Subscription::get_type()`. |16| Variable subscription product type | `variable-subscription` | `WC_Product_Variable_Subscription::get_type()`. |17| Subscription variation product type | `subscription_variation` | `WC_Product_Subscription_Variation::get_type()`. |18| Action Scheduler group | `wc_subscription_scheduled_event` | Used for subscription scheduled events. |1920Subscription statuses are WooCommerce order statuses with `wc-` prefix in storage: `wc-pending`, `wc-active`, `wc-on-hold`, `wc-cancelled`, `wc-switched`, `wc-expired`, and `wc-pending-cancel`. Object APIs usually use unprefixed values.2122## Subscription meta keys2324These keys map to `WC_Subscription` props in both CPT and HPOS subscription data stores.2526| Meta key | Prop/date key | Purpose |27|---|---|---|28| `_billing_period` | `billing_period` | `day`, `week`, `month`, or `year`. |29| `_billing_interval` | `billing_interval` | Billing interval integer. |30| `_suspension_count` | `suspension_count` | Customer/admin suspension count. |31| `_cancelled_email_sent` | `cancelled_email_sent` | Cancellation email guard. |32| `_requires_manual_renewal` | `requires_manual_renewal` | Manual renewal flag. |33| `_trial_period` | `trial_period` | Trial period unit. |34| `_last_order_date_created` | `last_order_date_created` | Last related parent/renewal order created date used by WCS. |35| `_schedule_start` | `start` / `schedule_start` | Subscription start datetime. |36| `_schedule_trial_end` | `trial_end` / `schedule_trial_end` | Trial end datetime. |37| `_schedule_next_payment` | `next_payment` / `schedule_next_payment` | Next renewal datetime. |38| `_schedule_cancelled` | `cancelled` / `schedule_cancelled` | Cancellation datetime. |39| `_schedule_end` | `end` / `schedule_end` | End/expiration datetime. |40| `_schedule_payment_retry` | `payment_retry` / `schedule_payment_retry` | Retry datetime. |41| `_subscription_switch_data` | `switch_data` | Switch order execution payload. |4243Use `WC_Subscription::update_dates()`, `delete_date()`, setters like `set_billing_period()`, and `save()`. Directly updating these meta keys can desync validation and scheduled actions.4445## Product subscription meta4647Subscription product data lives on `product` or `product_variation` posts.4849| Meta key | Purpose |50|---|---|51| `_subscription_price` | Recurring price. |52| `_subscription_sign_up_fee` | Sign-up fee. |53| `_subscription_period` | Billing period. |54| `_subscription_period_interval` | Billing interval. |55| `_subscription_length` | Subscription length. |56| `_subscription_trial_period` | Trial period unit. |57| `_subscription_trial_length` | Trial length. |58| `_subscription_gifting` | Product-level gifting override: `enabled`, `disabled`, or empty for global setting. |59| `_subscription_one_time_shipping` | One-time shipping flag. |60| `_subscription_payment_sync_date` | Renewal synchronization setting. |6162Use `WC_Subscriptions_Product` helpers such as `get_price()`, `get_period()`, `get_interval()`, `get_length()`, `get_trial_length()`, `get_sign_up_fee()`, and `get_gifting()`.6364## Related order relation meta6566WCS relates ordinary orders to subscriptions with order meta and relation stores. Do not infer relation from parent ID alone.6768| Meta key | Relation |69|---|---|70| `_subscription_renewal` | Renewal order to subscription. |71| `_subscription_switch` | Switch order to subscription. |72| `_subscription_resubscribe` | Resubscribe order to subscription. |7374Use `wcs_get_subscription_ids_for_order()`, `wcs_get_subscriptions_for_order()`, `wcs_get_subscriptions_for_renewal_order()`, `wcs_get_subscriptions_for_switch_order()`, and `$subscription->get_related_orders()`.7576## Switcher flow7778Switching replaces or adds subscription line items through checkout. It is not a simple product ID update.79801. A user clicks the switch link printed in My Account by `WC_Subscriptions_Switcher::print_switch_link()`.812. The link points to the product/grouped product URL with `switch-subscription`, `item`, and `_wcsnonce` query args.823. `subscription_switch_handler()` validates ownership, nonce, and item switchability.834. `validate_switch_request()` blocks non-subscription products and identical product/variation/quantity switches.845. `set_switch_details_in_cart()` adds cart item data under `subscription_switch`.856. `WCS_Switch_Totals_Calculator` calculates proration, switch direction, first payment timestamp, and possible prepaid-term changes.867. Checkout line item meta records prorated amounts on the switch order and switched item links on subscription items.878. `process_checkout()` creates `_subscription_switch_data` on the switch order and may create pending switch items on the existing subscription.889. When the switch order is paid/completed, `complete_subscription_switches()` applies the payload to the subscription.8910. WCS fires `woocommerce_subscriptions_switch_completed` and item-level switch actions after completion.9091## Custom switch flows9293WCS does not expose a simple customer REST endpoint for "switch this subscription item to that product". The built-in switcher is a cart/checkout/order-completion flow. Custom account UI, AJAX, or REST layers must wrap that flow or deliberately reproduce its order payload.9495Safe pattern:96971. Verify the current user can switch the exact subscription item with `WC_Subscriptions_Switcher::can_item_be_switched_by_user()`.982. Build a temporary cart/session context for the current user and add the replacement product with `subscription_switch` cart item data.993. Let `WCS_Switch_Totals_Calculator` calculate prorations, `first_payment_timestamp`, `end_timestamp`, `force_payment`, and switch direction.1004. Return preview totals from the calculated cart/order data, not from hand-written price math.1015. On confirmation, create the switch order with the same `_subscription_switch_data` shape WCS checkout writes.1026. If an immediate payment is due, process it through the gateway/order payment flow; if no payment is due, complete/apply the switch through WCS completion logic.1037. Let `complete_subscription_switches()` apply the change and let WCS fire its normal hooks.104105Do not implement switching by directly calling `$subscription->remove_item()` and `$subscription->add_product()` from a customer action. That bypasses switch orders, prorations, tax/fee/coupon handling, old item archival, pending switch item types, cancellation of older unpaid switch orders, and completion hooks.106107For read-only previews or eligibility checks, it is fine to expose custom endpoints that return allowed products, switchable item IDs, and WCS-calculated preview totals. For writes, prefer a service that uses WCS cart/checkout objects internally.108109## Switch cart data110111The cart item key is `subscription_switch`. Typical shape:112113```php114$cart_item['subscription_switch'] = array(115 'subscription_id' => 123,116 'item_id' => 456,117 'next_payment_timestamp' => 1770000000,118 'upgraded_or_downgraded' => 'upgraded', // or downgraded/crossgraded, after calculation119 'first_payment_timestamp' => 1771000000, // after proration calculation120 'end_timestamp' => 1780000000, // when length changes121 'recurring_payment_prorated' => true,122 'force_payment' => true,123);124```125126`item_id` is the subscription line item being replaced. When adding an item to a subscription without replacing one, `item_id` can be empty and `wcs_cart_contains_switches( 'add' )` is relevant.127128## Switch order data129130Switch orders store `subscription_switch_data`, persisted as `_subscription_switch_data` on the order. Shape by subscription ID:131132```php133$switch_data[ $subscription_id ] = array(134 'switches' => array(135 $switch_order_item_id => array(136 'remove_line_item' => 456,137 'add_line_item' => 789,138 'switch_direction' => 'upgrade',139 ),140 ),141 'billing_schedule' => array(142 '_billing_period' => 'month',143 '_billing_interval' => 1,144 ),145 'dates' => array(146 'update' => array(147 'next_payment' => '2026-06-01 00:00:00',148 'trial_end' => 0,149 'end' => '2027-06-01 00:00:00',150 ),151 'delete' => array( 'trial_end' ),152 ),153 'coupons' => array( 111 ),154 'fee_items' => array( 222 ),155 'shipping_line_items' => array( 333 ),156);157```158159WCS can cancel older unpaid switch orders for the same subscription when a new switch order is created.160161## Switched item types and meta162163WCS uses custom order item types during and after switch execution.164165| Item type | Meaning |166|---|---|167| `line_item_pending_switch` | New line item staged on a subscription before switch completion. |168| `line_item_switched` | Old subscription line item archived after replacement. |169| `line_item_removed` | Removed subscription line item. |170| `coupon_pending_switch`, `fee_pending_switch`, `shipping_pending_switch` | Staged recurring coupon/fee/shipping items. |171| `coupon_switched`, `fee_switched`, `shipping_switched` | Archived old recurring coupon/fee/shipping items. |172173Important item meta:174175| Item meta | Stored on | Purpose |176|---|---|---|177| `_switched_subscription_item_id` | New subscription line item | Old subscription item ID. |178| `_switched_subscription_new_item_id` | Old subscription line item | New subscription/order line item ID. |179| `_switched_subscription_sign_up_fee_prorated` | Switch order line item | Portion of order line total from prorated sign-up fee. |180| `_switched_subscription_price_prorated` | Switch order line item | Portion of order line total from prorated recurring price. |181| `_has_trial` | Pending switch item | Marks new item with trial. |182183## Switch extension points184185| Need | Hook/filter | Args |186|---|---|---|187| Check product switchability | `wcs_is_product_switchable` | `$is_switchable, $product, $variation` |188| Check item switchability | `woocommerce_subscriptions_can_item_be_switched` | `$can, $item, $subscription` |189| Check user can switch item | `woocommerce_subscriptions_can_item_be_switched_by_user` | `$can, $item, $subscription` |190| Switch URL | `woocommerce_subscriptions_switch_url` | `$url, $item_id, $item, $subscription` |191| Switch link markup/text/classes | `woocommerce_subscriptions_switch_link`, `woocommerce_subscriptions_switch_link_text`, `woocommerce_subscriptions_switch_link_classes` | Link context. |192| Retain coupons | `woocommerce_subscriptions_retain_coupon_on_switch` | `$retain, $coupon_code, $coupon, $subscription` |193| Switch added to cart | `woocommerce_subscriptions_switch_added_to_cart` | `$subscription, $existing_item, $cart_item_key, $cart_item` |194| Proration price/day | `wcs_switch_proration_old_price_per_day`, `wcs_switch_proration_new_price_per_day` | Calculator context. |195| Switch type | `wcs_switch_proration_switch_type` | `$type, $subscription, $cart_item, $old_price_per_day, $new_price_per_day` |196| Prorate recurring/sign-up fee | `wcs_switch_should_prorate_recurring_price`, `wcs_switch_should_prorate_sign_up_fee` | `$bool, $switch_item` |197| Extra amount | `wcs_switch_proration_extra_to_pay` | `$extra, $subscription, $cart_item, $days_in_old_cycle` |198| Completion | `woocommerce_subscriptions_switch_completed` | `$order` |199| Item switched | `woocommerce_subscriptions_switched_item` | `$subscription, $new_order_item, $old_subscription_item` |200| Subscription item switched | `woocommerce_subscription_item_switched` | `$order, $subscription, $new_item_id, $old_item_id` |201202## Gifting storage203204Gifting is included in Subscriptions. It lets purchaser and recipient differ.205206| Storage | Key | Purpose |207|---|---|---|208| Cart item data | `wcsg_gift_recipients_email` | Recipient email before checkout/subscription creation. |209| Subscription meta | `_recipient_user_email_address` | Recipient email captured at subscription creation before a user is resolved. |210| Subscription meta | `_recipient_user` | Recipient user ID after account lookup/creation. Primary gifted-subscription marker. |211| Parent order item meta | `_wcsg_cart_key` | Links checkout order item to the recurring cart/subscription item. |212| Parent order item meta | `wcsg_recipient` | Value format `wcsg_recipient_id_{user_id}`. |213| Parent order item meta | `wcsg_deleted_recipient_data` | JSON snapshot used after recipient deletion. |214| User meta | `wcsg_update_account` | Recipient account setup/update flag. |215| User meta | `wcsg_recipient_just_reset_password` | Recipient onboarding flag. |216217Gifted subscriptions are detected by `WCS_Gifting::is_gifted_subscription()`: true when `_recipient_user` exists or `_recipient_user_email_address` is present.218219## Gifting flow2202211. Product page/cart/checkout collects recipient email when `WCSG_Product::is_giftable()` is true.2222. Cart item stores `wcsg_gift_recipients_email`; gifting is blocked for renewal and switch cart items.2233. Checkout uses recipient email in recurring cart keys so different recipients produce separate subscriptions.2244. `woocommerce_checkout_subscription_created` writes `_recipient_user_email_address` to the subscription.2255. On parent order processing/completion, recipient management finds or creates the recipient user, writes `_recipient_user`, adds `wcsg_recipient` to the matching parent order item, and updates shipping fields from recipient user meta.2266. Recipients are granted view/pay/suspend/cancel capabilities for gifted subscriptions, but cannot change payment method.2277. `_recipient_user` is not copied to renewal orders.228229Product giftability:230231- Global enablement comes from WCSG admin settings.232- Product-level override is `_subscription_gifting`: `enabled`, `disabled`, or empty for global.233- Variable subscription parent returns giftable to render UI; each variation decides final `gifting` variation data.234- Product page gifting UI is suppressed during switching (`switch-subscription` query arg).235236## Memberships integration for gifts237238If WooCommerce Memberships is active and a giftable subscription product grants a plan:239240- Membership access is granted to the recipient when order item meta has `wcsg_recipient`.241- Purchaser access is skipped unless the same product was also purchased for the purchaser.242- The created `wc_user_membership` still stores `_subscription_id`.243- Because one order can contain the same product for multiple recipients, link the membership to the recipient's subscription, not just the first subscription in the order.244245## Common mistakes246247```php248// WRONG: direct date meta write can desync Action Scheduler.249update_post_meta( $subscription_id, '_schedule_next_payment', '2026-06-01 00:00:00' );250251// RIGHT:252$subscription = wcs_get_subscription( $subscription_id );253$subscription->update_dates( array( 'next_payment' => '2026-06-01 00:00:00' ), 'gmt' );254255// WRONG: treating a switch as a product meta update.256$subscription->remove_item( $old_item_id );257$subscription->add_product( wc_get_product( $new_product_id ) );258259// RIGHT: use WCS switch flow or reproduce its order payload deliberately.260$is_switch = wcs_order_contains_switch( $order );261262// WRONG: gifted subscription recipient is not the subscription customer.263$recipient_id = $subscription->get_user_id();264265// RIGHT:266$recipient_id = WCS_Gifting::get_recipient_user( $subscription );267```268269## Cross-references270271- Use `wcs-subscription-hooks` for general lifecycle hook selection.272- Use `wcs-renewal-scheduler` for renewal dates, Action Scheduler, and payment retry timing.273- Use `wcm-data-model-subscriptions-link` for Memberships CPT/meta and the Memberships-to-Subscriptions relation.