Demo only production

Payment Simulator API

Fake M-Pesa & bank transfer API for local dev, demos, and webhook testing. Not for real money.

Authentication required.

Generate your own API key — no signup, no env vars:

Or call POST https://nobody-needs-this.vercel.app/api/keys with an empty JSON body.

Send your key on every request (except public routes):

Revoke: DELETE /api/keys with your key. Admin routes need ADMIN_API_KEY in production.

Base URL: https://nobody-needs-this.vercel.app
All requests use Content-Type: application/json unless noted.

Endpoints

GET Health check

/api/health

Public

Returns service status. No authentication required.

Example response

{ "status": "healthy", "service": "payment-simulator", "environment": "production", "auth_required": true, "registration_enabled": true, "api_key_store": "redis" }

POST Create API key

/api/keys

Public

Generate a personal API key (shown once). No auth required when ENABLE_API_KEY_REGISTRATION is true. Rate-limited per IP.

curl -X POST "https://nobody-needs-this.vercel.app/api/keys" \
  -H "Content-Type: application/json" \
  -d '{}'

Example response

{ "success": true, "api_key": "ps_live_…", "message": "Save this key now…" }

DELETE Revoke API key

/api/keys

API key required

Revokes the API key sent on this request. Bootstrap/env keys cannot be revoked via this endpoint.

Example response

{ "success": true, "message": "API key revoked" }

POST M-Pesa STK Push

/api/payments/mpesa/stk-push

API key required

Initiate a fake M-Pesa STK push. With auto_complete (default true), a callback is simulated after ~2 seconds.

FieldNotes
phone_number yes 254XXXXXXXXX
amount yes Number
account_reference no Default: TEST
description no Default: Payment
callback_url no Webhook URL when payment completes
auto_complete no true = simulate callback after ~2s (default true)
force_success no true / false / omit for random (~95% success)
curl -X POST "https://nobody-needs-this.vercel.app/api/payments/mpesa/stk-push" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone_number":"254712345678","amount":1000,"account_reference":"ORDER-123","description":"Payment","callback_url":"https://your-app.com/webhooks/mpesa","auto_complete":true,"force_success":true}'

POST M-Pesa callback (manual)

/api/payments/mpesa/callback

API key required

Manually trigger the M-Pesa STK callback for a transaction. Use when auto_complete is false.

FieldNotes
transaction_id yes From stk-push response
force_success no true = success (0), false = cancelled (1032)
curl -X POST "https://nobody-needs-this.vercel.app/api/payments/mpesa/callback" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"transaction_id":"MPXABC123","force_success":true}'

POST Bank transfer

/api/payments/bank-transfer

API key required

Initiate a fake bank transfer. Auto-completes after ~3 seconds when auto_complete is true.

FieldNotes
account_number yes Beneficiary account
bank_code yes Bank identifier
amount yes Number
reference no Default: TEST
narration no Default: Payment
callback_url no Webhook URL on completion
auto_complete no Default true
force_success no true / false / random
curl -X POST "https://nobody-needs-this.vercel.app/api/payments/bank-transfer" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"account_number":"1234567890","bank_code":"01","amount":5000,"reference":"INV-123","narration":"Supplier payment","callback_url":"https://your-app.com/webhooks/bank","auto_complete":true,"force_success":true}'

POST Bank transfer complete (manual)

/api/payments/bank-transfer/complete

API key required

Manually complete a bank transfer when auto_complete is false.

FieldNotes
transaction_id yes From bank-transfer response
force_success no true = completed, false = failed
curl -X POST "https://nobody-needs-this.vercel.app/api/payments/bank-transfer/complete" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"transaction_id":"BNKABC123","force_success":true}'

GET Transaction status

/api/payments/:transaction_id

API key required

Get the current state of a single transaction.

FieldNotes
transaction_id yes Path parameter, e.g. MPX… or BNK…

GET List transactions

/api/payments

API key required

List in-memory transactions. Optional query filters.

FieldNotes
status query pending | processing | completed | failed | cancelled
method query mpesa | bank_transfer

POST Reset all transactions

/api/payments/reset

Admin key required in production

Clears all stored transactions. In production requires ADMIN_API_KEY.

curl -X POST "https://nobody-needs-this.vercel.app/api/payments/reset" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Webhooks

Include callback_url when starting a payment. When the flow completes, the simulator POSTs JSON to that URL (same shape as real providers).

For local testing, run ruby webhook_receiver.rb on port 4567 and use:

On serverless hosts (e.g. Vercel), prefer auto_complete: false and call the manual callback endpoints, or use a public inbox like webhook.site.

M-Pesa result codes

CodeMeaning
0Success
1032User cancelled
1037Timeout (no PIN)
2001Wrong PIN
1Insufficient balance