Implementation worksheet · 5 min read
A Trial Expiration Test Matrix for AI-Built SaaS
Test eight cases before launch: expiry with no card (what locks, what stays readable), expiry with card on file (charge fires once, not twice), conversion mid-trial (no double charge at term end), cancellation mid-trial (access until expiry, then clean lapse), expiry during an active session (in-app state changes without data loss), the day-before warning email (fires once, links to payment), re-registration after expiry (old data policy enforced), and timezone edges (expiry at UTC midnight vs the user's midnight). Make them testable by injecting a clock the test can shift — 'set account trial to expire in 60 seconds' beats waiting 14 days or hand-editing production dates.
Every SaaS ships trial logic; almost none test the expiry side, because waiting out a trial is annoying and shifting dates in production is scary. The result is the classic launch-week ticket: 'my trial ended and my data is gone' — usually not gone, just locked behind a state nobody designed.
Put it into practice
1. Make expiry a designed state, not an absence
Expired-no-card is its own account state with defined behavior per your entitlement table's lapse column: what's read-only, what's exportable, what upgrade restores. If the answer is 'whatever the code happens to do', that's the bug.
2. Inject a shiftable clock
The app reads time from one place, and staging lets you set an account's trial_ends_at to now-plus-a-minute. This single affordance turns all eight cases from 'wait two weeks' into a test suite an afternoon can run.
3. Test the charge path for exactly-once
Card on file + expiry must produce one charge, one receipt, one entitlement flip — including when the expiry job retries after a crash. Idempotency keys on the charge call; the test kills the job mid-run and re-runs it.
4. Test expiry mid-session
A user working at the moment of expiry should hit a save-safe wall: current work preserved, next gated action prompts upgrade. Losing an in-progress form to a plan gate converts a warm lead into a refund story.
5. Verify the warning email fires once
Day-before (or your chosen offset), once per account, with a working payment link — and not for accounts that already converted or cancelled. Duplicate warning emails are the most common cron-retry symptom users actually see.
The eight cases
Copy this structure into your review document and record your observed result for each row.
| Case | Setup | Expected | Verified |
|---|---|---|---|
| Expiry, no card | trial ends, no payment method | lapse state per entitlement table | |
| Expiry, card on file | trial ends, card saved | one charge, entitlements flip | |
| Convert mid-trial | upgrade on day 7 | paid now; no second charge at term | |
| Cancel mid-trial | cancel day 7 | access to expiry, then clean lapse | |
| Expiry mid-session | active session at expiry | work saved; next action gated | |
| Warning email | T-1 day | exactly one, correct link | |
| Re-register after expiry | same email signs up again | data policy enforced, no free reset | |
| Timezone edge | user at UTC+13 | expiry at documented boundary |
A failure worth checking
The silent lockout: expiry flips a flag, every route redirects to the pricing page, and the user's data is technically retained but practically unreachable — no export, no read-only view, no message beyond 'upgrade'. Users experience it as data loss, word it as data loss in reviews, and no refund un-writes the review. The lapse column exists so this state is designed, not defaulted.
Common questions
Should trials require a card up front?
It's a conversion trade, not a correctness one — but it changes which test cases dominate: card-up-front makes the exactly-once charge path critical; no-card makes the lapse state critical. Test hardest wherever your choice puts the risk.
How do I test this in an AI-built app?
Same matrix — describe the expiry behavior explicitly when specifying the app (the entitlement table's lapse column is the spec), then run the eight cases against staging with the shifted clock. Generated billing logic tests exactly like hand-written billing logic: deliberately.
Basis and scope
This is a proposed implementation method using illustrative examples, not a measured benchmark or a customer case study. Prepared with AI assistance. Validate product-specific behavior against current documentation and your own test environment.