Skip to main content

benecaster_cutoff_billing_resume_complete

Action Premium

Fires after the cutoff billing resume has swept the paused cohort. Each subscription resumed successfully has its paused_reason cleared.

A failure here is the mirror image of a pause failure, and it is easier to miss. A subscription that fails to resume is not being charged while the subscriber’s feed works again — the podcaster delivers a paid product for free, indefinitely, with nothing in Stripe to suggest anything is wrong. Same arrays, opposite loss.

React when a lapsed licence stops (and later restarts) subscriber billing

Premium Intermediate

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.

<?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();
}

View on GitHub →

Parameters

Name Type Default Description
$resumed int Subscriptions successfully resumed and untagged.
$failures array **The argument that matters.** One entry per subscription the Stripe call failed on, each `{ subscription_id: string, show_id: int, message: string }`. Every entry is a subscriber whose billing did not restart while their feed works again. Empty on a clean run. Treat a non-empty array as an incident, not as telemetry.
$skipped_shows array Shows the sweep could not act on at all, as `show_id => reason`. Two reasons: `test_mode` — the show is configured for test mode while holding live subscriptions, so Benecaster cannot reach them; `no_keys` — its Stripe credentials are missing or unusable. Nothing on a skipped show is resumed. Empty on a clean run.

Notes

Resuming is scoped to the tagged cohort, so anything the podcaster cancelled or paused themselves during the outage is left exactly as they left it. See PauseReasons for why the tag is what makes that possible.

Need this built rather than just documented? See our services →