CIIFragments Studio is CII-accredited: recover up to 20% of your software development spendLearn more

API integration for PayPal

We build your PayPal connector

Your application calls the PayPal API to take payments, run subscriptions and handle disputes without the buyer typing a card number.

  • Senior product team
  • payment integrations in production
  • from scoping to monitoring
In short

What does the PayPal API provide and why integrate it as a payment method?

PayPal is the most widely used digital wallet in the world, particularly appreciated by online shoppers for its simplicity and buyer protection. Its API lets your application take a one-time payment, manage subscriptions, trigger a refund, and be notified of each payment event. You integrate it to offer PayPal alongside card payment in a checkout, or to collect payments in markets where PayPal is the dominant payment method. It is also the reference solution for managing buyer disputes directly from your back office.

Use cases

What our clients build on the PayPal API

01

Ecommerce checkout with a real capture

The browser return does not open the order. PAYMENT.CAPTURE.COMPLETED triggers fulfilment, not a page callback.

02

SaaS subscription driven by events

Activation, suspension and failed payment open or close access. No boolean kept by hand in two systems.

03

Dispute queue in the back office

CUSTOMER.DISPUTE.CREATED feeds a queue with a deadline and attachments, instead of an email found too late.

04

Collecting for several merchants

PayPal-Auth-Assertion lets you act for a merchant without one token per account, with the partner BN code.

For you

What it changes in how you take payment

Engineering in service of a measurable outcome: fewer drop-offs, accurate access, disputes handled.

Fewer drop-offs at payment

The buyer pays with an account they already have. No 16-digit entry on mobile, often the step that closes the cart.

Access aligned with payment

The PayPal subscription opens, suspends and closes the right. You stop matching an export against an internal table.

Disputes leave the support inbox

Each chargeback lands in your tool, with a deadline. You reply on time instead of discovering it on the debit.

Books that actually balance

Captures and refunds carry an order reference. Matching stops being an export read by hand.

Method

How we ship your PayPal connector

01

Scoping

Checkout, subscription or platform, 3DS rule, which events matter. We settle the model before writing a line of code.

02

Development

Typed connector, RSA verification on the raw body, PayPal-Request-Id derived from the business intent, retry queue.

03

Acceptance testing

PayPal-Mock-Response for business errors, sandbox for the signature, simulator only for delivery mechanics.

04

Monitoring

Alerts on unprocessed events, a capture log, a health dashboard. You know a flow is broken before your customers do.

What the API allows

What the PayPal API allows

Orders and capture
Create an order, buyer approval, capture. Business state is read from the capture, not from the browser return.
Subscriptions and plans
Plans, activation, suspension, reactivation, expiry and failed payment. Each transition is a named event.
Disputes and refunds
A queue opened on CUSTOMER.DISPUTE.CREATED, refund of a capture with PayPal-Request-Id to avoid a double credit.
Webhooks and 3DS
Up to 10 URLs per application, SCA_WHEN_REQUIRED or SCA_ALWAYS, and liability_shift as a capture rule, not a forgotten field.
Glossary

The vocabulary of the PayPal API

PayPal-Request-Id
Idempotency header on some POSTs, remembered for up to 45 days on the captured-payment refund example. It is not universal: the API reference says so endpoint by endpoint.
paypal-transmission-sig
Base64 webhook signature. The signed message is transmissionId | timeStamp | webhookId | crc32, where crc32 is the raw-body CRC32 in decimal, verified with SHA256withRSA.
webhookId
The subscription identifier, in neither the header nor the body. Without persisting it when the URL is created, signature verification is impossible.
liability_shift
The 3DS result that says whether chargeback risk moved to the issuer. It is a business rule (capture or not), not a technical field to ignore.
PayPal-Auth-Assertion
JWT identifying the merchant you act for. PayPal recommends payer_id over email, and an unsigned JWT (alg none) for this case.
SCA_WHEN_REQUIRED
Default verification.method: PayPal triggers 3DS only when local rules require it. SCA_ALWAYS attempts it on every card.
Good to know

The real constraints of the PayPal API

01

The signature is not an HMAC

Rebuild transmissionId|timeStamp|webhookId|crc32 on the raw body, CRC32 in decimal, then RSA-SHA256 with the certificate from paypal-cert-url. Parse-then-reserialize fails verification.

02

