Skip to content

Embedded Fields (EF)

Introduction

This guide covers how to integrate Embedded Fields (EF) into your merchant checkout page. Embedded Fields allow you to embed secure payment form fields directly into your website while maintaining PCI compliance.

This guide includes:

  • Backend and Frontend Integration
  • Customization Options
  • Handling Callbacks
  • Field Configuration
  • Ensuring PCI Compliance

What are Embedded Fields?

Embedded Fields are a method of integrating payment forms directly into a merchant's website or application, rather than redirecting users to an external payment page. This provides a seamless checkout experience while keeping sensitive card data secure.


Pre-Integration Requirements

Before integrating Embedded Fields, ensure you have the following:

Requirement Description Where to Obtain
Client ID & Secret OIDC credentials for authentication Merchant Portal (Merchant/API details)
x-client-key Public key identifying the merchant (used in frontend) Merchant Portal

For environment-specific URLs and configuration values, see Environments.


Backend Integration

The backend integration involves two steps: obtaining an authentication token and creating a payment session.

Step 1: Obtain an Access Token

Authenticate using the OIDC Client Credentials flow to obtain an access token.

UAT Endpoint:

POST https://uat-auth.finrelay.io/realms/uat/protocol/openid-connect/token

Production Endpoint:

POST https://auth.finrelay.io/realms/production/protocol/openid-connect/token

Request Headers:

Content-Type: application/x-www-form-urlencoded

Request Body:

grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 300,
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "scope": "email profile"
}

For more details on authentication, see Authentication.

Step 2: Create a Payment Session

Once authenticated, create a payment session by calling the sessions endpoint.

UAT Endpoint:

POST https://uat-api.finrelay.io/api/sessions

Production Endpoint:

POST https://api.finrelay.io/api/sessions

Request Headers:

Content-Type: application/json
Accept: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

Request Body:

{
  "terminal_id": "TERM001",
  "reference": "ORDER-123456",
  "description": "Order payment",
  "currency": "EUR",
  "amount": 25600,
  "transaction_type": "AUTHORIZE",
  "success_url": "https://merchant.example.com/success",
  "cancel_url": "https://merchant.example.com/cancel",
  "customer": {
    "first_name": "John",
    "last_name": "Doe",
    "address": "123 Main Street",
    "city": "Sampletown",
    "country": "BA",
    "postal_code": "10000",
    "email": "john.doe@example.com",
    "phone": "+1234567890"
  },
  "country": "BA"
}

Response:

{
  "id": "tcNyatfpvOjxxIDEdcYH",
  "terminal_id": "TERM001",
  "reference": "ORDER-123456",
  "description": "Order payment",
  "currency": "EUR",
  "amount": 25600,
  "success_url": "https://merchant.example.com/success",
  "cancel_url": "https://merchant.example.com/cancel",
  "customer": {
    "first_name": "John",
    "last_name": "Doe",
    "address": "123 Main Street",
    "city": "Sampletown",
    "country": "BA",
    "postal_code": "10000",
    "email": "john.doe@example.com",
    "phone": "+1234567890"
  },
  "country": "BA",
  "status": "CREATED",
  "transaction_type": "AUTHORIZE"
}

The id field in the response is your session_id — use this to initialize the Embedded Fields on the frontend.

Session Request Parameters

Field Type Required Description
terminal_id string Yes Terminal identifier
reference string(40) Yes Unique order reference for the terminal
description string Yes Order description
currency string(3) Yes ISO 4217 currency code (e.g., "EUR")
amount integer Yes Amount in minor units (e.g., 25600 = €256.00)
transaction_type string Yes "AUTHORIZE" or "PURCHASE"
success_url string Yes Redirect URL after successful payment
cancel_url string Yes Redirect URL after cancellation or failure
customer object No Customer details (pre-populated if provided)
country string(2) No ISO 3166-1 alpha-2 country code

Frontend Integration

