Apple Pay / Google Pay

Apple Pay and Google Pay digital wallet integration through Adyen's Drop-in component providing secure, convenient mobile payment options with tokenization for Vrio API processing.

Overview

This guide explains how to implement Apple Pay and Google Pay using Adyen's Web Drop-in component and process payments through Vrio's API. The Adyen Drop-in renders Apple Pay and Google Pay buttons automatically when available on the customer's device and browser.

Integration Approach:

Adyen's Drop-in component handles wallet detection, button rendering, and token creation. On submit, it returns a wallet-specific token (applePayToken or googlePayToken) that you pass to Vrio's API as the payment_token.

Supported Payment Methods & Prerequisites

Apple Pay

Supported Devices: iPhone, iPad, Mac with Touch ID/Face ID, Apple Watch

Setup Requirements:

  1. Apple Merchant ID: Configure in your Adyen Customer Area under Settings > Apple Pay
  2. Domain Verification: Register and verify your domain with Apple through Adyen
  3. SSL Certificate: Must use HTTPS in production (localhost works for testing)
  4. Adyen Configuration: Enable Apple Pay in your Adyen Customer Area

Google Pay

Supported Devices: Android devices, Chrome browser (desktop and mobile)

Setup Requirements:

  1. Google Pay Configuration: Enable Google Pay in your Adyen Customer Area
  2. Merchant ID: Your Google Pay merchant ID (configured in Adyen)
  3. SSL Certificate: Must use HTTPS in production (localhost works for testing)

Adyen Customer Area Setup

Before implementing the frontend, configure the following in your Adyen Customer Area:

  1. Navigate to Settings > Payment Methods
  2. Enable Apple Pay and/or Google Pay
  3. For Apple Pay, complete the domain verification process
  4. Navigate to Developers > API credentials
  5. Under Client settings, add your website origin to Allowed origins (e.g., https://yourdomain.com)
  6. Copy your Client Key (format: test_... or live_...)

Implementation Example

The Adyen Drop-in uses a paymentMethodsResponse JSON object to determine which payment methods to render. You can obtain this from Adyen's POST /paymentMethods API endpoint, or construct it manually as shown below.

You will need the following values from the Adyen Customer Area:

ValueWhere to Find
Client KeyDevelopers > API credentials > Client Key
Google Pay Merchant IDSettings > Payment Methods > Google Pay
Adyen Merchant AccountAccount > Merchant accounts
Apple Merchant IDSettings > Payment Methods > Apple Pay
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Adyen — Drop-in</title>
  <link rel="stylesheet"
    href="https://checkoutshopper-test.cdn.adyen.com/checkoutshopper/sdk/6.31.1/adyen.css"
    integrity="sha384-Kxk+HDAUmRogabIUxszUgK7HjuEtcwCqxw8ecoDrF8WxjrfcVlVaRSImp//bEp8W"
    crossorigin="anonymous">
  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
    }
    .form-group {
      margin-bottom: 20px;
    }
    label {
      display: block;
      margin-bottom: 5px;
      font-weight: bold;
    }
    input, select, textarea {
      width: 100%;
      padding: 10px;
      border: 1px solid #ccc;
      border-radius: 4px;
      box-sizing: border-box;
      font-family: inherit;
    }
    textarea {
      font-family: monospace;
      font-size: 0.8rem;
      resize: vertical;
    }
    button {
      background-color: #007cba;
      color: white;
      padding: 12px 20px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 16px;
      margin-bottom: 10px;
    }
    button:disabled {
      background-color: #ccc;
      cursor: not-allowed;
    }
    .error {
      color: #e74c3c;
      margin-top: 5px;
    }
    .success {
      color: #27ae60;
      margin-top: 5px;
    }
    .info {
      background-color: #e8f4fd;
      border: 1px solid #bee5eb;
      border-radius: 4px;
      padding: 10px;
      margin-bottom: 20px;
    }
    .section {
      border: 1px solid #ddd;
      padding: 20px;
      margin-bottom: 20px;
      border-radius: 4px;
    }
    .section h3 {
      margin-top: 0;
    }
  </style>
