π Hosted Fields SDK
Secure SDK for embedding payment input fields (card number, expiry date, CVV, etc.) inside iframes. It is designed to ensure PCIβcompliance by isolating sensitive data from the merchant's site. Supports both new card payments and paying with previously saved cards (vaulted tokens).
π Usage
- Include the SDK on your checkout page (currently available via CDN only):
<!-- Sandbox -->
<script src="https://engine-sandbox.pay.tech/hf/sdk/lib/umd/index.js"></script>
<!-- Production -->
<script src="https://engine.pay.tech/hf/sdk/lib/umd/index.js"></script>
Inject these URLs via your server-side config or a build-time variable β never hardcode production URLs in source code.
- Initialize the SDK with your paymentId:
const sdk = HostedFieldsSDK({ paymentId: 'payment_id_required', theme: 'dark' });
// optional: show a loading skeleton while the SDK fetches session data
sdk.renderLoadingSkeleton('#loading-container', 4);
- paymentId (string, required) β your payment session ID.
- theme ('light' | 'dark', optional) β initial color theme for all hosted fields. Defaults to
'light'. If you pass customfieldStylesand use the dark theme, make sure to also providefieldStyles.darkoverrides β otherwise your light-mode colors will persist in dark mode.
renderLoadingSkeletonrenders animated placeholder rows inside the given container. Call it right after creating the SDK instance β beforeinit()β so users see a loading state instead of blank space. The skeleton is replaced once you mount the actual fields inonReady.
- Prepare containers for the fields on your page:
<!-- shown while the SDK loads, hidden once fields are mounted -->
<div id="loading-container"></div>
<form autocomplete="on" onsubmit="submitPayment(event)">
<div id="cardNumber" style="height: 3.5rem;"></div>
<div style="display: flex; gap: 1rem;">
<div id="expiryDate" style="height: 3.5rem; flex: 1;"></div>
<div id="cvv" style="height: 3.5rem; flex: 1;"></div>
</div>
<div id="cardholderName" style="height: 3.5rem;"></div>
<button type="submit">Submit</button>
</form>
Each field container needs an explicit height (e.g.
3.5rem) β without it the iframe will collapse and the field won't be visible.The
idyou give to the loading container is the selector you pass torenderLoadingSkeleton. Hide the container yourself once the fields are mounted (see step 4).If you support saved cards, you will need two separate screens instead of a single form. See the Saved Cards section for the full HTML structure.
- Mount the payment fields inside
onReady. Define your callbacks and helper functions before callingsdk.init()βonReadyfires onceinit()successfully loads the session data:
sdk.onReady(() => {
loadCheckoutData();
});
// If you support saved cards, check sdk.getSavedCards() here
// and show the saved cards screen instead β see the Saved Cards section.
function loadCheckoutData() {
document.getElementById('loading-container').style.display = 'none';
// fieldStyles is optional β the SDK has built-in default styles.
// Pass fieldStyles only if you want to customize colors, borders, etc.
// If you use a dark theme with custom styles, also provide fieldStyles.dark overrides.
sdk.mountField('#cardNumber', {
fieldType: 'cardNumber',
label: 'Card Number',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' }
});
sdk.mountField('#expiryDate', {
fieldType: 'expiryDate',
label: 'Expiry Date',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' }
});
sdk.mountField('#cvv', {
fieldType: 'cvv',
label: 'Card Security Code',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' }
});
sdk.mountField('#cardholderName', {
fieldType: 'cardholderName',
label: 'Cardholder Name',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' }
});
}
sdk.onError((err) => console.error('SDK Error:', err));
sdk.onFieldValid((info) => console.log('Field valid:', info));
- Start the SDK:
sdk.init();
- Tokenize on form submission:
async function submitPayment(e) {
e.preventDefault(); // prevent the form from reloading the page
try {
const result = await sdk.handleSubmit({
customerEmail: '[email protected]',
billingCountryCode: 'US'
});
if (result?.redirectUrl) window.location.replace(result.redirectUrl);
} catch (error) {
console.error('SDK error on submit:', error);
}
}
π Available Methods
HostedFieldsSDK(config)
Creates a new SDK instance. Call as a regular function β do not use new.
Parameters:
- paymentId (string, required) β the paymentId you received after creating the payment.
Checkout session requirements:
- Payment method must be BASIC_CARD.
- State must be CHECKOUT.
- Both amount (non-zero) and currency must be set on the payment.
.init()
Initializes the SDK and validates your payment id. Must be called before mounting fields.
.onError(callback)
Registers a callback to handle initialization or runtime errors. The callback receives an error object with a message.
β οΈ Register
onErrorbefore callinginit(). Errors frominit()(invalidpaymentId, network failures, invalid checkout state) only reach this callback if it's already registered β otherwise they fall back toconsole.errorand you have no way to surface them to the user.
.onFieldValid(callback)
Registers a callback that is triggered when a field becomes valid (e.g. after the user corrects an invalid input). The callback receives an object with fieldType β the type of the field that became valid ('cardNumber', 'expiryDate', 'cvv', or 'cardholderName').
.mountField(containerSelector, options)
Injects a secure payment input field (inside an iframe) into the specified container on your page.
β οΈ Calling
mountFieldwith afieldTypethat's already mounted is a no-op β the existing field stays as-is and your new options/styles are ignored. To re-mount with different options, callunmountField(fieldType)first.
Parameters:
- containerSelector (string, required) β CSS selector for a container div in your HTML.
- options (object, required):
- fieldType (string, required) β the type of payment field to render. One of 'cardNumber', 'expiryDate', 'cvv', 'cardholderName'.
- label (string, optional) β label text displayed on the input.
- cardBrand (string, optional) β pre-set the card brand for CVV validation. Use when mounting a CVV field for a saved card, where the brand is already known and the cardNumber field is not mounted. Accepts the same brand strings as setCardBrand().
- fieldStyles (object, optional) β customize appearance:
- variant β 'standard' or 'outlined'
- labelPosition β 'floating' or 'above'
- fieldWrapper β custom CSS for the field wrapper
- inputBase β custom CSS for the input
- invalidInputBase β styles applied when the input is invalid
- labelBase β custom CSS for the label
- invalidLabelBase β styles applied to the label when input is invalid
- dark (object, optional) β overrides applied only in dark theme. Accepts the same keys: fieldWrapper, inputBase, labelBase, invalidInputBase, invalidLabelBase. Layered on top of base styles when the theme is 'dark'. If no dark overrides are provided, the base custom styles remain active in both themes.
Example with dark overrides:
sdk.mountField('#cardNumber', {
fieldType: 'cardNumber',
label: 'Card Number',
fieldStyles: {
inputBase: {
backgroundColor: '#fff',
border: '1px solid #D0D5DD',
color: '#1D2939',
},
labelBase: {
color: '#667085',
backgroundColor: '#fff',
},
dark: {
inputBase: {
backgroundColor: 'transparent',
border: '1px solid #475467',
color: '#F5F5F6',
},
labelBase: {
color: '#98A2B3',
backgroundColor: '#1D2939',
},
},
},
});
.unmountField(fieldType)
Removes a mounted field from the DOM and resets its internal state. The field can be re-mounted into a different container afterwards.
Parameters:
- fieldType (string, required) β the type of field to remove: 'cardNumber', 'expiryDate', 'cvv', or 'cardholderName'.
Example:
sdk.unmountField('cvv');
.unmountFields(fieldTypes)
Removes multiple mounted fields at once. Convenience wrapper around .unmountField().
Parameters: - fieldTypes (string[], required) β array of field types to remove.
Example:
sdk.unmountFields(['cardNumber', 'expiryDate', 'cvv', 'cardholderName']);
.handleSubmit(additionalFields)
Collects card data from mounted fields, validates it, and submits the payment.
Parameters: - additionalFields (object, optional) β additional data to include in the payment payload. Keys must be valid field names; values are strings.
Available field names:
- saveCard β (string: 'true' | 'false') tokenize and save the card for future payments (new card flow). Note: the value is a string, not a boolean.
- selectedCardId β (string) ID of a saved card to pay with; only the CVV field value is collected, all other card fields are ignored.
- customerFirstName, customerLastName, customerDateOfBirth, customerEmail, customerCitizenshipCountryCode, customerPhone, customerAccountNumber, customerPersonalId
- billingCountryCode, billingAddressLine1, billingAddressLine2, billingCity, billingState, billingPostalCode
- documentType, documentNumber, pin
Validation behavior:
- On submission, all mounted fields are validated. If any are invalid the SDK marks them with an error state, focuses the first invalid field, and rejects with 'Please fill in all required fields correctly.'
- If the checkout session has BIN restrictions, the card is validated against them after submission. Invalid brand rejects with 'Card number brand is not valid.'; invalid issuing country rejects with 'Card number country is not valid.'
Returns: a promise with the payment result. The response always includes a redirectUrl β navigate the customer to it to complete the flow (e.g. 3-D Secure or success page).
Example:
const result = await sdk.handleSubmit({
customerEmail: '[email protected]',
billingCountryCode: 'US',
billingCity: 'New York'
});
.setTheme(theme)
Dynamically switches the color theme for all mounted hosted fields.
Parameters: - theme ('light' | 'dark', required) β the theme to apply.
Example:
sdk.setTheme('dark');
.setCardBrand(brand)
Manually sets the card brand for the CVV field. Use this when paying with a saved card β since the cardNumber field is not mounted, the brand cannot be detected automatically. The brand determines the expected CVV length (e.g. Amex requires 4 digits).
Parameters:
- brand (string, required) β card brand identifier (e.g. 'visa', 'mastercard', 'amex').
Example:
sdk.setCardBrand('visa');
.getSavedCards()
Returns the list of saved cards for the current customer.
β οΈ Only call this method inside or after
onReadyβ the session data is not loaded untilinit()completes. Calling it earlier will return an empty array.
Returns: Array<{ id: number, brand: string, panMasked: string, expiryMonth: number, expiryYear: number }>
Example:
const cards = sdk.getSavedCards();
// [{ id: 42, brand: 'visa', panMasked: 'β’β’β’β’ 4242', expiryMonth: 12, expiryYear: 2027 }]
.deleteCard(cardId)
Sends a delete request for the specified saved card via the payment provider API.
Parameters: - cardId (number | string, required) β the ID of the card to delete.
Returns a Promise that resolves on success or rejects on failure (also triggers the onError callback).
Example:
await sdk.deleteCard(42);
.renderLoadingSkeleton(containerSelector, count, height?)
Renders animated loading skeleton placeholders inside a container. Useful while the SDK is initializing or while switching between screens. Skeletons automatically reflect the current theme (light / dark).
Parameters:
- containerSelector (string, required) β CSS selector of the container to render skeletons into.
- count (number, required) β number of skeleton rows to render.
- height (string, optional) β height of each skeleton row (e.g. '3.5rem', '4rem'). If omitted, the skeleton fills its container via CSS β control the height by setting it on the container element itself.
Example:
sdk.renderLoadingSkeleton('#form-fields', 4);
sdk.renderLoadingSkeleton('#card-row', 1, '4rem');
β οΈ Errors
The SDK surfaces errors in two ways:
- Promise rejections from async methods (
init,handleSubmit,deleteCard) βawaitthem inside atry/catch. - The
onErrorcallback β fired for both async failures and runtime issues that have no calling promise (e.g. a hosted-field iframe failing to load). Register it beforeinit()so it catches initialization errors.
Errors from async methods reach onError and reject the returned promise β handle whichever fits your flow, but do not duplicate UI feedback.
Common errors
| Message | Thrown / fired by | When it happens |
|---|---|---|
Invalid or unauthorized merchant token |
init() |
paymentId is missing or empty in the SDK config. |
Invalid checkout state or method |
init() |
The checkout session does not meet the requirements: state must be CHECKOUT, payment method must be BASIC_CARD, and both amount and currency must be set. |
HostedFields not initialized. Call .init() first. |
mountField(), handleSubmit(), deleteCard() |
Method was called before init() resolved. Call SDK methods inside or after onReady. |
Container not found: <selector> |
mountField() |
The CSS selector passed to mountField doesn't match any element in the DOM. Check that the container exists and the selector is correct. |
Please fill in all required fields correctly. |
handleSubmit() |
One or more mounted fields are invalid. The SDK marks them with an error state and auto-focuses the first invalid one. |
Card number brand is not valid. |
handleSubmit() |
The card brand is not in the checkout's allowed BIN brands. Only relevant when the merchant has BIN restrictions configured. |
Card number country is not valid. |
handleSubmit() |
The card's issuing country is rejected by the checkout's BIN country restrictions. |
Field "<fieldType>" failed to load |
onError only |
The hosted-field iframe failed to load (network error, CSP blocking, etc.). The container shows a fallback "Failed to load β please refresh" placeholder. |
For network failures from deleteCard() and the underlying form-submission step in handleSubmit(), the rejection error comes directly from the API and is also passed to onError.
Recommended pattern
sdk.onError((err) => {
// surface to the user (toast, banner, etc.)
showErrorToast(err.message);
});
sdk.onReady(() => {
mountFields();
});
sdk.init();
async function submitPayment(e) {
e.preventDefault();
try {
const result = await sdk.handleSubmit({ /* ... */ });
if (result?.redirectUrl) window.location.replace(result.redirectUrl);
} catch (error) {
// already surfaced via onError β no need to duplicate
console.error('Submit failed:', error);
}
}
π³ Saved Cards
The SDK supports paying with previously saved cards. The general flow is:
- After
onReady, callsdk.getSavedCards()to get the customer's saved cards. - Render the card list in your UI (brand, masked number, expiry).
- When the user selects a card β mount the CVV field, then call
sdk.setCardBrand(brand). - Call
sdk.handleSubmit({ selectedCardId })to pay with the selected card.
To offer saving a new card, pass saveCard: 'true' to handleSubmit (the value is a string, not a boolean).
A CVV hosted field is required for saved card payments. Even though the card number and expiry are already stored on the backend, the customer must re-enter their CVV on every transaction. You must mount a
cvvfield and ensure it is valid before callinghandleSubmitβ otherwise the SDK will reject the submission.The CVV value never touches your server. It is captured inside an isolated iframe and sent directly to the payment provider by the SDK. Your page has no access to the raw value at any point. This is what keeps the integration PCI-compliant β you render the UI, the SDK handles all sensitive data.
Styling is fully yours. The saved cards list is plain HTML rendered by the merchant β the SDK only provides the data (
id,brand,panMasked,expiryMonth,expiryYear). You have full control over layout, card item design, selected state, delete button, icons, and animations. The only hosted element is the CVV field itself, which is an iframe styled viafieldStyles.
Key SDK operations
Selecting a card β unmount any previous CVV iframe, mount a fresh one inside the selected card's container, then set the brand so the CVV field knows the expected code length (3 digits for most brands, 4 for Amex):
sdk.unmountField('cvv');
sdk.mountField('#cvv-container', {
fieldType: 'cvv',
label: 'Card Security Code',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' },
});
sdk.setCardBrand(brand);
β οΈ Always unmount CVV before mounting it again for a different card. Without this, the previously typed CVV value stays in the field β the user may not notice and accidentally pay with the wrong code.
Paying with a saved card β pass selectedCardId to handleSubmit. You do not need to pass cardNumber, expiryDate, or cardholderName β those come from the saved card on the backend. The SDK uses the CVV value from the mounted CVV field automatically.
const result = await sdk.handleSubmit({
selectedCardId,
billingCountryCode: 'CY',
});
Deleting a card β deleteCard() automatically updates the local list, so getSavedCards() reflects the change immediately. If the deleted card was the selected one, unmount its CVV.
await sdk.deleteCard(cardId);
const updatedCards = sdk.getSavedCards();
β οΈ Deletion is irreversible. The SDK has no built-in confirmation step β you can call
sdk.deleteCard()directly on a button click, or wrap it in your own confirmation modal for safety. Both options are fine; the modal is recommended for production.
Example with a custom confirmation modal:
function onDeleteClick(card) {
// open your own modal β the SDK doesn't render one
showConfirmModal({
title: 'Delete card?',
message: `${card.brand.toUpperCase()} β’β’β’β’ ${card.panMasked.slice(-4)} will be removed.`,
onConfirm: async () => {
await sdk.deleteCard(card.id);
renderCardList(sdk.getSavedCards());
},
});
}
Switching screens β when going from the saved-cards screen to the new-card form, unmount the saved-card CVV and mount the four basic fields. To go back, unmount all four (including CVV) before re-rendering the saved cards list:
// β new card form
sdk.unmountField('cvv');
sdk.mountField('#cardNumber', { fieldType: 'cardNumber', label: 'Card Number' });
sdk.mountField('#expiryDate', { fieldType: 'expiryDate', label: 'Expiry Date' });
sdk.mountField('#cvv', { fieldType: 'cvv', label: 'CVV' });
sdk.mountField('#cardholderName', { fieldType: 'cardholderName', label: 'Cardholder Name' });
// β back to saved cards
sdk.unmountFields(['cardNumber', 'expiryDate', 'cvv', 'cardholderName']);
For a complete working implementation that ties all of this together, see the full saved-cards example below.
πΌ Full example form
This example is for a checkout page without saved cards. For saved cards support see the next example. The HTML is intentionally minimal β add your own styles, icons, and layout as needed.
<div class="card">
<div class="card-header">
<h3>Basic card</h3>
<button id="themeToggle" onclick="toggleTheme()">Toggle theme</button>
</div>
<!-- shown while the SDK loads, hidden once fields are mounted -->
<div id="loading-container"></div>
<form class="card-body" autocomplete="on" onsubmit="submitPayment(event)">
<div id="cardNumber"></div>
<div class="field-row">
<div id="expiryDate"></div>
<div id="cvv"></div>
</div>
<div id="cardholderName"></div>
<button type="submit">Submit payment</button>
</form>
</div>
<!-- Load the Hosted Fields SDK -->
<script src="https://your-domain.com/hf/sdk/lib/umd/index.js"></script>
<script>
const sdk = HostedFieldsSDK({ paymentId: '<payment_id>', theme: 'dark' });
// show a loading skeleton while the SDK fetches session data
sdk.renderLoadingSkeleton('#loading-container', 4);
sdk.onReady(() => {
loadCheckoutData();
});
function loadCheckoutData() {
document.getElementById('loading-container').style.display = 'none';
sdk.mountField('#cardNumber', {
fieldType: 'cardNumber',
label: 'Card Number',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' }
});
sdk.mountField('#expiryDate', {
fieldType: 'expiryDate',
label: 'Expiry Date',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' }
});
sdk.mountField('#cvv', {
fieldType: 'cvv',
label: 'Card Security Code',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' }
});
sdk.mountField('#cardholderName', {
fieldType: 'cardholderName',
label: 'Cardholder Name',
fieldStyles: { variant: 'outlined', labelPosition: 'floating' }
});
}
// Toggle between light and dark themes
let isDark = true;
function toggleTheme() {
isDark = !isDark;
sdk.setTheme(isDark ? 'dark' : 'light');
}
sdk.onError((err) => console.error("SDK Error:", err));
sdk.onFieldValid((info) => console.log("Field valid:", info));
sdk.init();
async function submitPayment(e) {
e.preventDefault(); // prevent the form from reloading the page
try {
const result = await sdk.handleSubmit({
customerEmail: '[email protected]',
billingCountryCode: 'US'
});
if (result?.redirectUrl) window.location.replace(result.redirectUrl);
} catch (error) {
console.error('SDK error on submit:', error);
}
}
</script>
πΌ Full example: saved cards + basic card
The HTML below is intentionally minimal β it focuses on SDK integration logic, not visual design. In a real implementation you would add your own button styles, icons, card brand logos, loading states, and layout as needed. Container
idvalues (e.g.#cvv,#cvv-container) can be any name you choose β just make sure the same selector is used in both your HTML and the correspondingmountFieldcall.
<button onclick="toggleTheme()">Toggle theme</button>
<!-- Saved cards screen -->
<div id="saved-cards-screen">
<div id="saved-cards-list"></div>
<div id="cvv-container"></div>
<button onclick="showBasicCardForm()">+ Add new card</button>
<button onclick="paySavedCard()">Submit payment</button>
</div>
<!-- Basic card screen -->
<div id="basic-card-screen" style="display:none">
<button onclick="showSavedCards()">β Back</button>
<div id="cardNumber"></div>
<div class="field-row">
<div id="expiryDate"></div>
<div id="cvv"></div>
</div>
<div id="cardholderName"></div>
<label><input type="checkbox" id="saveCard"> Save card for future payments</label>
<button onclick="payBasicCard()">Submit payment</button>
</div>
<!-- Delete confirmation modal -->
<div id="delete-modal" style="display:none">
<div>
<p>Delete <span id="delete-modal-card"></span>?</p>
<button onclick="closeDeleteModal()">Cancel</button>
<button id="confirm-delete">Delete</button>
</div>
</div>
<script src="https://your-domain.com/hf/sdk/lib/umd/index.js"></script>
<script>
const sdk = HostedFieldsSDK({ paymentId: '<payment_id>', theme: 'dark' });
let selectedCardId = null;
let isDark = true;
function toggleTheme() {
isDark = !isDark;
sdk.setTheme(isDark ? 'dark' : 'light');
}
const fieldStyles = {
variant: 'outlined',
labelPosition: 'floating',
};
sdk.onReady(() => {
loadCheckoutData();
});
function loadCheckoutData() {
const cards = sdk.getSavedCards();
if (cards.length > 0) {
showSavedCards();
} else {
showBasicCardForm();
}
}
sdk.onError((err) => console.error('SDK Error:', err));
sdk.init();
// ββ Saved cards screen ββββββββββββββββββββββββββββββββββββββββββ
function showSavedCards() {
sdk.unmountFields(['cardNumber', 'expiryDate', 'cvv', 'cardholderName']);
document.getElementById('saved-cards-screen').style.display = 'block';
document.getElementById('basic-card-screen').style.display = 'none';
renderCardList(sdk.getSavedCards());
}
function renderCardList(cards) {
const list = document.getElementById('saved-cards-list');
list.innerHTML = '';
cards.forEach(card => {
const item = document.createElement('div');
item.dataset.cardId = card.id;
item.innerHTML = `
<span>${card.brand.toUpperCase()}</span>
<span>${card.panMasked}</span>
<span>${card.expiryMonth}/${card.expiryYear}</span>
<button onclick="selectCard(${card.id}, '${card.brand}')">Select</button>
<button onclick="deleteCard(${card.id})">Delete</button>
`;
list.appendChild(item);
});
}
function selectCard(cardId, brand) {
selectedCardId = cardId;
// highlight selected card
document.querySelectorAll('#saved-cards-list > div').forEach(el => {
el.style.outline = el.dataset.cardId === String(cardId) ? '1px solid #512ccc' : 'none';
});
// Always unmount before mounting β clears any CVV entered for a previously
// selected card. Without this the old value stays if the user switches cards.
sdk.unmountField('cvv');
sdk.mountField('#cvv-container', {
fieldType: 'cvv',
label: 'Card Security Code',
fieldStyles,
});
// inform the CVV field of the card brand (affects required digit count)
sdk.setCardBrand(brand);
}
// Opens a custom confirmation modal β the SDK has no built-in one.
// You can also call sdk.deleteCard() directly without confirmation if you prefer.
function deleteCard(cardId) {
const card = sdk.getSavedCards().find(c => c.id === cardId);
if (!card) return;
document.getElementById('delete-modal-card').textContent =
`${card.brand.toUpperCase()} β’β’β’β’ ${card.panMasked.slice(-4)}`;
document.getElementById('delete-modal').style.display = 'block';
document.getElementById('confirm-delete').onclick = async () => {
closeDeleteModal();
await sdk.deleteCard(cardId);
if (selectedCardId === cardId) {
sdk.unmountField('cvv');
selectedCardId = null;
}
const updatedCards = sdk.getSavedCards();
if (updatedCards.length === 0) showBasicCardForm();
else renderCardList(updatedCards);
};
}
function closeDeleteModal() {
document.getElementById('delete-modal').style.display = 'none';
}
async function paySavedCard() {
if (!selectedCardId) return alert('Please select a card');
try {
const result = await sdk.handleSubmit({
selectedCardId,
billingCountryCode: 'US',
});
if (result?.redirectUrl) window.location.replace(result.redirectUrl);
} catch (error) {
console.error('SDK error on submit:', error);
}
}
// ββ Basic card screen βββββββββββββββββββββββββββββββββββββββββββ
function showBasicCardForm() {
sdk.unmountField('cvv'); // remove CVV from saved card container
document.getElementById('saved-cards-screen').style.display = 'none';
document.getElementById('basic-card-screen').style.display = 'block';
sdk.mountField('#cardNumber', { fieldType: 'cardNumber', label: 'Card Number', fieldStyles });
sdk.mountField('#expiryDate', { fieldType: 'expiryDate', label: 'Expiry Date', fieldStyles });
sdk.mountField('#cvv', { fieldType: 'cvv', label: 'Card Security Code', fieldStyles });
sdk.mountField('#cardholderName', { fieldType: 'cardholderName', label: 'Cardholder Name', fieldStyles });
}
async function payBasicCard() {
try {
const result = await sdk.handleSubmit({
saveCard: document.getElementById('saveCard').checked ? 'true' : 'false',
billingCountryCode: 'US',
});
if (result?.redirectUrl) window.location.replace(result.redirectUrl);
} catch (error) {
console.error('SDK error on submit:', error);
}
}
</script>