Skip to main content

benecaster_promote_grace_extended

Action Premium

Fires after a podcaster manually extends a transferred subscriber’s grace period from the Promotion History panel.

The old deadline and the new one arrive by different routes. $promotion is the row as it was before the extension, so $promotion['promote_grace_period_ends_at'] is still the old date; the new one is the third argument. Reading both is what lets a listener report the change rather than just the result.

$new_expiry_ts is a UTC Unix timestamp, not a formatted string — deliberately, so there is no clock to mistake and no format to parse.

Does not fire on a failed write, when no subscription has that id, or when the row has no grace period to extend.

Keep a CRM in step with manual grace-period decisions

Premium Intermediate

Clearing and extending a grace period are decisions a human makes in the Promotion History panel, one subscriber at a time — the podcaster has checked the destination plugin, or has decided somebody deserves longer. Those decisions leave no trace outside the database on their own.

benecaster_promote_grace_cleared and benecaster_promote_grace_extended fire after each one so an external system can mirror it: close the support ticket that was opened when the migration started, cancel a scheduled “your access ends soon” campaign, or record the extension against the account so the next person to look does not undo it.

Notes

$promotion is the row as it was BEFORE the operation, and for _cleared that is the only reason it is useful. Clearing nulls both promote_grace_period_ends_at and promoted_to_bridge, so a listener that re-read the row itself would find two nulls and would have lost the two facts worth knowing: which plugin the subscriber was moved to, and what deadline was just cancelled. The same applies to _extended$promotion['promote_grace_period_ends_at'] is the old deadline; the new one is the third argument.

$new_expiry_ts is a UTC Unix timestamp, not a formatted string. That is deliberate: there is no clock to mistake and no format to parse. Format it yourself with wp_date() if you are showing it to a human — date_i18n() expects the site offset already baked in and would render this real UTC epoch as if it were already site-local.

Neither action fires when the underlying write fails, and neither fires for a subscription row that does not exist — so a listener never records an operation that did not happen locally.

The cron expiry path fires neither of these. A grace period that simply runs out is benecaster_promote_grace_expired, and an expiry vetoed through benecaster_promote_grace_expiry_should_revoke fires nothing at all. These two are specifically the manual operations a podcaster performs.

<?php
add_action( 'benecaster_promote_grace_cleared', function ( array $promotion, int $user_id ): void {
    my_crm_client()->close_task( 'podcast_migration_reauth', [
        'user_id'   => $user_id,
        'show_id'   => $promotion['show_id'],
        'tier_slug' => $promotion['tier_slug'],
        // Still readable here - the row is the pre-clear snapshot.
        'moved_to'  => $promotion['promoted_to_bridge'],
    ] );
}, 10, 2 );

add_action( 'benecaster_promote_grace_extended', function ( array $promotion, int $user_id, int $new_expiry_ts ): void {
    my_crm_client()->reschedule_reminder( $user_id, $new_expiry_ts );
}, 10, 3 );

View on GitHub →

Parameters

Name Type Default Description
$promotion array The subscription row as it stood immediately before the extension. `promote_grace_period_ends_at` is the deadline being replaced.
$user_id int Subscriber's WordPress user ID. Also present in `$promotion`.
$new_expiry_ts int The new grace-period deadline as a UTC Unix timestamp.

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