Card expiry warning email (NPPD-1524)#4756
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new Newspack-managed “Card expiry warning” email for WooCommerce Subscriptions, including cron-driven scanning of expiring CC payment tokens and wiring the email into the unified Emails registry + preview token system.
Changes:
- Introduces
Card_Expiry_Warning(cron scheduling, token-first DB scan, idempotent send tracking, and reset on payment-method update). - Adds a new reader-revenue email template (
card-expiry-warning.php) and registers it in the Emails registry UI. - Extends email preview sample substitutions and adds unit + manual integration smoke tests.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
includes/plugins/woocommerce-subscriptions/class-card-expiry-warning.php |
New cron + scan + send implementation for the card-expiry warning email. |
includes/plugins/woocommerce-subscriptions/class-woocommerce-subscriptions.php |
Boots the new Card Expiry Warning integration from the WC Subscriptions integration init. |
includes/templates/reader-revenue-emails/card-expiry-warning.php |
New MJML-style + block email template for the warning email. |
includes/wizards/newspack/class-emails-section.php |
Registers the email in the unified Emails registry (chip/recipient/dependency metadata). |
includes/wizards/newspack/class-email-preview.php |
Adds preview sample substitutions for the new card-expiry tokens. |
tests/unit-tests/card-expiry-warning.php |
Unit tests for config/registry wiring and filterable window behavior. |
tests/integration/card-expiry-warning-smoke.php |
WP-CLI eval-file smoke test for end-to-end behavior on a real WC/WCS site. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
51b2d86 to
c5e7984
Compare
fce23a9 to
4f55717
Compare
facde84 to
98b0e2d
Compare
98b0e2d to
05ae747
Compare
|
Force-pushed after rebase onto the updated slice 2 branch, which now contains Three of the tokens this PR originally added ( Net effect on this PR's review surface: no behavioral changes — same tokens, Resolved review threads remain resolved. |
Add a new Newspack-managed "Card expiry warning" email that runs on a daily cron scan and sends ~14 days before a reader's credit card expires on an active WooCommerce Subscription. - New Card_Expiry_Warning class with daily wp_cron scan, token-first DB query for expiring CC tokens, idempotency via subscription meta - Email template matching existing receipt/cancellation structure - Registry entry in unified emails UI (reader-revenue chip) - Preview substitutions for new tokens - PHPUnit tests for config, registry, filter, and ordering Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In CI, WooCommerce Subscriptions is not active so Card_Expiry_Warning::init() never runs. Call add_email_config() directly in tests instead of relying on the filter being hooked. Also add a test for the min-days-before-expiry guard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…D-1524) Runnable via `wp eval-file tests/integration/card-expiry-warning-smoke.php`. Covers: cron scheduling, happy-path send, idempotency, clear_sent_flag handler, new-card re-send, unattached-card filtering, and cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- card-expiry-warning unit test: assert cancellation and woo-renewal-reminder indices are not false before comparing ordering. - card-expiry-warning smoke test: add Reader Activation prereq check so it fails fast instead of silently capturing 0 emails; drop the temporary "delete after merge" notice; add a README documenting tests/integration/. - Card_Expiry_Warning::maybe_send_warning(): use $customer->first_name as a fallback when subscription billing_first_name is empty. - Card_Expiry_Warning::clear_sent_flag(): drop the unused $new_payment_method parameter and matching accepted_args from the action.
05ae747 to
fa0f290
Compare
What this PR does
Adds a new Newspack-managed Card expiry warning email (NPPD-1524). Sent ~14 days before a reader's saved card expires on an active WC Subscription, prompting them to update their payment method before renewal fails silently.
Depends on slice 2 (#4732) for the chip mechanism, which depends on slice 1 (#4727).
Code changes
Backend —
includes/plugins/woocommerce-subscriptions/class-card-expiry-warning.php(new)Card_Expiry_Warningclass. Registers the email config onnewspack_email_configs, schedules a daily cron viawp_schedule_event, and hookswoocommerce_subscription_payment_method_updatedto clear the idempotency flag. Unschedules the cron onnewspack_deactivation.wp_woocommerce_payment_tokens+wp_woocommerce_payment_tokenmetafindstype='CC'tokens whose last-valid-day falls within the configured window (default 14 days). For each match,WCS_Payment_Tokens::get_subscriptions_from_token()resolves to the active subscriptions actually using that token for automatic renewals. This avoids scanning all subscriptions: the matching set is narrow by definition.get_subscriptions_from_token()matches the token string against the gateway-specific subscription meta, scoped to automatic-renewal subs only._newspack_card_expiry_warning_sentstoring{token_id}:{MM}/{YYYY}. Self-invalidates when the token changes (new token_id) or for a new expiry cycle (different MM/YYYY). Explicitly cleared viawoocommerce_subscription_payment_method_updatedso an updated card resets the warning state immediately.apply_filters( 'newspack_card_expiry_warning_days', 14 ). No UI control in v1 — filter-only.get_subscriptions_from_token()returns them — deliberate narrowing for v1, documented inline), automatic-renewal only (manual subs have no stored token),WC_Payment_Token_CConly (skips eCheck, PayPal, etc.), and not-yet-expired cards only.Backend —
includes/templates/reader-revenue-emails/card-expiry-warning.php(new)receipt.phpandcancellation.php. Returnspost_title,post_content(Gutenberg blocks for the email editor), andemail_html(MJML-style HTML for delivery). Usesnewspack_get_theme_colors()andnewspack_get_social_markup()for consistent branding.*BILLING_FIRST_NAME*,*CARD_LAST_4*,*EXPIRY_DATE*,*RENEWAL_DATE*,*UPDATE_PAYMENT_URL*(resolves towc_get_account_endpoint_url( 'payment-methods' )).Backend — registry + bootstrap
includes/wizards/newspack/class-emails-section.php: addscard-expiry-warningtoget_email_registry()(inserted aftercancellation, beforewoo-renewal-reminder) withchip='reader-revenue',recommended=true,plugin_dependency='woocommerce-subscriptions',recipient='reader'.includes/wizards/newspack/class-email-preview.php: extendsget_sample_substitutions()with new tokens —*CARD_LAST_4*→4242,*EXPIRY_DATE*→12/2026,*RENEWAL_DATE*→ a stable future date,*UPDATE_PAYMENT_URL*→#.includes/plugins/woocommerce-subscriptions/class-woocommerce-subscriptions.php: includes the new class and callsCard_Expiry_Warning::init()from the WC Subs integration init.Tests
PHPUnit (
tests/unit-tests/card-expiry-warning.php, new): email config registration + required keys + template file existence + placeholder shape; registry entry presence, chip, recommended, plugin_dependency, recipient, position in the registry; default 14-day window + filter override.Integration smoke test (
tests/integration/card-expiry-warning-smoke.php, new): runnable viawp eval-fileon a site with WooCommerce + WC Subscriptions active. Creates real DB fixtures (user, CC tokens, subscription) and exercises the full scan pipeline — 7 scenarios covering cron scheduling, happy-path send with wp_mail capture, idempotency, clear_sent_flag handler, new-card re-send, unattached-card filtering, and cleanup. 7/7 passing on local.No frontend test changes — no UI surface in v1.
Why slice this PR
Card expiry warning is a self-contained email. Keeping it as a separate PR off slice 2 makes it easier to review the cron + token scan logic on its own and ships independent reader-facing value as soon as slice 2 lands.
Manual testing
Tested locally with Newspack, WooCommerce, and WooCommerce Subscriptions active. Verified:
wp cron event list | grep newspack_card_expiry_warning_scanconfirms the daily cron is scheduledwp cron event run newspack_card_expiry_warning_scan), verified the email sent to the customer email with correct token substitutionsAll Submissions: