Testland
Browse all skills & agents

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-sandbox
View source

paypal-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

  • Tests for code using PayPal Checkout (PayPal button, etc.).
  • Webhook handling.
  • OAuth-based sandbox flows.

Authoring

Setup

  1. Create developer account at developer.paypal.com (opens in new window).
  2. Create Business + Personal sandbox accounts (one Business for merchant; one or more Personal for buyers).
  3. Get sandbox client ID + secret.

Install

npm install @paypal/checkout-server-sdk
pip install paypalserversdk

OAuth2 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):

CardBehaviour
4111 1111 1111 1111Visa Sandbox success
5555 5555 5555 4444Mastercard success
4032 0359 8001 0008Decline

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 test

CI 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-patternWhy it failsFix
Use live PayPal credentials in testsReal moneySandbox-only
Test with manual buyer approvalSlow; not CI-suitablePlaywright + sandbox buyer login
Skip webhook signature verificationSpoofableAlways verify
Hardcode sandbox account IDsFragile to account changesPer-env IDs
Test only the API pathReal flow requires checkout UIPlaywright e2e
Legacy Payments v1 APIDeprecatedMigrate to Orders v2
Treat CREATED as finalOrder needs captureTest the full lifecycle
One-shot test for refundsRefunds are asyncWait for webhook

Limitations

  • Sandbox UI is slower than prod. Playwright e2e against sandbox is flaky-prone.
  • Sandbox accounts can be rate-limited. CI parallelism may conflict.
  • Card sandbox testing less first-class than balance-based testing; PayPal expects wallet flows.
  • Webhook delivery in sandbox sometimes delayed; tests need timeouts.
  • Legacy Payments v1 still works but is deprecated; new code should use Orders v2.

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.