Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Adyen with V0

To use Adyen with V0 by Vercel, embed the Adyen Web Drop-in or Components UI library in your V0-generated Next.js app and create a Next.js API route that uses your Adyen API key to create a payment session. Adyen's Drop-in handles the entire payment form UI, 3D Secure authentication, and payment method selection — your API route returns the session data and Adyen's SDK takes care of the rest.

What you'll learn

  • How to install and initialize the Adyen Web SDK in a Next.js App Router project
  • How to create a Next.js API route that generates Adyen payment sessions using the Adyen Node.js library
  • How to mount the Adyen Drop-in component inside a V0-generated React checkout page
  • How to handle Adyen payment result callbacks and redirect the user on success or failure
  • How to configure Adyen environment variables correctly in Vercel for both test and live environments
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read35 minutesPaymentMarch 2026RapidDev Engineering Team
TL;DR

To use Adyen with V0 by Vercel, embed the Adyen Web Drop-in or Components UI library in your V0-generated Next.js app and create a Next.js API route that uses your Adyen API key to create a payment session. Adyen's Drop-in handles the entire payment form UI, 3D Secure authentication, and payment method selection — your API route returns the session data and Adyen's SDK takes care of the rest.

Enterprise Payments in V0 Apps with Adyen Drop-in

Adyen powers payments for companies like eBay, Spotify, and McDonald's — and the same payment infrastructure is available to any developer through the Adyen API. For V0 developers building apps that need to accept payments globally with support for cards, wallets, buy-now-pay-later, and local payment methods, Adyen is the enterprise-grade choice. Unlike Stripe, Adyen requires a direct merchant account application rather than instant sign-up, but it offers lower processing fees at scale, more granular fraud controls, and broader global payment method coverage.

Adyen's Web integration uses a session-based flow that keeps your secret API key off the browser. Your Next.js API route calls the Adyen Checkout Sessions endpoint with your API key to create a session object — this session contains a one-time token and all the payment details. The session is sent to the browser, where the Adyen Web SDK reads it and mounts either the Drop-in (a complete payment form with method selection) or individual Components (one component per payment method like AdyenCard, AdyenPayPal, etc.) into a div element. The SDK communicates directly with checkoutshopper-live.adyen.com for the heavy lifting, including 3DS2 authentication challenges.

V0 is ideal for generating the surrounding checkout page: order summaries, product cards, pricing tables, and the confirmation screen after payment. You focus on the business logic and overall UX, while Adyen's Drop-in handles the PCI-compliant payment form rendering. This tutorial covers the complete integration from generating the checkout UI in V0 to testing with Adyen's test card numbers.

Integration method

Next.js API Route

V0 generates the checkout page layout and surrounding UI. A Next.js API route calls the Adyen Checkout API to create a payment session using your server-side API key. The Adyen Web Drop-in or Components library (loaded via @adyen/adyen-web) mounts into a container div in your React component, handles all payment form rendering, and communicates with Adyen's servers at checkoutshopper-live.adyen.com directly from the browser.

Prerequisites

  • A V0 account at v0.dev and a Next.js project to work with
  • An Adyen merchant account — apply at adyen.com (test accounts are available immediately at ca-test.adyen.com)
  • An Adyen API key from your Customer Area (Developers → API credentials → API key)
  • Your Adyen Merchant Account name and Client Key (also in Developers → API credentials)
  • A Vercel account for deployment and environment variable configuration

Step-by-step guide

1

Generate the Checkout Page UI in V0

