Stripe setup
End-to-end Stripe configuration for both ToT (direct charges) and Storefront (Connect Express). Walk through this once per environment — test mode for development, live mode for production.
The two payment models
| ThisOrThat | Storefront | |
|---|---|---|
| Model | Direct charges on platform | Stripe Connect Express |
| Funds settle into | Your platform account | Operator's Connect account |
| Capture method | Manual (sale-lock safety) | Automatic |
| Checkout type | Payment Intent (custom UI) | Stripe-hosted Checkout Session |
| Platform fee | Built into spread (Pull/Flip) | Application fee (3% default) |
| Per-operator setup | None — single account | Express onboarding per shop |
1 · Create your Stripe account
- Sign up at dashboard.stripe.com/register
- Complete the platform-business profile (this is the meta-account that owns the platform fee)
- Enable Connect in Settings → Connect — needed for the Storefront's per-shop accounts
- Set your platform name and statement descriptor (Settings → Public details)
2 · Get test-mode keys
- In Stripe dashboard, ensure "Test mode" toggle (top-right) is ON
- Go to Developers → API keys
- Copy:
Publishable key→pk_test_...Secret key→sk_test_...(click "Reveal")
On Railway → ToT service → Variables tab:
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
On Railway → Storefront service → Variables:
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
3 · Set up webhooks
Stripe needs to call back when things happen — order paid, payment failed, refund issued. Without working webhooks, paid orders never flip to "paid" and labels can't print.
ToT webhook endpoint
- Stripe dashboard → Developers → Webhooks → Add endpoint
- Endpoint URL:
https://thisorthat.slabtrack.io/api/stripe/webhook - Events to listen for:
checkout.session.completedpayment_intent.succeededpayment_intent.payment_failedpayment_intent.canceledcharge.refundedcharge.dispute.createdcharge.dispute.closed
- Save → click the new endpoint → reveal
Signing secret→whsec_... - Paste into Railway as
STRIPE_WEBHOOK_SECRETon the ToT service
Storefront webhook endpoint
- Stripe dashboard → Developers → Webhooks → Add endpoint
- Endpoint URL:
https://storefront.slabtrack.io/api/stripe/webhook - Listen on "Events on Connected accounts" — toggle this checkbox
- Events:
account.updatedcheckout.session.completedcheckout.session.async_payment_succeededcheckout.session.async_payment_failedcheckout.session.expiredpayment_intent.payment_failedpayment_intent.canceledcharge.refunded
- Save → reveal signing secret → paste as
STRIPE_WEBHOOK_SECRETon the Storefront service
Test-mode and live-mode webhooks have different signing secrets. When you flip to live,
create new endpoints (with the production URLs) and update STRIPE_WEBHOOK_SECRET
on Railway to the live endpoint's secret.
4 · Operator Stripe Connect onboarding (Storefront only)
Each shop owner needs to onboard their own Stripe account so funds settle directly into theirs. The flow is:
- Operator visits
/dashboard/[slug]/settings - Clicks "Connect Stripe Account"
- Storefront calls
POST /api/stripe/connect/onboardwhich creates a Stripe Express account + onboarding link - Operator is redirected to Stripe's hosted onboarding (provides bank info, identity, etc.)
- Stripe redirects back to
/api/stripe/connect/return account.updatedwebhook fires → flipsstripeChargesEnabled+stripeDetailsSubmittedon the Storefront row- Public storefront's buy buttons activate
In test mode, Stripe accepts "Skip this step" for most fields. You can onboard a test operator in 30 seconds. For dev, use these test values:
- Test bank: routing
110000000, account000123456789 - Test SSN:
000-00-0000 - Test debit card:
4000056655665556
5 · Test card numbers
| Card | Behavior |
|---|---|
4242 4242 4242 4242 | Always succeeds. Use for happy-path tests. |
4000 0000 0000 9995 | Insufficient funds — declines after auth. |
4000 0000 0000 0002 | Generic decline at auth time. |
4000 0027 6000 3184 | Requires 3D Secure auth (test the SCA flow). |
4000 0000 0000 9979 | Stolen card. |
For all test cards: any future expiry date, any 3-digit CVC, any 5-digit ZIP.
6 · Going live
- Toggle Stripe dashboard to Live mode
- Activate your account (Stripe asks for business verification)
- Get live keys:
sk_live_...andpk_live_... - Create live webhook endpoints (same URLs but they'll get new secrets in live mode)
- Replace ALL Stripe-related env vars on Railway with live equivalents
- Trigger Railway redeploys so the new secrets load
- Run a real $1 purchase end-to-end before announcing launch
Once you flip to sk_live_, every charge is real. The test card
4242... will fail. Always test in test mode first; only flip to live
when you've confirmed: webhook fires → Order flips → label can be purchased → buyer
email sends.
Refund flow
Both ToT and Storefront support full + partial refunds via the operator dashboard.
- ToT: Admin → Orders → click order → "Refund" — supports partial.
- Storefront:
/dashboard/[slug]/orders→ click order → "Refund" — full only currently.
Both routes hit Stripe's refunds API with reverse_transfer + refund_application_fee (Storefront only) so the platform's 3% fee gets returned alongside the buyer's principal. Idempotency key on the refund call prevents double-refund from a misclick.
Production readiness checklist
- ✓ Live keys (
sk_live_,pk_live_) on both ToT + Storefront - ✓ Live webhook endpoints registered in Stripe
- ✓ Live
STRIPE_WEBHOOK_SECRETon each repo - ✓ Connect enabled (Settings → Connect → "Get started")
- ✓ Account activated (out of "test-only" mode)
- ✓ Statement descriptor configured
- ✓ At least one test operator successfully onboarded via Express in test mode
- ✓ Real $1 buy from an external card succeeded end-to-end (paid → label → tracking email)