Voids the authorization of an AUTHORIZE-intent payment (order id or one of its capture ids). CAPTURE-intent orders cannot be cancelled via the API — they expire on their own, so cancelling one is rejected rather than faked. After a reauthorization the void still goes to the original authorization: PayPal refuses to void a reauthorization (CANNOT_BE_VOIDED, "Please void the original parent authorization"). Nothing is sent once the authorization holding the funds reports VOIDED.
Manual capture of an AUTHORIZE-intent payment. pspPaymentId is the order id or one of its capture ids (what completion and every capture return once money moved). Each partial capture is its own charge under its own required key — a reused key replays the earlier capture via PayPal-Request-Id and moves no new money.
PayPal reads a capture without an amount as the FULL authorized amount,
so amount: undefined goes out as the explicit uncaptured remainder:
the holding authorization's amount minus the captures taken from it, and
never more than the order amount minus every capture on the order that
took money (PENDING ones included, DECLINED/FAILED ones took none). A
capture covering the remainder is sent with final_capture: true, which
closes the authorization; a smaller one keeps it open for the next
capture. A remainder that rests on a capture another authorization could
have given is an estimate that errs low (see capturesAgainst), and only a
capture of all the order has left closes the authorization on it. The
flag reads the remainder at call time, so a same-key retry can send a
different body; PayPal replays by PayPal-Request-Id either way. An
explicit amount is not held to the remainder: PayPal judges its overage
limit.
Once earlier captures took the whole authorization or the whole order, capturing the rest sends no capture and answers with the payment, under the same key or a new one: the adapter keeps no state, so it cannot tell a retry whose response was lost from a new call, and a capture without an amount would ask PayPal for the full authorized amount again. Capturing the rest of an authorization voided or denied before captures took it all rejects before any capture call.
Tokenize-first completion: the client's confirm() resolves with the approved order id as clientToken; money moves here. CAPTURE-intent orders capture, AUTHORIZE-intent orders authorize (capture comes later via capturePayment). A completion repeated under another key answers with the order's existing capture or authorization, never a second one.
Missed-webhook recovery over GET /v1/notifications/webhooks-events: the same payload shapes webhooks deliver, normalized by the same mapper, so dedupe by event.id — which real PayPal events always carry — works identically for delivered and fetched events. PayPal documents no retention period for the list; its events dashboard searches only the last 30 days.
Pages the merchant's Subscriptions v1 subscriptions as unified records. The cursor is the next page number (GET /v1/billing/subscriptions pages by number: page_size 1–20, default 10). List items omit the inline plan, so each item is completed by the same fields=plan detail GET retrieve uses — a page costs 1 + N requests (N ≤ 20). total_required=true rides every page, although the reference documents it for the plans list rather than this one, because total_pages ends the walk exactly when PayPal returns it; a next link or a full page decide otherwise (a possibly-empty final page beats silently truncating the walk). None of the documented filters (plan_ids, statuses, date ranges, filter) is applied; the reference describes the call as listing all subscriptions for the merchant account and gives statuses no default (not sandbox-verified).
Throws PayFanoutError (code "invalid_request") on unparseable payloads; maps genuinely unknown-but-valid event types to type "unknown" rather than throwing.
Refunds settle against the CAPTURE — order ids are resolved to it here.
reason is not forwarded: PayPal's only reason field, note_to_payer,
is payer-facing text ("Appears in both the payer's transaction history
and the emails that the payer receives"), and a reason code is not a
message for the payer.
Accepts the order id (active payment window) or the capture id (the durable canonical id — PaymentInfo.pspPaymentId after capture). Orders age out of GET a few days after completion, captures do not.
Polls an async refund (eCheck-funded ones sit PENDING) to a terminal state.
PATCH-amends a CREATED/APPROVED order in place (same order id, so the mounted PayPal button keeps working). COMPLETED orders reject with invalid_request. Currency changes require an explicit amount — the old minor amount is not silently reinterpreted in the new currency. The PATCH applies whole or not at all, and touches only attributes PayPal's patch table lists: shipping's name and address, and a statement descriptor the order already has. A name left out of an update keeps the order's current one (the table has no remove for it). Adding a descriptor to an order created without one is refused before any PATCH: pass statementDescriptor when creating the session.
Side-effect-free credential probe behind a host's "Test connection" button. The client-credentials token mint IS the ideal probe: it reads nothing and moves no money, yet it exercises the exact clientId/clientSecret the adapter authenticates with. Always performs a real token fetch (never trusting a warm cache), and classifies from the RAW HTTP status so a rejected key (401/403) is never confused with a transient outage — the mint issues a single request, so an auth rejection is never retried. Secrets are never echoed back in the message.
Postback verification: PayPal itself confirms the delivery headers + exact raw bytes. Missing headers, a missing webhookId, an empty body, or a body that is not exactly one JSON object answer false locally without a network call; transport trouble fails closed (false), never open.
Stops PayPal-side billing, verified-idempotent. The cancel endpoint accepts only ACTIVE/SUSPENDED subscriptions (anything else answers 422 SUBSCRIPTION_STATUS_INVALID). The derived PayPal-Request-Id header is forwarded best-effort even though the cancel operation declares no such parameter — replay safety rests on the state machine plus re-fetch: on any cancel rejection the subscription is re-fetched, and a terminal billing state (CANCELLED or EXPIRED — billing already stopped) resolves as success with the honest record. Non-terminal states rethrow the cancel error. The required
reasonbody field is filled with a fixed factual default.