Configure Hosted Fields SDK
Hosted fields SDK is a secure, embeddable payment form SDK that integrates card payment fields (number, expiry, CVV, and cardholder name) into your web application via iFrames. This SDK provides a PCI-compliant way to handle sensitive payment data.
Installation
- Include the SDK in your project directly via a script tag. The SDK script should be loaded from your environment's Base URL.
<!-- Load the SDK directly -->
<script src="https://api.example.com/sdk/index.umd.js"></script>- Alternatively, you can load it dynamically in JavaScript:
const script = document.createElement("script");
script.src = "https://api.example.com/sdk/index.umd.js";
document.head.appendChild(script);
script.onload = async () => {
// SDK is ready on window.HostedFieldsSDK
};Initialisation
The SDK uses a two-phase initialisation pattern:
- createElements(config) — creates a session manager (synchronous)
- elements.create(type, options) — creates individual field instances (synchronous)
- field.mount(selector) — mounts a field to the DOM (async, returns a Promise)
This pattern enables multi-step / wizard forms where fields are created in advance and mounted only when their DOM containers become visible.
Basic Usage
// 1. Create the session manager
const elements = window.HostedFieldsSDK.createElements({
baseUrl: 'https://api.example.com',
apiKey: '<your_api_key>',
merchantAccountId: '<your_merchant_id>',
paymentRequestId: '<your_payment_request_id>',
style: {
height: '40px',
fontSize: '16px',
color: '#333',
},
});
// 2. Create field instances
const cardNumber = elements.create('cardNumber', {
placeholder: '0000 0000 0000 0000',
});
const expiryDate = elements.create('expiryDate', {
placeholder: 'MM/YY',
});
const cvv = elements.create('cvv', {
placeholder: 'CVV',
});
const cardholderName = elements.create('cardholderName', {
placeholder: 'Cardholder Name',
});
// 3. Mount all fields (can be done at different times for wizard flows)
await Promise.all([
cardNumber.mount('#card-number'),
expiryDate.mount('#card-expiry'),
cvv.mount('#card-csc'),
cardholderName.mount('#cardholder-name'),
]);
Session Configuration (ElementsConfig)
interface ElementsConfig {
baseUrl?: string; // Base URL for iframe endpoints (defaults to window.location.origin)
apiKey: string; // Your API key for authentication (required)
merchantAccountId: string; // Your merchant account ID (required)
paymentRequestId: string; // The payment request session identifier (required)
timeout?: number; // Network timeout in ms for field submissions (default: 30000)
initTimeout?: number; // How long to wait for each iframe readiness (default: 30000)
loadingBehavior?: 'disabled' | 'readonly'; // How fields behave during loading/submission (optional)
style?: Partial<CommonStyling>; // Global styling applied to all fields (optional)
}
Field Options (FieldOptions)
Passed to elements.create(type, options):
interface FieldOptions {
placeholder?: string; // Placeholder text (optional)
style?: Partial<CommonStyling>; // Field-specific styling, overrides global (optional)
loadingBehavior?: 'disabled' | 'readonly'; // Override global loading behavior (optional)
}Field Types
The SDK supports four field types:
| Type | Description | Required |
cardNumber |
Card number with Luhn validatio | Yes |
expiryDate |
Expiry date (MM/YY format | Yes |
cvv |
Security code (CVV/CVC) | Yes |
cardholderName |
Cardholder name | No |
Mount Point
The field.mount() method accepts either:
- CSS Selector (string):
'#card-number','.payment-field' - HTMLElement: Direct reference to a DOM element
The target element is replaced by the iframe. Make sure the mount point is a disposable <div> placeholder.
Styling
Styling Options (CommonStyling)
interface CommonStyling {
height?: string; // Field height (e.g., '40px', '100%')
width?: string; // Field width (e.g., '100%')
fontSize?: string; // Font size (e.g., '16px')
fontFamily?: string; // Font family (e.g., 'Arial, sans-serif')
color?: string; // Text color (e.g., '#333', 'rgb(51, 51, 51)')
background?: string; // Background color (e.g., '#fff', 'transparent')
paddingTop?: string; // Top padding (e.g., '10px')
paddingBottom?: string; // Bottom padding (e.g., '10px')
paddingLeft?: string; // Left padding (e.g., '12px')
paddingRight?: string; // Right padding (e.g., '12px')
lineHeight?: string; // Line height (e.g., '40px', '1.5')
letterSpacing?: string; // Letter spacing (e.g., '1px', '0.5em')
placeholderColor?: string; // Placeholder text color (e.g., '#999')
}Supported CSS Value Formats
| Value | Format | Sample |
| Length Units | Pixels | '10px', '40px' |
| Rem | '1rem', '2.5rem' |
|
| Em | '1em', '1.5em' |
|
| Percentage | '100%', '50%' |
|
| Viewport units | '100vw', '50vh' |
|
| Auto/inherit | 'auto', 'inherit' |
|
| Colours | Hex | '#ffffff', '#fff' |
| RGB | 'rgb(255, 255, 255)' |
|
| RGBA | 'rgba(255, 255, 255, 0.5)' |
|
| Named colours | 'white', 'black', 'red', etc. |
|
| Font Families | Generic | 'serif', 'sans-serif', 'monospace' |
| Safe fonts | 'Arial', 'Helvetica', 'Times New Roman', 'Georgia' |
Global vs Field-Specific Styles
- Global styling applies to all fields:
const elements = window.HostedFieldsSDK.createElements({
// ...credentials
style: { height: '40px', fontSize: '16px' },
});- Field-specific styling overrides global:
const cardNumber = elements.create('cardNumber', {
placeholder: '0000 0000 0000 0000',
style: { color: '#007bff' }, // Override global color for this field only
});Field Interactions
Event Listeners
Each Field instance provides an .on(event, callback) method. It returns an unsubscribe function.
Events can be registered before the field is mounted — they are buffered and replayed automatically.
// Subscribe to events
const unsubFocus = cardNumber.on('focus', () => console.log('Focused'));
const unsubBlur = cardNumber.on('blur', () => console.log('Blurred'));
const unsubInput = cardNumber.on('input', () => console.log('Input changed'));
// Unsubscribe when no longer needed
unsubFocus();
Available Events
| Event | Description |
focus |
Field received focus. |
blur |
Field lost focus. |
input |
User typed or changed the value. |
ready |
Iframe is mounted and ready. |
Available Methods per Field
| Method | Returns | Description |
mount(sel) |
'Promise |
Mount the field into a DOM container. |
unmount() |
void |
Remove from DOM but keep instance alive. |
destroy() |
void |
Full teardown (unmount + clear listeners). |
focus() |
void |
Programmatically focus the input. |
blur() |
void |
Programmatically blur the input. |
validate() |
'Promise |
Validate the current value. |
update(opts) |
void |
Merge new options and push to iframe. |
on(event, cb) |
() => void |
Subscribe to an event; returns unsubscribe fn. |
Elements-Level State Management
The elements instance provides methods to control state across all fields:
**setLoading(isLoading: boolean)**— Toggles loading behaviour (disabling/readonly) for all mounted fields.**setDisabled(disabled: boolean, type?: FieldType)**— Enable or disable all fields, or a specific field by type.**setReadonly(readonly: boolean, type?: FieldType)**— Set all fields (or a specific one) to readonly.
Validation
Per-field validation
const result = await cardNumber.validate();
// { valid: true }
// or
// { valid: false, error: { type: 'invalid_card_number', message: '...', field: 'cardNumber' } }Global validation (validateAll)
(validateAll)Validates all mounted fields at once. Requires cardNumber, expiryDate, and cvv to be created and mounted.
const result = await elements.validateAll();
// { valid: true }
// or
// {
// valid: false,
// errors: {
// cardNumber: { type: 'invalid_card_number', message: '...', field: 'cardNumber', retryable: true }
// }
// }Submitting Payment Data
elements.tokenize()
Submit payment data and create a token. Requires cardNumber, expiryDate, and cvv to be created and mounted.
Important: The
tokenize()will automatically abort if fields are obviously invalid. Best Practice: Always callvalidateAll()before callingtokenize()to get a full error map for your UI.
// 1. Validate all fields first
const validation = await elements.validateAll();
if (!validation.valid) {
console.error('Form contains invalid fields:', validation.errors);
return;
}
// 2. If validation passes, attempt to tokenize
const result = await elements.tokenize();
if (result.token) {
// Send the token to your backend securely
await processPaymentOnBackend(result.token);
} else {
console.error('Tokenization failed:', result.error?.message);
}Lifecycle & Cleanup
Unmount vs Destroy
**field.unmount()**— Removes the iframe from the DOM but keeps the Field instance alive. Registered event listeners are preserved and will be reattached on the next mount() call.**field.destroy()**— Full teardown: unmounts and clears all listeners.
Cleaning up the session
Call elements.destroy() when removing the payment form. This destroys all field instances.
// Clean up all fields, remove iframes, and clear event listeners
elements.destroy();In single-page applications, always call destroy() when the payment form component unmounts.
Error Handling
Error Codes
Validation Errors:
field_invalid- field validation failedfield_required- required field is emptyinvalid_card_number- invalid card number formatinvalid_expiry_date- invalid or expired expiry dateinvalid_cvv- invalid CVV/CVC codeinvalid_cardholder_name- invalid cardholder namevalidation_failed- general validation failure
Network & Token Errors:
network_timeout- network request timed outinvalid_config- invalid SDK configuration providedforbidden_origin- domain is not authorized to load the hosted fieldstoken_creation_failed- token creation failed on the serverunknown_error- unknown error occurred
Security & Compliance
This SDK is designed from the ground up to minimise your PCI DSS compliance scope by ensuring sensitive card data never touches your application environment.
Data Protection & Architecture
- ✅ Isolated IFrames: All sensitive card data is exclusively processed within secure iFrames.
- ✅ No Data Leakage: Data never leaves the secure iframe boundary. Input masking prevents data leakage via browser history.
- ✅ PCI DSS SAQ A: Designed specifically to support SAQ A for merchants by isolating PAN and CVV.
- ✅ Secure Communication: Uses secure cross-origin communication with strict origin validation. No external API calls are allowed from the parent domain.
Integration Best Practices (Do's & Don'ts)
To maintain the integrity of the secure fields, adhere to the following rules:
- ❌ Never expose the fields instance to the window object or global scope.
- ❌ Never log, inspect, or attempt to extract raw field data in the browser console.
- ❌ Never pass user-controlled data directly into the SDK styles or configuration.
- ✅ Always keep the fields instance private within your module scope.
- ✅ Always use HTTPS in production environments.
- ✅ Always validate generated tokens on your backend before processing payments.
Strict Input & Styling Validation
The SDK actively validates all configurations to prevent CSS injection attacks and ensure visual integrity.
- Rejected CSS Patterns:
url(),calc(),!important, CSS variables (var()), animations/transforms. - Safe Formatting: Numeric values are auto-normalised with safe units (
px,rem,%). Colours and font families are validated against a strict whitelist of safe formats and generic/system fonts. - DOM Validation: Mount point selectors are validated before any iframe creation occurs.
Complete Example
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Payment Form</title>
</head>
<body>
<div class="payment-form">
<h1>Payment Details</h1>
<div class="form-group">
<label>Card Number</label>
<div class="field-wrapper" id="card-number"></div>
</div>
<div class="form-group">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div>
<label>Expiry Date</label>
<div class="field-wrapper" id="card-expiry"></div>
</div>
<div>
<label>CVV</label>
<div class="field-wrapper" id="card-csc"></div>
</div>
</div>
</div>
<button onclick="window.handlePaymentSubmit()">Pay Now</button>
</div>
<script src="https://api.example.com/sdk/index.umd.js"></script>
<script type="module">
let elements;
async function initializePaymentForm() {
try {
// 1. Create session manager
elements = window.HostedFieldsSDK.createElements({
baseUrl: 'https://api.example.com',
apiKey: '<your_api_key>',
merchantAccountId: '<your_merchant_id>',
paymentRequestId: '<your_payment_request_id>',
style: {
height: '40px',
fontSize: '16px',
},
});
// 2. Create fields
const cardNumber = elements.create('cardNumber', {
placeholder: '0000 0000 0000 0000',
});
const expiryDate = elements.create('expiryDate', {
placeholder: 'MM/YY',
});
const cvv = elements.create('cvv', {
placeholder: 'CVV',
});
// 3. Register event listeners (before mount is OK)
cardNumber.on('blur', async () => {
const result = await cardNumber.validate();
if (!result.valid) {
console.warn('Card number:', result.error.message);
}
});
// 4. Mount all fields
await Promise.all([
cardNumber.mount('#card-number'),
expiryDate.mount('#card-expiry'),
cvv.mount('#card-csc'),
]);
console.log('Payment form ready');
} catch (error) {
console.error('Initialization failed:', error.message);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializePaymentForm);
} else {
initializePaymentForm();
}
window.handlePaymentSubmit = async () => {
if (!elements) return;
const validation = await elements.validateAll();
if (!validation.valid) {
alert('Please correct the highlighted fields.');
return;
}
try {
const result = await elements.tokenize();
if (result.token) {
alert('Success! Token: ' + result.token);
} else {
alert('Payment failed: ' + (result.error?.message || 'Unknown error'));
}
} catch (error) {
alert('Submission failed. Please try again.');
}
};
</script>
</body>
</html>
Updated about 1 month ago
