A stripe-mock alternative that's stateful, hosted, and fails on demand

stripe-mock is Stripe's official mock HTTP server, and it's genuinely good at its stated job: it knows every Stripe API URL, validates your request parameters against the real API's JSON Schema, and returns spec-generated sample responses โ€” fast, offline, in your test suite. If that's all you need, install it and move on.

But its README is refreshingly blunt about what it doesn't do, by design:

This guide is exactly that: define your own payments mock โ€” hosted on a public HTTPS URL (so CI, teammates, mobile builds, and deployed previews can all hit it), stateful (POST a payment, GET it back, PATCH it settled), with declines, timeouts, and exact retry sequences on demand, plus HMAC-signed webhooks. No account, no keys, no binary to run.

Testing your checkout flow, decline UX, retry logic, or webhook handler? That's this page. Testing the actual Stripe SDK integration itself? Use Stripe test mode โ€” see the honest comparison below.

1. A Stripe-shaped mock in one paste

New โ€” skip the paste: one click (or curl -X POST https://mockbird.mockbird.workers.dev/api/projects -H 'content-type: application/json' -d '{"preset":"payments"}') creates a ready payments sandbox: charges/refunds/subscriptions/customers CRUD plus a Stripe-shaped POST /v1/payment_intents → /v1/payment_intents/:id/confirm flow and /v1/balance. The paste below builds a custom-shaped mock instead โ€” use it when you want finer control over the Stripe shapes.
BASE=https://mockbird.mockbird.workers.dev
P=$(curl -s -X POST $BASE/api/projects -H 'content-type: application/json' \
     -d '{"name":"payments-mock","blank":true}')
PID=$(echo $P | sed 's/.*"id": *"\([^"]*\)".*/\1/')
KEY=$(echo $P | sed 's/.*"adminKey": *"\([^"]*\)".*/\1/')

curl -s -X POST $BASE/api/projects/$PID/resources -H "x-admin-key: $KEY" \
  -H 'content-type: application/json' -d '{
  "name":"payment_intents",
  "fields":[
    {"name":"amount","type":"number"},
    {"name":"currency","type":"oneOf","values":["usd","eur","gbp"]},
    {"name":"status","type":"oneOf","values":["requires_payment_method","processing","succeeded","canceled"]},
    {"name":"customerEmail","type":"email"},
    {"name":"created","type":"pastDate"}
  ],
  "seed":5
}'

You now have GET/POST/PATCH/DELETE /m/<PID>/payment_intents with 5 seeded records:

curl "https://mockbird.mockbird.workers.dev/m/<PID>/payment_intents?limit=1"
[
  {
    "id": 1,
    "amount": 91,
    "currency": "gbp",
    "status": "canceled",
    "customerEmail": "ezra.nielsen37@example.com",
    "created": "2026-07-23T09:12:02.663Z"
  }
]

The field set is yours โ€” add clientSecret (uuid), cardLast4, captureMethod (oneOf), whatever your app reads. Filters work out of the box: ?status=processing, ?amount_gte=1000.

2. The stateless contrast: POST it, then GET it back

This is the exact thing stripe-mock's README says it will never do โ€” and it's usually the first thing a checkout test needs:

# your checkout creates an intent
curl -s -X POST $BASE/m/$PID/payment_intents -H 'content-type: application/json' \
  -d '{"amount":2000,"currency":"usd","status":"processing","customerEmail":"jo@example.com"}'
# โ†’ {"amount":2000,"currency":"usd","status":"processing","customerEmail":"jo@example.com","id":6}

# your UI polls โ€” the record is really there
curl -s $BASE/m/$PID/payment_intents/6      # status: "processing"

# the test driver plays the gateway: settle it
curl -s -X PATCH $BASE/m/$PID/payment_intents/6 -H 'content-type: application/json' \
  -d '{"status":"succeeded"}'

Every write is visible to every later read, from any client โ€” your browser, your CI job, a teammate's curl. That's a database under the mock, not a fixture file.

3. Declines on demand โ€” the error responses stripe-mock can't return

stripe-mock returns a success fixture even for requests that should fail. Here, any request fails exactly when you tell it to:

curl -si -X POST "$BASE/m/$PID/payment_intents?mock_status=402" \
  -H 'content-type: application/json' -d '{"amount":100,"currency":"usd","status":"processing"}'
# HTTP/2 402
# {"error": "simulated 402 error (mock_status)"}
# and no phantom record is created โ€” the write is not applied

Any status 400โ€“599 works (mock_status=429 for rate-limit UX, 503 for gateway-down banners), and mock_delay=4000 composes with it for slow-failure paths. Full recipes in testing loading & error states.

4. Exact retry choreography: declined, declined, succeeded

Card-decline retry logic is miserable to test against anything real. mock_seq scripts the exact sequence of outcomes:

# first attempt 402, second 402, third succeeds โ€” exactly
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
  "$BASE/m/$PID/payment_intents?mock_seq=402,402,201&mock_seq_key=card-retry-9" \
  -H 'content-type: application/json' \
  -d '{"amount":20,"currency":"eur","status":"processing"}'
# run it 3x: 402, 402, 201

Each mock_seq_key tracks its own position, so parallel tests don't trample each other's sequences.

5. Signed webhooks โ€” no stripe listen, no tunnel

Point the project's webhook at your handler and every create/update/delete fires a signed POST, with a familiar-looking secret:

curl -s -X PUT $BASE/api/projects/$PID/webhook -H "x-admin-key: $KEY" \
  -H 'content-type: application/json' -d '{"url":"https://your-app.example.com/hooks/payments"}'
# โ†’ {"ok":true,"webhook":{"secret":"whsec_a60818b5d376โ€ฆ","events":["created","updated","deleted"]}}

Deliveries carry X-Mockbird-Signature: sha256=โ€ฆ (HMAC-SHA256 of the raw body) so your verification code path actually runs. Verify snippet and delivery-log details in sending test webhooks; a full payment-webhook walkthrough is in the mock payment API guide.

stripe-mock concern โ†’ Mockbird

You wantedstripe-mockMockbird
POST data visible on later GETsโœ— stateless by designโœ” persisted (real CRUD)
Simulate declines / specific errorsโœ— success fixtures only?mock_status=402 (any 400โ€“599)
Scripted outcome sequencesโœ—?mock_seq=402,402,201
Slow-gateway latencyโœ—?mock_delay=3000 (+ jitter, chaos)
Webhook deliveriesโœ—โœ” HMAC-signed, whsec_ secret
Reachable by CI / teammates / previewsyour machine or networkโœ” public HTTPS URL
Setupbrew / Docker / Go binaryone curl, no install
Exact Stripe URLs + param validationโœ” generated from the real specโœ— your own shapes

Honest comparison โ€” where stripe-mock (and test mode) win

The clean split: stripe-mock for sanity-checking calls against Stripe's schema, Stripe test mode for integration truth, Mockbird when the thing under test is your flow โ€” checkout states, decline UX, retry logic, webhook verification, order screens โ€” and you want a stateful backend that any client can reach without a payments account. They compose; plenty of teams use all three.

Notes & limits

Create yours

The paste block in section 1 is the whole setup โ€” or open the dashboard and click it together. The same project also serves GraphQL, exports OpenAPI + TypeScript types, and ejects to db.json anytime โ€” no lock-in.