# Adyen

- Tool: Bubble
- Difficulty: Advanced
- Time required: 3–4 hours
- Last updated: July 2026

## TL;DR

Connect Bubble to Adyen using the API Connector with a Private X-API-Key header targeting Adyen's Sessions endpoint. A single POST to /v71/sessions returns a sessionId and sessionData that Bubble stores in custom states, then redirects users to Adyen's hosted Drop-in — no raw card data ever touches Bubble. HMAC-verified inbound webhooks require a paid Bubble plan and a Backend Workflow.

## Adyen + Bubble: Enterprise Payments via the Sessions Flow

Adyen is not a self-serve payment gateway. Before writing a single API call, confirm with your Adyen account manager that your merchant account has API access enabled and that you have received your API Key, Merchant Account code, and Client Key. Volume minimums apply — Adyen is optimised for businesses processing significant payment volumes.

Once you have credentials, the integration pattern is elegant: a single POST to Adyen's /v71/sessions endpoint creates a payment session server-side (safely on Bubble's servers via the API Connector), and Adyen's own hosted Drop-in UI handles all card data entry. Bubble stores the returned sessionId and sessionData in custom page states and redirects or embeds the Drop-in. After payment, Adyen sends an AUTHORISATION webhook to your Bubble Backend Workflow endpoint, where you update the order record.

Two key Bubble-specific details to know before you start: the live API base URL is NOT a generic Adyen domain — it uses a merchant-specific subdomain prefix assigned by Adyen (e.g., your-prefix-checkout-live.adyenpayments.com). And Backend Workflows, which are required for inbound webhooks, are only available on Bubble's paid plans (Starter and above).

## Before you start

- An active Adyen merchant account with API access enabled — contact your Adyen account manager to confirm
- Your Adyen API Key, Merchant Account code, and Client Key (from the Adyen Customer Area → Developers → API credentials)
- A Bubble app on a paid plan (Starter or above) if you need inbound webhook receipt via Backend Workflows
- A test order workflow already built in Bubble (or a plan for one), including a Bubble database type for Orders with fields: amount (number), currency (text), status (text), adyen_session_id (text)
- Familiarity with Bubble's API Connector plugin and workflow editor

## Step-by-step guide

### 1. Step 1: Confirm Adyen Access and Gather Credentials

Before opening Bubble, log into the Adyen Customer Area (ca-test.adyen.com for test, ca-live.adyen.com for live). Navigate to Developers → API credentials. You need three values: the API Key (a long string starting with AQE...), the Merchant Account code (a short identifier assigned to your business), and the Client Key (used for the Drop-in UI if you embed it via an HTML element).

Important: also identify your live base URL prefix. In the Customer Area, go to Account → Merchant accounts → your account → and look for the 'Live endpoint URL prefix' field. This prefix forms the subdomain of your live checkout URL. Write it down now — many Bubble builders discover this only when they try to go live and get 401 errors. Store it separately from the test URL.

If any of these values are missing or API access is not yet enabled, contact your Adyen account manager before proceeding. Adyen does not offer self-serve API key generation for all regions and account tiers.

```
{
  "test_base_url": "https://checkout-test.adyen.com/v71",
  "live_base_url": "https://{YOUR_PREFIX}-checkout-live.adyenpayments.com/checkout/v71",
  "api_key": "AQEy...your_api_key...",
  "merchant_account": "YourMerchantAccountCODE",
  "client_key": "live_XXXX...or test_XXXX..."
}
```

**Expected result:** You have your API Key, Merchant Account code, Client Key, and your live URL prefix written down and ready to enter into Bubble.

### 2. Step 2: Install and Configure the API Connector

Open your Bubble app and go to the Plugins tab. Click 'Add plugins', search for 'API Connector' (the official plugin by Bubble), and install it. Then click 'API Connector' in the plugin list to open its settings panel.

Click 'Add another API' and name it 'Adyen Checkout'. In the 'Shared headers' section, add a new header: Key = `X-API-Key`, Value = your API Key. Critically, check the 'Private' checkbox next to this header. This ensures the key is injected server-side by Bubble's infrastructure and never sent to the browser. Add a second shared header: Key = `Content-Type`, Value = `application/json`.

In the base URL field, enter the Adyen test base URL: `https://checkout-test.adyen.com/v71`. You will switch this to the live URL when going to production.

Do not add the API Key as a URL parameter or as an Action-level header — the Private shared header is the correct and secure location.

```
{
  "api_name": "Adyen Checkout",
  "base_url": "https://checkout-test.adyen.com/v71",
  "shared_headers": {
    "X-API-Key": "<private: your_api_key>",
    "Content-Type": "application/json"
  }
}
```

