DEV Community

FetchSandbox
FetchSandbox

Posted on

Webhook handlers that silently return 402: the event ordering bug most test suites miss

How to prove a handler is order-safe using permutation fuzzing

You're testing your Paddle webhook handler. You've got a test for subscription.created and subscription.activated, and maybe another one where you send them out of order. Looks good. All green. But weeks later, a customer hits a 402 on subscription.activated. No error in your logs. No alert. Just a failed charge and a confused user.

The problem isn't your test suite, it's the order of events.

A Paddle webhook handler that does an INSERT on subscription.created and an UPDATE on subscription.activated will silently return 402 when subscription.activated arrives first. The row doesn't exist yet. No error is raised. But if you send them in the "happy path" order, it works. Your test suite never sees the failure because it only runs one sequence. The bug is invisible until it hits production.

To prove a handler is order-safe, we built a permutation fuzzer in fetchsandbox MCP. It fires the same events in every permutation, including duplicates, and checks if the final state is the same in all of them. If it is, the handler is order-safe. If not, it returns the exact sequence that breaks it.

Here's what it found for the Paddle handler above:

  • When you send subscription.activated before subscription.created: the handler returns a 402.

  • When you send them in the "happy path" order: it returns a 200.

  • The fuzzer returns order_independent=False and shows you the diverging sequence.

After fixing the handler to use an upsert instead of separate INSERT and UPDATE, the fuzzer returns order_independent=True across every permutation and duplicate.

If you test API integrations in sandboxes constantly, you've hit this one too. I run it in Cursor every time I test a new webhook integration.

The fuzzer is in fetchsandbox MCP. It reads final state after each permutation, and reports order_independent=True only when every ordering converges to the same final state.

If you're trying to find webhook ordering bugs, or prove a handler is order-safe, this is the test you didn't know you needed.

Read the full spec for Paddle webhook testing at Paddle sandbox. Learn how fetchsandbox MCP works at fetchsandbox MCP.

Top comments (1)

Collapse
 
raju_dandigam profile image
Raju Dandigam

The ordering bug here is a good reminder that webhook correctness is mostly state-machine correctness, not just signature validation and retries. A handler that returns a clean status while silently violating event order is exactly the kind of failure happy-path integration tests miss.

What has helped us is storing per-event receipts with arrival order, dedupe key, and transition result so a flaky run can be reconstructed instead of guessed at. Did you end up building invariant checks around legal event sequences, or was the winning fix further upstream in queueing?