To integrate Embedded Fields into your frontend, you need the session_id from the backend and your client_key from the merchant portal.

Step 1: Load the Embedded Fields Script

UAT:

<script src="https://uat-api.finrelay.io/embedded-fields.js"></script>

Production:

<script src="https://api.finrelay.io/embedded-fields.js"></script>

Step 2: Initialize the Payment Component

const initCheckout = async (sessionId, clientKey) => {
  // Initialize the payment component
  const paymentComponent = await window.PaymentComponent({
    locale: 'en-US',
    environment: 'test', // Use 'production' for live transactions
    clientKey: clientKey,
    sessionId: sessionId
  });

  // Create the card payment component
  const cardPaymentComponent = paymentComponent.create('card');

  // Mount it to a DOM element
  cardPaymentComponent.mount('#card-wrapper');

  return cardPaymentComponent;
};

Step 3: Submit the Payment

function submitPayment(cardPaymentComponent) {
  const isValid = cardPaymentComponent.validate();

  if (isValid) {
    const customer = cardPaymentComponent.getCustomer();

    cardPaymentComponent.submitPayment({
      customer: {
        ...customer,
        firstName: 'John',
        lastName: 'Doe',
        address: '123 Main Street',
        city: 'Sampletown',
        zip: '10000',
        country: 'BA',
        phone: '+1234567890',
        email: 'john.doe@example.com'
      },
      callback: (response, error) => {
        if (response?.approved) {
          // Payment successful
          console.log('Payment approved:', response);
        } else {
          // Payment failed
          console.log('Payment failed:', response);
        }

        if (error) {
          console.error('Payment error:', error);
        }
      }
    });
  }
}

Complete Example

<!DOCTYPE html>
<html>
<head>
  <title>Checkout</title>
</head>
<body>
  <div id="card-wrapper"></div>
  <button id="pay-button">Pay Now</button>


  <script src="https://uat-api.finrelay.io/embedded-fields.js"></script>

  <script>
    let cardPaymentComponent;

    async function init() {
      const sessionId = 'YOUR_SESSION_ID'; // From backend
      const clientKey = 'YOUR_CLIENT_KEY'; // From merchant portal

      const paymentComponent = await window.PaymentComponent({
        locale: 'en-US',
        environment: 'test',
        clientKey: clientKey,
        sessionId: sessionId
      });

      cardPaymentComponent = paymentComponent.create('card');
      cardPaymentComponent.mount('#card-wrapper');
    }

    document.getElementById('pay-button').addEventListener('click', () => {
      if (cardPaymentComponent.validate()) {
        cardPaymentComponent.submitPayment({
          callback: (response, error) => {
            if (response?.approved) {
              window.location.href = '/success';
            } else {
              alert('Payment failed');
            }
          }
        });
      }
    });

    init();
  </script>
</body>
</html>

Field Configuration

The Embedded Fields component provides granular control over which fields are displayed and their validation requirements.

Card Payment Fields

These fields are always displayed and cannot be hidden:

Field Description Configurable Default State
Card Number The 16-digit card number No Always visible, required
Expiry Date Card expiration date (MM/YY) No Always visible, required
CVV/CVC 3 or 4 digit security code No Always visible, required

Customer Information Fields

These fields can be shown, hidden, or made required through the customerConfig object:

Field Show/Hide Property Required Property Default Visibility Default Required
Cardholder Name hasHolderName holderNameRequired Hidden No
Cardholder Email hasHolderEmail holderEmailRequired Hidden No

Configuration Reference

const componentConfig = {
  componentConfig: {
    customerConfig: {
      // Cardholder Name Configuration
      hasHolderName: true,        // Show the cardholder name field
      holderNameRequired: true,   // Make the name field required

      // Cardholder Email Configuration
      hasHolderEmail: true,       // Show the cardholder email field
      holderEmailRequired: false  // Email is optional
    }
  },
  componentListeners: {
    onChange: (e) => {
      console.log('Field changed:', e);
    },
    onError: (e) => {
      console.error('Error:', e);
    }
  }
};

