Requests a cancel. Accepts the bare pspReference: no money facts are needed. Adyen documents only that a captured payment can no longer be cancelled; a cancel after the capture is assumed to be acknowledged and to fail in the CANCELLATION webhook, and a rejection in the answer surfaces as an error.
Requests a capture. Adyen answers { status: "received" }, so the reported
status is "processing" and no amountCaptured is invented — the settled amount
is knowable only from the CAPTURE webhook. An acknowledgement echoing a
different amount or currency is rejected, and one echoing none is accepted
(see readAcknowledgement).
Tokenize-first completion. clientToken is the JSON the client adapter
produced (see parseClientToken):
paymentMethod plus the
browser data 3-D Secure uses — or the bare paymentMethod earlier
client adapters send, which creates the payment (POST /payments), or{ details } from a finished action (handleAction(), or the
redirectResult of a 3-D Secure redirect return), which finishes it
(POST /payments/details).The signed context is the only trusted source of amount, currency,
reference and capture method. A refusal is raised as a mapped
PayFanoutError rather than folded into a "failed" PaymentInfo. An action
surfaces as requires_action with Adyen's answer on raw. Adyen can answer
an action without a pspReference (its native 3-D Secure 2 example does);
pspPaymentId is then the empty string, which capturePayment,
cancelPayment and refundPayment refuse.
Every answer is compared with the session it completes. A /payments
answer naming another merchant reference or amount is refused: the
request named the session's own, so the answer belongs to another request
(an idempotencyKey reused across sessions replays the first answer), and
unless it is Refused, Error or Cancelled the refusal carries
outcomeUnknown (see anotherRequestsAnswer). A
/payments/details answer stands for the session only when it names the
session's merchant reference and amount (see detailsBelongToSession); one
that does not carries no pspPaymentId and reads "processing". The
AUTHORISATION webhook, whose merchantReference is one of the signed
values, then supplies the reference
(encodeAdyenPaymentRef(event.pspPaymentId, event.amount, event.currency)).
Creates NO Adyen object: the payment only exists once completePayment
posts /payments. Amount, currency, reference, capture method and the
checkout fields are signed into the returned pspSessionId, which is also
the session's clientSecret — the browser reads the payload half to drive
Adyen Web's own copy and cannot tamper with the amount.
The shopper email Adyen asks for on Visa and JCB 3-D Secure 2 payments is
receiptEmail, or billingDetails.email when there is none; both ride
the signed context, whose payload half the browser can read. Refused with
invalid_request, before the shopper enters a card, when the return URL is
missing or malformed (see assertReturnUrl) or receiptEmail is not an
address of at most 256 characters. A billingDetails.email like that is
left out instead: billing details are optional data, withheld rather than
failing the session.
Throws PayFanoutError (code "invalid_request") on unparseable payloads; maps genuinely unknown-but-valid event types to type "unknown" rather than throwing.
Requests a refund. Always "pending": Adyen acknowledges the request and the REFUND / REFUND_FAILED webhook carries the outcome. With no payment read, an amountless refund takes its value from the composite pspPaymentId, which is the authorised amount: after a partial capture or refund Adyen refuses it.
Webhook handling — same adapter, separate concerns. Both async: future PSPs may need remote key retrieval; the contract stays uniform. MUST operate on the RAW request body bytes/string — re-serializing a parsed body breaks signatures (the conformance suite tests this).
Adyen is a PUSH-ONLY provider: its
pspReferenceis a write target, there is no read for a payment or a refund, and every modification answers with a bare acknowledgement whose outcome arrives by webhook. Two consequences shape this adapter:supportsPaymentRetrieval: false,supportsRefundRetrieval: falseandmodificationOutcome: "asynchronous"; capture and cancel resolve "processing" and refunds "pending", never a terminal state Adyen has not confirmed.PaymentInfo.pspPaymentIdis the composite"{pspReference}:{value}:{currency}". A capture or refund needs the payment's currency (and, when no amount is given, its value); with no read and no persistence, the money facts ride the reference. The part before the first ":" is Adyen's own pspReference — the one webhooks report — and cancels accept it bare.