Skip to main content

Stripe

One Stripe account processes the store, events and donations. Development uses test mode: test cards work, real cards are refused, and nothing is charged. Production uses live mode.

Test cards (test mode only)​

Any future expiry (for example 12/34), any CVC (123; four digits for Amex), any postal code.

Successful payments​

CardNumberUse
Visa4242 4242 4242 4242The standard successful payment
Visa Debit4000 0566 5566 5556Debit transaction
Mastercard5555 5555 5555 4444Successful Mastercard
Mastercard Debit5200 8282 8282 8210Debit card
American Express3782 822463 10005Amex (4-digit CVC)
Discover6011 1111 1111 1117Discover
JCB3566 0020 2036 0505International card

Declined payments​

ScenarioNumberExpected behaviour
Generic decline4000 0000 0000 0002Bank rejects the payment
Insufficient funds4000 0000 0000 9995Customer lacks funds
Lost card4000 0000 0000 9987Card reported lost
Stolen card4000 0000 0000 9979Card reported stolen
Expired card4000 0000 0000 0069Fails on expiry
Incorrect CVC4000 0000 0000 0127Security code rejected
Processing error4000 0000 0000 0119Processing error

For ACH (donations), Stripe's test mode offers a test bank account in the "US bank account" tab. Full list: docs.stripe.com/testing.

Where to check a test transaction​

WhereWhat you see
Stripe Dashboard (test mode) → Paymentsthe PaymentIntent, its metadata (app, order/registration/donation id)
Stripe Dashboard → Developers → Webhooksdelivery attempts to https://dev.emaw-app.com/rest/stripe/webhook/
Admin CRM (dev) → Store → Orders / Events → Registrations / Contact → Orders tabthe record, now paid, and the linked contact
Deposco sandboxthe Sales Order (store only)
Your inboxconfirmation email via Microsoft Graph

Test versus live​

TestLive
Keyssk_test_ on the backend, pk_test_ on the websitesk_live_, pk_live_
Webhook endpointdev.emaw-app.com/rest/stripe/webhook/ with its own whsec_app.emaw-app.com/rest/stripe/webhook/ with its own whsec_
Stripe objectspayment method configurations, donation product, billing portal configuration created in test modecreated again in live mode; different ids
Where configuredbackend dev .env, website GitHub variablesbackend prod .env (Portainer), Pages production variables

Objects do not carry over between modes. Every pmc_, prod_, bpc_ and whsec_ must be created twice.

Webhook​

Single endpoint POST /rest/stripe/webhook/, signature-checked, de-duplicated in Redis, dispatched on metadata.app. It must be subscribed to all nine events (checkout.session.completed, async_payment_succeeded, async_payment_failed, expired, charge.refunded, invoice.paid, invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted). Updating an endpoint replaces its event list, so re-list all nine. The endpoint's API version must match STRIPE_API_VERSION (2026-09-30.endive).

Local bridging with the Stripe CLI: Backend → Stripe.

Go-live checklist​

Follow docs/donations-go-live.md for donations; the store and events use the same steps minus the subscription objects:

  1. Activate the live account and payment methods (cards; ACH Direct Debit for donations).
  2. Create the live webhook endpoint (or update the existing one) with all nine events; copy its whsec_.
  3. Create live payment method configuration(s), the donations product and the billing portal configuration; copy the ids.
  4. Set STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STORE_*, EVENTS_*, DONATIONS_* in the production backend .env; redeploy.
  5. Set VITE_STRIPE_PUBLISHABLE_KEY (pk_live_) in the website's production variables; deploy.
  6. Smoke test with a small real charge, then refund it from the dashboard.
  7. Watch webhook deliveries and error.log for the first days.

Refunds and disputes​

Refund from the Stripe Dashboard; the charge.refunded webhook updates the order, registration or donation. Deposco orders already submitted must be cancelled in Deposco by hand (deposco/README.md "Refunds and cancellations").