A PayPal sandbox rehearsal for your webhook handling
Run a real business day in PayPal's sandbox, then see exactly what came back.
Rehearsal turns a short plan into real sandbox orders, refunds and invoices, then shows each call beside the webhook PayPal sent for it, signature checked.
Start here
- Pick a planA recorded plan, or describe a business and let the planner write one.
- Read exactly what it will doA dry run lists every call and amount before anything is sent.
- Run it live in the sandboxWatch each call and the webhook PayPal sends for it arrive side by side.
It costs nothing: every call goes to PayPal's sandbox, never live PayPal. Live runs are capped at USD 150.00 a step and USD 400.00 a run, 8 steps, one run at a time, 5 per session a day.
The bug it reproduces
Two recorded sandbox runs: the classic duplicate-delivery bug with PayPal's real redelivery, then the one-line fix
- One payment event, fulfilled twicePayPal delivered the same payment event 2 times, 2 of 2 verified as PayPal's, and the store fulfilled the order 2 times.Watch the replay
- Fulfilled once, the repeat ignoredSame scenario. The store fulfilled the order once and recognized 1 redelivery as a repeat. The fix is one line, store/lib/merchant.ts:18.Watch the replay
Read the finder's report, side by side.
Recorded plans
Ready to run as they are
- Tiny Bakery2 card orders + an invoice, USD 345Read the plan
- Open Dispute Awaiting Responsea dispute on a banked purchaseRead the plan
What it can and cannot do
Each limit is read from the code; test counts are measured
Where it can reach
- PayPal hosta literal in code, not a setting
- api-m.
sandbox. paypal.com - Live PayPal reachable
- No, by construction
- Event targetsexact handler URL; the hosted demo allows only the sample store
- Allowlisted only
How much it can move
Per run, the defaults in code; a local install can change them with REHEARSAL_MAX_* variables.
- Largest single step
- USD 500.00
- Largest run
- USD 2,000.00
- Steps per run
- 30
- Disputes per run
- 2
- Hosted here, behind the passcodefixed in code; a setting can lower them, never raise them
- USD 150.00 a step, USD 400.00 a run, 8 steps, 1 dispute
How it proves things
- Writes with an idempotency keyrefused before sending if one is missing
- every POST, PUT, PATCH
- Webhooks the sample store acts ononly after PayPal's verify call says SUCCESS; cert host on an exact list of 4
- Verified only
- Webhooks the receiver recordsevery arrival, each marked verified, failed or not checked
- All, marked
- Verdictsno evidence reads INCONCLUSIVE, never OK
- On evidence only
The AI planner
- Its plan is re-checkedschema, caps and allowlist on every reply
- Always
- Per call
- 4,000 tokens · 60 s
- Description read4,000 from the CLI or MCP server
- 2,000 chars max here
Keys stay on the server and are never printed, logged or returned. The MCP server is stdio only and runs on your own machine with your own keys.
Tests passing
1,257
640 engine · 142 sample store · 76 receiver · 399 console · measured 2026-11-10 on v0.7.6
What a pass here does not prove
- Live PayPal Everything runs against the sandbox. A pass is evidence about your code's handling, not a promise about production.
- Timing PayPal's sandbox can deliver late. When evidence does not arrive in time, the verdict is INCONCLUSIVE and you rerun.
- Every dispute stage Disputes open and wait on the merchant. The sandbox refuses require-evidence and adjudicate at the stage a new dispute starts in, so those are out of scope.
- Every failure class Proven on real runs: duplicate-webhook and refund-after-partial; the reports are in the repo under evidence/. Unclaimed-payout and overdue-invoice exist but are not claimed here. Dispute-on-a-refunded-order cannot be reached in this version and is reported as not tested.
- Load This rehearses business events one at a time. It is not a load or volume test.