const cardPaymentComponent = paymentComponent.create('card', componentConfig);

Configuration Options

hasHolderName (boolean)

Controls whether the cardholder name input field is displayed.

  • true: The name field is displayed
  • false (default): The name field is hidden

holderNameRequired (boolean)

When hasHolderName is true, controls whether the field is required.

  • true: The name field must be filled before submitting
  • false (default): The name field is optional

Note

Setting holderNameRequired: true without hasHolderName: true has no effect since the field is not visible.

hasHolderEmail (boolean)

Controls whether the cardholder email input field is displayed.

  • true: The email field is displayed
  • false (default): The email field is hidden

holderEmailRequired (boolean)

When hasHolderEmail is true, controls whether the field is required.

  • true: The email field must be filled with a valid email
  • false (default): The email field is optional

Note

Setting holderEmailRequired: true without hasHolderEmail: true has no effect since the field is not visible.

Common Configuration Patterns

const componentConfig = {
  componentConfig: {
    customerConfig: {
      // All customer fields hidden by default
    }
  }
};
const componentConfig = {
  componentConfig: {
    customerConfig: {
      hasHolderName: true,
      holderNameRequired: true
    }
  }
};
const componentConfig = {
  componentConfig: {
    customerConfig: {
      hasHolderEmail: true,
      holderEmailRequired: false
    }
  }
};
const componentConfig = {
  componentConfig: {
    customerConfig: {
      hasHolderName: true,
      holderNameRequired: true,
      hasHolderEmail: true,
      holderEmailRequired: true
    }
  }
};

Providing Customer Data at Submission

You can provide or override customer information when submitting the payment:

cardPaymentComponent.submitPayment({
  customer: {
    firstName: 'John',
    lastName: 'Doe',
    address: '123 Main Street',
    city: 'Sampletown',
    zip: '10000',
    country: 'US',
    phone: '+1-555-123-4567',
    email: 'john.doe@example.com'
  },
  callback: (response, error) => {
    // Handle response
  }
});
Customer Field Description
firstName Customer's first name
lastName Customer's last name
address Street address
city City name
zip Postal/ZIP code
country ISO 3166-1 alpha-2 country code (e.g., 'US', 'GB', 'DE')
phone Phone number
email Email address

Tip

Use cardPaymentComponent.getCustomer() to retrieve any customer data entered in the embedded fields, then spread it with your own data: { ...customer, firstName: 'Override' }.


Event Handling

Available Callbacks

Callback Description
onChange Triggered when any field value changes
onError Triggered when a validation or processing error occurs

Example with Event Listeners

const componentConfig = {
  componentConfig: {
    customerConfig: {
      hasHolderEmail: true,
      holderEmailRequired: false
    }
  },
  componentListeners: {
    onChange: (event) => {
      console.log('Field changed:', event);
      // Use this to track form completion progress
    },
    onError: (error) => {
      console.error('Error occurred:', error);
      // Display error to user or log for debugging
    }
  }
};

const cardPaymentComponent = paymentComponent.create('card', componentConfig);

Validation

Use the validate() method to check if all required fields are filled correctly:

const isValid = cardPaymentComponent.validate();

// For detailed validation information:
const validationObject = cardPaymentComponent.getDataWithValidation();
// Returns which fields are invalid and their error messages

Customization

Embedded Fields can be customized to match your website's branding by overriding CSS variables.

CSS Variables

