# Stripe Checkout

## Objective

Take payment through a Stripe-hosted checkout with prices the browser cannot alter.

A server-initiated Stripe-hosted checkout session carrying the correct customer, trusted pricing, and a return path that resolves the same regardless of when confirmation arrives.

## 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. Create the checkout session on the server. Take only an identifier for what is being bought from the browser, then look up the price, currency, quantity limits, and tax treatment from the app's own records.
2. Resolve the billing customer before creating the session: reuse the one already recorded for this workspace or account, and create a new one only when none exists. Store the identifier the moment it is known so a retry does not create a second customer.
3. Carry the app's own reference for the purchase into the session so the confirmation that arrives later can be matched back to a workspace, a user, and an intended outcome without guessing.
4. Grant nothing on the browser returning to the success page. Access is granted only when a verified server-side confirmation says the money was taken, which may arrive before, during, or long after the redirect.
5. Reconciling the ongoing state of customers, subscriptions, and invoices belongs to the Stripe Customer and Subscription Sync brief. This one ends at a confirmed first payment; do not build a second event handler that competes with 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

- Anything the browser sends about price, quantity, currency, or discount must be discarded. Read those from server-held records, or a user can edit the request and buy at a price you never offered.
- A user can abandon a checkout, let it expire, complete it, or pay by a method that settles hours later. Each of those needs a defined local state, and the pending case must not look identical to a failure.
- Confirmations from the provider must be verified as genuine before anything is granted, and must be safe to receive more than once. Providers retry, so the same confirmation arriving twice must not create two purchases or two subscriptions.
- The redirect back and the server confirmation race each other. The success page must handle arriving first by showing a pending state that resolves on its own, and handle arriving second by showing the finished result, without ever telling the user something contradictory.
- The same person clicking through checkout twice, or reloading the initiation, must not end up with two customers or two charges. Make session creation idempotent against the app's own reference.
- If the provider is down or the session cannot be created, say so plainly and leave the user's cart or intent intact so they can try again, rather than sending them to a blank page.
- A user who closes the tab mid-payment and returns later must find their account in a truthful state: either paid, or not paid with a clear way to resume.
- Never place a secret credential in the page. Only values the provider designates as publishable may reach the browser.

## 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

- [ ] Checkout sessions are created server-side and price, quantity, and currency come only from server-held records.
- [ ] A workspace resolves to exactly one billing customer, even across repeated or retried checkouts.
- [ ] Access is granted only after a verified server-side confirmation, never from the browser's return.
- [ ] Completed, expired, abandoned, and asynchronously settled sessions each map to a distinct, correct local state.
- [ ] Repeated confirmations for the same purchase produce no duplicate records or duplicate access.
- [ ] The success page resolves correctly whether it loads before or after the confirmation arrives.
- [ ] A provider failure at initiation shows a clear message and preserves the user's intent to retry.
- [ ] 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.