</head>
<body>
  <h1>Adyen Drop-in Integration</h1>

  <div class="info">
    <strong>Testing Requirements:</strong>
    <ul>
      <li><strong>Apple Pay:</strong> Safari on iOS/macOS with cards in Apple Wallet</li>
      <li><strong>Google Pay:</strong> Chrome browser with Google Pay configured</li>
      <li><strong>Both:</strong> HTTPS required (localhost works for testing)</li>
    </ul>
  </div>

  <div class="section">
    <h3>Configuration</h3>

    <div class="form-group">
      <label for="client-key">Adyen Client Key:</label>
      <input type="text" id="client-key" placeholder="test_..." required>
    </div>

    <div class="form-group">
      <label for="amount">Amount (in minor units):</label>
      <input type="number" id="amount" value="100" min="1" required>
    </div>

    <div class="form-group">
      <label for="currency">Currency:</label>
      <select id="currency">
        <option value="USD">USD</option>
        <option value="EUR">EUR</option>
        <option value="GBP">GBP</option>
        <option value="CAD">CAD</option>
      </select>
    </div>

    <div class="form-group">
      <label for="country-code">Country Code:</label>
      <select id="country-code">
        <option value="US">US</option>
        <option value="GB">GB</option>
        <option value="CA">CA</option>
        <option value="DE">DE</option>
        <option value="FR">FR</option>
        <option value="NL">NL</option>
      </select>
    </div>

    <div class="form-group">
      <label for="payment-methods-json">Payment Methods Response JSON:</label>
      <textarea id="payment-methods-json" rows="10">
{
  "paymentMethods": [
    {
      "configuration": {
        "merchantId": "YOUR_GOOGLE_PAY_MERCHANT_ID",
        "gatewayMerchantId": "YOUR_ADYEN_MERCHANT_ACCOUNT"
      },
      "name": "Google Pay",
      "type": "googlepay"
    },
    {
      "brands": ["amex", "discover", "mc", "visa"],
      "configuration": {
        "merchantId": "YOUR_APPLE_MERCHANT_ID",
        "merchantName": "YOUR_ADYEN_MERCHANT_ACCOUNT"
      },
      "name": "Apple Pay",
      "type": "applepay"
    }
  ]
}
      </textarea>
    </div>

    <button onclick="initializeDropin()">Initialize Drop-in</button>
    <div id="init-status"></div>
  </div>

  <div class="section">
    <h3>Drop-in</h3>
    <div id="card-container"></div>
    <div id="dropin-status"></div>
  </div>

  <div class="section">
    <h3>Payment Results</h3>
    <div id="payment-result"></div>
  </div>

  <script src="https://pay.google.com/gp/p/js/pay.js"></script>
  <script src="https://checkoutshopper-test.cdn.adyen.com/checkoutshopper/sdk/6.31.1/adyen.js"
    integrity="sha384-fIc53mLoRD6ppwKm3QRjk2UjXFJ8D9dp8Fw/4nqIFBxCXxmbwS7MDSePtTiWXAzt"
    crossorigin="anonymous"></script>
  <script>
    async function initializeDropin() {
      const clientKey = document.getElementById('client-key').value.trim();
      const amount = parseInt(document.getElementById('amount').value, 10);
      const currency = document.getElementById('currency').value;
      const countryCode = document.getElementById('country-code').value;
      const paymentMethodsRaw = document.getElementById('payment-methods-json').value.trim();
      const statusDiv = document.getElementById('init-status');
      const dropinStatusDiv = document.getElementById('dropin-status');

      if (!clientKey) {
        statusDiv.innerHTML = '<div class="error">Please enter an Adyen Client Key</div>';
        return;
      }

      let paymentMethodsResponse;
      try {
        paymentMethodsResponse = JSON.parse(paymentMethodsRaw);
      } catch (e) {
        statusDiv.innerHTML = '<div class="error">Invalid JSON: ' + e.message + '</div>';
        return;
      }

      statusDiv.innerHTML = '';
      dropinStatusDiv.innerHTML = '';
      document.getElementById('card-container').innerHTML = '';

      try {
        const { AdyenCheckout, Dropin } = window.AdyenWeb;

        const checkout = await AdyenCheckout({
          environment: 'test',
          clientKey,
          paymentMethodsResponse,
          amount: { value: amount, currency },
          locale: 'en-US',
          countryCode,
          analytics: { enabled: false },

          onSubmit: async (state, component, actions) => {
            console.log('onSubmit — state.data', state.data);

            let method = 'unknown';
            let methodId = null;
            let token = null;

            if (state?.data?.paymentMethod?.type === 'applepay') {
              method = 'Apple Pay';
              methodId = 4;
              token = state?.data?.paymentMethod?.applePayToken;
            } else if (state?.data?.paymentMethod?.type === 'googlepay') {
              method = 'Google Pay';
              methodId = 3;
              token = state?.data?.paymentMethod?.googlePayToken;
            }

            // Display the token information
            const resultDiv = document.getElementById('payment-result');
            resultDiv.innerHTML = `
              <div class="success">
                <strong>Token Created!</strong><br>
                <strong>Payment Method:</strong> ${method} (payment_method_id: ${methodId})<br>
                <strong>Token:</strong><pre>${token}</pre><br>
                <small>Use this token as payment_token in your Vrio API call</small>
              </div>
            `;

            // In production: POST the token to your server, then call
            // actions.resolve({ resultCode: 'Authorised' }) or actions.reject()
            actions.resolve({ resultCode: 'Authorised' });
          },

          onPaymentCompleted: (result) => {
            console.log('onPaymentCompleted', result);
            dropinStatusDiv.innerHTML = '<div class="success">Payment completed: ' + result.resultCode + '</div>';
          },

          onPaymentFailed: (result) => {
            console.log('onPaymentFailed', result);
            dropinStatusDiv.innerHTML = '<div class="error">Payment failed: ' + result.resultCode + '</div>';
          },

          onError: (error) => {
            console.log('onError', error);
            dropinStatusDiv.innerHTML = '<div class="error">Error: ' + error.message + '</div>';
          },
        });

        new Dropin(checkout).mount('#card-container');
        statusDiv.innerHTML = '<div class="success">Drop-in initialized successfully</div>';

      } catch (err) {
        statusDiv.innerHTML = '<div class="error">Initialization error: ' + err.message + '</div>';
        console.error('Init error', err);
      }
    }
  </script>
