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. Composes payment-flow-states-reference.

paypal-sandbox

Overview

PayPal Sandbox is a parallel environment that mirrors the prod PayPal API. Per developer.paypal.com/tools/sandbox, 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); 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.
  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:

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