# Failed Payment Recovery

## Objective

Help customers fix a failed payment before you cut off their access.

A dunning process: provider-driven retry tracking, a defined grace period with stated restrictions, de-duplicated notices, and a direct path to repair the payment method.

## Before You Begin

This feature is being added to an application that already exists and already
works. Do not scaffold a new project, and do not assume a blank slate.

Inspect the codebase first and establish:

- The existing application structure and where code of this kind already lives.
- The framework and version in use.
- The existing design system — colours, spacing, typography, and component conventions.
- Existing UI components you can reuse instead of writing new ones.
- The existing database structure, if this feature needs to persist anything.
- The existing authentication and authorization system, if this feature is user-scoped.
- Dependencies already installed, so you don't add a library that duplicates one.
- The existing test setup and conventions.

Only start writing code once you understand the above. If the application
already implements part of this feature, extend it rather than replacing it.

## Implementation Instructions

1. Treat the provider's retry schedule and invoice status as the source of truth for where an account is in dunning. Do not run a parallel retry timer.
2. Define one grace period and state exactly what is restricted during it. Halve-working software with no explanation generates support tickets, not payments.
3. Give the failing account a persistent in-app path to update the payment method and retry the invoice immediately, rather than waiting for the next provider attempt.
4. De-duplicate customer communication by invoice and attempt, not by webhook delivery. Providers retry webhooks and deliver them out of order.
5. Restore full access as soon as payment succeeds, without waiting for a nightly job. Subscription state modelling and webhook verification are owned by Billing Portal; build on it.

## UI and UX Requirements

Match the application's existing design system exactly. Reuse its components,
spacing, and typography. This feature should look like it was always there.

## Responsive Requirements

Works on mobile, tablet, and desktop. Touch targets are large enough to hit on a
phone, and nothing overflows horizontally at 320px.

## Accessibility Requirements

- Fully keyboard navigable.
- Correct semantic elements and ARIA roles.
- Visible focus states.
- Meets WCAG AA contrast.
- Dynamic changes are announced to screen readers.
- Respects prefers-reduced-motion.

## Edge Cases

- The same failure webhook will arrive more than once. Sending a second identical dunning email is worse than sending none.
- Payment can succeed through the provider's own retry while the user is mid-way through updating their card. Recheck status before charging again.
- The person who receives dunning notices may not be the account owner. Billing recipients are owned by Billing Contact Management; send there.
- A card declined for insufficient funds and a card that has expired need different instructions. Use the provider's decline reason where it is safe to show.
- Access must not be revoked and restored repeatedly as retries fail and succeed. Move in one direction per invoice cycle.
- When the final retry fails, decide in advance whether the subscription cancels or lapses to a read-only state, and make the data recoverable either way.

## Testing

Exercise the feature end to end in the running application. Cover every edge case
above, then run the existing test suite and confirm nothing regressed.

## Acceptance Criteria

- [ ] Dunning stage is read from provider status rather than computed locally.
- [ ] The grace period length and its exact feature restrictions are defined in one place and shown to the user.
- [ ] A given invoice attempt produces at most one customer notice regardless of webhook retries.
- [ ] The user can update the payment method and trigger an immediate retry from inside the app.
- [ ] Successful payment restores access within the same request cycle, not on a schedule.
- [ ] The terminal outcome after the last retry is explicit and preserves the account's data.
- [ ] The feature matches the existing design system.
- [ ] No existing functionality is broken.

## Adaptation Rules

- Match the existing design system. Do not introduce a new colour palette,
  spacing scale, or component library.
- Reuse existing components and utilities wherever they fit.
- Follow the naming, file layout, and code style already present.
- Do not upgrade, replace, or remove existing dependencies to make this
  feature fit. Adapt the feature to the app, not the app to the feature.
- Do not break existing functionality. If a change is genuinely required in
  existing code, make the smallest one that works and say so.
- If something in these instructions conflicts with how the application is
  built, follow the application and explain the deviation.

## Final Verification

Before you report the work as done:

1. Re-read the acceptance criteria above and check each one against what you
   actually built.
2. Run the application and exercise the feature end to end.
3. Run the existing test suite and confirm you have broken nothing.
4. Check the feature on mobile, tablet, and desktop widths.
5. Check keyboard navigation and focus handling.
6. Summarize what changed: files added, files modified, and anything you
   deliberately did differently because of how this application is built.

If any acceptance criterion is unmet, fix it before reporting completion.
