npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@payfanout/adapter-adyen

v1.1.3

Published

Client-side Adyen adapter for PayFanout (Adyen Web Card component, hosted iframe fields, tokenize-first). No secrets, no server code.

Readme

@payfanout/adapter-adyen

Client-side Adyen adapter for PayFanout: the Adyen Web Card component (card data captured inside Adyen's hosted iframes, SAQ-A eligible), tokenize-first.

No secrets. This package ships to the browser and holds only the public clientKey, whose origins you allowlist in the Adyen Customer Area.

It implements the ClientPaymentAdapter contract from @payfanout/core, so @payfanout/react renders it through the same <PaymentFields> / <PayButton> as every other PSP.

📖 Documentation: https://donapulse.github.io/payfanout/ · Set up Adyen · React usage

Installation

pnpm add @payfanout/react @payfanout/adapter-adyen react react-dom

Adyen Web is not an npm dependency; the adapter injects the pinned build lazily from Adyen's CDN on first mount.

Usage

import { PayFanoutProvider, PaymentFields, PayButton } from "@payfanout/react";
import { AdyenClientAdapter } from "@payfanout/adapter-adyen";

const adyen = new AdyenClientAdapter({
  clientKey: import.meta.env.VITE_ADYEN_CLIENT_KEY,
  environment: "sandbox",
  countryCode: "NL",
});

<PayFanoutProvider adapters={[adyen]} initialPsp="adyen" completionEndpoint="/api/complete">
  <PaymentFields clientSecret={session.clientSecret} />
  {/* completionEndpoint finishes the tokenize-first flow automatically — no onServerCompletion. */}
  <PayButton onResult={(result) => showOutcome(result)}>Pay</PayButton>
</PayFanoutProvider>
  • environment selects the value handed to Adyen Web (sandbox → test, live → live), the CDN host Adyen Web loads from, and the client key it takes: test_… on sandbox, live_… on live. A live account served from another region sets adyenEnvironment to live-us, live-au, live-nea, live-in or live-apse (compared case-insensitively), and Adyen Web then loads from that region's host, checkoutshopper-{value}.cdn.adyen.com, as Adyen requires. live-apse is accepted because Adyen Web types it and maps it to its own hosts, which serve the pinned build, though Adyen's v6 integration guides do not list it; check it with Adyen before going live on it. Nothing is inferred: the constructor throws invalid_request for a client key that does not start with test_ on sandbox or live_ on live (a legacy origin key included), for an adyenEnvironment that contradicts environment, and for one Adyen Web does not support.
  • countryCode is required — Adyen Web takes it on the checkout instance.
  • The session's clientSecret is the server adapter's signed session token; the adapter reads its payload half (amount and currency) so Adyen Web shows the right figures. It never needs the signing key.
  • confirm() resolves { status: "requires_confirmation", clientToken } where clientToken is a JSON envelope taken from Adyen Web's state: the encrypted card blob (paymentMethod) and the browser data 3-D Secure 2 uses (browserInfo, origin, billingAddress, riskData), nothing else. The host passes it to the server's completePayment — <PayButton> / completionEndpoint wire this automatically. Only the matching major release of @payfanout/adapter-adyen-server decodes the envelope, so upgrade the server adapter first.

Customization

options.appearance becomes Adyen's styles object (base, error, placeholder, validated) and options.fieldOptions passes through to the Card component untouched — the host wins on conflicts, except the two keys the adapter must own: showPayButton (forced to false, since your <PayButton> drives submission) and onChange (where the encrypted blob and the validity stream arrive). options.locale sets the SDK's locale for that mount.

sdkVersion loads another Adyen Web build from Adyen's CDN, and sdkUrl / stylesheetUrl load the script / stylesheet from your own host. The adapter carries Adyen's integrity hashes for its pinned build only, so an overridden file loads without an integrity check: sdkVersion turns it off for both files, sdkUrl and stylesheetUrl for their own.

cspNonce puts your Content-Security-Policy nonce on both tags the adapter injects, the script and the stylesheet <link>, for a page whose script-src or style-src allows them by nonce; the adapter never reads a nonce from the page. See the Adyen guide for what it covers.

3-D Secure

A challenge comes back from the server: completePayment reports requires_action with Adyen's action object on PaymentInfo.raw, and an empty pspPaymentId when Adyen answers without a pspReference (its native 3-D Secure 2 example does). Hand it to handleAction(handle, action): Adyen Web replaces the card fields with the threeDS2 fingerprint or challenge and runs it inline, and the result is a fresh clientToken — the onAdditionalDetails data, { details: { threeDSResult } } — which the host completes with exactly as it did the first one. That answer can carry another action; handle it the same way. handleAction is Adyen-specific — the unified contract has no action step, because most PSPs resolve challenges inside confirm().

Adyen can still choose its redirect flow. handleAction then navigates the page to Adyen, which sends the shopper back to the session's returnUrl with a redirectResult query parameter; adyenRedirectResultToken(redirectResult) turns it into the clientToken the return page completes the payment with, within the signed session's expiry.

One challenge runs at a time per mounted field set: calling handleAction again while one is outstanding resolves { status: "failed" } with invalid_request rather than replacing the pending resolver, which would strand the first caller's promise. The promise settles when Adyen reports the shopper's details, or as failed when Adyen Web reports an error through onError first — authentication_required, or a retryable psp_unavailable when Adyen Web names it a network or script error, or its message reads as one — or when the fields are unmounted first. Race it against your own timer if you need a deadline on an abandoned challenge. Once handleAction has run, the card fields are gone: confirm() on that handle resolves failed with invalid_request, and the fields must be remounted to pay again.

Notes

  • Card data is captured only inside Adyen's hosted iframes; there is no raw card input, and no PAN/CVV ever touches your DOM.
  • onChange fires once with { complete: false, empty: true } on mount, then with { complete } on every SDK validity change, so "disable Pay until complete" works out of the box.
  • The adapter loads Adyen Web 6.45.2 (ADYEN_WEB_VERSION) with the Subresource Integrity hashes Adyen publishes for it and crossorigin="anonymous", so the browser refuses a modified script or stylesheet. A <script> your page adds itself for the default URL needs the same integrity, exported as ADYEN_WEB_SCRIPT_INTEGRITY, and a crossorigin attribute: while window.AdyenWeb is not defined yet, a conflicting tag makes loadSdk() reject with invalid_request. A stylesheet <link> your page adds for the default URL is used as it is, so give it ADYEN_WEB_STYLESHEET_INTEGRITY and a crossorigin attribute to keep the check.
  • If the script fails to load, the next mount fetches it again; if it loaded without defining window.AdyenWeb, the next mount checks again instead of failing from a cached result. A stylesheet that failed stays on the page and is reused, and it never blocks the fields from mounting. The mount does wait for the stylesheet to load or fail, and for the sheets it @imports: Adyen's adyen.css imports none, but a stylesheetUrl of your own that imports from a slow host delays the first mount on each page load.

Documentation

License

MIT