Search docs

Are you looking for test card numbers?

Would you like to contact support?

Start searching Adyen's documentation...

  Documentation

Build your own payment form

Learn how to collect a shopper's payment details using your own payment form.

Before you can make a payment with our API integration, you need to collect any required payment details from your shopper. Here we describe how to do this by building your own payment form.

Building your own form gives you full control over the look and feel of your checkout. If you'd rather not build your own payment form, you can also collect the shopper's payment details using our pre-built JavaScript Components. These are available for many popular payment methods.

This guide assumes that you've already completed Step 1 of our API integration guide, and have presented a list of payment methods to the shopper.

To collect payment details from the shopper, your payment form can be either:

  • Hard-coded: After making a /paymentMethods call to determine the payment details you need to collect, you build a static form that collects them from the shopper.

    A hard-coded form is quicker to implement, but the payment details you need to collect from a shopper can change. You should regularly poll the /paymentMethods endpoint to check for any updates to required payment details. We recommend this approach if you're only working with a small number of payment methods or countries.

  • Dynamically generated: For each transaction, you make a /paymentMethods call to determine the payment details you need to collect. Then use the response to generate a form that collects them from the shopper.

    This takes more time to implement, but ensures that the required payment details you collect from the shopper are up-to-date.

After you decide which form you will build, follow the steps below to:

  1. Determine which shopper details you need to collect in your form. These are different for each payment method.
  2. For card payments, integrate Secured Fields Component. The Component collects and encrypts sensitive credit card details. You can then pass these values to your API request to make a payment.

If you are PCI Level 1 or 2 certified you can alternatively collect and submit raw card data. See Collecting raw card data for more information.

Step 1: Determine required shopper details

