React when a lapsed licence stops (and later restarts) subscriber billing
When a podcaster’s own Benecaster licence reaches Day-30 hard cutoff, Benecaster pauses collection on every built-in-membership Stripe subscription so nobody is charged for a feed that has stopped, and resumes exactly that cohort when the licence recovers. Both sweeps announce their outcome. Hook them if your add-on mirrors subscriber state anywhere else — a CRM, an analytics warehouse, a Slack channel, your own emails.
Four things about these hooks that are not obvious.
Pause fires at cutoff, not at grace start. During the 30-day grace window subscribers receive the full paid product, so they keep paying for it. Nothing billing-related fires anywhere in the grace lifecycle — if you need a signal earlier, hook benecaster_license_grace_started instead and treat it as a warning rather than a state change.
Resume touches only what Benecaster paused. A subscription the podcaster cancelled or paused themselves during the outage is never resumed, because the cohort is selected by a paused_reason tag Benecaster writes and by the row still being chargeable. Do not “help” by resuming the rest — you would restart billing the podcaster deliberately stopped.
$skipped_shows is not a rounding error, and 'test_mode' is the one to read carefully. A show in test mode cannot be reached through its live keyset, and nothing records which mode created a given subscription — so those subscribers are still being charged and Benecaster cannot stop it. If your add-on surfaces billing state anywhere, surface these too.
Only built-in membership is ever affected. Subscriptions in MemberPress, WooCommerce Subscriptions, PMPro, RCP or Patreon are not ours to pause and Benecaster never touches them. If your add-on bridges one of those, the podcaster stopping their own billing is a manual step and these hooks will not fire for it.
There are matching benecaster_cutoff_billing_pause_started / benecaster_cutoff_billing_resume_started actions carrying just the candidate count, fired before any Stripe call, so a sweep that dies mid-way is still bounded in your logs.
Code
<?php
add_action( 'benecaster_cutoff_billing_pause_complete', 'my_addon_billing_paused', 10, 3 );
add_action( 'benecaster_cutoff_billing_resume_complete', 'my_addon_billing_resumed', 10, 3 );
/**
* @param int $paused Subscriptions successfully paused and tagged.
* @param array $failures [ [ 'subscription_id' => string, 'show_id' => int, 'message' => string ], … ]
* @param array $skipped_shows show_id => 'test_mode' | 'no_keys'
*/
function my_addon_billing_paused( int $paused, array $failures, array $skipped_shows ): void {
// The interesting argument is $failures, not $paused. A failure means
// that subscriber is STILL BEING CHARGED for a feed returning 403 —
// the one outcome worth waking somebody up for.
if ( [] === $failures && [] === $skipped_shows ) {
return;
}
my_addon_alert( sprintf(
'Benecaster paused %d subscriptions; %d failed, %d shows unreachable.',
$paused,
count( $failures ),
count( $skipped_shows )
) );
}
function my_addon_billing_resumed( int $resumed, array $failures, array $skipped_shows ): void {
// A resume failure is the inverse harm: those subscribers still have
// access and are NOT being billed for it.
my_addon_sync_billing_state();
}
Hooks Used
Need this built rather than just documented? See our services →