</body>
</html>

Note: The example above includes both Google Pay and Apple Pay in the payment methods JSON. You can include only the payment methods you need — just remove the entry you don't want from the paymentMethods array.

Sending Tokens to Vrio API

Once you have obtained a payment token from Apple Pay or Google Pay via the Adyen Drop-in, you can process the payment through Vrio's API. The onSubmit callback provides the token in state.data.paymentMethod.applePayToken or state.data.paymentMethod.googlePayToken.

Step 1: Configure Your Merchant Account

Ensure your Adyen merchant account is properly configured in your Vrio dashboard with the API Key, Merchant Account, and has Apple Pay/Google Pay as available payment methods.

Step 2: Create an Order

First, create an order using Vrio's order creation endpoint (NOTE: you can also pass the payment_token in Step 1 if you are using the process action when creating the order):

curl -X POST "https://api.vrio.app/orders" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "connection_id": 1,
    "campaign_id": 18,
    "offers": "[{\"offer_id\":89, \"order_offer_quantity\": 1,\"order_offer_price\":0.50}]",
    "email": "[email protected]",
    "bill_fname": "John",
    "bill_lname": "Doe",
    "bill_phone": "+1234567890",
    "bill_address1": "123 Main St",
    "bill_city": "Anytown",
    "bill_state": "NY",
    "bill_zipcode": "12345",
    "bill_country": "US",
    "ship_fname": "John",
    "ship_lname": "Doe",
    "ship_address1": "123 Main St",
    "ship_city": "Anytown",
    "ship_state": "NY",
    "ship_zipcode": "12345",
    "ship_country": "US"
  }'

This will return an order object with an order_id.

Step 3: Process Payment with Token

Use the order processing endpoint with the wallet token from the Adyen Drop-in:

Apple Pay Payment Processing

curl -X POST "https://api.vrio.app/orders/{order_id}/process" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "payment_method_id": 4,
    "payment_token": "APPLE_PAY_TOKEN_FROM_DROPIN",
    "merchant_id": 456
  }'

Google Pay Payment Processing

curl -X POST "https://api.vrio.app/orders/{order_id}/process" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "payment_method_id": 3,
    "payment_token": "GOOGLE_PAY_TOKEN_FROM_DROPIN",
    "merchant_id": 456
  }'

Note: Be sure to pass the merchant_id associated with the payment_token if there are multiple Adyen merchants available on a router.

Troubleshooting

"paymentMethodsResponse was provided but no payment methods were found"

This error appears in the browser console when the paymentMethods array in your JSON is empty. Ensure you have valid payment method entries in the JSON textarea before initializing.

CORS Errors on Analytics Endpoint

If you see CORS errors related to checkoutanalytics-test.adyen.com, ensure your website origin is added to the Allowed origins list in your Adyen Customer Area under Developers > API credentials > Client settings. The Drop-in example above disables analytics (analytics: { enabled: false }) to avoid this during testing.

Common Issues

"Apple Pay / Google Pay buttons not appearing"

  • Ensure you're using HTTPS (required for both Apple Pay and Google Pay)
  • Verify you're using a supported browser (Safari for Apple Pay, Chrome for Google Pay)
  • Check that payment methods are added to your wallet (Apple Wallet or Google Pay)
  • Confirm Apple Pay / Google Pay are enabled in your Adyen Customer Area
  • Verify the paymentMethodsResponse JSON includes the correct payment method type

Apple Pay Not Appearing

  • Use Safari on macOS or iOS device
  • Ensure Touch ID, Face ID, or Apple Watch is set up
  • Add test cards to Apple Wallet
  • Verify your domain is registered with Apple through Adyen
  • Check that the merchantId in your payment methods JSON matches your Apple merchant ID in Adyen

Google Pay Not Appearing

  • Use Chrome browser (desktop or Android)
  • Sign in to your Google account in Chrome
  • Add payment methods in Chrome settings (chrome://settings/payments)
  • Enable "Pay with Google Pay" in Chrome settings
  • Verify the merchantId and gatewayMerchantId in your payment methods JSON are correct

Token is null or undefined

  • Verify the onSubmit callback is receiving state.data.paymentMethod
  • Check the browser console for the full state.data object
  • Ensure the customer completed the wallet authentication (Touch ID, Face ID, etc.)

For additional support, consult the Adyen Web Drop-in Documentation and Adyen Test Card Numbers.