Open V0 and describe the checkout experience you want to build. Adyen's Drop-in will occupy a specific container div in your layout — the rest of the page (order summary, product details, trust badges, progress indicators) is entirely up to you and is what V0 excels at generating. Think about your checkout flow: do you want a single-page checkout where everything is visible at once, or a multi-step flow (cart → shipping → payment → confirmation)? Single-page is simpler to build and converts well for low-complexity purchases. For the payment step, all you need is a container element — a div with a specific ID — where the Adyen SDK will inject its own HTML. Ask V0 to generate a realistic-looking checkout page with hardcoded placeholder data so you can see how the full page looks. Include an order summary with item names, quantities, and prices, plus a right column or bottom section reserved for the payment form. Also ask V0 to generate the post-payment confirmation page that users land on after a successful payment — this page reads the payment result from URL query parameters that Adyen appends on redirect. Use V0's Design Mode to polish typography, colors, and spacing so the page matches your brand before adding any payment logic.

V0 Prompt

Create a checkout page at app/checkout/page.tsx with a two-column layout. Left column: an order summary card showing 3 placeholder line items with name, quantity, and price, a subtotal row, a tax row, and a bold total. Right column: a 'Secure Payment' heading with a lock icon, a div with id='adyen-dropin-container' and className='min-h-48', and a loading skeleton that shows while the payment form is mounting. Also create a confirmation page at app/checkout/confirmation/page.tsx that shows a success message, order number, and a 'Continue Shopping' button.

Paste this in V0 chat

Pro tip: Ask V0 to use a neutral gray background for the adyen-dropin-container div with rounded corners and padding — this creates a natural visual frame for the payment form that Adyen injects.

Expected result: V0 generates a polished two-column checkout page with the payment container div in place, and a separate confirmation page ready to receive the payment result.

2

Install the Adyen Web SDK and Node.js Library

Install two packages: @adyen/adyen-web for the browser-side Drop-in UI, and @adyen/api-library for making server-side calls to the Adyen API from your Next.js API routes. The @adyen/adyen-web package is a client-side library — it gets imported in your React component and mounts the payment form UI into the container div. It communicates directly with Adyen's checkout shopper endpoints (checkoutshopper-live.adyen.com or checkoutshopper-test.adyen.com) from the browser, so you do not need to proxy every payment interaction through your API. The @adyen/api-library package is used exclusively in your Next.js API routes for server-side operations: creating sessions, refunding payments, retrieving payment details, and managing stored payment methods. This separation is important for security — your Adyen API key only ever appears in server-side code. Add both to your package.json and import @adyen/adyen-web CSS in your global stylesheet or layout. The Adyen Web SDK also requires importing its CSS file (node_modules/@adyen/adyen-web/dist/adyen.css) to render correctly — without it the form will be unstyled.

V0 Prompt

Add @adyen/adyen-web and @adyen/api-library to the package.json dependencies. Import the Adyen CSS in app/globals.css using @import '@adyen/adyen-web/dist/adyen.css'. Create a lib/adyen.ts file that exports an initialized CheckoutAPI from @adyen/api-library configured with process.env.ADYEN_API_KEY and process.env.ADYEN_ENVIRONMENT (either 'TEST' or 'LIVE'). Export the ADYEN_MERCHANT_ACCOUNT from process.env as a named export.

Paste this in V0 chat

lib/adyen.ts
1// lib/adyen.ts
2import { Client, Config, CheckoutAPI } from '@adyen/api-library';
3
4const config = new Config();
5config.apiKey = process.env.ADYEN_API_KEY ?? '';
6config.environment = (process.env.ADYEN_ENVIRONMENT as 'TEST' | 'LIVE') ?? 'TEST';
7
8const client = new Client({ config });
9
10export const checkout = new CheckoutAPI(client);
11export const MERCHANT_ACCOUNT = process.env.ADYEN_MERCHANT_ACCOUNT ?? '';

Pro tip: The Adyen CSS import must be a global import — placing it in app/globals.css ensures it loads for the page that mounts the Drop-in. Importing it only inside the component can cause the styles to flicker on initial render.

Expected result: Both packages are installed, the Adyen CSS is imported globally, and lib/adyen.ts exports the configured CheckoutAPI instance ready for use in API routes.

3

Create the Payment Session API Route

Build the Next.js API route at app/api/adyen/session/route.ts that your checkout page calls to create a payment session. This is the only Adyen API call that goes through your server — once the session is created, the browser talks to Adyen directly. The session endpoint requires your merchant account, the payment amount (in the smallest currency unit — cents for USD/EUR, pence for GBP), the currency code, a return URL where Adyen redirects after payment (for 3D Secure redirects), and a reference string (your order ID). Optionally include the shopper's email and name for fraud scoring. The session response contains a sessionId and sessionData — both are required by the browser-side SDK. The return URL should point to your confirmation page (e.g., https://your-app.vercel.app/checkout/confirmation) because some payment methods like bank transfers and 3DS2 redirect users away from your site and back. In test mode, use an amount like 1000 (meaning $10.00) and the 'TEST' environment. Adyen's test environment is completely separate from live — test API keys only work against test URLs and do not process real charges. The route should also accept the cart details from the request body so you can pass the real order total dynamically.

V0 Prompt

Create app/api/adyen/session/route.ts that accepts POST requests with { amount: number, currency: string, reference: string, returnUrl: string } in the body. Import checkout and MERCHANT_ACCOUNT from lib/adyen.ts. Call checkout.sessions() with the PaymentSessionsRequest type, including merchantAccount, amount, currency, reference, and returnUrl. Return the sessionId and sessionData from the response as JSON. Add error handling that returns a 500 status with the error message.

Paste this in V0 chat

app/api/adyen/session/route.ts
1// app/api/adyen/session/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { checkout, MERCHANT_ACCOUNT } from '@/lib/adyen';
4import type { CreateCheckoutSessionRequest } from '@adyen/api-library/lib/src/typings/checkout/createCheckoutSessionRequest';
5
6export async function POST(req: NextRequest) {
7 try {
8 const { amount, currency, reference, returnUrl } = await req.json();
9
10 if (!amount || !currency || !reference || !returnUrl) {
11 return NextResponse.json(
12 { error: 'amount, currency, reference, and returnUrl are required' },
13 { status: 400 }
14 );
15 }
16
17 const request: CreateCheckoutSessionRequest = {
18 merchantAccount: MERCHANT_ACCOUNT,
19 amount: { value: amount, currency },
20 reference,
21 returnUrl,
22 channel: 'Web',
23 shopperInteraction: 'Ecommerce',
24 };
25
26 const response = await checkout.PaymentsApi.sessions(request);
27
28 return NextResponse.json({
29 sessionId: response.id,
30 sessionData: response.sessionData,
31 });
32 } catch (error) {
33 console.error('Adyen session error:', error);
34 return NextResponse.json(
35 { error: 'Failed to create payment session' },
36 { status: 500 }
37 );
38 }
39}

Pro tip: Always generate your order reference before calling the sessions API — use a UUID or your database order ID. Adyen uses this reference to match webhooks to your orders, so it must be unique per transaction.

Expected result: POST requests to /api/adyen/session return a JSON object with sessionId and sessionData. You can verify this in the Vercel dashboard's function logs or using a tool like Postman with your test credentials.

4

Mount the Adyen Drop-in in Your React Component

Now wire the checkout page component to fetch a session from your API route and mount the Adyen Drop-in. The Drop-in must be initialized client-side — import AdyenCheckout from @adyen/adyen-web inside a useEffect hook (not at the top level) to avoid SSR issues, since the Adyen SDK accesses browser APIs like window and document. Alternatively, use Next.js's dynamic import with { ssr: false } to lazy-load the Adyen module. The initialization flow is: (1) fetch the session from /api/adyen/session on component mount, (2) initialize AdyenCheckout with the session data, client key, and environment, (3) create a Drop-in instance from the checkout object, (4) mount the Drop-in into the container div using its ID or a ref. The Drop-in accepts onPaymentCompleted and onError callback functions. The onPaymentCompleted callback fires when the payment is authorized — it receives a result object with a resultCode like 'Authorised', 'Refused', or 'Pending'. On 'Authorised', redirect the user to your confirmation page. The client key (NEXT_PUBLIC_ADYEN_CLIENT_KEY) is different from your API key — it is a public credential used by the browser SDK to identify your merchant account on Adyen's CDN, and it is safe to expose as a NEXT_PUBLIC_ variable.

