Skip to main content

Override the built-in re-authorization check, or resolve a row it merely vetoed

Premium Advanced

By default, Promote-to-Bridge already checks this for you, for the four core bridges: every daily cron tick asks the destination bridge’s is_user_active( $user_id, $show_id ) before deciding whether to expire a row, and a subscriber confirmed active there is kept automatically, no filter required. Use this recipe when that built-in check isn’t enough:

  • A custom bridge. Your own BridgeInterface implementation’s is_user_active() doesn’t
    answer the question the way you need.
  • A more specific check. The example below checks for an active payment method, which
    plain active/inactive can’t tell you.
  • Resolving the row. The built-in check only vetoes for the day — it never calls
    clear_grace_period() — so a row it keeps still shows as past-due in Promotion History forever, re-asked fresh on every tick. If you want the row to stop looking stuck, you still have to resolve it yourself.

benecaster_promote_grace_expiry_should_revoke runs in the daily cron for every row whose grace period has elapsed, after the built-in active-in-destination-bridge check has set its default (false if the subscriber is confirmed active there, true otherwise — including when the bridge can’t answer, e.g. a deactivated plugin). Your callback receives that default as $should_revoke and has the final word — return it unchanged to defer to the built-in check, or override it either direction. Returning false skips the entire expiry for that row: the token is not revoked, the status does not move to expired_grace, the “your podcast subscription has ended” email is not queued, and benecaster_promote_grace_expired does not fire.

That last point is why this filter composes cleanly with Push promote-grace expiry events into a CRM — the action never fires for a row you vetoed, so a CRM listener will not record a churn that did not happen.

Notes

A veto means “not today”, not “never”. Vetoing deliberately leaves promote_grace_period_ends_at untouched, so the row is still past-due and tomorrow’s cron tick offers it to your filter again. That is why the filter takes a plain bool and has no “defer by N days” return path — the re-ask is built in.

It also means a callback that only ever returns false leaves the row permanently past-due, and the podcaster will see it sitting in Promotion History as in-grace with an elapsed date. Resolve the row once you know the answer: call clear_grace_period() when the subscriber has genuinely re-authorized, or extend_grace_period() to move the deadline to a date the podcaster can see.

The veto is evaluated with a strict true !== comparison, so returning a truthy non-boolean — a 1, a non-empty string — vetoes rather than consenting. Return real booleans.

$promotion is the hydrated subscription row: id, user_id, show_id, tier_slug, billing_interval, status, promote_grace_period_ends_at, promoted_to_bridge, the stripe_* columns and the pause columns. $user_id duplicates $promotion['user_id']. Return the incoming $should_revoke rather than a hard-coded true whenever you decline to intervene, so you compose with other callbacks instead of overriding them.

Code

<?php
add_filter( 'benecaster_promote_grace_expiry_should_revoke', function ( bool $should_revoke, array $promotion, int $user_id ): bool {
    // Only intervene for the destination plugin we actually know how to ask.
    if ( 'memberpress' !== ( $promotion['promoted_to_bridge'] ?? '' ) ) {
        return $should_revoke;
    }

    if ( ! my_destination_plugin_has_active_payment_method( $user_id ) ) {
        return $should_revoke; // Let the expiry proceed as normal.
    }

    // They finished setting up. Resolve the row so it stops coming back to us
    // and so the podcaster sees it as cleared in Promotion History.
    \Benecaster\Plugin::instance()
        ->make( \Benecaster\Bridge\PromotionService::class )
        ->clear_grace_period( (int) $promotion['id'] );

    return false;
}, 10, 3 );

View on GitHub →

Hooks Used

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