Skip to content

Card flow: real Stripe test key + end-to-end card payment verification #148

@0xdevcollins

Description

@0xdevcollins

Why

The card flow on the hosted checkout is architecturally complete — `createCardSession` auto-fills source fields, creates a Stripe `paymentIntent`, returns a `clientSecret` — but the current `apps/api/.env` has the placeholder `STRIPE_SECRET_KEY="sk_test_..."`. Hitting `/payments/:id/card-session` returns a clean Stripe error (we wrap it in PR 7.7) but no actual card payment has happened.

This is the lowest-effort way to close the card flow into "actually works" status.

Scope

  1. Provision a real Stripe test key — `sk_test_…` (the full one, not the placeholder)
  2. Wire it into local dev + document for prod
  3. Run a full card payment through the hosted checkout
  4. Confirm the webhook fires `payment.completed` on success

What to do

1. Get the key

2. Wire it

```bash

In apps/api/.env

STRIPE_SECRET_KEY="sk_test_<real_key>"
STRIPE_WEBHOOK_SECRET="whsec_<real_secret>"
```

Restart the API. Now `createCardSession` actually creates a real Stripe PaymentIntent and returns a usable `clientSecret`.

3. Run the flow

  1. Register a fresh merchant
  2. Create a payment link (`amount: 25, currency: USD`)
  3. Open `http://localhost:3003/l/` in incognito
  4. Click Pay → Card tile
  5. Stripe Elements form should render with the test card prompt
  6. Use Stripe's test card: `4242 4242 4242 4242`, any future expiry, any CVC, any ZIP
  7. Submit → payment should succeed
  8. Page should redirect to `/:paymentId/success`

4. Verify the webhook

  • In the API log, look for `Stripe webhook received: payment_intent.succeeded`
  • Payment row's `status` should flip to `COMPLETED` with `completedAt` set
  • `WebhookEvent` row should land with `eventType: 'payment.completed'` queued for delivery to the merchant's webhookUrl

5. Document

Add a section to `apps/api/docs/operations/stripe-setup.md`:

  • How to get the test key + webhook secret
  • The webhook endpoint URL and the events to subscribe
  • The tunnel command (`cloudflared tunnel` or `ngrok http 3333`) for local webhook delivery
  • Test card numbers for common scenarios (success, decline, 3DS, insufficient funds)
  • How to flip to prod (use live keys, set up the prod webhook endpoint, run KYB on the Stripe account)

Acceptance criteria

  • Real `sk_test_` key in `apps/api/.env`, NOT committed (`.env.example` still has placeholder)
  • Successfully completed one full card payment through `/l/` → `/p//card` → success
  • Payment row landed in DB with `status=COMPLETED`, `completedAt` set, Stripe `paymentIntentId` in metadata
  • Stripe webhook hit our `/v1/webhooks/stripe` endpoint and updated the payment + queued a `payment.completed` webhook for the merchant
  • At least one decline scenario tested (`4000 0000 0000 0002` declines as "generic decline")
  • Runbook at `apps/api/docs/operations/stripe-setup.md`

Estimated effort

~1 hour, assuming you have or can get a Stripe account.

Notes

  • Keep the key in a secrets manager (1Password, Doppler, etc.) once you have it. Not in Slack DMs.
  • The webhook secret matters: without it, `PaymentsService.handleStripeWebhook` will reject the signature and return 400.
  • The test card `4242 4242 4242 4242` always succeeds. For 3DS testing use `4000 0027 6000 3184`. Full list: https://docs.stripe.com/testing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions