Adyen
Accept payments with Adyen. Use this skill when the project needs to integrate
You are a payments specialist who integrates Adyen into projects. Adyen is an enterprise-grade payment platform used by large-scale merchants. It supports online, in-app, and in-person payments with a single integration. ## Key Points - Test dashboard: `ca-test.adyen.com` - Test card: `4111 1111 1111 1111`, expiry any future date, CVC `737` - 3D Secure test card: `5212 3456 7890 1234` - Use the `TEST` environment in the API client - Always verify HMAC signatures on webhooks before processing - Use `shopperReference` consistently to link Adyen shoppers to your users - Return `[accepted]` from webhook endpoints — Adyen retries on other responses - Use Sessions integration (not Advanced flow) for new projects — simpler and PCI-compliant - Store `pspReference` from every payment for refunds and reconciliation - Use Adyen's test cards with specific amounts to trigger error scenarios - Enable 3D Secure for all card payments to shift liability - Granting access based on the redirect `resultCode` alone without webhook confirmation ## Quick Example ```bash npm install @adyen/api-library ```
skilldb get payment-services-skills/AdyenFull skill: 246 linesAdyen Payment Integration
You are a payments specialist who integrates Adyen into projects. Adyen is an enterprise-grade payment platform used by large-scale merchants. It supports online, in-app, and in-person payments with a single integration.
Core Philosophy
Single platform for all channels
Adyen unifies online payments, in-store POS, and mobile into one platform. A single API processes card payments, wallets, bank transfers, and local payment methods across 200+ countries.
Payment methods are market-specific
Adyen lets you enable local payment methods (iDEAL in Netherlands, Bancontact in Belgium, PIX in Brazil) from the dashboard. The Drop-in component renders the right methods automatically based on the shopper's country.
Webhooks are authoritative
Adyen uses asynchronous notifications for payment results. Never rely solely on the API response — always confirm final payment status via webhooks.
Setup
Install
npm install @adyen/api-library
Initialize
import { Client, Config, CheckoutAPI } from '@adyen/api-library';
const config = new Config();
config.apiKey = process.env.ADYEN_API_KEY;
config.merchantAccount = process.env.ADYEN_MERCHANT_ACCOUNT;
const client = new Client({ config });
client.setEnvironment(
process.env.NODE_ENV === 'production' ? 'LIVE' : 'TEST',
process.env.ADYEN_LIVE_URL_PREFIX // required for live only
);
const checkout = new CheckoutAPI(client);
Key Techniques
Create a checkout session (Drop-in / Components)
const session = await checkout.PaymentsApi.sessions({
amount: { currency: 'EUR', value: 2999 }, // amount in minor units
countryCode: 'NL',
merchantAccount: process.env.ADYEN_MERCHANT_ACCOUNT,
reference: `order-${orderId}`,
returnUrl: `https://yourdomain.com/checkout/result?ref=${orderId}`,
shopperReference: userId,
shopperEmail: 'shopper@example.com',
lineItems: [
{
id: 'item-1',
description: 'Pro Plan',
quantity: 1,
amountIncludingTax: 2999,
},
],
});
// Return session.id and session.sessionData to the frontend
Frontend Drop-in component
<script src="https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/5.64.0/adyenWeb.js"></script>
<link rel="stylesheet" href="https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/5.64.0/adyenWeb.css" />
<div id="dropin-container"></div>
<script>
const { AdyenCheckout, Dropin } = window.AdyenWeb;
const checkout = await AdyenCheckout({
environment: 'test',
clientKey: 'test_xxx',
session: {
id: sessionId, // from server
sessionData: sessionData, // from server
},
onPaymentCompleted: (result) => {
// result.resultCode: 'Authorised', 'Refused', 'Pending'
window.location.href = `/checkout/result?resultCode=${result.resultCode}`;
},
onError: (error) => console.error(error),
});
const dropin = new Dropin(checkout, {
paymentMethodsConfiguration: {
card: { hasHolderName: true, holderNameRequired: true },
},
});
dropin.mount('#dropin-container');
</script>
Recurring / subscription payments (tokenization)
// First payment: store the token
const session = await checkout.PaymentsApi.sessions({
amount: { currency: 'EUR', value: 999 },
merchantAccount: process.env.ADYEN_MERCHANT_ACCOUNT,
reference: `sub-initial-${userId}`,
returnUrl: 'https://yourdomain.com/checkout/result',
shopperReference: userId,
shopperInteraction: 'Ecommerce',
recurringProcessingModel: 'Subscription',
storePaymentMethod: true,
});
// Subsequent payments: charge the stored token
const result = await checkout.PaymentsApi.payments({
amount: { currency: 'EUR', value: 999 },
merchantAccount: process.env.ADYEN_MERCHANT_ACCOUNT,
reference: `sub-renewal-${Date.now()}`,
shopperReference: userId,
shopperInteraction: 'ContAuth',
recurringProcessingModel: 'Subscription',
paymentMethod: {
type: 'scheme',
storedPaymentMethodId: storedTokenId, // from RECURRING_CONTRACT webhook
},
});
Refunds and captures
import { ModificationsAPI } from '@adyen/api-library';
const modifications = new ModificationsAPI(client);
// Capture (if using manual capture)
await modifications.captureAuthorisedPayment(pspReference, {
amount: { currency: 'EUR', value: 2999 },
merchantAccount: process.env.ADYEN_MERCHANT_ACCOUNT,
reference: `capture-${orderId}`,
});
// Refund
await modifications.refundCapturedPayment(pspReference, {
amount: { currency: 'EUR', value: 2999 },
merchantAccount: process.env.ADYEN_MERCHANT_ACCOUNT,
reference: `refund-${orderId}`,
});
Webhook Processing
| Event | Action |
|---|---|
AUTHORISATION (success) | Grant access / fulfill order |
AUTHORISATION (failure) | Mark order failed |
CAPTURE | Confirm funds captured |
CANCELLATION | Mark order cancelled |
REFUND | Process refund in your system |
RECURRING_CONTRACT | Store token reference for future charges |
CHARGEBACK | Handle dispute |
import { hmacValidator } from '@adyen/api-library';
export async function POST(req: Request) {
const body = await req.json();
const validator = new hmacValidator();
for (const item of body.notificationItems) {
const notification = item.NotificationRequestItem;
// Verify HMAC signature
if (!validator.validateHMAC(notification, process.env.ADYEN_HMAC_KEY)) {
return new Response('Invalid HMAC', { status: 401 });
}
switch (notification.eventCode) {
case 'AUTHORISATION':
if (notification.success === 'true') {
await fulfillOrder(notification.merchantReference, notification.pspReference);
} else {
await markOrderFailed(notification.merchantReference, notification.reason);
}
break;
case 'REFUND':
if (notification.success === 'true') {
await processRefund(notification.originalReference);
}
break;
case 'RECURRING_CONTRACT':
await storePaymentToken(
notification.additionalData?.['recurring.shopperReference'],
notification.additionalData?.['recurring.recurringDetailReference']
);
break;
}
}
// Adyen requires this exact response
return new Response('[accepted]', { status: 200 });
}
Testing
Adyen provides a full test environment:
- Test dashboard:
ca-test.adyen.com - Test card:
4111 1111 1111 1111, expiry any future date, CVC737 - 3D Secure test card:
5212 3456 7890 1234 - Use the
TESTenvironment in the API client
Best Practices
- Always verify HMAC signatures on webhooks before processing
- Use
shopperReferenceconsistently to link Adyen shoppers to your users - Return
[accepted]from webhook endpoints — Adyen retries on other responses - Use Sessions integration (not Advanced flow) for new projects — simpler and PCI-compliant
- Store
pspReferencefrom every payment for refunds and reconciliation - Use Adyen's test cards with specific amounts to trigger error scenarios
- Enable 3D Secure for all card payments to shift liability
Anti-Patterns
- Granting access based on the redirect
resultCodealone without webhook confirmation - Not returning
[accepted]in webhook responses — causes Adyen to retry endlessly - Hardcoding payment methods instead of letting Drop-in render them dynamically
- Skipping HMAC validation on webhooks
- Using the Advanced flow when Sessions would suffice — more code, more PCI scope
- Not handling the
RECURRING_CONTRACTwebhook — you lose the token reference - Storing full card details instead of using Adyen tokenization
Install this skill directly: skilldb add payment-services-skills
Related Skills
Braintree
Accept payments with Braintree (PayPal). Use this skill when the project needs
Checkout Com
Accept payments with Checkout.com. Use this skill when the project needs to
Coinbase Commerce
Accept cryptocurrency payments with Coinbase Commerce. Use this skill when the
Creem
Accept payments with Creem as merchant of record. Use this skill when the project
Klarna
Accept payments with Klarna. Use this skill when the project needs to integrate
Lemonsqueezy
Accept payments with Lemon Squeezy as merchant of record. Use this skill when