Skip to main content

Getting Started

Introduction

Hosted Fields enables easy integration of a vault credit card payment method while maintaining full control over the form UI.

Features
  • Light Integration: Minimal setup required.
  • Secure Payment: PCI-compliant with iframe-based field isolation.
  • Customizable: Full control over layout, styles, and translations.
  • Vault Credit Card Only: Specifically designed for vault credit card forms.

Prerequisites

Ensure you have:

  • Basic JavaScript, HTML, and CSS knowledge.
  • An active credit card payment method.
  • A Purse API key.
  • A client session.

Limitations

Important

Hosted Fields are only supported with vault credit cards. For other payment methods, use the Headless Checkout SDK or Hosted Form.

Quick Start Guide

Step 1: Load the Headless Checkout SDK

Add the Headless Checkout SDK to your checkout page:

<script type="module">
import * as Purse from "https://cdn.purse-sandbox.com/headless-checkout/latest/purse.esm.js";
</script>

ℹ️ The SDK is served from Purse's CDN to meet PCI-DSS requirements. Do not self-host this file.

Step 2: Build The Form Markup

Create container elements where the hosted fields will be rendered:

<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>

Step 3: Initialize the Checkout

Use your client session token to create an instance of the checkout:

import * as Purse from "https://cdn.purse-sandbox.com/headless-checkout/latest/purse.esm.js";

// Initialize the checkout with your session data
const checkout = await Purse.createHeadlessCheckout(clientSession.widget.data);

API V1

import * as Purse from "https://cdn.purse-sandbox.com/headless-checkout/latest/purse.esm.js";

// Initialize the checkout with your session data
const checkout = await Purse.createHeadlessCheckout({
apiKey: "YOUR_API_KEY",
entityId: "YOUR_ENTITY_ID",
environment: "sandbox",
paymentSession: "YOUR_SESSION"
});

Step 4: Render Hosted Fields

Subscribe to available payment methods, get the hosted fields instance, and render them:

// Subscribe to available payment methods
checkout.paymentMethods.subscribe((methods) => {
const creditCard = methods.find(m => m.method === "creditcard");

if (creditCard) {
// Get hosted fields instance
const hostedFields = creditCard.getHostedFields({
fields: {
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"
}
}
});

// Render the fields
hostedFields.render();
}
});

💡 The render() method mounts the secure iframes to the specified target elements.

Step 5: Customize Field Appearance

You can customize the appearance of the hosted fields using the theme configuration:

const hostedFields = creditCard.getHostedFields({
fields: {
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" }
},
theme: {
input: {
fontSize: "16px",
color: "#111827",
backgroundColor: "#ffffff",
':focus': {
color: "#111827"
},
':invalid': {
color: "#dc2626"
}
}
}
});

hostedFields.render();

Available Theme Properties

The theme object supports the following scopes for Hosted Fields:

  • global: Applies to all fields (colors, fonts, spacing)
  • input: Styles for input fields (padding, borders, focus states)
Limited Theme Support

Hosted Fields only support global and input theme options. For more extensive theming including labels and helper text, consider using Hosted Form.

Each scope supports CSS properties and pseudo-selectors like :focus, :hover, :invalid, etc.

See Theme references to get the full list of available keys.

Custom Fonts

Contact Required

For compliance reasons, please contact us before using custom fonts. You need to provide the font file to the Purse team.

To use custom fonts, include the font reference in the theme.global.fontSrc property:

const hostedFields = creditCard.getHostedFields({
fields: {
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" }
},
theme: {
global: {
fontSrc: 'custom-font-file',
fontFamily: 'MyFont, sans-serif',
fontSize: '16px'
},
input: {
fontSize: "16px",
color: "#111827"
}
}
});
Pre-approved Fonts

The following Google Fonts are pre-approved and ready to use in the secure fields:

  • Inter
  • Lato
  • Montserrat
  • Noto
  • Nunito
  • Open Sans
  • Raleway
  • Roboto
  • Work Sans

Just set the fontFamily property to the font name.

Handling Form Submission

Handle the form submission and call checkout.submitPayment():

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();
console.log("Payment successful!");
// 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

Possible errors:

  • Expired session → Session has expired, create a new client session
  • Invalid field configuration → Missing required field targets
  • Payment method is not supported → The payment method is not compatible with Hosted Fields
  • Technical errors → The payment method cannot be initialized

Complete Example

Here's a full working example ready to copy-paste:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hosted Fields - Payment Form</title>
<style>
body {
font-family: system-ui, sans-serif;
max-width: 500px;
margin: 50px auto;
padding: 20px;
}
.field {
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 4px;
font-weight: 500;
}
.input-container {
border: 1px solid #ccc;
border-radius: 4px;
padding: 10px;
height: 40px;
}
.row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
button {
width: 100%;
padding: 12px;
background: #0066cc;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.error {
color: #dc2626;
font-size: 14px;
margin-top: 8px;
display: none;
}
</style>
</head>
<body>
<h1>Payment Form</h1>

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

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

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

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

<script type="module">
import * as Purse from "https://cdn.purse-sandbox.com/headless-checkout/latest/purse.esm.js";

// Initialize checkout with your session token
const checkout = await Purse.createHeadlessCheckout("client-session-token");

// Get credit card payment method and render hosted fields
checkout.paymentMethods.subscribe((methods) => {
const creditCard = methods.find(m => m.method === "creditcard");

if (creditCard) {
const hostedFields = creditCard.getHostedFields({
fields: {
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" }
},
theme: {
input: {
fontSize: "16px",
color: "#111827",
':focus': {
color: "#111827"
},
':invalid': {
color: "#dc2626"
}
}
}
});

hostedFields.render();
}
});

// Handle form submission
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();
console.log("Payment successful!");
// 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";
}
});
</script>
</body>
</html>

Summary

Hosted Fields is ideal when you want:

  • Full UI control for vault credit card forms
  • PCI-compliant field handling without managing sensitive data
  • Seamless integration into your existing checkout page
  • Customizable styling to match your brand

For other payment methods or a simpler integration, consider using the Hosted Form or Headless Checkout SDK directly.