V0 Prompt

Update app/checkout/page.tsx to fetch a payment session from /api/adyen/session on mount (POST with amount: 1000, currency: 'USD', reference: a generated UUID, returnUrl: window.location.origin + '/checkout/confirmation'). Store sessionId and sessionData in state. Use useEffect with dynamic import of @adyen/adyen-web to initialize AdyenCheckout with the session, environment set from process.env.NEXT_PUBLIC_ADYEN_ENVIRONMENT, and clientKey from process.env.NEXT_PUBLIC_ADYEN_CLIENT_KEY. Mount a Drop-in into the div with id='adyen-dropin-container'. On onPaymentCompleted, redirect to /checkout/confirmation?resultCode=Authorised. Show a loading state while the session is being fetched.

Paste this in V0 chat

app/checkout/page.tsx
1'use client';
2
3import { useEffect, useState, useRef } from 'react';
4import { useRouter } from 'next/navigation';
5
6const ADYEN_ENV = process.env.NEXT_PUBLIC_ADYEN_ENVIRONMENT ?? 'test';
7const ADYEN_CLIENT_KEY = process.env.NEXT_PUBLIC_ADYEN_CLIENT_KEY ?? '';
8
9export default function CheckoutPage() {
10 const router = useRouter();
11 const dropinRef = useRef<HTMLDivElement>(null);
12 const [sessionLoading, setSessionLoading] = useState(true);
13 const [error, setError] = useState<string | null>(null);
14
15 useEffect(() => {
16 let dropin: { unmount: () => void } | null = null;
17
18 async function initAdyen() {
19 try {
20 // 1. Create session
21 const res = await fetch('/api/adyen/session', {
22 method: 'POST',
23 headers: { 'Content-Type': 'application/json' },
24 body: JSON.stringify({
25 amount: 1000,
26 currency: 'USD',
27 reference: crypto.randomUUID(),
28 returnUrl: `${window.location.origin}/checkout/confirmation`,
29 }),
30 });
31 const { sessionId, sessionData } = await res.json();
32 setSessionLoading(false);
33
34 // 2. Init Adyen Checkout
35 const { default: AdyenCheckout } = await import('@adyen/adyen-web');
36 const checkout = await AdyenCheckout({
37 environment: ADYEN_ENV,
38 clientKey: ADYEN_CLIENT_KEY,
39 session: { id: sessionId, sessionData },
40 onPaymentCompleted(result: { resultCode: string }) {
41 router.push(`/checkout/confirmation?resultCode=${result.resultCode}`);
42 },
43 onError(error: Error) {
44 console.error('Adyen error:', error);
45 setError('Payment failed. Please try again.');
46 },
47 });
48
49 // 3. Mount Drop-in
50 if (dropinRef.current) {
51 dropin = checkout.create('dropin').mount(dropinRef.current);
52 }
53 } catch (err) {
54 setError('Failed to load payment form. Please refresh the page.');
55 setSessionLoading(false);
56 }
57 }
58
59 initAdyen();
60 return () => dropin?.unmount();
61 }, [router]);
62
63 return (
64 <div className="max-w-2xl mx-auto p-6">
65 <h1 className="text-2xl font-bold mb-6">Checkout</h1>
66 {error && <p className="text-red-600 mb-4">{error}</p>}
67 {sessionLoading && <div className="h-48 bg-gray-100 rounded-lg animate-pulse" />}
68 <div ref={dropinRef} id="adyen-dropin-container" />
69 </div>
70 );
71}

