paypal-sandbox
Wraps PayPal Sandbox testing patterns: sandbox account creation (Business + Personal accounts in developer.paypal.com), the Orders v2 API (create / capture / refund), webhook event simulator (developer.paypal.com webhook simulator), sandbox-account-specific test cards, and the OAuth2 client-credentials flow for sandbox. Use when testing PayPal-integrated code.
Install with skills.sh (any agent)
npx skills add testland/qa --skill paypal-sandboxpaypal-sandbox
Overview
PayPal Sandbox is a parallel environment that mirrors the prod PayPal API. Per developer.paypal.com/tools/sandbox (opens in new window), sandbox accounts (Business + Personal) are created in the developer dashboard; tests use sandbox client credentials.
The current canonical API is Orders v2 (developer.paypal.com/docs/api/orders/v2 (opens in new window)); older Payments API (v1) is deprecated.
When to use
Authoring
Setup
Install
npm install @paypal/checkout-server-sdk
pip install paypalserversdkOAuth2 client credentials
import paypal from '@paypal/checkout-server-sdk';
const env = new paypal.core.SandboxEnvironment(
process.env.PAYPAL_SANDBOX_CLIENT_ID!,
process.env.PAYPAL_SANDBOX_SECRET!,
);
const client = new paypal.core.PayPalHttpClient(env);Create order
const request = new paypal.orders.OrdersCreateRequest();
request.requestBody({
intent: 'CAPTURE',
purchase_units: [{ amount: { currency_code: 'USD', value: '10.00' } }],
});
const order = await client.execute(request);
expect(order.result.status).toBe('CREATED');
expect(order.result.id).toBeTruthy();Capture order (after buyer approval)
const captureRequest = new paypal.orders.OrdersCaptureRequest(order.result.id);
captureRequest.requestBody({});
const capture = await client.execute(captureRequest);
expect(capture.result.status).toBe('COMPLETED');In test code, you need a sandbox buyer to approve the order via the PayPal checkout UI - for fully-automated tests, this requires Playwright + a sandbox Personal account login.
Sandbox test cards
Per developer.paypal.com/tools/sandbox/card-testing (opens in new window):
| Card | Behaviour |
|---|---|
| 4111 1111 1111 1111 | Visa Sandbox success |
| 5555 5555 5555 4444 | Mastercard success |
| 4032 0359 8001 0008 | Decline |
PayPal Sandbox is more PayPal-balance-oriented than card- oriented; sandbox buyers also have fake "PayPal balance."
Webhook simulator
Per developer.paypal.com/api/rest/webhooks/event-names (opens in new window): the developer dashboard exposes a Webhook Simulator that sends any event type to your registered URL.
For automated tests, use the simulator's API:
curl -X POST 'https://api-m.sandbox.paypal.com/v1/notifications/simulate-event' \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-d '{
"url": "https://example.com/webhook",
"event_type": "PAYMENT.CAPTURE.COMPLETED",
...
}'Webhook signature verification
Per developer.paypal.com/api/rest/webhooks/rest (opens in new window): PayPal webhooks include PAYPAL-TRANSMISSION-SIG and related headers; verify via PayPal's verification endpoint or local SDK helper.
import { verifyWebhookSignature } from '@paypal/checkout-server-sdk';
const isValid = await verifyWebhookSignature({
authAlgo: headers['paypal-auth-algo'],
certUrl: headers['paypal-cert-url'],
transmissionId: headers['paypal-transmission-id'],
transmissionSig: headers['paypal-transmission-sig'],
transmissionTime: headers['paypal-transmission-time'],
webhookId: process.env.PAYPAL_WEBHOOK_ID!,
webhookEvent: notificationPayload,
});
expect(isValid).toBe(true);Running
npm testCI integration
jobs:
paypal-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
- run: npm ci && npm test
env:
PAYPAL_SANDBOX_CLIENT_ID: ${{ secrets.PAYPAL_SANDBOX_CLIENT_ID }}
PAYPAL_SANDBOX_SECRET: ${{ secrets.PAYPAL_SANDBOX_SECRET }}Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Use live PayPal credentials in tests | Real money | Sandbox-only |
| Test with manual buyer approval | Slow; not CI-suitable | Playwright + sandbox buyer login |
| Skip webhook signature verification | Spoofable | Always verify |
| Hardcode sandbox account IDs | Fragile to account changes | Per-env IDs |
| Test only the API path | Real flow requires checkout UI | Playwright e2e |
| Legacy Payments v1 API | Deprecated | Migrate to Orders v2 |
Treat CREATED as final | Order needs capture | Test the full lifecycle |
| One-shot test for refunds | Refunds are async | Wait for webhook |
Limitations
References
Related skills
3ds-test-flow-reference
Cross-gateway, protocol-level reference for 3-D Secure (3DS 2.x) test coverage. Covers the EMVCo frictionless / challenge / not-applicable flow paths, SCA under EU PSD2, and the per-PAN test cards for Stripe, Adyen, and Braintree. The gateway-specific wrappers (adyen-test-mode / stripe-test-cards-and-webhooks / braintree-test-cards) compose this skill for single-gateway work, so use one of those for a single-gateway query. Use this skill when designing multi-gateway 3DS test coverage, auditing a 3DS redirect round-trip, or investigating a challenge-flow regression that is not gateway-specific.
adyen-test-mode
Wraps Adyen test-mode patterns: test-API-key initialization, the canonical Adyen test cards (Visa 4111 1111 1111 1111; 5454 5454 5454 5454 3DS frictionless; 4917 6100 0000 0000 3DS 2 challenge), the per-flow result codes (Authorised / Refused / Pending / RedirectShopper / Error), the HMAC-SHA256 webhook validation, and notifications-vs-API duality (Adyen separates synchronous API calls from asynchronous notifications). Use when testing Adyen-integrated code.
braintree-test-cards
Wraps Braintree (PayPal-owned) sandbox testing patterns: sandbox merchant credentials, Drop-in / Hosted Fields client-side patterns, the Transaction lifecycle (submitted_for_settlement → settled), Braintree's distinctive test-card behaviours (specific PANs trigger specific errors), and the webhook verification (Braintree Webhook Parser). Use when testing Braintree-integrated code.
chargeback-flow-test-author
Workflow-driven skill that builds the chargeback / dispute test suite: canonical reason codes (Visa 10.4 fraud, 13.1 services-not-provided; Mastercard MCC 4855), per-gateway dispute APIs (Stripe Disputes, Adyen Chargeback notifications, PayPal Disputes), the evidence-submission flow + window, and disposition outcomes (won / lost / accepted). Use when designing dispute coverage; for refunds use refund-test-matrix-builder, for webhook redelivery + idempotency use payment-webhook-replay, and for the lifecycle state model use payment-flow-states-reference.
payment-flow-states-reference
Pure-reference catalog of payment lifecycle state machines across Stripe, Adyen, PayPal, and Braintree: canonical states (created / requires_action / processing / succeeded / requires_capture / canceled / failed), authorisation vs capture, asynchronous webhook states, and refund / dispute / chargeback transitions. Use when designing tests for payment flows or auditing state-handling code; this is the state model, not a builder - to author suites on it use refund-test-matrix-builder (refunds), chargeback-flow-test-author (disputes), or payment-webhook-replay (webhook replay).
payment-webhook-replay
Workflow-driven skill that builds payment webhook replay + recovery tests. Covers the idempotency contract (every webhook handler must handle redelivery without side effects), the replay simulators (Stripe CLI `stripe trigger`, Adyen Customer Area resend, PayPal Webhook Simulator, Braintree webhook test), the signature-verification gauntlet (HMAC-SHA256 per gateway, expired-timestamps rejection), and the partial-failure recovery scenarios. Use when designing webhook robustness tests.
pci-dss-scope-reference
Pure-reference catalog of PCI DSS v4.0 scope reduction techniques + the testable scope boundaries. Covers the SAQ levels (A through D, picked by how cardholder data flows), the PAN-storage prohibitions (only first-6 + last-4 retained; nothing else cleartext), the tokenization + hosted-fields scope-reduction patterns (Stripe Elements / Adyen Drop-in / Braintree Hosted Fields keep PAN off your servers), Network-Segmentation as PCI scope-reduction, and the testable behaviours the scope boundary creates. This is the catalog of WHY the boundary matters and what it makes testable - not a checker that verifies a given integration against the standard. Use when designing or auditing the PCI scope of a payment integration.
refund-test-matrix-builder
Workflow-driven skill that builds a refund test matrix from a payment-flow inventory: refund variants (full / partial / multiple-partials / over-refund / refund-on-disputed / refund-on-already-refunded), per-gateway nuances (Stripe, Adyen, PayPal, Braintree refund APIs), and timing variants (immediate / next-day / declined-by-bank), one test case per cell. Use for refund coverage on a new integration; for chargeback / dispute coverage use chargeback-flow-test-author, for webhook redelivery + idempotency use payment-webhook-replay, and for the state model use payment-flow-states-reference.
stripe-subscription-billing-test-author
Builds test suites for Stripe recurring-billing flows: trial-to-paid conversion, proration on plan upgrade and downgrade, dunning on failed renewal, cancel and reactivation, and the full subscription webhook event matrix (invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted, invoice.paid). Uses Stripe Billing test clocks (POST /v1/test_helpers/test_clocks) to time-travel through billing cycles without calendar delay. Distinct from stripe-test-cards-and-webhooks (one-time PaymentIntents) and payment-webhook-replay (idempotency + replay robustness). Does not cover single-event CLI replay or handler idempotency testing (see payment-webhook-replay for those). Use when authoring tests for subscription or recurring-billing integrations.
stripe-test-cards-and-webhooks
Wraps Stripe API testing patterns: test-mode initialization, the canonical test cards (4242 success; 4000 0000 0000 0002 declined; 4000 0027 6000 3184 3DS challenge per 3ds-test-flow-reference), the Stripe CLI webhook flow (`stripe listen --forward-to`), the Stripe CLI fixture commands (`stripe trigger payment_intent.succeeded`), and the webhook signature verification (Stripe-Signature header + HMAC-SHA256). Use when testing Stripe-integrated code.