verification
use it
# add to stunt.yaml
services:
adyen:
adapter: embedded:adyen-style
# then
$ stunt upThe embedded: source extracts this adapter from the binary — no git clone, fully offline.
how to mock the Adyen Checkout + Notification API locally
Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of Adyen Checkout + Notification API. It works offline, keeps state across requests and restarts, and every response is synthetic: the route shapes, status codes, pagination, and webhook delivery follow the real Adyen Checkout + Notification API v68 surface — the data is fake, the behavior is real.
backing stores
paymentsmodifications
api surface (14)
generated from the adapter manifest — exactly what boots, nothing more · covers 23% of the real API's 39 routes (derived from spec adyen-openapi CheckoutService-v68 + PaymentService-v68 @ 68, 68)
14 routes
POST/v68/payments
POST/v68/payments/details
GET/v68/payments
POST/v68/paymentMethods
POST/v68/paymentLinks
GET/v68/paymentLinks/{linkId}
POST/v68/payments/{paymentPspReference}/captures
POST/v68/payments/{paymentPspReference}/refunds
POST/v68/payments/{paymentPspReference}/reversals
POST/v68/payments/{paymentPspReference}/cancels
POST/v68/notifications/test
POST/v68/webhooks
GET/v68/webhooks
DELETE/v68/webhooks/{webhookId}
VM-verified behaviors (39)
- ✓a missing X-API-Key is a 401 security envelope
- ✓an unseeded API key is rejected identically
- ✓the seeded test key authorises; header spelling does not matter
- ✓the gate spans the whole surface, not just /payments
- ✓a plain card authorises instantly with a 16-digit 881 pspReference
- ✓the refused test card (...0002) answers Refused with refusalReason, still 200
- ✓simulate_fail refuses an instant payment outright
- ✓Idempotency-Key replays the same pspReference; a fresh call mints a new one
- ✓GET ?reference= returns the payment object; unknown references are 422 010
- ✓the unfiltered list is cursor-paginated; a garbage cursor is a 400
- ✓a 3DS card returns IdentifyShopper with a threeDS2 action and no pspReference yet
- ✓submitting the fingerprint authorises and mints the pspReference
- ✓paymentData tokens are single-use
- ✓challenge cards (...0081) add a ChallengeShopper round with a fresh token
- ✓simulate_fail refuses at completion with threeDSError
- ✓a missing paymentData or missing fingerprint is a 422 validation error
- ✓an idempotent replay re-renders the payment's CURRENT state, not the original response (deviation, asserted as-is)
- ✓the catalogue is scheme/ideal/paypal/applepay/googlepay, storedPaymentMethods empty
- ✓merchantAccount is required
- ✓any merchantAccount string is accepted — no account registry (deviation, asserted as-is)
- ✓creating a link assigns PL-N, defaults expiresAt to +24h, starts active
- ✓a zero amount or missing reference is a 422
- ✓an unknown link id is a 404 with errorCode 191
- ✓authorising a payment on the link's reference completes the link on read
- ✓a past expiresAt flips the link to expired on read, and terminal statuses persist
- ✓capture chains a CAP-prefixed pspReference and returns status received
- ✓a partial capture keeps the remainder capturable; over-capture is 422 702
- ✓refunds draw down the captured balance; over-refund is 422 705
- ✓refund before capture is 704, currency mismatch 708, zero amount 703
- ✓cancel is only for uncaptured payments; reversal is terminal from captured
- ✓a cancelled payment rejects every later modification
- ✓an unknown pspReference is a 422 010
- ✓webhook registration masks the hmacKey in every view
- ✓an authorisation delivers the standard envelope signed with the hook's own key
- ✓a capture chains originalReference and signs with the mock key via the events filter
- ✓a refused payment notifies AUTHORISATION with success "false"
- ✓only the first matching hook receives an event (deviation, asserted as-is)
- ✓deleting a hook silences it; the next matching hook takes over
- ✓the merchant-side receiver answers [accepted] and tolerates redelivery
Not implemented (4)
- No Payouts, Disputes, or Terminal API (cloud/POS) endpoints
- No paymentMethods/balance, gift-card, or stored-value endpoints
- No amountUpdates, technicalCancels, or auto-rescue modification routes
- No stored payment method lifecycle — tokens cannot be stored, listed, or disabled
Differs from the real API (8)
- Per-hook hmacKey and events filter are local extensions — real Adyen hides the key
- simulate_fail flag on /v68/payments forces Refused (simulator-only failure injection)
- paymentLinks url is synthetic — no hosted checkout page; pay via the link reference
- paymentMethods serves a fixed 5-method catalog with empty storedPaymentMethods
- Webhook HMAC signs the base64-encoded signing string with eventCode in field 7; deliveries go to the first matching hook only
- Idempotency replay re-renders the payment's current state, not the original response
- Modification pspReferences carry CAP/REF/REV/CAN prefixes; real Adyen's are numeric
- paymentMethods accepts any merchantAccount; real Adyen 403s on unknown accounts
want more of the Adyen Checkout + Notification API surface? adapters are YAML + Starlark — the authoring guide covers adding routes, fixtures, and state machines. Open an issue for anything big first, or just file a PR — merged routes ship in the next release.