Pro tip: Always call dropin.unmount() in the useEffect cleanup function — failing to unmount causes React to throw 'Target already occupied' errors if the component re-renders or hot-reloads in development.

Expected result: The checkout page loads, fetches a session, and mounts the Adyen Drop-in showing available payment methods. In test mode, the Drop-in shows a card form. Entering Adyen's test card number 4111 1111 1111 1111 with any future expiry and CVC completes a test payment.

5

Add Environment Variables in Vercel and Switch to Live

Configure all Adyen environment variables in your Vercel project before deploying. Open Vercel → your project → Settings → Environment Variables and add the following variables. ADYEN_API_KEY is your secret key from Customer Area → Developers → API credentials — this must never have the NEXT_PUBLIC_ prefix, as it is a server-side secret. ADYEN_MERCHANT_ACCOUNT is your merchant account name (found in the top-left of your Customer Area dashboard). ADYEN_ENVIRONMENT is the string 'TEST' for test mode or 'LIVE' for production — the @adyen/api-library uses this to route requests to the correct API endpoint. NEXT_PUBLIC_ADYEN_CLIENT_KEY is your client key from API credentials — it uses the NEXT_PUBLIC_ prefix because the browser SDK needs it to communicate with Adyen's CDN. NEXT_PUBLIC_ADYEN_ENVIRONMENT is the lowercase version ('test' or 'live') used by the browser SDK, which has different casing requirements from the server-side library. When you are ready to go live, change both environment variables from TEST/test to LIVE/live and obtain a live API key — live API keys are only issued after your Adyen account team reviews and activates your account. For enterprise payment flows involving marketplace splits, recurring billing, or advanced fraud rules, RapidDev's team can help configure the full Adyen integration including webhook handling and payment result processing.

.env.local
1# .env.local (development never commit this file)
2# Server-side only (no NEXT_PUBLIC_ prefix)
3ADYEN_API_KEY=AQE...your-test-api-key
4ADYEN_MERCHANT_ACCOUNT=YourMerchantAccountName
5ADYEN_ENVIRONMENT=TEST
6
7# Client-side (NEXT_PUBLIC_ prefix required)
8NEXT_PUBLIC_ADYEN_CLIENT_KEY=test_...your-client-key
9NEXT_PUBLIC_ADYEN_ENVIRONMENT=test

Pro tip: Adyen test and live environments use completely separate API keys — a test API key will not work against live endpoints. Always verify your environment variable values match the intended environment before deploying.

Expected result: The app deploys to Vercel and the checkout flow works end-to-end in test mode. Server-side variables are accessible only in API routes, and client-side NEXT_PUBLIC_ variables are included in the browser bundle for the Adyen Web SDK.

Common use cases

One-Page Checkout for E-Commerce

Build a complete checkout page where customers can see their order summary, choose a payment method (card, PayPal, Apple Pay, Google Pay, BNPL), and complete payment without leaving your site. Adyen Drop-in renders all available payment methods in a unified UI based on the customer's location and device.

V0 Prompt

Create a checkout page with two columns: a left column showing an order summary card with line items, subtotal, tax, and total; and a right column with a 'Payment' heading and a div with id='adyen-dropin-container' where the Adyen Drop-in will be mounted. Add a loading spinner that shows while the payment session is being fetched from /api/adyen/session. Make the page mobile-responsive with the order summary stacking above the payment section on small screens.

Copy this prompt to try it in V0

Subscription Billing with Stored Cards

Tokenize a customer's card during the first payment so future subscription charges can be processed without the customer re-entering their details. Adyen's recurring payment tokens (storedPaymentMethodId) let you charge customers on a schedule from your API route without any frontend interaction.

V0 Prompt

Build a subscription sign-up page with three plan cards (Basic, Pro, Enterprise) showing price, features, and a 'Get Started' button for each. When a plan is selected, show a payment section below the cards with the selected plan highlighted. The payment section should mount an Adyen card component that collects card details. Add a checkbox labeled 'Save card for future billing' checked by default.

