PayPal Webhook Guide

How to set up, verify, test, and monitor PayPal webhooks — plus why you should migrate from IPN.

Last updated March 2026. Covers PayPal REST API v2 and Webhooks API.

1

PayPal IPN vs Webhooks

PayPal offers two notification systems. IPN (Instant Payment Notification) is the legacy approach. Webhooks are the modern replacement. PayPal recommends webhooks for all new integrations.

FeatureIPN (Legacy)Webhooks (Modern)
Data formatForm-encoded POSTJSON
VerificationPOST back to PayPal to confirmCertificate-based signature in headers
Event selectionAll-or-nothingSubscribe to specific event types
Multiple endpointsOne URL onlyMultiple webhook listeners
Retry behaviorLimited retriesAutomatic retries with backoff for up to 3 days
API versionClassic NVP/SOAPREST API v2

Migration note: If you are still using IPN, plan to migrate. PayPal has been deprecating Classic APIs and IPN may stop working for newer payment products. Webhooks give you structured JSON, better reliability, and fine-grained event filtering.

2

PayPal Webhook Event Types

PayPal has dozens of event types. Here are the most commonly used ones:

PAYMENT.SALE.COMPLETED PAYMENT.CAPTURE.COMPLETED CHECKOUT.ORDER.APPROVED BILLING.SUBSCRIPTION.CREATED CUSTOMER.DISPUTE.CREATED

By use case:

3

Setting Up in PayPal Developer Dashboard

To configure webhooks in PayPal:

  1. Log in to the PayPal Developer Dashboard at developer.paypal.com.
  2. Navigate to Apps & Credentials. Select your app (or create one).
  3. Scroll to the Webhooks section and click Add Webhook.
  4. Enter your endpoint URL — this must be HTTPS and publicly accessible.
  5. Select the event types you want to receive (e.g. PAYMENT.CAPTURE.COMPLETED).
  6. Click Save. PayPal will assign a Webhook ID — you will need this for signature verification.

Remember: PayPal has separate Sandbox and Live environments. Configure webhooks in both if you need to test and receive production notifications.

4

Verifying PayPal Webhook Signatures

PayPal uses certificate-based signature verification, which is more complex than the HMAC approach used by Stripe or GitHub. Each webhook request includes these headers:

Verification concept:

// The verification message PayPal signs is:
// transmissionId + "|" + timestamp + "|" + webhookId + "|" + crc32(body)
//
// To verify manually:
// 1. Download the cert from Paypal-Cert-Url
// 2. Validate the cert chains to PayPal's root CA (symantec/verisign)
// 3. Construct the expected message string
// 4. Verify the Paypal-Transmission-Sig against the message
//    using the public key from the certificate

// In practice, use PayPal's verify-webhook-signature API:
POST /v1/notifications/verify-webhook-signature
{
  "auth_algo": "SHA256withRSA",
  "cert_url": "https://api.paypal.com/...",
  "transmission_id": "...",
  "transmission_sig": "...",
  "transmission_time": "...",
  "webhook_id": "YOUR_WEBHOOK_ID",
  "webhook_event": { /* the full event body */ }
}

Recommendation: Use the PayPal SDK or the verify-webhook-signature API endpoint rather than implementing cert verification yourself. Manual cert validation requires downloading certificates, checking the chain of trust, and handling cert rotation — the SDK handles all of this for you.

// Node.js example using PayPal REST SDK
const paypal = require('@paypal/checkout-server-sdk');

app.post('/webhook/paypal', express.json(), async (req, res) => {
  const headers = {
    'paypal-auth-algo': req.headers['paypal-auth-algo'],
    'paypal-cert-url': req.headers['paypal-cert-url'],
    'paypal-transmission-id': req.headers['paypal-transmission-id'],
    'paypal-transmission-sig': req.headers['paypal-transmission-sig'],
    'paypal-transmission-time': req.headers['paypal-transmission-time'],
  };

  // Call PayPal's verification API
  const response = await fetch(
    'https://api-m.paypal.com/v1/notifications/verify-webhook-signature',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + accessToken,
      },
      body: JSON.stringify({
        auth_algo: headers['paypal-auth-algo'],
        cert_url: headers['paypal-cert-url'],
        transmission_id: headers['paypal-transmission-id'],
        transmission_sig: headers['paypal-transmission-sig'],
        transmission_time: headers['paypal-transmission-time'],
        webhook_id: 'YOUR_WEBHOOK_ID',
        webhook_event: req.body,
      }),
    }
  );

  const result = await response.json();
  if (result.verification_status === 'SUCCESS') {
    // Process the verified event
    console.log('Event type:', req.body.event_type);
    res.status(200).json({ received: true });
  } else {
    console.error('Verification failed');
    res.status(401).json({ error: 'Invalid signature' });
  }
});
5

Testing with SDNBros

Testing PayPal webhooks during development requires a public URL. Here is the fastest approach:

  1. Open the SDNBros Webhook Tester — you get a public endpoint URL instantly, no signup required.
  2. In the PayPal Developer Dashboard, go to your app's Webhooks section and click Add Webhook. Paste your SDNBros URL.
  3. Select the event types you need (e.g. PAYMENT.CAPTURE.COMPLETED).
  4. Use PayPal's Webhook Simulator in the Developer Dashboard to send test events, or make a Sandbox purchase to trigger real events.
  5. Switch back to SDNBros — the event arrives in real time. SDNBros auto-detects it as a PayPal webhook and shows the event type, all transmission headers, and the full JSON payload.

For local development, forward webhooks from your SDNBros endpoint directly to your local server with sdnbros tunnel.

6

Monitoring in Production

PayPal webhooks can fail silently — a missed CUSTOMER.DISPUTE.CREATED event means you lose the dispute response window. Set up monitoring:

Create a free SDNBros account to start monitoring your PayPal webhooks in production.

Common PayPal Webhook Errors

ErrorCauseFix
VERIFICATION_FAILUREWrong Webhook ID, or cert URL blocked by firewallDouble-check your Webhook ID in the PayPal Dashboard. Ensure your server can reach api.paypal.com to download certs.
Webhook timeoutEndpoint takes too long to respond (PayPal expects response within 30s)Return 200 immediately and process the event asynchronously in a background job.
Duplicate eventsPayPal retries on non-2xx. Your handler processes the same event twice.Store the transmission_id or event id and check for duplicates. Make your handler idempotent.
Sandbox vs Live mismatchWebhook configured in Sandbox but triggered by Live transaction (or vice versa)Configure webhooks separately in both Sandbox and Live apps. Verify the correct environment.
Events not arrivingWrong event types selected, or webhook disabled after repeated failuresCheck the Webhooks section in the Developer Dashboard. Re-enable the webhook and verify event type selection.

Test PayPal webhooks free

Create a public endpoint and inspect PayPal events. No signup is needed for the tester.

Open Webhook Tester Monitor in Production

Related guides: What is a Webhook? · Webhook vs API · Webhook Security · Retry Strategies · Stripe · GitHub · Shopify · Slack

More: What is a Webhook? | Webhook Security Guide | Webhook Idempotency | Compare Tools