Implement callbacks to customise your card payment flow for web.
Components emit events based on user interaction or validation. You can use these to implement callback functions, which allow you to inject your own business logic and user experience customisations into the payment flow at critical moments. They ensure that while the SDK handles the complex technical aspects of payment processing, you retain full control over the customer experience and can seamlessly integrate payments into your broader business workflows and systems.
Callbacks enable you to:
- Validate business rules before payments proceed.
- Display custom error, failure, or success messages.
- Tailor user interfaces to match your brand's look and feel.
- Integrate with your own systems for fraud detection or customer management.
- Control exactly how your customers experience both successful and failed transactions.
- Handle card-specific requirements like 3DS authentication and tokenisation.
- Manage saved payment methods and customer billing information.
The card components require SDK initialisation callbacks to gather shopper and shipping information during transaction processing:
onGetShopper(required): Called to retrieve current shopper data (ID, email, name, etc.) for tokenisation, transaction submission, and token management.onGetShippingAddress(required when shipping is needed): Called to get shipping address information for the transaction.
These callbacks ensure the card components always use the latest customer data from your application state, forms, or APIs when processing payments.
All card-specific events are optional and can be mixed and matched based on your business needs.
The SDK initialisation callbacks (onGetShopper, onGetShippingAddress) work alongside card-specific callbacks to provide a complete payment experience. SDK callbacks handle backend transaction data, while card callbacks handle form interactions, validation, and payment processing events.
The following tables lists all events supported by the different pre-built components.
| Event | Billing address | Card-on-file | Click-once | New card |
|---|---|---|---|---|
onBlur | ||||
onCardBrandCannotRecognised | ||||
onCardBrandDetected | ||||
onChange | ||||
onClick | ||||
onCollectEnd | ||||
onCollectStart | ||||
onCustomValidation | ||||
onCvcEntered | ||||
onDeleteTokenFailed | ||||
onDeleteTokenSuccess | ||||
onFocus | ||||
onGetFingerprintResult | ||||
onOnceCardClick | ||||
onPostAuthentication | ||||
onPostAuthorisation | ||||
onPostInitiateAuthentication | ||||
onPostTokenisation | ||||
onPreAuthentication | ||||
onPreAuthorisation | ||||
onPreDeleteToken | ||||
onPreInitiateAuthentication | ||||
onPreRenderTokens | ||||
onPreTokenisation | ||||
onRetrieveTokensFailed | ||||
onSubmitError | ||||
onUpdateTokenFailed | ||||
onUpdateTokenSuccess | ||||
onValidation | ||||
onValidationPassed | ||||
onValidationFailed | ||||
tokenItemBuilder | ||||
tokenLabelBuilder |
This callback is triggered after the payment transaction has been processed and authorisation has been completed (successfully or unsuccessfully).
You can use it to:
- Retrieve the full authorisation result from your backend using the transaction identifiers.
- Redirect customers to a success page with order confirmation.
- Update stock levels for purchased items.
- Send order confirmation emails to customers.
- Record successful transactions for business intelligence.
| Event data | Description |
|---|---|
dataobject | Object containing transaction identifiers. |
data.merchantTransactionIdstring | Your unique identifier for the transaction. Use this with systemTransactionId to retrieve full authorisation details from Unity backend. |
data.systemTransactionIdstring | The system's unique identifier for the transaction. Use this with merchantTransactionId to retrieve full authorisation details from Unity backend. |
const cardSubmit = sdk.create('card-submit', {
onPostAuthorisation: async (data) => {
console.log('Authorisation completed');
console.log('Merchant Transaction ID:', data.merchantTransactionId);
console.log('System Transaction ID:', data.systemTransactionId);
// Get authorisation result from merchant backend
const authorisationResult = await getAuthorisationResultFromGateway(data.merchantTransactionId, data.systemTransactionId);
// Handle the authorisation result
if (authorisationResult.state === 'Authorised') {
// Payment successful
console.log('Payment authorised successfully');
// Update inventory
updateInventory(data.merchantTransactionId);
// Send confirmation email
sendConfirmationEmail(customerEmail, data.merchantTransactionId);
// Track analytics
trackPurchase(data.merchantTransactionId, authorisationResult.correlationId);
// Redirect to success page
window.location.href = `/payment-success?txn=${data.merchantTransactionId}`;
} else if (authorisationResult.state === 'Declined') {
console.error('Payment declined');
handleDeclinedPayment(authorisationResult);
} else if (authorisationResult.state === 'Failed') {
console.error('Payment failed');
showErrorMessage('Payment failed. Please try again or use a different payment method.');
} else if (authorisationResult.state === 'Pending') {
console.log('Payment pending further processing');
showMessage('Payment is being processed. You will receive confirmation shortly.', 'info');
}
}
});This callback is triggered before the transaction authorisation, allowing you to provide additional transaction data or control whether to proceed.
You can use it to:
- Retrieve token details from your backend using the
gatewayTokenId. - Update the transaction decision in your backend.
- Integrate with Kount or other fraud detection services.
- Perform AVS (Address Verification System) checks.
- Apply business rules based on transaction amount or customer history.
- Control whether to proceed with authorisation by returning
null.
| Parameter | Description |
|---|---|
dataobject | Object containing token information. |
data.gatewayTokenIdstring | The token ID from the payment gateway. Use this ID to retrieve full token details from the Unity backend and update transaction decision. |
Use the gatewayTokenId with the Get masked card data related to gateway token API to retrieve full token details including card scheme, funding source (credit/debit), masked PAN, and expiry date.
After evaluating token details and transaction data, use the Modify session API to update the authorisation decision on your backend before returning from this callback. Set "authorisation": true in the session data to proceed with the transaction.
const cardSubmit = sdk.create('card-submit', {
onPreAuthorisation: async (data) => {
console.log('Pre-authorisation for token:', data.gatewayTokenId);
// Merchant can use gatewayTokenId to retrieve token details and update transaction decision on merchant BE
const transactionDecision = await getAuthorisationDecision(data.gatewayTokenId);
if (!transactionDecision) {
// To not proceed
return null;
}
// Perform pre-payment validation
const deviceSessionId = await getKountSessionId();
const isHighRisk = await checkCustomerRiskProfile();
const customerTier = await getCustomerTier();
// Get billing address if AVS is enabled
const billingAddress = await getBillingAddress();
return {
addressVerification: billingAddress ? {
countryCode: billingAddress.countryCode,
houseNumberOrName: billingAddress.address,
postalCode: billingAddress.postalCode,
city: billingAddress.city,
state: billingAddress.state
} : undefined,
riskScreeningData: {
deviceSessionId: deviceSessionId,
performRiskScreening: true,
customData: {
customerTier: customerTier,
orderType: 'ecommerce',
previousTransactionCount: await getPreviousTransactionCount(),
riskScore: isHighRisk ? 'high' : 'low'
}
}
};
}
});This callback is triggered before card tokenisation begins, allowing you to control whether tokenisation should proceed.
You can use it to:
- Validate card information before tokenisation.
- Check if the customer wants to save their payment method.
- Apply business rules for token storage.
- Implement custom security checks.
This callback receives no parameters.
const cardSubmit = sdk.create('card-submit', {
onPreTokenisation: () => {
// Check if customer consented to save card
const saveCardConsent = document.getElementById('save-card-checkbox')?.checked;
if (!saveCardConsent) {
console.log('Customer declined to save card - skipping tokenisation');
return false;
}
// Additional validation
const isValidForStorage = validateCardForStorage();
if (!isValidForStorage) {
console.log('Card not suitable for storage - skipping tokenisation');
return false;
}
console.log('Proceeding with card tokenisation');
return true;
}
});This callback is triggered after card tokenisation completes with the tokenisation result.
You can use it to:
- Retrieve full token details from your backend using the
gatewayTokenId. - Evaluate token details to decide whether to proceed with authentication/authorisation.
- Update the user interface to show saved payment methods.
- Link the token to the customer's account.
- Log tokenisation events for analytics.
| Parameter | Description |
|---|---|
dataobject | Object containing tokenisation result. |
data.gatewayTokenIdstring | The unique identifier for the tokenised card. Use this ID to retrieve full token details from Unity backend. |
Use the gatewayTokenId with the Get masked card data related to gateway token API to retrieve full token details including card scheme, funding source (credit/debit), masked PAN, and expiry date.
After evaluating token details and transaction data, use the Modify session API to update the authorisation decision on your backend before returning from this callback. Set "authorisation": true in the session data to proceed with the transaction.
const cardSubmit = sdk.create('card-submit', {
onPostTokenisation: async (data) => {
console.log('Card tokenised successfully. Token ID:', data.gatewayTokenId);
// If merchant wants to evaluate the token details to decide whether to proceed with authentication/authorisation
// Merchant BE uses the returned gatewayTokenId to get Token Details from Unity
const tokenDetails = await fetchTokenDetailsFromBackend(data.gatewayTokenId);
// Merchant BE evaluates the token details to update authentication/authorisation decision via Unity Update session API
const decision = await evaluateTokenAndUpdateSession(tokenDetails);
// Store token for future use
await saveTokenToCustomerAccount({
tokenId: data.gatewayTokenId,
customerId: getCurrentCustomerId()
});
// Update UI to show saved card
updateSavedPaymentMethodsUI(data.gatewayTokenId);
// Track tokenisation event
trackEvent('card-tokenized', {
tokenId: data.gatewayTokenId,
timestamp: new Date().toISOString()
});
// Show success message
showMessage('Payment method saved successfully', 'success');
}
});This callback is triggered when the input loses focus.
You can use it to:
- Validate field data when user moves to next field.
- Show field-specific help or validation messages.
- Track user interaction patterns for UX analytics.
- Auto-format field values on blur.
| Parameter | Description |
|---|---|
eventEvent | The blur event object containing target and field information. |
event.typestring | The event type (always blur). |
event.targetHTMLInputElement | The input element that lost focus. |
event.target.valuestring | The current value of the field. |
event.target.idstring | The field identifier (e.g., card-number, card-expiry-date). |
event.target.namestring | The field name attribute. |
event.target.validityValidityState | The HTML5 validation state of the field. |
event.target.validity.validboolean | Whether the field passes HTML5 validation. |
event.target.validity.valueMissingboolean | Whether a required field is empty. |
event.currentTargetHTMLInputElement | The element that the event listener is attached to. |
event.timestampnumber | The date and time when the event occurred. |
const cardNumberComponent = sdk.create('card-number', {
onBlur: (event) => {
console.log('Card number field blurred:', event.target.value);
// Validate card number on blur
if (event.target.value) {
const isValid = validateCardNumber(event.target.value);
if (!isValid) {
showFieldError('card-number', 'Please enter a valid card number');
} else {
clearFieldError('card-number');
}
}
// Track field interaction
trackFieldInteraction('card-number', 'blur', {
hasValue: !!event.target.value,
timestamp: new Date().toISOString()
});
}
});This callback is triggered when the card number input changes, but the entered digits don't match any known card brand patterns.
You can use it to:
- Display appropriate error messages for unsupported card types.
- Guide users to use supported payment methods.
- Log attempts to use unsupported card brands for analytics.
- Redirect users to alternative payment options.
| Parameter | Description |
|---|---|
eventCustomEvent | The card brand recognition failure event object. |
event.typestring | The event type (always cardbrandcannotrecognised). |
event.targetHTMLInputElement | The card number input field. |
event.target.valuestring | The current card number value that couldn't be recognised. |
event.target.selectionStartnumber | The current cursor/selection start position. |
event.target.selectionEndnumber | The current cursor/selection end position. |
event.detailobject | Additional details about the recognition failure. |
event.detail.inputLengthnumber | The length of the current input value. |
event.detail.attemptsnumber | The number of recognition attempts made. |
event.detail.lastKnownBrandstring | The last successfully detected brand (if any). |
event.detail.supportedBrandsarray | Array of the card brands you support. |
event.detail.inputPatternstring | The current input pattern (first 6 digits or BIN). |
const cardNumberComponent = sdk.create('card-number', {
onCardBrandCannotRecognised: (event) => {
console.log('Card brand could not be recognised for:', event.target.value);
// Show error message for unsupported card
showFieldError('card-number', 'This card type is not supported. Please use Visa, MasterCard, or American Express.');
// Log unsupported card attempt
trackEvent('unsupported-card-attempted', {
cardNumberPrefix: event.target.value.substring(0, 6),
timestamp: new Date().toISOString()
});
// Show alternative payment methods
showAlternativePaymentMethods();
// Disable submit button until valid card is entered
disableSubmitButton();
}
});This callback is triggered when the card number input changes and a recognised card brand (e.g., Visa, MasterCard) is detected from the input value.
You can use it to:
- Update the card brand logo display in real-time.
- Apply brand-specific validation rules or formatting.
- Show relevant rewards or benefits for the detected card.
- Enable or disable certain features based on the card brand.
| Parameter | Description |
|---|---|
eventCustomEvent | The card brand detection event object. |
event.typestring | The event type (always cardbranddetected). |
event.targetHTMLInputElement | The card number input field. |
event.target.valuestring | The current card number value. |
event.detailobject | Enhanced card brand detection details. |
event.detail.cardBrandobject | The detected card brand information. |
event.detail.cardBrand.brandstring | The card brand identifier (e.g., visa, mastercard). |
event.detail.cardBrand.displayNamestring | The user-friendly brand name (e.g., Visa, MasterCard). |
event.detail.cardBrand.typestring | The card type (e.g., credit, debit, prepaid). |
event.detail.cardBrand.categorystring | The card category (e.g., consumer, business, corporate). |
event.detail.cardBrand.logostring | The URL to the card brand logo image. |
event.detail.cardBrand.colorsobject | The brand colour scheme for UI theming. |
event.detail.cardBrand.colors.primarystring | The primary brand colour (hex code). |
event.detail.cardBrand.colors.secondarystring | The secondary brand colour (hex code). |
event.detail.cardBrand.cardNumberLengthnumber | The expected card number length for this brand. |
event.detail.cardBrand.cvcLengthnumber | The expected CVC length for this brand (3 or 4). |
event.detail.cardBrand.supportedCountriesarray | Array of country codes where this brand is supported. |
event.detail.isCardBrandAcceptboolean | Whether this card brand is accepted by you. |
event.detail.confidencenumber | The confidence level of the brand detection (0-1). |
event.detail.alternativeBrandsarray | Array of possible alternative brand matches. |
const cardNumberComponent = sdk.create('card-number', {
onCardBrandDetected: (event) => {
const cardBrand = event.detail.cardBrand;
const isAccepted = event.detail.isCardBrandAccept;
console.log(`Card brand detected: ${cardBrand.brand}`, isAccepted);
// Update card brand logo
updateCardBrandLogo(cardBrand.brand);
// Check if card brand is accepted
if (!isAccepted) {
showFieldError('card-number', `${cardBrand.displayName} cards are not accepted. Please use a different card.`);
disableSubmitButton();
} else {
clearFieldError('card-number');
enableSubmitButton();
// Show brand-specific benefits
showCardBrandBenefits(cardBrand.brand);
}
// Apply brand-specific validation
applyBrandSpecificValidation(cardBrand.brand);
// Track card brand usage
trackEvent('card-brand-detected', {
brand: cardBrand.brand,
accepted: isAccepted,
timestamp: new Date().toISOString()
});
}
});This callback is triggered when the input value changes in one or more fields.
You can use it to:
- Update UI elements based on field values in real-time.
- Perform progressive validation as users type.
- Calculate and display dynamic totals or fees.
- Enable or disable form elements based on input values.
| Parameter | Description |
|---|---|
eventCustomEvent | The input change event object with enhanced properties. |
event.typestring | The event type (always change). |
event.targetHTMLInputElement | The input element that changed. |
event.target.valuestring | The current value of the field. |
event.target.namestring | The field name identifier (e.g., cardNumber, expiryDate). |
event.target.idstring | The field ID attribute. |
event.detailobject | Enhanced event details provided by the SDK. |
event.detail.isValidboolean | Whether the current field value is valid (if validation is enabled). |
event.detail.isEmptyboolean | Whether the field is empty. |
event.detail.isCompleteboolean | Whether the field is completely filled (i.e., meets the expected length). |
event.detail.cardBrandobject | Card brand information (for card number fields only). |
event.detail.cardBrand.brandstring | The detected card brand (e.g., visa, mastercard). |
event.detail.cardBrand.displayNamestring | The user-friendly brand name (e.g., Visa, MasterCard). |
event.detail.cardBrand.logostring | The URL to the card brand logo image. |
event.detail.cardBrand.cvcLengthnumber | The expected CVC length for this card brand (3 or 4). |
event.detail.formattedValuestring | The formatted display value (with spaces, dashes, etc.). |
event.detail.errorsarray | Array of validation error objects (if validation failed). |
event.detail.errors[].codestring | The error code for programmatic handling. |
event.detail.errors[].messagestring | A human-readable error message. |
const cardNumberComponent = sdk.create('card-number', {
onChange: (event) => {
console.log('Card number changed:', event.target.value);
// Update card brand display
if (event.detail.cardBrand) {
updateCardBrandDisplay(event.detail.cardBrand);
}
// Progressive validation
if (event.target.value.length >= 4) {
const isValid = event.detail.isValid;
if (isValid) {
clearFieldError('card-number');
showFieldSuccess('card-number');
} else {
showFieldError('card-number', 'Please enter a valid card number');
}
}
// Enable submit button when all fields are filled
checkFormCompletion();
// Track input progress
trackFieldProgress('card-number', {
length: event.target.value.length,
isValid: event.detail.isValid,
timestamp: new Date().toISOString()
});
}
});
const cardExpiryComponent = sdk.create('card-expiry-date', {
onChange: (event) => {
console.log('Expiry date changed:', event.target.value);
// Check for near-expiry warnings
if (event.detail.isValid) {
const expiryDate = new Date(event.target.value);
const now = new Date();
const monthsUntilExpiry = (expiryDate.getFullYear() - now.getFullYear()) * 12 +
(expiryDate.getMonth() - now.getMonth());
if (monthsUntilExpiry <= 1) {
showWarning('Your card expires soon. Please ensure it will be valid when your payment is processed.');
}
}
}
});This callback is triggered when the submit button is clicked, before any payment processing begins.
You can use it to:
- Perform final form validation before payment processing.
- Show loading states or disable the button to prevent double-clicks.
- Track conversion funnel progression for analytics.
- Execute pre-payment business logic or confirmations.
| Parameter | Description |
|---|---|
eventMouseEvent | The click event object from the submit button. |
event.typestring | The event type (always click). |
event.targetHTMLButtonElement | The submit button element that was clicked. |
event.target.disabledboolean | Whether the button is currently disabled. |
event.target.textContentstring | The button's text content. |
event.target.formHTMLFormElement | The form element containing the button (if any). |
event.currentTargetHTMLButtonElement | The element the event listener is attached to. |
event.clientXnumber | The X coordinate of the mouse pointer relative to the viewport. |
event.clientYnumber | The Y coordinate of the mouse pointer relative to the viewport. |
event.buttonnumber | Which mouse button was pressed (0 = left, 1 = wheel, 2 = right). |
event.ctrlKeyboolean | Whether the Ctrl key was held during the click. |
event.shiftKeyboolean | Whether the Shift key was held during the click. |
event.altKeyboolean | Whether the Alt key was held during the click. |
event.metaKeyboolean | Whether the Meta key (Cmd on Mac) was held during the click. |
event.timeStampnumber | The time when the event occurred. |
event.preventDefaultfunction | Method to prevent the default submission behaviour. |
event.stopPropagationfunction | Method to stop the event from bubbling up. |
const cardSubmit = sdk.create('card-submit', {
onClick: (event) => {
console.log('Submit button clicked');
// Track the payment attempt
trackEvent('payment-attempt-started', {
paymentMethod: 'card',
timestamp: new Date().toISOString(),
amount: getCurrentAmount(),
currency: getCurrentCurrency()
});
// Perform final validation
const validationErrors = performFinalValidation();
if (validationErrors.length > 0) {
event.preventDefault();
showValidationErrors(validationErrors);
return;
}
// Check inventory one final time
const inventoryCheck = checkInventoryAvailability();
if (!inventoryCheck.available) {
event.preventDefault();
showError('Some items in your cart are no longer available. Please review your order.');
return;
}
// Show loading state
showSubmitLoading(true);
event.target.disabled = true;
// Optional: Show confirmation dialog for high-value transactions
const amount = getCurrentAmount();
if (amount > 1000) {
const confirmed = confirm(`Please confirm your payment of ${formatCurrency(amount)}`);
if (!confirmed) {
event.preventDefault();
showSubmitLoading(false);
event.target.disabled = false;
return;
}
}
// Log that payment is proceeding
console.log('Payment processing initiated');
}
});This callback is triggered when device fingerprinting and data collection ends.
You can use it to:
- Hide loading indicators shown during data collection.
- Log data collection completion for debugging.
- Proceed to the next step in the payment flow.
- Track data collection performance metrics.
| Parameter | Description |
|---|---|
dataobject | The data collection result object. |
data.successboolean | Whether data collection completed successfully. |
data.sessionIdstring | The session ID used for data collection. |
data.fingerprintDatastring | The collected device fingerprint data (if successful). |
data.providerstring | The fraud detection provider used (e.g., kount). |
data.durationnumber | The time taken for data collection in milliseconds. |
data.startTimenumber | The date and time when collection started. |
data.endTimenumber | The date and time when collection completed. |
data.dataPointsobject | The summary of collected data points. |
data.dataPoints.deviceInfoboolean | Whether device information was collected. |
data.dataPoints.browserInfoboolean | Whether browser information was collected. |
data.dataPoints.networkInfoboolean | Whether network information was collected. |
data.dataPoints.behavioralInfoboolean | Whether behavioural patterns were collected. |
data.errorError | The error object if data collection failed (optional). |
data.error.codestring | The error code for programmatic handling. |
data.error.messagestring | A human-readable error description. |
data.error.providerstring | The provider that generated the error. |
data.retryAttemptsnumber | The number of retry attempts made during collection. |
data.fallbackUsedboolean | Whether fallback collection methods were used. |
const cardSubmit = sdk.create('card-submit', {
onCollectEnd: (data) => {
console.log('Data collection completed:', data);
// Hide loading indicator
hideMessage();
hideDataCollectionSpinner();
// Track collection metrics
trackEvent('device-data-collection-completed', {
success: data.success,
duration: data.duration,
timestamp: new Date().toISOString()
});
if (data.success) {
console.log('Device fingerprinting successful');
// Store fingerprint data for fraud detection
storeFingerprintData(data.fingerprintData);
// Enable submit button now that data collection is complete
enableSubmitButton();
// Show completion indicator
showSuccessMessage('Security check completed', 2000);
} else {
console.warn('Data collection failed:', data.error);
// Handle collection failure - continue without fingerprinting
logError('fingerprint-collection-failed', {
error: data.error?.message,
duration: data.duration
});
// Still allow payment to proceed (fingerprinting is optional)
enableSubmitButton();
showWarningMessage('Security check unavailable - payment will proceed normally');
}
}
});This callback is triggered when device fingerprinting and data collection begins.
You can use it to:
- Show loading indicators to inform users about the security process.
- Track the start of data collection for performance monitoring.
- Display educational messages about fraud prevention.
- Initialise any UI elements related to the collection process.
| Parameter | Description |
|---|---|
dataobject | The data collection initialisation object. |
data.providerstring | The fraud detection provider (e.g., kount). |
data.sessionIdstring | The session ID for the data collection process. |
data.timeoutnumber | The maximum time allowed for data collection, in milliseconds. |
data.startTimenumber | The date and time when collection is starting. |
data.configobject | Provider-specific configuration settings. |
data.config.merchantIdstring | Your merchant identifier for the fraud detection service. |
data.config.environmentstring | The environment setting. |
data.config.versionstring | The provider SDK version being used. |
data.dataTypesarray | The types of data to be collected (e.g., ['device', 'browser', 'network']). |
data.retryConfigobject | The retry configuration for failed collection attempts. |
data.retryConfig.maxAttemptsnumber | The maximum number of retry attempts. |
data.retryConfig.backoffMsnumber | The backoff time between retries in milliseconds. |
data.fallbackEnabledboolean | Whether fallback collection methods are enabled. |
const cardSubmit = sdk.create('card-submit', {
onCollectStart: (data) => {
console.log('Data collection started:', data);
// Show informative loading message
showMessage('Performing security check...', 'info');
showDataCollectionSpinner();
// Track collection start
trackEvent('device-data-collection-started', {
provider: data.provider,
sessionId: data.sessionId,
timeout: data.timeout,
timestamp: new Date().toISOString()
});
// Disable submit button during collection
disableSubmitButton();
// Show educational tooltip about fraud prevention
showTooltip('security-info',
'We\'re performing a quick security check to protect against fraud. This helps keep your payment safe.');
// Set up timeout warning
const warningTime = data.timeout * 0.8; // Warn at 80% of timeout
setTimeout(() => {
if (!isDataCollectionComplete()) {
showWarningMessage('Security check taking longer than expected...');
}
}, warningTime);
// Log provider-specific information
if (data.provider === 'kount') {
console.log('Kount fraud detection initiated with session:', data.sessionId);
} else if (data.provider === 'threatmetrix') {
console.log('ThreatMetrix profiling started with session:', data.sessionId);
}
}
});This callback is triggered when a customer enters a CVC value in the click-once component's CVC field.
You can use it to:
- Enable the submit button when CVC is provided for saved cards.
- Perform real-time CVC validation.
- Track security code entry for analytics.
- Show completion indicators for the payment form.
| Parameter | Description |
|---|---|
dataobject | The CVC entry event data. |
data.isValidboolean | Whether the entered CVC is valid for the card type. |
data.cvcValuestring | The masked CVC value (e.g., '' or '*'). |
data.cvcLengthnumber | The length of the entered CVC. |
data.expectedLengthnumber | The expected CVC length for this card brand. |
data.cardTypestring | The type of card the CVC was entered for (e.g., visa, amex). |
data.cardBrandobject | Detailed card brand information. |
data.cardBrand.displayNamestring | The user-friendly brand name (e.g., Visa, American Express). |
data.cardBrand.requiresCvcboolean | Whether this card brand typically requires CVC. |
data.maskedCardNumberstring | The masked card number associated with the CVC. |
data.tokenIdstring | The token ID of the saved card (for click-once components). |
data.validationErrorsarray | Array of validation error objects (if validation failed). |
data.validationErrors[].codestring | The error code for programmatic handling. |
data.validationErrors[].messagestring | A human-readable error message. |
data.inputSourcestring | How the CVC was entered (e.g., keyboard). |
data.timeTakennumber | The time taken to enter the CVC, in milliseconds. |
const clickOnceComponent = sdk.create('click-once', {
onCvcEntered: (data) => {
console.log('CVC has been entered for card:', data.maskedCardNumber);
// Validate CVC format
if (data.isValid) {
console.log('Valid CVC entered');
// Clear any previous errors
clearFieldError('cvc');
// Show success indicator
showFieldSuccess('cvc');
// Enable submit button
enableSubmitButton();
// Track successful CVC entry
trackEvent('cvc-entered-valid', {
cardType: data.cardType,
maskedCardNumber: data.maskedCardNumber,
timestamp: new Date().toISOString()
});
// Show payment ready indicator
showPaymentReadyMessage();
} else {
console.log('Invalid CVC entered');
// Show validation error
showFieldError('cvc', 'Please enter a valid security code');
// Keep submit button disabled
disableSubmitButton();
// Track invalid CVC attempts
trackEvent('cvc-entered-invalid', {
cardType: data.cardType,
timestamp: new Date().toISOString()
});
}
// Update UI to reflect CVC completion
updateFormCompletionStatus();
}
});This callback is triggered when a card token deletion request fails, for example due to a network error or authorisation issues.
You can use it to:
- Display user-friendly error messages for deletion failures.
- Log deletion errors for debugging and monitoring.
- Offer retry options for failed deletions.
- Track failure rates for saved payment method management.
| Parameter | Description |
|---|---|
dataDeleteCardTokenResponseFailed | The failed deletion response object. |
data.errorobject | The error details for the failed deletion. |
data.error.messagestring | A human-readable error message. |
data.error.codestring | The specific error code for programmatic handling. |
data.tokenIdstring | The ID of the token that failed to be deleted. |
data.correlationIdstring | The correlation ID for tracking the request. |
const cardOnFileComponent = sdk.create('card-on-file', {
onDeleteTokenFailed: (data) => {
console.log('Token deletion failed:', data.error);
// Log the error for debugging
logError('token-deletion-failed', {
tokenId: data.tokenId,
errorCode: data.error.code,
errorMessage: data.error.message,
correlationId: data.correlationId,
timestamp: new Date().toISOString()
});
// Handle different error types
let userMessage = 'Failed to delete payment method. Please try again.';
if (data.error.code === 'UNAUTHORIZED') {
userMessage = 'You are not authorised to delete this payment method.';
} else if (data.error.code === 'TOKEN_NOT_FOUND') {
userMessage = 'This payment method no longer exists.';
// Remove from UI since it doesn't exist
removeTokenFromUI(data.tokenId);
} else if (data.error.code === 'NETWORK_ERROR') {
userMessage = 'Network error. Please check your connection and try again.';
// Offer retry option
showRetryButton(() => retryDeleteToken(data.tokenId));
} else if (data.error.code === 'TOKEN_IN_USE') {
userMessage = 'This payment method cannot be deleted as it is currently being used.';
}
// Show error message to user
showErrorMessage(userMessage);
// Track deletion failure analytics
trackEvent('token-deletion-failed', {
tokenId: data.tokenId,
errorCode: data.error.code,
errorType: data.error.message,
timestamp: new Date().toISOString()
});
// Re-enable delete button if it was disabled
enableDeleteButton(data.tokenId);
}
});This callback is triggered when a card token is successfully deleted from the vault.
You can use it to:
- Update the UI to remove the deleted payment method.
- Show success confirmation messages to users.
- Track successful payment method deletions for analytics.
- Update local storage or state management.
| Parameter | Description |
|---|---|
dataDeleteCardTokenResponseSuccess | The successful deletion response object. |
data.tokenIdstring | The ID of the successfully deleted token. |
data.maskedCardNumberstring | The masked card number of the deleted token. |
data.cardSchemestring | The card scheme of the deleted token. |
data.correlationIdstring | The correlation ID for tracking the request. |
data.timestampstring | When the deletion was completed, in ISO 8601 format. |
const cardOnFileComponent = sdk.create('card-on-file', {
onDeleteTokenSuccess: (data) => {
console.log('Token deleted successfully:', data.tokenId);
// Remove the token from the UI
removeTokenFromUI(data.tokenId);
// Show success message
showSuccessMessage(`${data.cardScheme} ending in ${data.maskedCardNumber.slice(-4)} has been removed successfully.`);
// Update local data
removeTokenFromLocalStorage(data.tokenId);
// Track successful deletion
trackEvent('token-deletion-success', {
tokenId: data.tokenId,
cardScheme: data.cardScheme,
maskedCardNumber: data.maskedCardNumber,
correlationId: data.correlationId,
timestamp: new Date().toISOString()
});
// Check if this was the last saved card
const remainingTokens = getRemainingTokenCount();
if (remainingTokens === 0) {
showEmptyStateMessage('No saved payment methods. Add a new card to save time on future purchases.');
hideCardOnFileSection();
}
// If this was the default card, update default selection
if (isDefaultCard(data.tokenId)) {
updateDefaultCard();
}
// Log successful deletion
console.log(`Payment method deleted: ${data.cardScheme} ${data.maskedCardNumber} at ${data.timestamp}`);
// Optional: Trigger email notification about deleted payment method
if (shouldNotifyOfDeletion()) {
sendPaymentMethodDeletedNotification(data);
}
}
});This callback is triggered when an input field receives focus.
You can use it to:
- Show helpful tooltips or instructions when users focus on fields.
- Highlight field requirements or formatting examples.
- Track user interaction patterns and form navigation.
- Update UI elements to show the active field state.
| Parameter | Description |
|---|---|
eventFocusEvent | The focus event object. |
event.typestring | The event type (always focus). |
event.targetHTMLInputElement | The input field that received focus. |
event.target.namestring | The field name identifier. |
event.target.valuestring | The current value of the field. |
event.target.idstring | The field ID attribute. |
event.target.placeholderstring | The field's placeholder text. |
event.target.selectionStartnumber | The starting position of text selection. |
event.target.selectionEndnumber | The ending position of text selection. |
event.target.validityValidityState | The HTML5 validation state of the field. |
event.currentTargetHTMLInputElement | The element the event listener is attached to. |
event.relatedTargetHTMLElement | The element that previously had focus (if any). |
event.timeStampnumber | The date and time when the event occurred. |
const cardNumberComponent = sdk.create('card-number', {
onFocus: (event) => {
console.log('Card number field focused');
// Clear any previous errors
clearFieldError('card-number');
// Show helpful formatting tooltip
showTooltip('card-number-help', 'Enter your 16-digit card number without spaces or dashes');
// Highlight the field container
highlightField('card-number');
// Track field interaction
trackEvent('field-focused', {
fieldName: 'card-number',
hasExistingValue: !!event.target.value,
timestamp: new Date().toISOString()
});
// Show accepted card brands
showAcceptedCardBrands();
}
});
const cardExpiryComponent = sdk.create('card-expiry-date', {
onFocus: (event) => {
console.log('Expiry date field focused');
// Show format example
showTooltip('expiry-help', 'MM/YY format (e.g., 12/25)');
// Clear errors
clearFieldError('card-expiry-date');
// Highlight field
highlightField('card-expiry-date');
// Track focus
trackEvent('field-focused', {
fieldName: 'card-expiry-date',
timestamp: new Date().toISOString()
});
}
});
const cardCvcComponent = sdk.create('card-cvc', {
onFocus: (event) => {
console.log('CVC field focused');
// Show security code help
showTooltip('cvc-help', 'Enter the 3-digit security code from the back of your card (4 digits for Amex)');
// Show CVC location diagram
showCvcLocationDiagram();
// Clear errors
clearFieldError('card-cvc');
// Track focus
trackEvent('field-focused', {
fieldName: 'card-cvc',
timestamp: new Date().toISOString()
});
}
});This callback is used to retrieve device fingerprint data for fraud detection and authentication purposes.
You can use it to:
- Integrate with third-party fraud detection services (Kount, ThreatMetrix, etc.).
- Provide device fingerprint data for enhanced security.
- Implement custom device identification logic.
- Return cached fingerprint data if available.
This callback receives no parameters but should return fingerprint data.
const cardSubmit = sdk.create('card-submit', {
onGetFingerprintResult: async () => {
console.log('Fingerprint data requested');
try {
// Check if we have cached fingerprint data
let fingerprintData = getCachedFingerprintData();
if (!fingerprintData) {
console.log('Generating new fingerprint data');
// Generate new fingerprint using Kount
if (window.ka && window.ka.collect) {
fingerprintData = await new Promise((resolve, reject) => {
window.ka.collect({
sessionId: getKountSessionId(),
onSuccess: (sessionId) => resolve(sessionId),
onFailure: (error) => reject(error)
});
});
}
// Generate fingerprint using ThreatMetrix
else if (window.tmx && window.tmx.profiling) {
fingerprintData = await window.tmx.profiling.generateSessionId();
}
// Fallback: Generate basic browser fingerprint
else {
fingerprintData = generateBasicFingerprint();
}
// Cache the fingerprint data for future use
cacheFingerprintData(fingerprintData);
}
// Track successful fingerprint generation
trackEvent('fingerprint-generated', {
method: getFingerprintMethod(),
sessionId: fingerprintData,
cached: !!getCachedFingerprintData(),
timestamp: new Date().toISOString()
});
console.log('Returning fingerprint data:', fingerprintData);
return fingerprintData;
} catch (error) {
console.error('Fingerprint generation failed:', error);
// Log the error
logError('fingerprint-generation-failed', {
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
// Return a fallback fingerprint or empty string
const fallbackFingerprint = generateFallbackFingerprint();
trackEvent('fingerprint-fallback-used', {
error: error.message,
fallbackData: fallbackFingerprint,
timestamp: new Date().toISOString()
});
return fallbackFingerprint;
}
}
});
// Helper functions for fingerprint generation
function generateBasicFingerprint() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillText('Basic fingerprint', 2, 2);
const fingerprint = btoa(JSON.stringify({
userAgent: navigator.userAgent,
language: navigator.language,
platform: navigator.platform,
screenResolution: `${screen.width}x${screen.height}`,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
canvas: canvas.toDataURL(),
timestamp: Date.now()
}));
return fingerprint.substring(0, 32); // Truncate for consistency
}
function generateFallbackFingerprint() {
return btoa(`fallback_${Date.now()}_${Math.random()}`).substring(0, 32);
}This callback is triggered when a customer selects a saved card in the click-once component.
You can use it to:
- Update the UI to show the selected card.
- Enable or disable the CVC field based on card requirements.
- Track card selection patterns for analytics.
- Load card-specific settings or preferences.
| Parameter | Description |
|---|---|
dataobject | The card selection event data. |
data.tokenIdstring | The ID of the selected card token. |
data.maskedCardNumberstring | The masked card number of the selected card. |
data.cardSchemestring | The card scheme identifier (e.g., visa, mastercard). |
data.cardSchemeNamestring | User-friendly card scheme name (e.g., Visa, MasterCard). |
data.expiryDatestring | The card expiry date in MM/YY format. |
data.expiryMonthstring | The expiry month (MM). |
data.expiryYearstring | The expiry year (YY). |
data.isExpiredboolean | Whether the card is currently expired. |
data.expiresWithinMonthsnumber | The number of months until expiry (negative if expired). |
data.cardTypestring | The card type (e.g., credit, debit, prepaid). |
data.fundingSourcestring | The funding source type. |
data.issuerNamestring | The name of the card issuing bank. |
data.issuerCountrystring | The country code of the card issuer. |
data.isCvcRequiredboolean | Whether CVC is required for this card. |
data.isDefaultboolean | Whether this is the customer's default payment method. |
data.lastUsedDatestring | The ISO date when this card was last used for payment. |
data.addedDatestring | The ISO date when this card was added to the vault. |
data.usageCountnumber | The number of times this card has been used. |
data.metadataobject | Additional metadata associated with the token. |
data.metadata.customerNicknamestring | The customer-provided nickname for the card (optional). |
data.metadata.verificationStatusstring | The verification status of the card. |
const clickOnceComponent = sdk.create('click-once', {
onOnceCardClick: (data) => {
console.log('User clicked on saved card:', data.maskedCardNumber);
// Update UI to show selected card
updateSelectedCardDisplay(data);
// Track card selection
trackEvent('saved-card-selected', {
tokenId: data.tokenId,
cardScheme: data.cardScheme,
maskedCardNumber: data.maskedCardNumber,
timestamp: new Date().toISOString()
});
// Show/hide CVC field based on requirements
if (data.isCvcRequired) {
showCvcField();
showMessage('Please enter your security code to continue');
disableSubmitButton(); // Keep disabled until CVC is entered
} else {
hideCvcField();
enableSubmitButton();
}
// Check if card is expired
const now = new Date();
const [month, year] = data.expiryDate.split('/');
const expiryDate = new Date(2000 + parseInt(year), parseInt(month) - 1);
if (expiryDate < now) {
showWarning('This card has expired. Please update your payment method or select a different card.');
disableSubmitButton();
showUpdateCardOption(data.tokenId);
}
// Show card-specific benefits
showCardBenefits(data.cardScheme);
// Update payment summary
updatePaymentSummary({
paymentMethod: `${data.cardScheme} ending in ${data.maskedCardNumber.slice(-4)}`,
requiresCvc: data.isCvcRequired
});
}
});This callback is triggered after 3DS authentication is completed, providing the authentication identifier.
You can use it to:
- Retrieve full authentication result from your backend using the authenticationId.
- Evaluate authentication result to update authorisation decision via Unity update session API.
- Provide the decision to the SDK via
onPreAuthorization. - Log authentication results for fraud monitoring.
| Parameter | Description |
|---|---|
dataobject | Object containing authentication result. |
data.authenticationIdstring | The unique identifier for the authentication attempt. |
Use the authenticationId with the Get 3DS authentication details API to retrieve the full authentication results including transaction status, ECI values, and CAVV data.
const cardSubmit = sdk.create('card-submit', {
onPostAuthentication: async (data) => {
console.log('3DS authentication completed. Authentication ID:', data.authenticationId);
// Send authenticationId to merchant BE to retrieve authentication result
const authResult = await fetchAuthenticationResultFromBackend(data.authenticationId);
// Merchant BE evaluates authentication result to update authorisation decision via Unity update decision API
const authorisationDecision = await evaluateAuthenticationAndUpdateAuthorization(authResult);
// Merchant BE returns the decision to merchant FE and provides the decision to the SDK via onPreAuthorization
// Track authentication completion
trackEvent('3ds-authentication-completed', {
authenticationId: data.authenticationId,
timestamp: new Date().toISOString()
});
}
});This callback is triggered after 3DS authentication is initiated, providing the authentication identifier.
You can use it to:
- Retrieve the full
PreInitiateAuthenticationresult from your backend using theauthenticationId. - Evaluate the
PreInitiateAuthenticationresult to decide whether to proceed with authentication/authorisation. - Update the authentication/authorisation decision via the Modify session API.
- Track 3DS initiation success rates and performance.
| Parameter | Description |
|---|---|
dataobject | Object containing authentication initiation result. |
data.authenticationIdstring | The unique identifier for the authentication session. |
Use the authenticationId with the Get 3DS pre-initiate authentication details API to retrieve pre-authentication results including SCA mandates, exemptions, and 3DS support.
const cardSubmit = sdk.create('card-submit', {
onPostInitiateAuthentication: async (data) => {
console.log('3DS authentication initiated. Authentication ID:', data.authenticationId);
// Merchant evaluates the 3DS PreInitiateAuthentication Result to decide whether to proceed with Authentication/Authorisation
// Merchant BE uses authenticationId to retrieve initiate authentication result
const authResult = await fetchAuthenticationResultFromBackend(data.authenticationId);
// Merchant BE evaluates the PreInitiateAuthentication result to update authentication/authorisation decision via Unity update decision api if needed
const decision = await evaluateAuthenticationAndUpdateSession(authResult);
// Merchant BE returns the decision to merchant FE
// Merchant FE answers to the onPreAuthentication callback whether to proceed with it
// Track initiation result
trackEvent('3ds-authentication-initiated', {
authenticationId: data.authenticationId,
timestamp: new Date().toISOString()
});
}
});This callback is triggered before 3DS authentication starts, allowing you to provide authentication configuration or control whether to proceed.
You can use it to:
- Provide the authentication decision evaluated after onPostInitiateAuthentication.
- Configure 3DS authentication parameters if proceeding.
- Control whether to proceed with authentication by returning null.
- Set up merchant-specific authentication data.
This callback receives no parameters.
const cardSubmit = sdk.create('card-submit', {
onPreAuthentication: async () => {
// Get authentication decision evaluated after onPostInitiateAuthentication
const decision = await getAuthenticationDecision();
if (!decision) {
// Not proceeded
return null;
}
// Provide authentication configuration if proceeding
return {
challengeWindowSize: decision.challengeWindowSize,
timeout: decision.timeout,
// other configurations
};
}
});This callback is triggered before a token deletion attempt, allowing you to show custom confirmation dialogs or perform validation.
You can use it to:
- Show custom confirmation dialogs for token deletion.
- Validate whether the user has permission to delete the token.
- Check if the token is currently being used in active subscriptions.
- Log token deletion attempts for security auditing.
| Parameter | Description |
|---|---|
tokenBaseCardToken | The card token that will be deleted. |
token.gatewayTokenIdstring | The unique identifier of the token. |
token.maskedPrimaryAccountNumberstring | The masked card number. |
token.schemestring | The card scheme. |
token.expiryDatestring | The card expiry date. |
const cardOnFileComponent = sdk.create('card-on-file', {
onPreDeleteToken: async (token) => {
console.log('Token deletion requested:', token.maskedPrimaryAccountNumber);
// Track deletion attempt
trackEvent('token-deletion-requested', {
tokenId: token.gatewayTokenId,
cardScheme: token.scheme,
maskedCardNumber: token.maskedPrimaryAccountNumber,
timestamp: new Date().toISOString()
});
try {
// Check if token is used in active subscriptions
const activeSubscriptions = await checkActiveSubscriptions(token.gatewayTokenId);
if (activeSubscriptions.length > 0) {
const subscriptionList = activeSubscriptions.map(sub => sub.name).join(', ');
const confirmed = confirm(
`This payment method is used for active subscriptions: ${subscriptionList}. ` +
`Deleting it may affect these subscriptions. Are you sure you want to continue?`
);
if (!confirmed) {
trackEvent('token-deletion-cancelled-subscriptions', {
tokenId: token.gatewayTokenId,
subscriptionCount: activeSubscriptions.length,
timestamp: new Date().toISOString()
});
return false;
}
}
// Show confirmation dialog
const cardDescription = `${token.scheme} ending in ${token.maskedPrimaryAccountNumber.slice(-4)}`;
const confirmed = confirm(`Are you sure you want to delete ${cardDescription}?`);
if (confirmed) {
// Log confirmation
trackEvent('token-deletion-confirmed', {
tokenId: token.gatewayTokenId,
cardScheme: token.scheme,
timestamp: new Date().toISOString()
});
// Show deletion in progress
showMessage('Deleting payment method...', 'info');
return true;
} else {
// Log cancellation
trackEvent('token-deletion-cancelled', {
tokenId: token.gatewayTokenId,
timestamp: new Date().toISOString()
});
return false;
}
} catch (error) {
console.error('Error during token deletion validation:', error);
// Log validation error
logError('token-deletion-validation-failed', {
tokenId: token.gatewayTokenId,
error: error.message,
timestamp: new Date().toISOString()
});
// Show error and allow user to decide
const confirmed = confirm(
'Unable to check payment method usage. Do you still want to delete it?'
);
return confirmed;
}
}
});This callback is triggered after 3DS authentication is initiated, providing the authentication identifier.
You can use it to:
- Retrieve the full
PreInitiateAuthenticationresult from your backend using theauthenticationId. - Evaluate the
PreInitiateAuthenticationresult to decide whether to proceed with authentication/authorisation. - Update the authentication/authorisation decision via the Modify session API.
- Track 3DS initiation success rates and performance.
| Parameter | Description |
|---|---|
dataobject | Object containing authentication initiation result. |
data.authenticationIdstring | The unique identifier for the authentication session. |
Use the authenticationId with the Get 3DS pre-initiate authentication details API to retrieve pre-authentication results including SCA mandates, exemptions, and 3DS support.
const cardSubmit = sdk.create('card-submit', {
onPostInitiateAuthentication: async (data) => {
console.log('3DS authentication initiated. Authentication ID:', data.authenticationId);
// Merchant evaluates the 3DS PreInitiateAuthentication Result to decide whether to proceed with Authentication/Authorisation
// Merchant BE uses authenticationId to retrieve initiate authentication result
const authResult = await fetchAuthenticationResultFromBackend(data.authenticationId);
// Merchant BE evaluates the PreInitiateAuthentication result to update authentication/authorisation decision via Unity update decision api if needed
const decision = await evaluateAuthenticationAndUpdateSession(authResult);
// Merchant BE returns the decision to merchant FE
// Merchant FE answers to the onPreAuthentication callback whether to proceed with it
// Track initiation result
trackEvent('3ds-authentication-initiated', {
authenticationId: data.authenticationId,
timestamp: new Date().toISOString()
});
}
});This callback is triggered after card tokenisation completes with the tokenisation result.
You can use it to:
- Retrieve full token details from your backend using the gatewayTokenId.
- Evaluate token details to decide whether to proceed with authentication/authorisation.
- Update the user interface to show saved payment methods.
- Link the token to the customer's account.
- Log tokenisation events for analytics.
| Parameter | Description |
|---|---|
dataobject | Object containing tokenisation result. |
data.gatewayTokenIdstring | The unique identifier for the tokenised card. |
Use the gatewayTokenId with the Get masked card data related to gateway token API to retrieve full token details including card scheme, funding source (credit/debit), masked PAN, and expiry date.
After evaluating token details and transaction data, use the Modify session API to update the authorisation decision on your backend before returning from this callback. Set "authorisation": true in the session data to proceed with the transaction.
const cardSubmit = sdk.create('card-submit', {
onPostTokenisation: async (data) => {
console.log('Card tokenised successfully. Token ID:', data.gatewayTokenId);
// If merchant wants to evaluate the token details to decide whether to proceed with authentication/authorisation
// Merchant BE uses the returned gatewayTokenId to get Token Details from Unity
const tokenDetails = await fetchTokenDetailsFromBackend(data.gatewayTokenId);
// Merchant BE evaluates the token details to update authentication/authorisation decision via Unity Update session API
const decision = await evaluateTokenAndUpdateSession(tokenDetails);
// Store token for future use
await saveTokenToCustomerAccount({
tokenId: data.gatewayTokenId,
customerId: getCurrentCustomerId()
});
// Track tokenisation success for analytics
trackEvent('card-tokenisation-success', {
tokenId: data.gatewayTokenId,
timestamp: new Date().toISOString()
});
// Update UI to show the card was saved
showSuccessMessage('Your payment method has been saved securely!');
// Add to saved cards list in UI
updateSavedPaymentMethodsUI(data.gatewayTokenId);
}
});This callback is triggered before 3DS authentication starts, allowing you to provide authentication configuration or control whether to proceed.
You can use it to:
- Provide the authentication decision evaluated after onPostInitiateAuthentication.
- Configure 3DS authentication parameters if proceeding.
- Control whether to proceed with authentication by returning null.
- Set up merchant-specific authentication data.
This callback receives no parameters.
const cardSubmit = sdk.create('card-submit', {
onPreAuthentication: async () => {
// Get authentication decision evaluated after onPostInitiateAuthentication
const decision = await getAuthenticationDecision();
if (!decision) {
// Not proceeded
return null;
}
// Provide authentication configuration if proceeding
return {
challengeWindowSize: decision.challengeWindowSize,
timeout: decision.timeout,
// other configurations
};
}
});This callback is triggered before the transaction authorisation, allowing you to provide additional transaction data or control whether to proceed.
You can use it to:
- Retrieve token details from your backend using the
gatewayTokenId. - Update transaction decision on merchant backend.
- Integrate with Kount or other fraud detection services.
- Perform AVS (Address Verification System) checks.
- Apply business rules based on transaction amount or customer history.
- Control whether to proceed with authorisation by returning
null.
| Parameter | Description |
|---|---|
dataobject | Object containing token information. |
data.gatewayTokenIdstring | The token ID from the payment gateway. Use this ID to retrieve full token details from Unity backend and update transaction decision. |
Use the gatewayTokenId with the Get masked card data related to gateway token API to retrieve full token details including card scheme, funding source (credit/debit), masked PAN, and expiry date.
After evaluating token details and transaction data, use the Modify session API to update the authorisation decision on your backend before returning from this callback. Set "authorisation": true in the session data to proceed with the transaction.
const cardSubmit = sdk.create('card-submit', {
onPreAuthorisation: async (data) => {
console.log('Pre-authorisation for token:', data.gatewayTokenId);
// Merchant can use gatewayTokenId to retrieve token details and update transaction decision on merchant BE
const transactionDecision = await getAuthorisationDecision(data.gatewayTokenId);
if (!transactionDecision) {
// To not proceed
return null;
}
// Perform pre-payment validation
const deviceSessionId = await getKountSessionId();
const isHighRisk = await checkCustomerRiskProfile();
const customerTier = await getCustomerTier();
// Get billing address if AVS is enabled
const billingAddress = await getBillingAddress();
return {
addressVerification: billingAddress ? {
countryCode: billingAddress.countryCode,
houseNumberOrName: billingAddress.address,
postalCode: billingAddress.postalCode,
city: billingAddress.city,
state: billingAddress.state
} : undefined,
riskScreeningData: {
deviceSessionId: deviceSessionId,
performRiskScreening: true,
customData: {
customerTier: customerTier,
orderType: 'ecommerce',
previousTransactionCount: await getPreviousTransactionCount(),
riskScore: isHighRisk ? 'high' : 'low'
}
}
};
}
});This callback is triggered before a token deletion attempt, allowing you to show custom confirmation dialogs or perform validation.
You can use it to:
- Show custom confirmation dialogs for token deletion.
- Validate whether the user has permission to delete the token.
- Check if the token is currently being used in active subscriptions.
- Log token deletion attempts for security auditing.
| Parameter | Description |
|---|---|
tokenBaseCardToken | The card token that will be deleted. |
token.gatewayTokenIdstring | The unique identifier of the token. |
token.maskedPrimaryAccountNumberstring | The masked card number. |
token.schemestring | The card scheme. |
token.expiryDatestring | The card expiry date. |
const cardOnFileComponent = sdk.create('card-on-file', {
onPreDeleteToken: async (token) => {
// Check if token is used in active subscriptions
const hasActiveSubscriptions = await checkActiveSubscriptions(token.gatewayTokenId);
if (hasActiveSubscriptions) {
const confirmed = confirm(
`This card is used for active subscriptions. Deleting it will cancel those subscriptions. Continue?`
);
if (!confirmed) return false;
}
// Show custom confirmation dialog
const confirmed = confirm(
`Delete ${token.scheme} card ending in ${token.maskedPrimaryAccountNumber.slice(-4)}?`
);
// Log deletion attempt for security auditing
if (confirmed) {
trackEvent('token-deletion-confirmed', {
tokenId: token.gatewayTokenId,
scheme: token.scheme,
lastFour: token.maskedPrimaryAccountNumber.slice(-4)
});
}
return confirmed;
}
});This callback is triggered before 3DS authentication initiation, allowing you to configure authentication parameters or control whether to proceed with authentication.
You can use it to:
- Configure 3DS authentication parameters and merchant data.
- Set up provider information for 3DS (acquirer profile is no longer required).
- Customise authentication timeout and retry behaviour.
- Control whether to proceed with 3DS authentication by returning null.
This callback receives no parameters.
To proceed with PreInitiateAuthentication:
const cardSubmit = sdk.create('card-submit', {
onPreInitiateAuthentication: () => {
// Configure 3DS authentication parameters
return {
providerId: 'your-3ds-provider-id', // optional
requestorAuthenticationIndicator: '01', // Payment transaction
timeout: 30 // 30 seconds timeout for fingerprinting
};
}
});If merchant doesn't want to proceed with PreInitiateAuthentication:
const cardSubmit = sdk.create('card-submit', {
onPreInitiateAuthentication: () => {
// Return null to skip authentication
return null;
}
});This callback is triggered before card tokenisation begins, allowing you to control whether tokenisation should proceed.
You can use it to:
- Validate card information before tokenisation.
- Check if the customer wants to save their payment method.
- Apply business rules for token storage.
- Implement custom security checks.
This callback receives no parameters.
const cardSubmit = sdk.create('card-submit', {
onPreTokenisation: () => {
console.log('About to tokenise card');
// Check if user has opted in to save payment method
const savePaymentMethod = document.getElementById('save-card-checkbox')?.checked;
if (!savePaymentMethod) {
console.log('User opted out of saving payment method');
return false;
}
// Apply business rules
const isValidForStorage = validateCardForStorage();
if (!isValidForStorage) {
console.log('Card not suitable for storage - skipping tokenisation');
return false;
}
console.log('Proceeding with card tokenisation');
return true;
}
});This callback is triggered after tokens are retrieved but before they're rendered, allowing filtering and transformation.
You can use it to:
- Filter tokens based on business rules or user preferences.
- Transform token display order and configuration.
- Set custom CVC requirements for specific tokens.
- Apply merchant-specific token visibility rules.
| Parameter | Description |
|---|---|
dataRetrieveCardTokensReponseSuccess | The successful response from the token retrieval API. |
data.gatewayTokensarray | Array of gateway tokens retrieved from the vault. |
data.gatewayTokens[].gatewayTokenIdstring | The unique identifier for the gateway token. |
data.gatewayTokens[].maskedPrimaryAccountNumberstring | The masked card number for display. |
data.gatewayTokens[].schemestring | The card scheme. |
data.gatewayTokens[].expiryDatestring | The card expiry date. |
data.gatewayTokens[].issuerNamestring | The name of the card issuing bank. |
data.gatewayTokens[].issuerCountryCodestring | The country code of the card issuer. |
data.schemeTokensarray | Array of scheme tokens retrieved from the vault (if applicable). |
data.correlationIdstring | The correlation ID for tracking the request. |
const cardOnFileComponent = sdk.create('card-on-file', {
onPreRenderTokens: (data) => {
// Filter and transform tokens based on business rules
const filteredTokens = data.gatewayTokens
.filter(token => {
// Only show non-expired cards
const currentDate = new Date();
const expiryParts = token.expiryDate.split('/');
const expiryDate = new Date(2000 + parseInt(expiryParts[1]), parseInt(expiryParts[0]) - 1);
return expiryDate > currentDate;
})
.filter(token => {
// Only show cards from supported countries
const supportedCountries = ['US', 'GB', 'CA', 'AU'];
return supportedCountries.includes(token.issuerCountryCode);
})
.sort((a, b) => {
// Sort by scheme preference
const schemeOrder = { 'Visa': 1, 'MasterCard': 2, 'Amex': 3 };
return (schemeOrder[a.scheme] || 999) - (schemeOrder[b.scheme] || 999);
});
// Transform to required format with custom CVC requirements
return filteredTokens.map(token => ({
id: token.gatewayTokenId,
isCvcRequired: token.scheme === 'Amex' ? false : true // Amex doesn't require CVC
}));
}
});This callback is triggered when the initial request to fetch saved card tokens fails.
You can use it to:
- Display error messages to users about token retrieval failures.
- Implement fallback UI when saved cards cannot be loaded.
- Track token retrieval failure rates for monitoring.
- Offer alternative payment methods when tokens are unavailable.
| Parameter | Description |
|---|---|
dataRetrieveCardTokensReponseFailed | The failed response from the token retrieval API. |
data.errorobject | The error details for the failed retrieval. |
data.error.messagestring | A human-readable error message. |
data.error.codestring | The specific error code for programmatic handling. |
data.correlationIdstring | The correlation ID for tracking the request. |
data.statusCodenumber | The HTTP status code of the failed request. |
data.timestampstring | When the error occurred, in ISO 8601 format. |
const cardOnFileComponent = sdk.create('card-on-file', {
onRetrieveTokensFailed: (data) => {
console.error('Failed to retrieve saved cards:', data.error);
// Track the error for analytics
trackError('token-retrieval-failed', {
errorCode: data.error.code,
correlationId: data.correlationId,
statusCode: data.statusCode
});
// Show user-friendly error message
let errorMessage = 'Unable to load your saved payment methods.';
if (data.error.code === 'NETWORK_ERROR') {
errorMessage = 'Network connection issue. Please check your internet connection.';
} else if (data.error.code === 'UNAUTHORIZED') {
errorMessage = 'Please log in again to access your saved payment methods.';
}
showNotification(errorMessage, 'error');
// Hide the saved cards section and show alternative options
document.getElementById('saved-cards-section').style.display = 'none';
document.getElementById('add-new-card-section').style.display = 'block';
}
});This callback is triggered when submission fails due to PXP-specific errors or validation issues.
You can use it to:
- Display user-friendly error messages for submission failures.
- Implement retry logic for transient errors.
- Track submission error rates and patterns.
- Handle different types of submission errors appropriately.
| Parameter | Description |
|---|---|
errorobject | The error object containing submission failure details. |
error.messagestring | A human-readable error message. |
error.errorCodestring | The specific error code for programmatic handling. |
error.correlationIdstring | The correlation ID for tracking the request (if available). |
error.validationErrorsarray | Array of field-specific validation errors (if applicable). |
error.timestampstring | When the error occurred, in ISO 8601 format. |
const cardSubmit = sdk.create('card-submit', {
onSubmitError: (error) => {
console.error('Card submission failed:', error);
// Track the error for analytics
trackError('card-submit-error', {
errorCode: error.errorCode,
correlationId: error.correlationId,
timestamp: error.timestamp
});
// Handle different types of errors
let userMessage = 'Payment submission failed. Please try again.';
switch (error.errorCode) {
case 'VALIDATION_FAILED':
userMessage = 'Please check your card details and try again.';
// Handle specific field validation errors
if (error.validationErrors) {
error.validationErrors.forEach(validationError => {
highlightFieldError(validationError.field, validationError.message);
});
}
break;
case 'NETWORK_ERROR':
userMessage = 'Network connection issue. Please check your internet connection and try again.';
break;
case 'CARD_DECLINED':
userMessage = 'Your card was declined. Please try a different payment method.';
break;
case 'INSUFFICIENT_FUNDS':
userMessage = 'Insufficient funds. Please try a different card or payment method.';
break;
case 'EXPIRED_CARD':
userMessage = 'Your card has expired. Please update your card details.';
break;
default:
userMessage = `Payment failed: ${error.message}`;
}
// Show error to user
showErrorNotification(userMessage);
// Re-enable submit button for retry
document.getElementById('submit-button').disabled = false;
}
});This callback is triggered when updating card token information (e.g., expiry date) fails.
You can use it to:
- Display error messages for failed token updates.
- Implement retry logic for token update failures.
- Track token update failure rates for monitoring.
- Handle different types of update errors appropriately.
| Parameter | Description |
|---|---|
dataUpdateCardTokenResponseFailed | The failed response from the token update API. |
data.errorobject | The error details for the failed update. |
data.error.messagestring | A human-readable error message. |
data.error.codestring | The specific error code for programmatic handling. |
data.tokenIdstring | The ID of the token that failed to be updated. |
data.correlationIdstring | The correlation ID for tracking the request. |
data.statusCodenumber | The HTTP status code of the failed request. |
data.timestampstring | When the error occurred, in ISO 8601 format. |
const cardOnFileComponent = sdk.create('card-on-file', {
onUpdateTokenFailed: (data) => {
console.error('Token update failed:', data.error);
// Track the error for analytics
trackError('token-update-failed', {
errorCode: data.error.code,
tokenId: data.tokenId,
correlationId: data.correlationId
});
// Handle different types of errors
let errorMessage = 'Failed to update card details. Please try again.';
switch (data.error.code) {
case 'INVALID_EXPIRY_DATE':
errorMessage = 'Please enter a valid expiry date.';
break;
case 'TOKEN_NOT_FOUND':
errorMessage = 'This payment method no longer exists.';
break;
case 'UNAUTHORIZED':
errorMessage = 'You are not authorised to update this payment method.';
break;
case 'NETWORK_ERROR':
errorMessage = 'Network connection issue. Please try again.';
break;
}
// Show error notification
showErrorNotification(errorMessage);
// Revert UI changes if update failed
document.getElementById(`token-${data.tokenId}`).classList.remove('updating');
// Show retry button
const retryButton = document.getElementById(`retry-update-${data.tokenId}`);
if (retryButton) {
retryButton.style.display = 'block';
}
}
});This callback is triggered when card token information is successfully updated.
You can use it to:
- Display success messages for token updates.
- Update the UI to reflect the changes.
- Track successful token updates for analytics.
- Sync updated token information with local storage.
| Parameter | Description |
|---|---|
dataUpdateCardTokenResponseSuccess | The successful response from the token update API. |
data.tokenIdstring | The ID of the successfully updated token. |
data.maskedCardNumberstring | The masked card number of the updated token. |
data.cardSchemestring | The card scheme of the updated token. |
data.updatedFieldsobject | The fields that were updated. |
data.updatedFields.expiryMonthstring | The new expiry month (if updated). |
data.updatedFields.expiryYearstring | The new expiry year (if updated). |
data.correlationIdstring | The correlation ID for tracking the request. |
data.timestampstring | When the update was completed, in ISO 8601 format. |
const cardOnFileComponent = sdk.create('card-on-file', {
onUpdateTokenSuccess: (data) => {
console.log('Token updated successfully:', data);
// Track the success for analytics
trackEvent('token-update-success', {
tokenId: data.tokenId,
correlationId: data.correlationId,
updatedFields: Object.keys(data.updatedFields)
});
// Show success message
showSuccessNotification('Payment method updated successfully!');
// Update the UI to reflect changes
const tokenElement = document.getElementById(`token-${data.tokenId}`);
if (tokenElement) {
tokenElement.classList.add('updated');
// Update expiry display if it was changed
if (data.updatedFields.expiryMonth && data.updatedFields.expiryYear) {
const expiryElement = tokenElement.querySelector('.expiry-date');
if (expiryElement) {
expiryElement.textContent = `${data.updatedFields.expiryMonth}/${data.updatedFields.expiryYear}`;
}
}
}
// Update local storage with new token data
updateLocalTokenData(data.tokenId, data.updatedFields);
// Remove any error states
document.getElementById(`token-${data.tokenId}`).classList.remove('error', 'updating');
}
});This callback is triggered when form validation occurs.
You can use it to:
- Handle validation results for all form fields.
- Display custom error messages based on validation results.
- Track validation patterns and field-specific error rates.
- Implement custom validation logic or additional checks.
| Parameter | Description |
|---|---|
validationResultsValidationResult[] | Array of validation results for all validated fields. |
validationResults[].validboolean | Whether the field passed validation. |
validationResults[].isNotValidatedboolean | Whether the field was not validated (optional field that's empty). |
validationResults[].errorsobject | Object containing field-specific errors (if validation failed). |
validationResults[].errors[fieldName]object | Error details for a specific field. |
validationResults[].errors[fieldName].codestring | The error code for programmatic handling. |
validationResults[].errors[fieldName].messagestring | A human-readable error message. |
const newCard = sdk.create('new-card', {
onValidation: (validationResults) => {
console.log('Card form validation:', validationResults);
let allValid = true;
let errorCount = 0;
validationResults.forEach(result => {
if (!result.valid && !result.isNotValidated) {
allValid = false;
errorCount++;
// Handle field-specific errors
if (result.errors) {
Object.keys(result.errors).forEach(fieldName => {
const error = result.errors[fieldName];
// Track validation errors for analytics
trackEvent('validation-error', {
field: fieldName,
errorCode: error.code,
errorMessage: error.message
});
// Show custom error message
showFieldError(fieldName, error.message);
console.log(`${fieldName}: ${error.message} (${error.code})`);
});
}
}
});
// Update form validity state
const submitButton = document.getElementById('submit-button');
if (submitButton) {
submitButton.disabled = !allValid;
submitButton.classList.toggle('valid', allValid);
}
// Update overall form status
const formStatus = document.getElementById('form-status');
if (formStatus) {
if (allValid) {
formStatus.textContent = 'All fields are valid';
formStatus.className = 'status-valid';
} else {
formStatus.textContent = `${errorCount} field(s) need attention`;
formStatus.className = 'status-invalid';
}
}
}
});This callback is triggered before transaction submission to allow merchants to validate their own fields alongside SDK component validation. It enables simultaneous validation of merchant-owned fields (shipping address, email, terms acceptance) and SDK components (billing address), eliminating the two-phase validation flow.
You can use it to:
- Validate merchant-owned form fields before payment submission.
- Display all validation errors (merchant + SDK) simultaneously on first submit attempt.
- Improve customer experience by eliminating multi-attempt validation.
- Validate terms and conditions acceptance.
- Perform custom business rule validation.
- Make async validation calls to external services.
Without onCustomValidation, validation happens sequentially: merchant fields are validated first, and only if they pass does SDK validation occur. This creates a poor user experience where customers discover SDK errors only after fixing merchant field errors. With onCustomValidation, all validation happens simultaneously.
| Return value | Description |
|---|---|
Promise<boolean> | Return true to proceed with submission, false to prevent submission. |
The onCustomValidation callback works with all card integration modes:
- New card: Validates merchant fields alongside card and billing address validation
- Click-once (card on file with CVC re-entry): Validates merchant fields with CVC and billing address validation
- Card on file: Validates merchant fields with saved card and billing address validation
const cardSubmit = sdk.create('card-submit', {
cardNumberComponent: cardNumber,
cardExpiryDateComponent: cardExpiry,
cardCvcComponent: cardCvc,
billingAddressComponents: {
billingAddressComponent: billingAddress
},
avsRequest: true,
onCustomValidation: async () => {
let allValid = true;
// Clear previous error messages
clearAllErrors();
// Validate shipping address
const shippingAddress = document.getElementById('shipping-address').value;
if (!shippingAddress || shippingAddress.trim().length < 5) {
showFieldError('shipping-address', 'Please enter a complete shipping address');
allValid = false;
}
// Validate email
const email = document.getElementById('email').value;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
showFieldError('email', 'Please enter a valid email address');
allValid = false;
}
// Validate terms acceptance
const termsAccepted = document.getElementById('terms-checkbox').checked;
if (!termsAccepted) {
showFieldError('terms', 'You must accept the terms and conditions');
allValid = false;
}
// Return false to prevent submission if validation failed
// SDK will still run its own validation and display billing address errors
return allValid;
},
onPreAuthorisation: async (data) => {
return { psd2Data: {} };
},
onPostAuthorisation: (result) => {
console.log('Payment completed:', result.merchantTransactionId);
window.location.href = '/success';
}
});const cardSubmit = sdk.create('card-submit', {
cardNumberComponent: cardNumber,
cardExpiryDateComponent: cardExpiry,
cardCvcComponent: cardCvc,
billingAddressComponents: {
billingAddressComponent: billingAddress
},
avsRequest: true,
onCustomValidation: async () => {
// Clear previous errors
clearAllErrors();
// Track validation results
const validationResults = {
shipping: false,
email: false,
phone: false,
terms: false,
inventory: false
};
// Validate shipping address with external API
const shippingAddress = getShippingAddress();
try {
const addressValid = await validateAddressWithAPI(shippingAddress);
if (addressValid) {
validationResults.shipping = true;
} else {
showFieldError('shipping-address', 'Invalid shipping address. Please verify the address.');
}
} catch (error) {
console.error('Address validation failed:', error);
showFieldError('shipping-address', 'Unable to validate address. Please check your entry.');
}
// Validate email format
const email = getEmail();
if (isValidEmail(email)) {
validationResults.email = true;
} else {
showFieldError('email', 'Please enter a valid email address.');
}
// Validate phone number
const phone = getPhone();
const phoneRegex = /^\+?[\d\s\-()]{10,}$/;
if (phone && phoneRegex.test(phone)) {
validationResults.phone = true;
} else {
showFieldError('phone', 'Please enter a valid phone number.');
}
// Validate terms and conditions
if (isTermsAccepted()) {
validationResults.terms = true;
} else {
showFieldError('terms', 'You must accept the terms and conditions to proceed.');
}
// Check product inventory
try {
const inStock = await checkInventory();
if (inStock) {
validationResults.inventory = true;
} else {
showGeneralError('Some items in your cart are no longer available. Please update your order.');
}
} catch (error) {
console.error('Inventory check failed:', error);
showGeneralError('Unable to verify product availability. Please try again.');
}
// Return overall validation result
return Object.values(validationResults).every(result => result === true);
},
onPreAuthorisation: async (data) => {
return { psd2Data: {} };
},
onPostAuthorisation: (result) => {
console.log('Payment completed:', result.merchantTransactionId);
window.location.href = `/success?txn=${result.merchantTransactionId}`;
},
onSubmitError: (error) => {
console.error('Payment error:', error.message);
showGeneralError('Payment failed. Please check your details and try again.');
}
});
// Helper functions
function clearAllErrors() {
document.querySelectorAll('.error-message').forEach(el => {
el.textContent = '';
el.style.display = 'none';
});
document.querySelectorAll('.field-error').forEach(el => {
el.classList.remove('field-error');
});
}
function showFieldError(fieldId, message) {
const errorElement = document.getElementById(`${fieldId}-error`);
if (errorElement) {
errorElement.textContent = message;
errorElement.style.display = 'block';
}
const fieldElement = document.getElementById(fieldId);
if (fieldElement) {
fieldElement.classList.add('field-error');
}
}
async function validateAddressWithAPI(address) {
const response = await fetch('/api/validate-address', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(address)
});
const result = await response.json();
return result.valid === true;
}
async function checkInventory() {
const cartItems = getCartItems();
const response = await fetch('/api/check-inventory', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items: cartItems })
});
const result = await response.json();
return result.allInStock === true;
}const clickOnce = sdk.create('click-once', {
isCvcRequired: true,
cardSubmitComponentConfig: {
billingAddressComponents: {
billingAddressComponent: billingAddress
},
avsRequest: true,
onCustomValidation: async () => {
// Validate merchant fields for click-once flow
let isValid = true;
const email = document.getElementById('email').value;
if (!email || !isValidEmail(email)) {
showFieldError('email', 'Valid email required');
isValid = false;
}
const terms = document.getElementById('terms-checkbox').checked;
if (!terms) {
showFieldError('terms', 'Please accept the terms');
isValid = false;
}
return isValid;
}
}
});const cardOnFile = sdk.create('card-on-file', {
isCvcRequired: true
});
const cardSubmit = sdk.create('card-submit', {
useCardOnFile: true,
cardOnFileComponent: cardOnFile,
billingAddressComponents: {
billingAddressComponent: billingAddress
},
avsRequest: true,
onCustomValidation: async () => {
// Validate merchant fields for card-on-file flow
return await validateShippingAndContactInfo();
}
});This callback is triggered when validation fails for a single field.
You can use it to:
- Display field-specific error messages when validation fails.
- Track validation failure patterns for analytics.
- Implement custom error handling for specific field types.
- Update UI to highlight invalid fields.
| Parameter | Description |
|---|---|
validationResultsValidationResult[] | Array of validation results for the failed field. |
validationResults[].validboolean | Always false for this event. |
validationResults[].errorsobject | Object containing field-specific errors. |
validationResults[].errors[fieldName]object | Error details for the specific field that failed. |
validationResults[].errors[fieldName].codestring | The error code for programmatic handling. |
validationResults[].errors[fieldName].messagestring | A human-readable error message. |
const component = sdk.create('card-number', {
onValidationFailed: (validationResults) => {
console.log('Validation failed:', validationResults);
validationResults.forEach(result => {
if (result.errors) {
Object.keys(result.errors).forEach(fieldName => {
const error = result.errors[fieldName];
// Track validation failure for analytics
trackEvent('field-validation-failed', {
field: fieldName,
errorCode: error.code,
errorMessage: error.message
});
// Show field-specific error styling
const fieldElement = document.getElementById(fieldName);
if (fieldElement) {
fieldElement.classList.add('error');
fieldElement.setAttribute('aria-invalid', 'true');
}
// Display error message near the field
showFieldErrorMessage(fieldName, error.message);
});
}
});
}
});This callback is triggered when validation is successful for a single field.
You can use it to:
- Display success indicators when field validation passes.
- Track validation success patterns for analytics.
- Clear previous error states and messages.
- Update UI to show valid field status.
| Parameter | Description |
|---|---|
validationResultsValidationResult[] | Array of validation results for the successful field. |
validationResults[].validboolean | Always true for this event. |
validationResults[].isNotValidatedboolean | Whether the field was actually validated or just not required. |
validationResults[].errorsobject | The error object. Empty object since validation passed. |
const component = sdk.create('card-number', {
onValidationPassed: (validationResults) => {
console.log('Validation passed:', validationResults);
validationResults.forEach(result => {
if (result.valid) {
const fieldElement = document.querySelector('[data-field-type="card-number"]');
if (fieldElement) {
// Remove error states
fieldElement.classList.remove('error');
fieldElement.classList.add('valid');
fieldElement.setAttribute('aria-invalid', 'false');
// Clear any error messages
const errorElement = fieldElement.parentElement?.querySelector('.error-message');
if (errorElement) {
errorElement.remove();
}
// Show success indicator
const successIcon = fieldElement.parentElement?.querySelector('.success-icon');
if (successIcon) {
successIcon.style.display = 'block';
}
}
// Track successful validation for analytics
trackEvent('field-validation-passed', {
field: 'card-number',
wasNotValidated: result.isNotValidated || false
});
}
});
}
});This callback is called when each token item's user interface is being constructed, allowing you to customise the HTML layout.
You can use it to:
- Create custom layouts for saved card tokens.
- Implement responsive designs for different screen sizes.
- Add custom branding and styling to token displays.
- Include additional elements like badges or status indicators.
| Parameter | Description |
|---|---|
elementIdsTokenBuilderElementIds | Object containing all the element IDs that need to be included in the HTML. |
elementIds.tokenImageIdstring | The ID for the card brand image element. |
elementIds.tokenLabelIdstring | The ID for the token label text element. |
elementIds.expiryDateIdstring | The ID for the expiry date display element. |
elementIds.editButtonIdstring | The ID for the edit button element. |
elementIds.deleteButtonIdstring | The ID for the delete button element. |
const cardOnFileComponent = sdk.create('card-on-file', {
tokenItemBuilder: (elementIds) => {
return `
<div class="custom-token-card" style="
border: 1px solid #e1e5e9;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: all 0.2s ease;
" onmouseover="this.style.boxShadow='0 4px 8px rgba(0,0,0,0.15)'"
onmouseout="this.style.boxShadow='0 2px 4px rgba(0,0,0,0.1)'">
<div style="display: flex; align-items: center; justify-content: space-between;">
<!-- Left side: Card info -->
<div style="display: flex; align-items: center; flex: 1;">
<div id="${elementIds.tokenImageId}" style="
width: 48px;
height: 32px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
"></div>
<div style="flex: 1;">
<div id="${elementIds.tokenLabelId}" style="
font-weight: 600;
font-size: 14px;
color: #2c3e50;
margin-bottom: 4px;
"></div>
<div id="${elementIds.expiryDateId}" style="
font-size: 12px;
color: #7f8c8d;
"></div>
</div>
</div>
<!-- Right side: Actions -->
<div style="display: flex; gap: 8px;">
<div id="${elementIds.editButtonId}" style="
cursor: pointer;
padding: 6px;
border-radius: 4px;
transition: background-color 0.2s;
" onmouseover="this.style.backgroundColor='#f8f9fa'"
onmouseout="this.style.backgroundColor='transparent'"></div>
<div id="${elementIds.deleteButtonId}" style="
cursor: pointer;
padding: 6px;
border-radius: 4px;
transition: background-color 0.2s;
" onmouseover="this.style.backgroundColor='#fee'"
onmouseout="this.style.backgroundColor='transparent'"></div>
</div>
</div>
</div>
`;
}
});This callback is called to generate the display text for each card token. It replaces the default masked card number.
You can use it to:
- Customise how card information is displayed to users.
- Include additional card details like issuer name or card type.
- Implement localized or branded token labels.
- Add custom formatting or styling to card displays.
| Parameter | Description |
|---|---|
tokenBaseCardToken | The card token object containing all available card information. |
token.gatewayTokenIdstring | The unique identifier for the gateway token (if gateway token). |
token.schemeTokenIdstring | The unique identifier for the scheme token (if scheme token). |
token.maskedPrimaryAccountNumberstring | The masked card number for display. |
token.schemestring | The card scheme. |
token.expiryDatestring | The card expiry date in MM/YY format. |
token.issuerNamestring | The name of the card issuing bank. |
token.issuerCountryCodestring | The country code of the card issuer. |
token.fundingSourcestring | The funding source type. |
const cardOnFileComponent = sdk.create('card-on-file', {
tokenLabelBuilder: (token) => {
// Create a comprehensive, user-friendly label
const cardEnding = token.maskedPrimaryAccountNumber.slice(-4);
const brandName = token.scheme || 'Card';
const issuer = token.issuerName ? ` - ${token.issuerName}` : '';
const fundingType = token.fundingSource ? ` (${token.fundingSource})` : '';
// Check if card is expiring soon
const currentDate = new Date();
const [month, year] = token.expiryDate.split('/');
const expiryDate = new Date(2000 + parseInt(year), parseInt(month) - 1);
const monthsUntilExpiry = (expiryDate.getFullYear() - currentDate.getFullYear()) * 12 +
(expiryDate.getMonth() - currentDate.getMonth());
let expiryWarning = '';
if (monthsUntilExpiry <= 2 && monthsUntilExpiry > 0) {
expiryWarning = ' ⚠️ Expires Soon';
} else if (monthsUntilExpiry <= 0) {
expiryWarning = ' ❌ Expired';
}
return `${brandName} •••• ${cardEnding}${issuer}${fundingType}${expiryWarning}`;
}
});The following snippet is an example of how you might handle a declined transaction when using the card submit component.
const cardSubmitComponent = sdk.create('card-submit', {
onPostAuthorisation: async (data) => {
console.log('Authorisation completed');
console.log('Merchant Transaction ID:', data.merchantTransactionId);
console.log('System Transaction ID:', data.systemTransactionId);
// Get authorisation result from merchant backend
const submitResult = await getAuthorisationResultFromGateway(data.merchantTransactionId, data.systemTransactionId);
console.log('Transaction result:', submitResult);
if (submitResult.status === 'Refused') {
// Transaction was declined by the issuer or payment provider
const refusedResult = submitResult as RefusedSubmitResult;
// Handle the declined transaction
handleDeclinedTransaction(refusedResult);
} else if (submitResult.status === 'Authorised') {
// Transaction was successful
handleSuccessfulTransaction(submitResult);
} else if (submitResult.status === 'Error') {
// System error occurred
handleSystemError(submitResult);
} else if (submitResult.status === 'Pending') {
// Transaction is pending further processing
handlePendingTransaction(submitResult);
}
}
});
function handleDeclinedTransaction(refusedResult) {
// Extract decline information
const stateData = refusedResult.stateData;
const providerResponse = refusedResult.providerResponse;
const fundingData = refusedResult.fundingData;
// Log decline details
console.log('Transaction declined:', {
stateCode: stateData?.code,
stateMessage: stateData?.message,
providerCode: providerResponse?.code,
providerMessage: providerResponse?.message,
merchantAdvice: providerResponse?.merchantAdvice
});
// Show user-friendly error message based on decline reason
const declineCode = providerResponse?.code;
let userMessage = 'Your payment was declined. Please try again or use a different payment method.';
switch (declineCode) {
case 'INSUFFICIENT_FUNDS':
userMessage = 'Your card has insufficient funds. Please try a different payment method.';
break;
case 'EXPIRED_CARD':
userMessage = 'Your card has expired. Please check your card details or use a different card.';
break;
case 'INVALID_CVV':
userMessage = 'The security code (CVV) is incorrect. Please check and try again.';
break;
case 'CARD_BLOCKED':
userMessage = 'Your card is blocked. Please contact your bank or use a different payment method.';
break;
case 'LIMIT_EXCEEDED':
userMessage = 'Transaction exceeds your card limit. Please try a smaller amount or different card.';
break;
default:
// Use merchant advice if available
if (providerResponse?.merchantAdvice?.message) {
userMessage = providerResponse.merchantAdvice.message;
}
}
// Display error to user
showErrorMessage(userMessage);
// Check if retry is recommended
if (providerResponse?.merchantAdvice?.code === 'RETRY') {
enableRetryOption();
} else if (providerResponse?.merchantAdvice?.code === 'DO_NOT_RETRY') {
disableRetryOption();
suggestAlternativePayment();
}
// Log analytics event for declined transaction
logDeclineAnalytics(declineCode, stateData?.code);
}The following snippet is an example of how you might handle authentication problems when using the card submit component.
const cardSubmitComponent = sdk.create('card-submit', {
onSubmitError: (error) => {
console.log('Submit error occurred:', error);
// Handle different types of authentication and processing errors
switch (error.ErrorCode) {
case 'SDK0503': // Transaction authentication rejected
showErrorMessage('Authentication was rejected by your bank. Please try again or contact your bank.');
enableRetryOption();
break;
case 'SDK0505': // Transaction authentication failed
showErrorMessage('Authentication failed. Please try again or contact your bank for assistance.');
enableRetryOption();
break;
case 'SDK0504': // SCA exemption required
showErrorMessage('Additional authentication is required. Please complete the verification process.');
// Handle SCA exemption flow
break;
default:
// Generic error handling
showErrorMessage('Payment processing failed. Please try again.');
logError(error);
}
}
});