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.
- 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.
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.
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) {
}
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.
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: { },
theme: {
global: {
fontFamily: 'Inter, sans-serif',
fontSize: '16px'
},
input: {
color: "#111827",
backgroundColor: "#ffffff",
':focus': { color: "#111827" },
':invalid': { color: "#dc2626" }
}
}
});
Custom fonts
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',
fontFamily: 'Inter, sans-serif',
fontSize: '16px'
}
}
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();
} catch (error) {
errorDiv.textContent = error.message || "Payment failed";
errorDiv.style.display = "block";
} finally {
submitBtn.disabled = false;
submitBtn.textContent = "Pay Now";
}
});
Error handling
- 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()