**Expected result:** The API Connector shows 'Adyen Checkout' with a lock icon on the X-API-Key header, confirming it is marked Private and will not be exposed to the browser.

### 3. Step 3: Add and Initialize the Sessions Call

Inside the 'Adyen Checkout' API group in the API Connector, click 'Add a call'. Name it 'Create Session'. Set the method to POST and the endpoint path to `/sessions` (which appends to your base URL to form the full Checkout API sessions endpoint).

In the body section, select JSON and enter the following structure. Mark `merchantAccount` and `amount.value` as dynamic so Bubble can inject them per transaction:

```json
{
  "merchantAccount": "<YourMerchantAccountCODE>",
  "amount": {
    "value": 1999,
    "currency": "EUR"
  },
  "reference": "ORDER-12345",
  "returnUrl": "https://yourdomain.com/payment-result"
}
```

For `amount.value`, enter a real integer like 1999. Remember: Adyen amounts are always integers in the smallest currency unit — €19.99 becomes 1999, £19.99 becomes 1999, ¥1999 stays 1999 (JPY has no minor unit). KWD uses ×1000.

Click 'Initialize call'. Bubble will execute the call with your test values and detect the response shape. The successful response includes `id` (the sessionId), `sessionData`, and `amount`. Bubble auto-maps these fields. If initialization fails with 'There was an issue setting up your call', verify the API Key header (Private checkbox) and that your test merchant account is active.

After successful initialization, set 'Use as' to 'Action' since this call creates a session (not a data fetch).

```
{
  "method": "POST",
  "endpoint": "/sessions",
  "body": {
    "merchantAccount": "<dynamic: merchant_account>",
    "amount": {
      "value": "<dynamic: amount_in_minor_units>",
      "currency": "<dynamic: currency_code>"
    },
    "reference": "<dynamic: order_reference>",
    "returnUrl": "<dynamic: return_url>",
    "countryCode": "<dynamic: country_code>"
  }
}
```

**Expected result:** Bubble shows 'Create Session' call with a green 'Initialized' status and detected fields including 'id' (sessionId), 'sessionData', 'amount', and 'reference'.

### 4. Step 4: Build the Checkout Page Workflow

On your Bubble checkout page, create the elements needed to trigger and display the Adyen checkout. You will need: a 'Pay Now' button, two custom states on the page (type: text) named `adyen_session_id` and `adyen_session_data`, and an HTML element for the Drop-in or a redirect mechanism.

Set up the 'Pay Now' button's workflow:
1. Add a step: 'Plugins → Adyen Checkout - Create Session'. Fill in the dynamic fields: `merchant_account` = your merchant account code (can be a Bubble option set or text constant), `amount_in_minor_units` = Current product's price × 100 (use the formula editor: `round down(Current product's price * 100)`), `currency_code` = 'EUR' (or your currency), `order_reference` = a unique ID (use the order's unique ID from your database), `return_url` = your payment-result page URL.

2. Add a step: 'Set state of' the page's `adyen_session_id` to `Result of step 1's id`.

3. Add a step: 'Set state of' the page's `adyen_session_data` to `Result of step 1's sessionData`.

4. Add a step: 'Go to page' or 'Open an external website' pointing to Adyen's hosted checkout page. The redirect URL format is: `https://checkoutshopper-test.adyen.com/checkoutshopper/securedfields/{sessionId}?sessionData={sessionData}&environment=test`. Replace `{sessionId}` and `{sessionData}` with your custom states using Bubble's dynamic text editor.

For production, switch to `checkoutshopper-live.adyen.com` and pass the live `environment=live` parameter.

When Adyen completes the payment, it redirects back to your `returnUrl` — use URL parameters on that page to detect the `sessionResult` query parameter and trigger a status-check workflow.

```
// Bubble workflow step: amount conversion formula
// In the formula editor for 'amount_in_minor_units':
// round down(Current product's price * 100)

// Redirect URL for Adyen hosted checkout (test)
// Construct in Bubble's dynamic text editor:
// https://checkoutshopper-test.adyen.com/checkoutshopper/securedfields/
// + Page's adyen_session_id
// + ?sessionData=
// + Page's adyen_session_data
// + &environment=test
```

**Expected result:** Clicking 'Pay Now' creates an Adyen session, stores the sessionId and sessionData in page states, and redirects the user to the Adyen-hosted payment page. After payment, the user returns to your returnUrl.

### 5. Step 5: Set Up a Backend Workflow for AUTHORISATION Webhooks

When Adyen processes a payment, it sends an AUTHORISATION notification to a webhook URL you register. This step is only possible on paid Bubble plans (Starter+) — if you are on the Free plan, skip this step and instead poll the Adyen sessions endpoint on your returnUrl page.

To set up the Backend Workflow:
1. In your Bubble app, go to Settings → API. Check 'This app exposes a Workflow API' and save.
2. In the Bubble editor, switch to the Backend Workflows view (the icon looks like a server). Click 'New API workflow', name it `adyen_notification`.
3. Check 'Detect request data' and click the 'Detect' button. Leave it running.
4. In Adyen's Customer Area, go to Developers → Webhooks → Standard webhook → Add new endpoint. Enter your Bubble endpoint URL: `https://yourtestapp.bubbleapps.io/api/1.1/wf/adyen_notification`.
5. Send a test webhook from Adyen. Bubble will detect the structure: `notificationItems[0].NotificationRequestItem` contains `pspReference`, `merchantReference`, `amount.value`, `eventCode`, `success`, and `additionalData.hmacSignature`.
6. After detection, click 'Save' in Bubble's Detect step to lock in the schema.

In the workflow steps, add logic to:
- Look up the Order in Bubble DB where `reference = notificationItems first item's merchantReference`
- If `eventCode = AUTHORISATION` and `success = true`, change the Order status to 'paid'
- Return HTTP 200 immediately (Bubble does this by default) with body `[accepted]` — Adyen requires this exact response within 10 seconds

IMPORTANT: Adyen's webhook payload includes an HMAC signature in `additionalData.hmacSignature`. Full HMAC verification using CryptoJS (concatenating the 8 required fields in exact order) is complex in Bubble but is the correct security practice. At minimum, verify the `merchantAccountCode` matches your account before updating order status.

RapidDev's team has implemented Adyen webhook flows in dozens of Bubble apps — if the HMAC verification setup is blocking your launch, reach out for a free scoping call at rapidevelopers.com/contact.

```
// Adyen AUTHORISATION webhook payload structure (simplified)
{
  "live": "false",
  "notificationItems": [
    {
      "NotificationRequestItem": {
        "additionalData": {
          "hmacSignature": "HMAC_VALUE_HERE"
        },
        "amount": {
          "currency": "EUR",
          "value": 1999
        },
        "eventCode": "AUTHORISATION",
        "eventDate": "2026-01-15T12:00:00+00:00",
        "merchantAccountCode": "YourMerchantAccountCODE",
        "merchantReference": "ORDER-12345",
        "paymentMethod": "visa",
        "pspReference": "ABC123DEF456",
        "reason": "",
        "success": "true"
      }
    }
  ]
}

// HMAC signing string field order (concatenate with colons):
// pspReference:originalReference:merchantAccountCode:merchantReference:amount.value:amount.currency:eventCode:success
// Empty fields use empty string, NOT null
```

**Expected result:** Adyen sends test AUTHORISATION webhooks to your Bubble Backend Workflow, the workflow detects the notificationItems structure, and you can add workflow steps to update order status in your Bubble database.

### 6. Step 6: Switch to Live Credentials and Go to Production

When you are ready to accept real payments, you must update three things in Bubble:

1. The API Connector base URL: Open Plugins → API Connector → Adyen Checkout. Change the base URL from `https://checkout-test.adyen.com/v71` to your live URL: `https://{YOUR_PREFIX}-checkout-live.adyenpayments.com/checkout/v71`. Replace `{YOUR_PREFIX}` with the merchant-specific subdomain prefix from your Adyen Customer Area. This is NOT a generic Adyen URL — every merchant gets a unique prefix.

2. The X-API-Key header: Click the Private X-API-Key header row and replace the test API key value with your live API key. The live key is a different credential generated in ca-live.adyen.com. After changing the key, re-initialize the Create Session call with a small live test amount (you can void or refund it immediately).

3. The checkout redirect URL: In your Bubble workflow, update the redirect URL from `checkoutshopper-test.adyen.com` to `checkoutshopper-live.adyen.com` and change the `environment` parameter to `live`.

4. The Backend Workflow webhook URL: Update the registered endpoint in Adyen's Customer Area (live) to point to your production Bubble app URL if different from the test URL.

Verify that the live merchant account has your preferred payment methods (Visa, Mastercard, iDEAL, etc.) enabled in the Customer Area → Account → Payment methods. Payment methods must be explicitly enabled — they are not all on by default.

```
{
  "test_config": {
    "base_url": "https://checkout-test.adyen.com/v71",
    "api_key": "test_XXXXXXX...",
    "checkout_redirect": "https://checkoutshopper-test.adyen.com/..."
  },
  "live_config": {
    "base_url": "https://{YOUR_MERCHANT_PREFIX}-checkout-live.adyenpayments.com/checkout/v71",
    "api_key": "live_XXXXXXX...",
    "checkout_redirect": "https://checkoutshopper-live.adyen.com/..."
  }
}
```

**Expected result:** Your Bubble app processes real payments through Adyen using live credentials, the live checkout redirect, and receiving AUTHORISATION webhooks at your production Backend Workflow URL.

## Best practices

- Always store the Adyen merchantReference (your Bubble Order unique ID) in the session creation call and in your Bubble database Order record — this is the only reliable link between an Adyen payment notification and a Bubble database entry.
- Mark both X-API-Key and merchantAccount as Private in the API Connector to prevent them from appearing in Bubble's client-side data. Never use these values in client-accessible expressions or JavaScript.
- Store separate API Connector configurations (or at minimum separate base URL and API key values) for test and live environments to prevent accidental cross-environment API calls.
- Set up Bubble's Data tab → Privacy rules for your Orders type so that customers can only view their own orders — Bubble's default is all data readable, which is a security risk for financial records.
- Use your Bubble database's unique ID as the Adyen `reference` field to ensure idempotency. If a webhook is delivered twice (Adyen retries), use Bubble's 'Only when Order's status is pending' condition to prevent double-processing.
- Monitor Workload Unit (WU) consumption in Bubble's Logs tab. Each API Connector call to Adyen consumes WU — avoid polling for payment status on a schedule; use webhooks (Backend Workflows) instead to minimize WU burn.
- Before going live, complete Adyen's sandbox test plan — test card numbers, 3DS authentication flows, and refund scenarios. Adyen's Customer Area provides test card numbers for simulating various decline reasons.
- When Adyen updates its API version (the /v71 segment), update your API Connector base URL promptly. A mismatched version returns 404 which looks like an auth error — verify the current version in Adyen's developer docs annually.

## Use cases

### B2B SaaS Subscription Payments

Enterprise software companies using Bubble to manage their customer portal can use Adyen to process multi-currency subscription payments with interchange-plus pricing and local payment method support across EU, APAC, and North America.

Prompt example:

```
Build a Bubble workflow that calls POST /v71/sessions with the customer's order amount, currency, and merchantAccount, stores the sessionId in a custom state, and redirects the user to the Adyen hosted checkout page.
```

### E-Commerce Order Processing

Online stores with high average order values can use Adyen's Sessions flow via Bubble to accept card payments without handling raw card data, receive AUTHORISATION webhooks to update order status, and trigger fulfillment workflows.

Prompt example:

```
When a user clicks Checkout, trigger a Bubble workflow that creates an Adyen session, stores the sessionData, opens the Adyen Drop-in in an HTML element, and listens for the AUTHORISATION webhook to mark the order as paid.
```

### Platform Payment Routing

Marketplaces and platform businesses that have negotiated Adyen Marketplace access can use Bubble to orchestrate split payments between platform and sellers, routing funds via Adyen's Transfer API.

Prompt example:

```
Build a Bubble Backend Workflow that receives an Adyen AUTHORISATION notification, looks up the corresponding order in the Bubble database, and triggers a transfer split between the platform and seller accounts.
```

## Troubleshooting

### API Connector Initialize call returns 'There was an issue setting up your call' or a 401 Unauthorized error

Cause: The X-API-Key header value is incorrect, the Private checkbox was not checked (so the key was truncated or not sent), or the test API key is being used against a wrong merchant account code.

Solution: Open Plugins → API Connector → Adyen Checkout. Click the X-API-Key header row and verify the Private checkbox is checked and the value exactly matches your API key from the Adyen Customer Area (no leading/trailing spaces). Also verify the `merchantAccount` field in the JSON body matches your Merchant Account code exactly (case-sensitive). Re-enter the key if unsure.

### Create Session call succeeds in Bubble but the Adyen Drop-in page shows 'Invalid session' or a generic error

Cause: The returnUrl parameter in the session creation body is either missing, not a valid HTTPS URL, or is a Bubble preview URL (e.g., version-test.bubbleapps.io) that Adyen hasn't allowed. Adyen validates the returnUrl domain.

Solution: Ensure the returnUrl is a fully qualified HTTPS URL on a domain you have registered with Adyen. For testing, use your live Bubble app URL (yourtestapp.bubbleapps.io) rather than the version-test preview URL. In the Adyen Customer Area, confirm the domain is in the allowed origins list under Developers → API credentials → Client settings.

### Live payments fail with 401 Unauthorized after test payments worked fine

Cause: The test base URL (checkout-test.adyen.com) was not updated to the live merchant-specific URL. The live URL requires the merchant-specific subdomain prefix (e.g., your-prefix-checkout-live.adyenpayments.com) — not a generic adyen.com URL.

Solution: Open the API Connector and update the Adyen Checkout base URL to your specific live URL: `https://{YOUR_PREFIX}-checkout-live.adyenpayments.com/checkout/v71`. Find your prefix in the Adyen Customer Area (live) → Account → Merchant accounts → Live endpoint URL prefix. Also confirm you are using the live API key (from ca-live.adyen.com), not the test key.

### Backend Workflow does not receive Adyen AUTHORISATION webhooks, or Adyen Customer Area shows 'Not reachable' for the endpoint

Cause: Either the Backend Workflow API is not enabled in Bubble Settings, the app is on the Free plan (which does not support Backend Workflows / API Workflows), or the webhook URL registered in Adyen includes '/initialize' which is only for Bubble's detection step, not the live endpoint.

Solution: Go to Settings → API and confirm 'This app exposes a Workflow API' is checked. Verify your Bubble plan is Starter or above. Ensure the URL registered with Adyen is in the format `https://yourapp.bubbleapps.io/api/1.1/wf/adyen_notification` without any '/initialize' suffix. Adyen also requires your endpoint to respond within 10 seconds — check Bubble's Logs tab for slow-running workflow steps.

### Payment amounts appear incorrect (e.g., a €19.99 purchase is charged as €0.20 or €1999.00)

Cause: Bubble is passing a decimal value (19.99) directly to Adyen's amount.value field instead of converting to the minor currency unit integer (1999). Alternatively, the multiplication was applied twice.

Solution: In your Bubble workflow's Create Session step, ensure the amount_in_minor_units value is calculated as `round down(Current product's price * 100)`. Verify the result in Bubble's debugger before sending to Adyen. For JPY, no multiplication is needed. For KWD (Kuwaiti Dinar), multiply by 1000.

## Frequently asked questions

### Does Adyen have a Bubble plugin in the Bubble marketplace?

As of early 2026, there is no official or widely-maintained Adyen plugin in the Bubble Plugin Marketplace. The recommended approach is the API Connector with a Private X-API-Key header, as described in this tutorial. If a community plugin appears, verify it is actively maintained and review its security model (does it store the API key privately?) before using it.

### Can I use Adyen with a Free plan Bubble app?

You can use the API Connector (create sessions, initiate payments) on the Free plan, but you cannot receive AUTHORISATION webhooks server-side — Backend Workflows are a paid plan feature. On the Free plan, you would need to poll the Adyen sessions endpoint on your returnUrl page to check payment status, which is less reliable and consumes more WU. Upgrade to Starter for webhook support.

### Why does the API Connector show 'There was an issue setting up your call' when I initialize?

This error means the initialize call did not return a successful 200 response. Common causes: the X-API-Key is wrong or missing the Private checkbox; the merchantAccount code has a typo; or the test base URL is malformed. Open Bubble's Logs tab immediately after the failed initialize to see the raw HTTP response code and error message from Adyen.

### How do I handle currency amounts correctly when passing them to Adyen?

Adyen requires amounts as integers in the smallest currency unit. For EUR, GBP, USD: multiply by 100 and round down (€19.99 → 1999). For JPY: no multiplication needed (¥500 → 500). For KWD (Kuwaiti Dinar): multiply by 1000. In Bubble's formula editor, use `round down(Current product's price * 100)` for standard currencies.

### What is the live API base URL for Adyen — is it a standard adyen.com domain?

No. Adyen's live checkout base URL is merchant-specific: `https://{YOUR_PREFIX}-checkout-live.adyenpayments.com/checkout/v71`. The prefix is unique to your merchant account and is assigned when your account goes live. Find it in the Adyen Customer Area (live) → Account → Merchant accounts → Live endpoint URL prefix. Using `https://checkout-live.adyen.com` (a common guess) will not work.

### How should I verify Adyen AUTHORISATION webhooks in Bubble?

Full HMAC verification concatenates 8 fields in a specific order (pspReference, originalReference, merchantAccountCode, merchantReference, amount.value, amount.currency, eventCode, success — empty fields use empty string, not null) and computes an HMAC-SHA256 using your webhook HMAC key. In Bubble, this requires a CryptoJS-based Custom Action via the Toolbox plugin. At minimum, verify that the `merchantAccountCode` in the webhook matches your account before updating any order status.

---

Source: https://www.rapidevelopers.com/bubble-integrations/adyen
© RapidDev — https://www.rapidevelopers.com/bubble-integrations/adyen
