Skip to Content
Hosted Fields

Hosted Fields

Hosted fields let you collect card data directly in your web page without the card number ever touching your servers. Each sensitive field (card number, expiry, CVV) runs inside a separate iframe hosted on the Gatelithix PCI-zone domain.

This approach gives you full control over your checkout design while maintaining PCI DSS compliance. The card data is tokenized inside the iframe and only the resulting token is returned to your page.

Installation

Script Tag (CDN)

<script src="https://js.gatelithix.com/v1/hosted-fields.js"></script>

npm

npm install @gatelithix/hosted-fields
import { HostedFields } from "@gatelithix/hosted-fields";

Quick Start

1. Add Container Elements

Add empty elements where you want the card fields to appear:

<form id="payment-form"> <div> <label>Card Number</label> <div id="card-number"></div> </div> <div> <label>Expiration</label> <div id="card-expiry"></div> </div> <div> <label>CVV</label> <div id="card-cvv"></div> </div> <button type="submit" id="pay-btn">Pay</button> </form>

2. Create Hosted Fields

Initialize the hosted fields instance with your publishable key:

const fields = Gatelithix.HostedFields.create({ publishableKey: "pk_test_your_publishable_key", fields: { cardNumber: { container: "#card-number" }, expiry: { container: "#card-expiry" }, cvv: { container: "#card-cvv" }, }, styles: { base: { color: "#333", fontSize: "16px", fontFamily: "Inter, sans-serif", "::placeholder": { color: "#aab7c4" }, }, invalid: { color: "#dc3545" }, }, });

3. Handle Events

Listen for field state changes to provide real-time feedback:

fields.on("change", (event) => { console.log(event.fieldType); // "cardNumber" | "expiry" | "cvv" console.log(event.complete); // true when field is valid and complete console.log(event.empty); // true when field is empty console.log(event.error); // validation error message or null console.log(event.brand); // card brand (cardNumber only): "visa", "mastercard", etc. }); fields.on("focus", (event) => { // Field received focus }); fields.on("blur", (event) => { // Field lost focus }); fields.on("error", (event) => { console.error("Field error:", event.error); });

4. Tokenize on Submit

When the user submits the form, call tokenize() to exchange the card data for a gateway token:

document.getElementById("payment-form").addEventListener("submit", async (e) => { e.preventDefault(); const result = await fields.tokenize(); if (result.error) { // Display error to user console.error(result.error); return; } // Send the token to your server console.log("Token:", result.token); console.log("Last 4:", result.last4); console.log("Brand:", result.brand); console.log("Exp:", result.expMonth + "/" + result.expYear); // POST the token to your server to create a payment await fetch("/api/charge", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ gateway_token: result.token }), }); });

Style Configuration

Pass a styles object when creating hosted fields. Styles are applied inside each iframe.

Style States

StateWhen Applied
baseDefault styles for the input.
focusWhen the input has focus.
invalidWhen the input contains invalid data.
completeWhen the input is valid and complete.

Allowed CSS Properties

For security, only the following CSS properties are allowed:

  • color
  • fontSize
  • fontFamily
  • fontWeight
  • fontStyle
  • letterSpacing
  • textAlign
  • lineHeight
  • padding
  • ::placeholder (supports color only)

Properties containing url(), expression(), javascript:, or data: URIs are rejected.

Example

const styles = { base: { color: "#333", fontSize: "16px", fontFamily: "'Inter', system-ui, sans-serif", fontWeight: "400", lineHeight: "1.5", padding: "12px", "::placeholder": { color: "#9ca3af" }, }, focus: { color: "#111", }, invalid: { color: "#dc3545", }, complete: { color: "#059669", }, };

Security

  • PCI isolation: Each field runs in a separate iframe loaded from the Gatelithix PCI-zone domain. Card data never leaves the iframe.
  • Origin validation: Iframes validate the parent page origin via postMessage to prevent cross-origin attacks.
  • Style sanitization: Only allowlisted CSS properties are accepted. Values containing url() or script injections are rejected.
  • Publishable keys only: Use pk_test_ or pk_live_ keys. Never use secret keys in browser code.
  • Direct vault submission: The iframe submits card data directly to the Gatelithix vault service. The card number is never sent via postMessage.

Full Integration Example

<!DOCTYPE html> <html> <head> <title>Checkout</title> <script src="https://js.gatelithix.com/v1/hosted-fields.js"></script> <style> .field-container { border: 1px solid #e5e7eb; border-radius: 6px; padding: 0; height: 44px; margin-bottom: 12px; } .field-container.focused { border-color: #0066FF; } .field-container.invalid { border-color: #dc3545; } </style> </head> <body> <form id="payment-form"> <div id="card-number" class="field-container"></div> <div id="card-expiry" class="field-container"></div> <div id="card-cvv" class="field-container"></div> <button type="submit">Pay $50.00</button> <div id="error-message"></div> </form> <script> const fields = Gatelithix.HostedFields.create({ publishableKey: "pk_test_your_publishable_key", fields: { cardNumber: { container: "#card-number" }, expiry: { container: "#card-expiry" }, cvv: { container: "#card-cvv" }, }, styles: { base: { fontSize: "16px", color: "#333" }, invalid: { color: "#dc3545" }, }, }); fields.on("focus", (e) => { document.querySelector(`#card-${e.fieldType === "cardNumber" ? "number" : e.fieldType}`) .classList.add("focused"); }); fields.on("blur", (e) => { document.querySelector(`#card-${e.fieldType === "cardNumber" ? "number" : e.fieldType}`) .classList.remove("focused"); }); fields.on("change", (e) => { const el = document.querySelector(`#card-${e.fieldType === "cardNumber" ? "number" : e.fieldType}`); el.classList.toggle("invalid", !!e.error); }); document.getElementById("payment-form").addEventListener("submit", async (e) => { e.preventDefault(); const result = await fields.tokenize(); if (result.error) { document.getElementById("error-message").textContent = result.error; return; } // Send result.token to your server alert("Token: " + result.token); }); </script> </body> </html>