Copy this prompt to try it in V0

Marketplace Split Payments

If you are building a marketplace where sellers receive a portion of each payment, Adyen Marketplace (formerly Adyen for Platforms) lets you split payments between your platform account and seller sub-accounts in a single API call. Build the seller payout dashboard and payment flow in V0.

V0 Prompt

Create a marketplace product page showing a product listing with seller info, price, and reviews. Include an 'Add to Cart' button and a checkout panel that shows the item price, platform fee breakdown, and total. Mount an Adyen Drop-in payment form in the checkout panel. After purchase, show an order confirmation with estimated delivery and seller contact details.

Copy this prompt to try it in V0

Troubleshooting

Drop-in container stays empty or shows 'Error occurred. Try again.' immediately on mount

Cause: The session creation API call failed, the client key or environment is incorrect, or the Adyen CSS was not imported so the Drop-in renders invisibly into a zero-height container.

Solution: Check the browser console and Network tab for the /api/adyen/session request. A 401 or 403 usually means the API key is invalid or the merchant account name is wrong. Confirm your NEXT_PUBLIC_ADYEN_CLIENT_KEY matches the client key in your Customer Area → API credentials. Verify the Adyen CSS is imported in globals.css.

TypeError: Cannot read properties of undefined reading 'sessions' or 'PaymentsApi'

Cause: The @adyen/api-library package changed its API surface between versions. The CheckoutAPI method path (checkout.sessions() vs checkout.PaymentsApi.sessions()) depends on the version installed.

Solution: Check your installed version with npm list @adyen/api-library. For v15+, use checkout.PaymentsApi.sessions(). For v14 and below, use checkout.sessions(). Pin to a specific version in package.json to avoid future breaking changes.

typescript
1// For @adyen/api-library v15+:
2const response = await checkout.PaymentsApi.sessions(request);
3
4// For @adyen/api-library v14 and below:
5const response = await checkout.sessions(request);
6// (or checkout.PaymentSessionsApi.sessions() depending on sub-version)

Payment completes in the browser but onPaymentCompleted is never called — user stays on the checkout page

Cause: Some payment methods (like bank redirects or 3DS2 challenges) redirect the user away from your page and return via the returnUrl. The onPaymentCompleted callback only fires for non-redirect flows. Redirect-based flows deliver the result as query parameters on the returnUrl page.

Solution: Read the Adyen result from URL query parameters on your confirmation page. Adyen appends sessionId, sessionResult, and redirectResult to the returnUrl. Pass these to AdyenCheckout.submitDetails() on the confirmation page to finalize the payment and get the resultCode.

typescript
1// app/checkout/confirmation/page.tsx
2'use client';
3import { useSearchParams } from 'next/navigation';
4import { useEffect, useState } from 'react';
5
6export default function ConfirmationPage() {
7 const params = useSearchParams();
8 const resultCode = params.get('resultCode') ?? params.get('sessionResult');
9 // Adyen sets resultCode=Authorised for non-redirect flows
10 // and sessionResult for redirect flows (need to call submitDetails)
11 return (
12 <div>
13 {resultCode === 'Authorised' ? 'Payment successful!' : `Status: ${resultCode}`}
14 </div>
15 );
16}

CORS error when the browser tries to reach checkoutshopper-live.adyen.com or checkoutshopper-test.adyen.com

Cause: Your allowed origin domains in the Adyen Customer Area do not include your Vercel deployment URL. Adyen validates the Origin header of browser requests against a whitelist of allowed domains set in your account.

