Skip to main content

Hosted Fields

Use the getHostedFields() method to render individual, PCI-compliant card input fields as secure iframes within your own custom form layout. Each field (card number, expiry, CVV…) is isolated inside a separate iframe while your page retains full control over the surrounding UI.

When to use this recipe
  • You want to build a fully custom card form UI (layout, labels, styles all yours).
  • You are collecting vault credit card data only.
  • You need PCI-DSS compliant field isolation without managing sensitive data yourself.
Vault credit card only

getHostedFields() is only available for vault credit card payment methods. For other methods, use getPaymentElement() instead. For a pre-built form that also handles theming, see Hosted Form.

Prerequisites

This recipe assumes you have a checkout instance already initialized. See the Getting Started guide if you haven't done this yet.


Check hosted fields support

Before rendering hosted fields, verify the target method or token supports them using the supportsHostedFields boolean available on both PurseHeadlessCheckoutPrimaryMethod and PurseHeadlessCheckoutPrimaryToken.

const creditCard = checkout.paymentMethods.value.find(m => m.method === "creditcard");
if (!creditCard?.supportsHostedFields) {
// Fall back to getPaymentElement() for this method
}

Checking the flag upfront lets you branch cleanly in your UI. The SDK also emits a console warning if you call getPaymentElement({ hostedFields }) on a method that does not support them.

1. Add form containers to your HTML

Create placeholder div elements for each card field. The SDK will inject secure iframes into these targets:

<form id="payment-form">
<div class="field">
<label>Card Number</label>
<div class="row">
<div id="card-number"></div>
<div id="brand-selector"></div>
</div>
</div>

<div class="field">
<label>Cardholder Name</label>
<div id="cardholder-name"></div>
</div>

<div class="row">
<div class="field">
<label>Expiry Date</label>
<div id="expiry-date"></div>
</div>
<div class="field">
<label>CVV</label>
<div id="cvv"></div>
</div>
</div>

<button type="submit" id="submit-btn">Pay Now</button>
<div id="error"></div>
</form>

2. Render the hosted fields

Call checkout.getPaymentElement() with the target partner and hostedFields config, then call render(). method is optional — the first compatible vault method on partner is picked automatically:

const hostedFields = checkout.getPaymentElement({
partner: "ingenico",
hostedFields: {
brandSelector: { target: "brand-selector" },
cardNumber: { target: "card-number", placeholder: "1234 5678 9012 3456" },
holderName: { target: "cardholder-name", placeholder: "John Doe" },
expDate: { target: "expiry-date", placeholder: "MM/YY" },
cvv: { target: "cvv", placeholder: "123" }
}
});

hostedFields.render();

💡 render() mounts one secure iframe per target element. The brandSelector field is optional — it displays a brand icon based on the card number entered.

A console warning is emitted if the resolved method does not actually support hosted fields. Throws PurseHeadlessCheckoutError PAYMENT_METHOD_NOT_FOUND if no method matches the partner.

3. Customize field appearance

Pass a theme object alongside hostedFields to style the fields from the outside. Only global and input scopes are supported:

const hostedFields = checkout.getPaymentElement({
partner: "ingenico",
hostedFields: { /* ...same as above... */ },
theme: {
global: {
fontFamily: 'Inter, sans-serif',
fontSize: '16px'
},
input: {
color: "#111827",
backgroundColor: "#ffffff",
':focus': { color: "#111827" },
':invalid': { color: "#dc2626" }
}
}
});
Theme scope reference

See Theme references for all available keys. For richer theming (labels, helper text, tooltips), use Hosted Form instead.

Custom fonts

Contact required

For compliance reasons, please contact the Purse team before using custom fonts — you must provide the font file in advance.

Pre-approved Google Fonts (Inter, Lato, Montserrat, Noto, Nunito, Open Sans, Raleway, Roboto, Work Sans) are ready to use without approval — just set fontFamily:

theme: {
global: {
fontSrc: 'custom-font-file', // only needed for non-pre-approved fonts
fontFamily: 'Inter, sans-serif',
fontSize: '16px'
}
}

4. Handle form submission

Call checkout.submitPayment() on form submit:

document.getElementById("payment-form").addEventListener("submit", async (e) => {
e.preventDefault();

const submitBtn = document.getElementById("submit-btn");
const errorDiv = document.getElementById("error");

submitBtn.disabled = true;
submitBtn.textContent = "Processing...";
errorDiv.style.display = "none";

try {
await checkout.submitPayment();
// Redirect or show success message
} catch (error) {
errorDiv.textContent = error.message || "Payment failed";
errorDiv.style.display = "block";
} finally {
submitBtn.disabled = false;
submitBtn.textContent = "Pay Now";
}
});

Error handling

Common errors
  • Expired session → Create a new client session
  • Invalid field configuration → Check that all target element IDs exist in the DOM
  • Payment method not supported → The method does not support Hosted Fields (only vault credit card is supported)
  • Technical error → The payment method could not be initialized

Complete example

Loading demo…

See also

  • Brand Management — Detect and manage co-branded cards with getHostedFields()
  • Hosted Form — Use a pre-built form UI via getPaymentElement({ hostedForm })
  • Customization — Theming the default payment element with getPaymentElement()