The simulator does not verify

On simulated events the identifier is the literal string WEBHOOK_ID, and POST /v1/notifications/verify-webhook-signature is not supported. Many teams think their code is broken.

03

Up to 25 retries over 3 days

Without a 2xx, PayPal retries up to 25 times over 3 days, then moves to Failed. A webhook down over a weekend produces duplicates to dedupe, not a silent loss.

04

Idempotency and rate limits to measure

PayPal-Request-Id is not available everywhere. No numeric rate limit is published: a processing rate is measured in test, then watched, not inferred from a doc.

Two PayPal products

Orders or Subscriptions?

Two APIs for two questions. The right choice depends on the money's lifecycle, not on the logo on the button.

CriterionOrdersOne-off paymentSubscriptionsRecurring
Business objectAn order, then a captureA plan, then a subscription
Event that closes itPAYMENT.CAPTURE.COMPLETEDBILLING.SUBSCRIPTION.ACTIVATED
What failure looks likeDecline or capture deniedBILLING.SUBSCRIPTION.PAYMENT.FAILED
Product accessOpened on captureOpened, suspended, reopened on events
Legacy to avoidDeprecated 3DS field locationBilling Agreements, deprecated
Platform casePayPal-Auth-Assertion, BN codeSame header, plus the subscription cycle
The right caseCart, order, one-off serviceSaaS, membership, access over time

The two combine: a first Orders payment at signup, then Subscriptions for renewal. It is a scoping trade-off, not a permanent choice.

Our expertise

What we measure on a PayPal integration

15 d
first PayPal flow in production
100 %
of webhooks verified with RSA-SHA256
< 1 min
latency between capture and your app
4
senior developers on the project

We combine PayPal with

The stack that surrounds PayPal on our projects.

  • Stripe
  • Pennylane
  • HubSpot
  • PostgreSQL
  • Node.js
FAQ

PayPal integration: your questions

Three steps. First obtain a token via POST /v1/oauth2/token with client_credentials, and read the scopes actually granted before the first business call. Then build the server-side connector: creating Orders or Subscriptions, with PayPal-Request-Id derived from the business intent on POSTs that support it. Finally handle webhooks: persist the webhookId when the URL is created, verify the RSA signature on the raw body (CRC32 in decimal), return 200, process in a queue. The sensitive part is not the API call, it is verification that is not HMAC, and the fact that PayPal retries up to 25 times over 3 days.

It depends on the model: an Orders checkout with capture is shorter than a subscription with access control, a dispute queue and a platform case. The hard part is almost never the PayPal button, it is the RSA signature, the simulator that does not verify, and webhook duplicates. A first useful flow ships in two to three weeks. A full chain with subscriptions and disputes is closer to six to eight weeks. We scope the perimeter up front and give a firm estimate before we start.

Orders collects on an order: create, approve, capture. The event that closes the flow is PAYMENT.CAPTURE.COMPLETED. Subscriptions manages a cycle over time: plan, activation, suspension, failed payment, with BILLING.SUBSCRIPTION.* events. REST Billing Agreements are deprecated in favour of Subscriptions. Many projects need both: a first Orders payment, then a subscription. The data model has to be designed for both at scoping, not bolted on afterwards.

Not with HMAC, unlike Stripe or GoCardless. Rebuild transmissionId|timeStamp|webhookId|crc32, where crc32 is the raw HTTP body CRC32 expressed in decimal. paypal-transmission-sig is verified with SHA256withRSA using the certificate downloaded from paypal-cert-url, after checking the host. The webhookId comes from the subscription configuration, not from the message. The simulator cannot be verified via POST /v1/notifications/verify-webhook-signature: to test the crypto, you need a real sandbox event. Under Express or NestJS, the webhook route needs the raw body, not reserialized JSON.

Yes, and it is a frequent request. We build the flow that pushes captures and refunds with the order reference (supplementary_data.related_ids.order_id on the capture), then a periodic consistency check between what PayPal exposes and what your books record, with an alert on gaps rather than a silent fix. An accounting entry is not repaired behind the accountant's back. Manual matching of a PayPal export disappears once the reference is carried end to end.

A PayPal integration project?

Let's talk. 30 minutes to scope your checkout or subscriptions, check what the API actually allows and tell you honestly what is feasible.

Discuss my PayPal project
Discuss my PayPal project