React to subscriber-initiated lifecycle events from the native billing portal
BillingPortalController fires three actions for subscriber self-service actions in the native portal. These complement webhook-driven events: portal actions identify the actor (current $user_id); webhook actions identify the eventual state after Stripe accepts. benecaster_subscription_cancellation_requested fires when cancel_at_period_end = true is set at Stripe (the terminal benecaster_subscription_cancelled still fires from the webhook when the period actually ends). benecaster_subscription_resumed fires on cancel_at_period_end = false flip. Payment method update fires after all three Stripe writes succeed.
Code
<?php
// Log every subscriber-initiated cancellation request for retention analytics.
add_action( 'benecaster_subscription_cancellation_requested', function ( int $user_id, int $show_id, string $stripe_subscription_id ): void {
my_addon_retention_log( [
'event' => 'cancellation_requested',
'user_id' => $user_id,
'show_id' => $show_id,
'requested_at' => current_time( 'mysql', true ),
] );
}, 10, 3 );
// Schedule a "win-back" follow-up 24 hours after a cancellation request.
add_action( 'benecaster_subscription_cancellation_requested', function ( int $user_id, int $show_id ): void {
wp_schedule_single_event( time() + DAY_IN_SECONDS, 'my_addon_send_winback_email', [ $user_id, $show_id ] );
}, 10, 2 );
// Cancel any scheduled win-back flow when a subscriber resumes.
add_action( 'benecaster_subscription_resumed', function ( int $user_id, int $show_id ): void {
wp_clear_scheduled_hook( 'my_addon_send_winback_email', [ $user_id, $show_id ] );
}, 10, 2 );