← all adapters

paypal-style

simulates PayPal Orders API v2

restadapter v0.2.017 routes

verification

VMVM tier — no SDK suite yet· surface 17 · not implemented 6 · differs 8

use it

# add to stunt.yaml
services:
  paypal:
    adapter: embedded:paypal-style

# then
$ stunt up

The embedded: source extracts this adapter from the binary — no git clone, fully offline.

how to mock the PayPal Orders API locally

Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of PayPal Orders 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 PayPal Orders API v2 surface — the data is fake, the behavior is real.

backing stores

orderscapturesrefundsaccess_tokens

api surface (17)

generated from the adapter manifest — exactly what boots, nothing more · covers 64% of the real API's 17 routes (derived from spec paypal-rest-api-specifications checkout_orders_v2 + payments_payment_v2 @ 2.32, 2.12)

17 routes
POST/v1/oauth2/token
POST/v2/checkout/orders
GET/v2/checkout/orders/{id}
POST/v2/checkout/orders/{id}/approve
POST/v2/checkout/orders/{id}/capture
POST/v2/checkout/orders/{id}/authorize
GET/v2/payments/authorizations/{id}
POST/v2/payments/authorizations/{id}/reauthorize
POST/v2/payments/authorizations/{id}/void
POST/v2/payments/authorizations/{id}/capture
GET/v2/payments/captures/{id}
POST/v2/payments/captures/{capture_id}/refund
GET/v2/payments/refunds/{id}
POST/v1/notifications/webhooks
GET/v1/notifications/webhooks
DELETE/v1/notifications/webhooks/{id}
POST/v1/notifications/verify-webhook-signature

VM-verified behaviors (37)

  • client_credentials over Basic auth mints a distinct Bearer with PayPal's token envelope
  • the minted bearer authorizes the Orders API
  • minting without HTTP Basic client auth is a 401 AUTHENTICATION_FAILURE envelope
  • a bearer that was never minted is rejected with 401
  • the advertised 9-hour expires_in is enforced — the token dies past it
  • any Basic credentials mint a token — no client registry (deviation, asserted as-is)
  • create assigns ORDERID-N, stamps create_time, and echoes intent + purchase_units
  • a CREATED order carries self/approve/capture links
  • get round-trips the order; unknown ids are 404 INVALID_RESOURCE_ID
  • orders are accepted without purchase_units — no create-time validation (deviation, asserted as-is)
  • capture or authorize before payer approval is 422 ORDER_NOT_APPROVED
  • simulate_fail approval keeps the order CREATED with 422 PAYER_ACTION_REQUIRED
  • approval flips CREATED -> APPROVED and swaps in capture/authorize links
  • re-approval is idempotent
  • capture completes the order and embeds a COMPLETED capture in purchase_units
  • the capture resource is readable via the payments API
  • re-capturing the order is 422 ORDER_ALREADY_CAPTURED
  • authorize completes the order and lands a CREATED authorization
  • re-authorizing the order is 422 ORDER_ALREADY_AUTHORIZED
  • GET shows the auth with a 3-day honor window from the virtual clock
  • reauthorize refreshes the honor window and syncs the embedded order copy
  • void is 204, moves the auth to VOIDED, and syncs the order
  • a voided authorization rejects every action with 422
  • malformed, mismatched-currency, and over-authorization amounts are 400s with real issue codes
  • a partial capture keeps final_capture false and moves the auth to CAPTURED
  • the capture is appended to the order and links up to the authorization
  • a captured authorization is terminal
  • refunds are created PENDING and reserve the unrefunded balance
  • the 3-second settle window flips PENDING -> COMPLETED on read
  • the settled refund is reported as refunded_amount on the capture
  • a FAILED refund frees the balance again
  • PayPal-Request-Id replays return the same order
  • a replay returns the order's current state, not a cached snapshot (deviation, asserted as-is)
  • requests without the header (or with a new one) are not deduplicated
  • webhook registration round-trips through the list
  • signature verification answers SUCCESS only for known webhook ids
  • deletion is 204, and repeats are 404

Not implemented (6)

  • No PATCH order update (PATCH /v2/checkout/orders/{id})
  • No payouts API (POST /v1/payments/payouts)
  • No disputes API (/v1/customer/disputes)
  • No subscriptions, billing plans, or catalog products APIs
  • No webhook event-types list or simulate-event endpoints
  • No order patch endpoint (PATCH /v2/checkout/orders/{id})

Differs from the real API (8)

  • Payer approval endpoint /approve is simulator-only; real flow is browser rel=approve
  • simulate_fail flags on approve and refund are stunt-only test hooks
  • Webhook deliveries unsigned; real PayPal uses a certificate-based signature scheme
  • verify-webhook-signature returns SUCCESS for any known webhook_id, no real check
  • Refund derives terminal state on read after fixed ~3s (PENDING to COMPLETED/FAILED)
  • Token endpoint validates only the Basic scheme — any credentials mint, grant_type ignored
  • Captures always complete immediately; no PENDING/ON_HOLD capture states
  • Idempotency replay re-renders the order's current state, not the cached original response
want more of the PayPal Orders 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.