Quickstart: Validating the Payment Failure Grace Period Feature
Quickstart: Validating the Payment Failure Grace Period Feature
This is a validation/run guide, not implementation code. It assumes the feature has been
implemented per data-model.md and contracts/. Full source can be diffed against the existing
reference implementation on origin/feat/1023-grace-cron-userpayment
(worktree: /private/tmp/kilo/wt-grace-pr) for a working baseline.
Prerequisites
- Local
yoyacoo_beenvironment running (adminapp),.envwith a valid Stripe test-mode
secret key andSTRIPE_WEBHOOK_SECRETset. MAIL_MAILER=log(or Mailtrap) so sent emails can be inspected without real delivery.- Test database migrated (
make t-migrate) including the new grace-tracking migration(s). - Stripe CLI installed and logged in, for forwarding webhooks
and triggering test events locally:stripe listen --forward-to localhost/webhook.
One-time Stripe Dashboard configuration (test mode)
Per spec.md Assumptions, this is manual operational setup, not app code:
- Stripe Dashboard → Settings → Billing → Subscriptions and emails.
- Manage failed payments → retry schedule = “Do not retry”.
- Subscription cancellation → enable, set to cancel 7 days after the invoice first became
past due.
Scenario 1 — Payment fails → grace period + immediate email
- Create a test subscriber with an active paid subscription (existing subscribe flow, or a Stripe
test-mode subscription attached to a seededUser). - Force the next renewal invoice to fail: use a Stripe test card that always fails
(4000000000000341) as the default payment method, or usestripe trigger invoice.payment_failedagainst the test subscription. - Expected:
UserPayment.statusbecomesPAST_DUE(3).hosted_invoice_urlandgrace_period_started_atare populated.- The subscriber immediately receives the 1st email (check the log mail driver output or
Mailtrap inbox) with subject【ヨヤクー】お支払い方法のご確認をお願いいたしますand a working
payment link. - The subscriber can still use every previously-available paid feature (spot-check one paid-only
screen/endpoint).
Scenario 2 — Day-3 / Day-6 reminders
- From Scenario 1’s state, manually backdate
grace_period_started_aton the test row (e.g., 3
days in the past) or wait/advance the system clock in a test/staging environment. - Run
php artisan grace:send-mail. - Expected: the 2nd email (
【ヨヤクー】お支払い方法の更新をお願いいたします) is sent, and
grace_mail_2_sent_atis set. Running the command again immediately does not resend it. - Repeat with the anchor backdated 6 days for the 3rd email
(【重要|ヨヤクー】有料プランのキャンセル前の最終確認です).
Scenario 3 — Manual payment recovery
- From a
PAST_DUEstate, complete payment via thehosted_invoice_urlfrom one of the emails
(orstripe trigger invoice.payment_succeeded/invoice.paidagainst the test subscription). - Expected:
UserPayment.statusreturns toSETTLED.grace_success_mail_sent_atis set exactly once even if bothinvoice.payment_succeededand
invoice.paidare received for the same payment.- The subscriber receives the payment-completed email
(【ヨヤクー】お支払いが完了しました) with the amount paid and payment date. - Running
grace:send-mailafterward sends no further dunning mail for this cycle.
Scenario 4 — Day-7 automatic cancellation
- From a
PAST_DUEstate with no payment, either let the Stripe test-mode subscription actually
reach its configured 7-day cancellation, or simulate it directly:
stripe trigger customer.subscription.deletedagainst the test subscription. - Expected:
UserPayment.statusbecomesDELETED.- The existing free-plan downgrade cleanup runs (spot-check a plan-limited resource is now
restricted per free-plan limits). - No further dunning mail is sent for this (now-closed) grace cycle.
Automated test suite
Run the feature’s automated tests (mirrors the reference branch’s admin/tests/Feature/Grace/*
suite):
make t-migrate
php artisan test --filter=Grace
composer phpstan
All of the above must pass with zero failures before considering the feature done, per the
project’s Test & Quality Gates (constitution principle III).