To determine what payment details you need to collect from the shopper:

  1. Make /paymentMethods call, providing:

    • countryCode: The shopper's country.
    • amount: The currency and value of the payment. 
    curl https://checkout-test.adyen.com/v41/paymentMethods \
    -H "X-API-key: [Your API Key here]" \
    -H "Content-Type: application/json" \
    -d '{
      "merchantAccount": "YourMerchantAccount",
      "countryCode": "NL",
      "amount": {
        "currency": "EUR",
        "value": 1000
      }
    }'
    # Set your X-API-KEY with the API key from the Customer Area.
    adyen = Adyen::Client.new
    adyen.api_key = "YOUR X-API-KEY"
    
    response = adyen.checkout.payment_methods({
      :merchantAccount => 'YourMerchantAccount',
      :countryCode => 'NL',
      :amount => {
        :currency => 'EUR',
        :value => 1000
      }
    })
    // Set your X-API-KEY with the API key from the Customer Area.
    Config config = new Config();
    config.setApiKey("Your X-API-KEY"));
    Client client = new Client(config);
    
    Checkout checkout = new Checkout(client);
    PaymentMethodsRequest paymentMethodsRequest = new PaymentMethodsRequest();
    paymentMethodsRequest.setMerchantAccount("YourMerchantAccount");
    paymentMethodsRequest.setCountryCode("NL");
    Amount amount = new Amount();
    amount.setCurrency("EUR");
    amount.setValue(1000L);
    paymentMethodsRequest.setAmount(amount);
    PaymentMethodsResponse response = checkout.paymentMethods(paymentMethodsRequest);
    // Set your X-API-KEY with the API key from the Customer Area.
    $client = new \Adyen\Client();
    $client->setXApiKey("YOUR X-API-KEY");
    $service = new \Adyen\Service\Checkout($client);
    
    $params = array(
      "merchantAccount" => "YourMerchantAccount",
      "countryCode" => "NL",
      "amount" => array(
        "currency" => "EUR",
        "value" => 1000
    );
    $result = $service->paymentMethods($params);

    A list of available paymentMethods will be returned in the response. Most of these will have a details array. 

    {
      "paymentMethods":[
        {
          "details": [
          {
            "key": "number",
            "type": "text"
          },
          {
            "key": "expiryMonth",
            "type": "text"
          },
          {
            "key": "expiryYear",
            "type": "text"
          },
          {
            "key": "cvc",
            "type": "text"
          },
          {
            "key": "holderName",
            "optional": true,
            "type": "text"
          }
          ],
          "name": "Credit Card",
          "type": "scheme"
        },
        {
          "details":[
          {
              "key":"sepa.ownerName",
              "type":"text"
          },
          {
              "key":"sepa.ibanNumber",
              "type":"text"
          }
          ],
          "name":"SEPA Direct Debit",
          "type":"sepadirectdebit"
        },
        ...
      ]
    }
  2. Use the details array of each payment method to determine what you need to collect from the shopper:

    • key: Shopper detail you need to collect using your payments form.
    • type: Input type for collecting the payment detail from the shopper:

      Type Description
      emailAddress Email address.
      radio Radio buttons displaying the options specified within the items array.
      select A list displaying the options specified within the items array. Present each name in this array to the shopper.
      tel Telephone number.
      text Generic string. For "type": "scheme", if you are PCI Level 1 or 2 certified you can alternatively collect and submit raw card data. See Collecting raw card data for more information. Otherwise, you need to generate encrypted card data using our Secured Fields Component.

    If a payment method does not have a details array, you do not need to collect any shopper details in your form.

    In the example below, for SEPA Direct Debit, you'd use text fields to collect:

    • sepa.ownerName: Name on the shopper's bank account.
    • sepa.ibanNumber: IBAN number of this account.
      {
      "paymentMethods":[
      ...
        {
        "details":[{
            "key":"sepa.ownerName",
            "type":"text"
          },{
            "key":"sepa.ibanNumber",
            "type":"text"
          }
        ],
        "name":"SEPA Direct Debit",
        "type":"sepadirectdebit"
      },
      ...
      ]
      }

Step 2: Collect shopper's card details with Secured Fields Component

If the shopper decides to pay with card, the fields that you need to collect are returned in the /paymentMethods call. You can collect raw card data if you are PCI Level 1 or 2 certified. Otherwise, you need to integrate our Secured Fields Component to securely collect and encrypt the shopper's card details.

When the shopper enters their card details on your website, the Component collects the data, and then creates an encrypted version that you can safely transmit when you make the payment.

If you are currently using a standalone implementation of Secured Fields, see the documentation for the older version here. For new integrations, we strongly recommend to use the Secured Fields Component.
Older versions will still be supported, but newer functionality will be released on Secured Fields Component.

See sample code

Check out Secured Fields Component on GitHub.

To add the Secured Fields Component to your payments form, you'll need to:

  1. Make sure that you have already added the Components JavaScript file and the required configuration on your payments page.

  2. Create a DOM element for Secured Fields:

    <div id="securedfields">
        <label>
            <span>Card number:</span>
            <span data-cse="encryptedCardNumber"></span>
        </label>
        <label>
            <span>Expiry date:</span>
            <span data-cse="encryptedExpiryDate"></span>
        </label>
        <label>
            <span>CVV/CVC:</span>
            <span data-cse="encryptedSecurityCode"></span>
        </label>
    </div>
  3. Create a function to listen to and handle the onChange event triggered by the Component:

    function handleOnChange(state, component) {
        state.isValid // true or false.
        state.data
        /* {type: "scheme",
            encryptedCardNumber: "adyenjs_0_1_18$MT6ppy0FAMVMLH...",
            encryptedExpiryMonth: "adyenjs_0_1_18$MT6ppy0FAMVMLH...",
            encryptedExpiryYear: "adyenjs_0_1_18$MT6ppy0FAMVMLH...",
            encryptedSecurityCode: "adyenjs_0_1_18$MT6ppy0FAMVMLH..."}
        */
    }
  4. Create an instance of the Secured Fields component, and mount it. You can also include additional configuration properties. For more information, see Configuring the Component.

    const sf = checkout.create('securedfields', {
       onChange: handleOnChange
    }).mount('#securedfields');
  5. The Component will trigger onChange event when the card brand changes and whenever the Secured Fields form becomes valid or invalid. When onChange callback is triggered and state.isValid is true, get the encrypted values from state.data. You'll use these data to make the payment.

     {
      type: "scheme",
      encryptedCardNumber: "adyenjs_0_1_18$MT6ppy0FAMVMLH...",
      encryptedExpiryMonth: "adyenjs_0_1_18$MT6ppy0FAMVMLH...",
      encryptedExpiryYear: "adyenjs_0_1_18$MT6ppy0FAMVMLH...",
      encryptedSecurityCode: "adyenjs_0_1_18$MT6ppy0FAMVMLH..."
     }

Configuring the Component

When instantiating the Secured Fields Component, you can optionally specify:

  • ariaLabels: Specify aria attributes for the input fields for web accessibility. If not specified, default labels will be applied.
  • autoFocus: Automatically shift the focus from date field to the CVC field. Defaults to true.
  • groupTypes: Defaults to ['mc','visa','amex']. Configure supported card types to facilitate brand detection used in the onBrand callback. See list of available card brands. If a shopper enters a card type not specified in the GroupTypes configuration, the onBrand callback will not be invoked.
  • placeholders: Specify the sample values you want to appear for card detail input fields. If not specified, default placeholders will be applied.
  • styles: Set a style object to customize the input fields. See Styling Secured Fields for a list of supported properties. If not specified, default styles will be applied.
  • type: Defaults to card. Set this to a specific card brand to limit the PAN that secured fields will accept as valid. If set to a single brand, the 'groupTypes' property will be ignored. See list of available card brands.
  • Additional Secured Fields callbacks.
    const sf = checkout.create('securedfields', {

    // Optional
    type: 'card',
    groupTypes: ['mc', 'visa', 'amex', 'bcmc', 'maestro'],
    styles: {
        error: {
            color: 'red'
        },
        validated: {
            color: 'green',
        },
        placeholder: {
            color: '#d8d8d8'
        }
    },
    placeholders: {
        encryptedCardNumber: '9999 9999 9999 9999',
        encryptedExpiryDate: 'mm/yy',
        encryptedSecurityCode: '1234'
    },
    ariaLabels: {
        lang: 'en-GB',
        encryptedCardNumber: {
            label: 'Credit or debit card number field'
        }
    },
    // Events
    onChange: function() {},
    onValid : function() {},
    onLoad: function() {},
    onConfigSuccess: function() {},
    onFieldValid : function() {},
    onBrand: function() {},
    onError: function() {},
    onFocus: function() {},
    onBinValue: function(bin) {}
}).mount('#securedfields');

Default styles, placeholders, and labels

If you don't provide configuration for styles, placeholders, and labels, the Component will use the following default properties.

{
styles: {
        base: {
            color: '#001b2b',
            fontSize: '16px',
            fontWeight: '400'
        },
        placeholder: {
            color: '#90a2bd',
            fontWeight: '200'
        },
        error: {
            color: '#001b2b'
        }
}
placeholders: {
    encryptedCardNumber: '1111 2222 3333 4444',
    encryptedExpiryDate: 'MM/YY',
    encryptedSecurityCode: '1234'
}
ariaLabels: {
        lang: 'en-GB',
        encryptedCardNumber: {
            label: 'Credit or debit card number',
            iframeTitle: 'Iframe for secured card data input field'
        },
        encryptedExpiryDate: {
            label: 'Credit or debit card expiration date',
            iframeTitle: 'Iframe for secured card data input field'
        },
        encryptedSecurityCode: {
            label: 'Iframe for secured card data input field'
        }
}

Callbacks

The Secured Fields Component will trigger events depending on shopper action. Implement callbacks to handle events and use these to customize your shopper's experience when specific events occur.

For more information on what each callback object contains, see Secured Fields callbacks.

  • onValid: Called when all fields are valid.
  • onLoad: Called once all the Secured Fields have been created but are not yet ready to use.
  • onConfigSuccess: Called once the Secured Fields are ready to use.
  • onFieldValid: Called when a specific field is validated and encrypted or becomes invalid.
  • onBrand: Called once we detect the card brand.
  • onError: Called when in case of an invalid card number, invalid expiry date, or incomplete field. Called again when errors are cleared.
  • onFocus: Called when a field gains or loses focus.
  • onBinValue: Provides the BIN Number of the card (up to 6 digits), called as the user types in the PAN.

Next steps