Solution: In Adyen Customer Area, go to Developers → API credentials → your credential → Allowed origins and add your Vercel domain (e.g., https://your-app.vercel.app and https://your-app.vercel.app for preview deployments). Also add http://localhost:3000 for local development.

Best practices

  • Always create a unique order reference (UUID) before calling the Adyen sessions endpoint — Adyen uses this reference to match webhook notifications to your orders and for reconciliation reports
  • Store the sessionId alongside your order record in the database immediately after creating the session, even before payment completes — this lets you look up payment status later if the user closes the tab mid-payment
  • Set up Adyen webhooks (Customer Area → Developers → Webhooks) to receive authorisation notifications server-side — never trust only the onPaymentCompleted client callback for order fulfillment, as users can close the tab before it fires
  • Use Adyen's test card numbers (4111 1111 1111 1111 for Visa, 5500 0000 0000 0004 for Mastercard) and test scenarios from the Adyen test card documentation to verify your error and decline handling before going live
  • Validate the amount server-side in your API route against your actual order total from the database — never trust the amount sent from the browser, which a user could manipulate
  • Keep your ADYEN_API_KEY strictly server-side with no NEXT_PUBLIC_ prefix — an exposed API key allows anyone to create refunds, pull transaction reports, or create fraudulent sessions on your merchant account
  • Always call dropin.unmount() in the React useEffect cleanup to prevent 'Target already occupied' errors and memory leaks when the component is unmounted or the page re-renders

Alternatives

Frequently asked questions

Do I need a special contract to use Adyen?

Yes. Unlike Stripe, Adyen does not offer instant self-serve sign-up for live processing. You apply for a merchant account, and an Adyen account manager reviews your business before activating live payments. Test accounts at ca-test.adyen.com are available immediately and free to use for development and testing without any approval process.

What is the difference between Adyen Drop-in and Adyen Components?

Drop-in is a ready-made payment form that includes a payment method selector, all available payment forms, and the entire flow in a single mount() call — it is the fastest way to get a full payment UI working. Components are individual UI elements (AdyenCard, AdyenPayPal, AdyenApplePay) that you mount separately and arrange yourself for a fully custom checkout layout. Drop-in is recommended for most use cases.

Why does the Adyen Drop-in load from checkoutshopper-live.adyen.com?

The Adyen Web SDK loads additional resources (payment method icons, locale files, 3DS frames) from Adyen's CDN at checkoutshopper-live.adyen.com (or checkoutshopper-test.adyen.com in test mode). This is expected and necessary — it is not a configuration error. Make sure your Content Security Policy headers allow connections to *.adyen.com if you have CSP rules configured in your Next.js app.

How do I handle the 3DS2 redirect in Next.js?

The Adyen Drop-in handles 3DS2 automatically within the component for most cases using an iframe. For payment methods that require a full-page redirect, Adyen will redirect to the returnUrl you set when creating the session. On the returnUrl page, read the redirectResult query parameter and call AdyenCheckout.submitDetails() to get the final payment result. The Drop-in handles this redirect reconciliation automatically if you mount it on the returnUrl page with the same session data.

Can I use the Adyen Web SDK in V0's preview environment?

V0's preview sandbox runs in an iframe, which can interfere with popup-based payment methods like Apple Pay and 3DS2 challenges. The Drop-in will render correctly in the preview, but redirect-based flows may not complete. Always test the full payment flow in your deployed Vercel app rather than in the V0 editor preview.

What test card numbers can I use with Adyen?

Adyen's primary test cards: Visa 4111 1111 1111 1111, Mastercard 5500 0000 0000 0004, Amex 3714 496353 98431. Use any future expiry date, any 3-digit CVC, and any name. To simulate declines, use specific card numbers from Adyen's documentation at docs.adyen.com/development-resources/testing/test-card-numbers — there are specific numbers for insufficient funds, expired card, and 3DS2 scenarios.

How do I process refunds with Adyen?

Refunds are initiated server-side by calling the Adyen Modifications API with the original payment's pspReference (the Adyen payment ID you receive in the webhook notification after authorization). In your Next.js API route, use checkout.ModificationsApi.refundCapturedPayment() from @adyen/api-library with the pspReference, amount, and merchant account. Adyen processes refunds asynchronously and sends a webhook notification when the refund is completed.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.