embedded-fields-card, #card-wrapper {
  --lib-font-family: var(--app-font-family);
  --lib-input-font-size: 1.7rem;
  --lib-accent-color: #556a61;
  --lib-accent-background-color: #DFE8E1;
  --lib-label-font-size: 1rem;
  --lib-warning-color: #ff8c70;
  --lib-warning-background-color: #fff6ee;
  --lib-input-color: #556a61;
  --lib-input-focus-border-color: #86a599;
  --lib-input-border-color: #d4d7d6;
  --lib-input-focus-color: #179261;
  --lib-place-holder-color: #e1e1e1;
  --lib-input-border-radius: 0.5rem;
  --lib-button-border-radius: 0.7rem;
  --lib-button-font-size: 1.1rem;
}

CSS Selectors

For more granular control, use CSS selectors to target specific elements:

embedded-fields-card div.input-container.lib-input-root div.lib-input-wrapper input {
  font-family: var(--app-font-family);
}

lib-input .lib-input-wrapper,
lib-iframe-input div.iframe-input-wrapper {
  border-style: initial !important;
  border-bottom: 1px solid var(--lib-input-border-color) !important;
  border-radius: initial !important;
}

PCI Compliance

Payment Card Industry Data Security Standard (PCI DSS) compliance is essential when handling card data.

Why Embedded Fields Help with PCI Compliance

Embedded Fields are designed to minimize your PCI scope:

  • Sensitive card data never touches your servers — card numbers, CVV, and expiration dates are captured directly by the secure iframe
  • Tokenization — actual card data is replaced with secure tokens
  • Encryption — all data transmission is encrypted

Merchant Levels

Level Transaction Volume Validation Method
Level 1 Over 6 million annually External audit by QSA
Level 2 1-6 million annually SAQ + external scan
Level 3 20,000-1 million online SAQ + external scan
Level 4 Under 20,000 online SAQ

Consequences of Non-Compliance

  • Fines from payment networks
  • Loss of payment processing privileges
  • Legal action by regulatory bodies
  • Reputation damage
  • Increased risk of data breaches

Troubleshooting

Common Issues

Unable to Load Embedded Fields

  1. Verify the script URL is correct for your environment
  2. Check that your client_key is valid
  3. Ensure the session_id has not expired

Unable to Create a Session

  1. Verify the Authorization header is correct
  2. Check that your access token has not expired
  3. Review the request payload for missing or invalid values

Payment Submission Fails

  1. Use cardPaymentComponent.validate() to check for validation errors
  2. Check cardPaymentComponent.getDataWithValidation() for detailed error information
  3. Review the onError callback for error details

Debugging Tips

Use the callback functions to monitor the payment flow:

const componentConfig = {
  componentListeners: {
    onChange: (e) => console.log('Change:', e),
    onError: (e) => console.error('Error:', e)
  }
};

Best Practices

  1. Obtain Required Parameters First — Retrieve sessionId from your backend and clientKey from the merchant portal before initializing

  2. Load Script Asynchronously — Load the embedded-fields.js script asynchronously to avoid blocking page rendering

  3. Handle All Callbacks — Implement both onChange and onError callbacks for better debugging and user experience

  4. Validate Before Submission — Always call validate() before submitPayment() to catch errors early

  5. Provide Clear Error Messages — Use validation results to show users exactly what needs to be corrected

  6. Test in UAT First — Always test your integration in the UAT environment before going to production


FAQ

What is the difference between Embedded Fields and Hosted Payment Page?

Embedded Fields integrate payment forms directly into your website, giving you full control over the UI/UX while maintaining PCI compliance.

Hosted Payment Page (HPP) redirects customers to a hosted checkout page. See the Hosted Payment Page documentation for details.

How do I obtain the necessary API keys?

All credentials (Client ID, Client Secret, x-client-key) can be obtained from the Merchant Portal under Merchant/API details.

Can I customize the appearance of Embedded Fields?

Yes, you can customize the appearance using CSS variables and selectors. See the Customization section.

What currencies are supported?

The supported currencies depend on your merchant configuration. Contact your account manager for details.

How do I test my integration?

Use the UAT environment for testing. Set environment: 'test' when initializing the PaymentComponent.