Hosted Fields SDK
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.
<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
};Configuration
Basic Setup
createHostedFields is an async function that returns a Promise resolving when all iframes are mounted.
// Ensure the SDK script is loaded before calling this
const fields = await window.HostedFieldsSDK.createHostedFields({
baseUrl: 'https://api.example.com',
apiKey: '<your_api_key>',
merchantAccountId: '<your_merchant_id>',
paymentRequestId: '<your_payment_request_id>',
cardNumber: {
mountPoint: '#card-number',
placeholder: '0000 0000 0000 0000',
},
expiryDate: {
mountPoint: '#card-expiry',
placeholder: 'MM/YY',
},
cvv: {
mountPoint: '#card-csc',
placeholder: 'CVV',
},
cardholderName: {
mountPoint: '#cardholder-name',
placeholder: 'Cardholder Name',
},
});
Configuration Object
interface FieldsConfig {
baseUrl?: string; // Base URL for iframe endpoints (optional)
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 requests (optional)
initTimeout?: number; // SDK initialization timeout in ms (optional)
loadingBehavior?: 'disabled' | 'readonly'; // How fields behave during loading/submission (optional)
style?: Partial<CommonStyling>; // Global styling applied to all fields (optional)
cardNumber: Partial<CommonField>; // Card number field configuration (required)
expiryDate: Partial<CommonField>; // Expiry date field configuration (required)
cvv: Partial<CommonField>; // CVV field configuration (required)
cardholderName?: Partial<CommonField>; // Cardholder name field configuration (optional)
}
interface CommonField {
mountPoint?: string | HTMLElement; // CSS selector or DOM element reference (required internally)
placeholder?: string; // Placeholder text (optional)
loadingBehavior?: 'disabled' | 'readonly'; // Override global loading behavior (optional)
style?: Partial<CommonStyling>; // Field-specific styling overriding global (optional)
}
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')
}
Mount Point Options
The mountPoint parameter accepts either:
- CSS Selector (string):
'#card-number','.payment-field' - HTMLElement: Direct reference to a DOM element
Dynamic Configuration Updates
Update field configurations at runtime using updateConfig():
await fields.updateConfig({
style: {
fontSize: '18px',
color: '#333',
},
cardNumber: {
placeholder: 'Enter Card Number',
},
});Important Restrictions:
- Cannot change
baseUrl,apiKey,merchantAccountId, ormountPoint- Only styling properties and placeholder text can be updated
Styling
Supported CSS Value Formats
Length Units
- Pixels:
'10px','40px' - Rem:
'1rem','2.5rem' - Em:
'1em','1.5em' - Percentage:
'100%','50%' - Viewport units:
'100vw','50vh' - Auto/inherit:
'auto','inherit'
Colors
- Hex:
'#ffffff','#fff' - RGB:
'rgb(255, 255, 255)' - RGBA:
'rgba(255, 255, 255, 0.5)' - Named colors:
'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 fields = await window.HostedFieldsSDK.createHostedFields({
style: { height: '40px', fontSize: '16px' },
cardNumber: { mountPoint: '#card-number' },
});Field-specific styling overrides global:
const fields = await window.HostedFieldsSDK.createHostedFields({
style: { color: '#333' },
cardNumber: {
mountPoint: '#card-number',
style: { color: '#007bff' }, // Override global color
},
});Field Interactions
Available Methods
Each field (cardNumber, expiryDate, cvv, cardholderName) provides these methods:
focus(): Set focus to the field.blur(): Remove focus from the field.validate(): Validate the individual field value asynchronously.onFocus(callback): Listen to focus events. Returns an unsubscribe function.onBlur(callback): Listen to blur events. Returns an unsubscribe function.onInput(callback): Listen to input/change events. Returns an unsubscribe function.onDisabledChange(callback): Listen to disabled state changes. Callback receives(disabled: boolean). Returns an unsubscribe function.onReadonlyChange(callback): Listen to readonly state changes. Callback receives(readonly: boolean). Returns an unsubscribe function.
Global State Management
The fields instance provides methods to control the loading and disabled state of all fields or specific fields at once:
setLoading(isLoading: boolean): Toggles theloadingBehavior(disabling or making readonly) for all fields based on your global/field configurations. Useful during tokenization or network requests.setDisabled(disabled: boolean, fieldKey?: string): Forcefully enables or disables all fields. Optionally provide afieldKey(e.g.,'cardNumber') to target a specific field.setReadonly(readonly: boolean, fieldKey?: string): Forcefully sets all fields to readonly or editable. Optionally provide afieldKeyto target a specific field.
Global Validation
validateAll()Validate all required fields at once. This method is crucial for checking the form state before attempting to submit payment data.
const result = await fields.validateAll();
// Returns (valid):
// { valid: true }
// OR (invalid):
// {
// valid: false,
// errors: {
// cardNumber: {
// type: 'invalid_card_number',
// message: 'Invalid card number format',
// field: 'cardNumber',
// retryable: true
// }
// }
// }
Submitting Payment Data
tokenize() Submit payment data and create a token.
Important: The
tokenize()method will automatically abort and will not send any network requests if the fields are obviously invalid. Best Practice: You should always callvalidateAll()before callingtokenize(). This allows you to retrieve a complete map of validation errors for all fields to update your UI accordingly.
// 1. Validate all fields first
const validation = await fields.validateAll();
if (!validation.valid) {
console.error('Form contains invalid fields:', validation.errors);
return;
}
// 2. If validation passes, attempt to tokenize
const result = await fields.tokenize();
if (result.token) {
// Send the token to your backend securely
await processPaymentOnBackend(result.token);
} else {
console.error('Tokenization failed:', result.error?.message);
}
Cleanup with destroy()
Call destroy() when removing the payment form from the page, especially in single-page applications.
// Clean up fields, remove iframes from DOM, and automatically remove all event listeners
fields.destroy();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 minimize 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: Dangerous CSS patterns are strictly rejected. This includes URL functions (url()), expressions (calc()), !important declarations, CSS variables (var()), and animations/transforms.
- Safe Formatting: Numeric values are auto-normalized with safe units (px, rem, %). Colors 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 fields;
async function initializePaymentForm() {
try {
fields = await window.HostedFieldsSDK.createHostedFields({
baseUrl: 'https://api.example.com',
apiKey: '<your_api_key>',
merchantAccountId: '<your_merchant_id>',
cardNumber: { mountPoint: '#card-number' },
expiryDate: { mountPoint: '#card-expiry' },
cvv: { mountPoint: '#card-csc' },
style: {
height: '40px',
fontSize: '16px',
},
});
} catch (error) {
console.error('Initialization failed:', error.message);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializePaymentForm);
} else {
initializePaymentForm();
}
window.handlePaymentSubmit = async () => {
if (!fields) return;
const validation = await fields.validateAll();
if (!validation.valid) {
alert('Please correct the highlighted fields.');
return;
}
try {
const result = await fields.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 4 hours ago
