Learn how to implement 3D Secure authentication with the Paysight Widget
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Paysight Widget - 3DS Integration</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.5;
margin: 0;
padding: 20px;
background-color: #f8fafc;
}
.container {
max-width: 600px;
margin: 40px auto;
background-color: white;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
padding: 24px;
}
.header {
text-align: center;
margin-bottom: 24px;
}
.header h1 {
margin: 0;
color: #0f172a;
font-size: 24px;
}
.header p {
margin: 8px 0 0;
color: #64748b;
}
.status-message {
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 16px;
display: none;
font-size: 14px;
}
.error {
background-color: #fee2e2;
border: 1px solid #ef4444;
color: #b91c1c;
}
.success {
background-color: #dcfce7;
border: 1px solid #22c55e;
color: #15803d;
}
.info {
background-color: #e0f2fe;
border: 1px solid #0ea5e9;
color: #0369a1;
}
.widget-container {
border: 1px solid #e5e7eb;
border-radius: 6px;
padding: 20px;
}
.progress-steps {
display: flex;
justify-content: space-between;
margin-bottom: 24px;
position: relative;
}
.progress-steps::before {
content: '';
position: absolute;
top: 14px;
left: 0;
right: 0;
height: 2px;
background-color: #e5e7eb;
z-index: 0;
}
.step {
position: relative;
z-index: 1;
background-color: white;
padding: 0 12px;
text-align: center;
}
.step-number {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #e5e7eb;
color: #64748b;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 8px;
font-weight: 500;
transition: all 0.2s ease;
}
.step-label {
font-size: 14px;
color: #64748b;
transition: all 0.2s ease;
}
.step.active .step-number {
background-color: #3b82f6;
color: white;
}
.step.active .step-label {
color: #0f172a;
font-weight: 500;
}
.step.complete .step-number {
background-color: #22c55e;
color: white;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Secure Payment</h1>
<p>Complete your payment with 3D Secure authentication</p>
</div>
<div class="progress-steps">
<div class="step active" id="step-1">
<div class="step-number">1</div>
<div class="step-label">Card Details</div>
</div>
<div class="step" id="step-2">
<div class="step-number">2</div>
<div class="step-label">3D Secure</div>
</div>
<div class="step" id="step-3">
<div class="step-number">3</div>
<div class="step-label">Confirmation</div>
</div>
</div>
<div id="info-message" class="status-message info"></div>
<div id="error-message" class="status-message error"></div>
<div id="success-message" class="status-message success"></div>
<div id="widget-container" class="widget-container"></div>
</div>
<!-- Add the Paysight Widget SDK -->
<script src="https://payment.paysight.io/widget-sdk.js"></script>
<script>
// Widget configuration
const config = {
productId: YOUR_PRODUCT_ID,
sessionId: \`session_\${Date.now()}\`,
amount: 2999,
environment: 'production',
threeDSRequired: true, // Enable 3DS
ecom: true,
currency: 'USD',
locale: 'en-US'
};
// Initialize widget
const widget = PaysightSDK.createWidget({
targetId: 'widget-container',
config,
onReady: handleWidgetReady,
onError: handleError,
onMessage: handleMessage
});
// Widget ready handler
function handleWidgetReady() {
console.log('Widget is ready');
updateProgress('step-1');
}
// Error handler
function handleError(error) {
console.error('Widget error:', error);
showError(error.message);
updateProgress('step-1');
}
// Message handler
function handleMessage(message) {
console.log('Message received:', message);
switch (message.type) {
case 'PAYMENT_START':
hideMessages();
showInfo('Processing payment...');
break;
case 'PAYMENT_3DS_START':
hideMessages();
showInfo('Starting 3D Secure verification...');
updateProgress('step-2');
break;
case 'PAYMENT_3DS_SUCCESS':
hideMessages();
showInfo('3D Secure verification successful, completing payment...');
break;
case 'PAYMENT_3DS_ERROR':
handlePaymentError(message.payload);
updateProgress('step-1');
break;
case 'PAYMENT_3DS_FAILURE':
handlePaymentError({
message: '3D Secure verification failed. Please try again.'
});
updateProgress('step-1');
break;
case 'PAYMENT_SUCCESS':
handlePaymentSuccess(message.payload);
updateProgress('step-3');
break;
case 'PAYMENT_ERROR':
handlePaymentError(message.payload);
updateProgress('step-1');
break;
}
}
// Payment success handler
function handlePaymentSuccess(payload) {
const { transactionId, amount, currency } = payload;
const formattedAmount = new Intl.NumberFormat('en-US', {
style: 'currency',
currency
}).format(amount / 100);
showSuccess(
`Payment successful! Amount: ${formattedAmount}, Transaction ID: ${transactionId}`
);
// Optional: Redirect to success page after delay
setTimeout(() => {
// window.location.href = '/payment-success';
}, 3000);
}
// Payment error handler
function handlePaymentError(payload) {
showError(payload.message);
}
// Progress management
function updateProgress(stepId) {
const steps = ['step-1', 'step-2', 'step-3'];
const currentIndex = steps.indexOf(stepId);
steps.forEach((step, index) => {
const element = document.getElementById(step);
element.classList.remove('active', 'complete');
if (index === currentIndex) {
element.classList.add('active');
} else if (index < currentIndex) {
element.classList.add('complete');
}
});
}
// UI Helper functions
function showSuccess(message) {
const element = document.getElementById('success-message');
element.textContent = message;
element.style.display = 'block';
hideOtherMessages('success-message');
}
function showError(message) {
const element = document.getElementById('error-message');
element.textContent = message;
element.style.display = 'block';
hideOtherMessages('error-message');
}
function showInfo(message) {
const element = document.getElementById('info-message');
element.textContent = message;
element.style.display = 'block';
hideOtherMessages('info-message');
}
function hideMessages() {
document.getElementById('success-message').style.display = 'none';
document.getElementById('error-message').style.display = 'none';
document.getElementById('info-message').style.display = 'none';
}
function hideOtherMessages(currentId) {
const messageIds = ['success-message', 'error-message', 'info-message'];
messageIds
.filter(id => id !== currentId)
.forEach(id => {
document.getElementById(id).style.display = 'none';
});
}
</script>
</body>
</html>
const config = {
productId: YOUR_PRODUCT_ID,
sessionId: 'unique-session-id',
amount: 2999,
environment: 'production',
threeDSRequired: true // Enable 3DS authentication
};
function handleMessage(message) {
switch (message.type) {
case 'PAYMENT_3DS_START':
// 3DS verification started
showLoadingUI();
break;
case 'PAYMENT_3DS_SUCCESS':
// 3DS verification successful
hideLoadingUI();
break;
case 'PAYMENT_3DS_ERROR':
// 3DS verification error
handleError(message.payload);
break;
case 'PAYMENT_3DS_FAILURE':
// 3DS verification failed
handleFailure(message.payload);
break;
}
}
function updateProgress(stepId) {
const steps = ['step-1', 'step-2', 'step-3'];
const currentIndex = steps.indexOf(stepId);
steps.forEach((step, index) => {
const element = document.getElementById(step);
element.classList.remove('active', 'complete');
if (index === currentIndex) {
element.classList.add('active');
} else if (index < currentIndex) {
element.classList.add('complete');
}
});
}
threeDSRequired: true
in configurationfunction showUserFeedback(step) {
const messages = {
'PAYMENT_3DS_START': 'Verifying your card with 3D Secure...',
'PAYMENT_3DS_SUCCESS': '3D Secure verification successful',
'PAYMENT_3DS_ERROR': 'Unable to complete 3D Secure verification'
};
showMessage(messages[step]);
}
const TIMEOUT_DURATION = 60000; // 60 seconds
let timeoutId = setTimeout(() => {
handleTimeout();
}, TIMEOUT_DURATION);
function handleTimeout() {
showError('3D Secure verification timed out. Please try again.');
updateProgress('step-1');
}
function handleError(error) {
if (error.code === '3DS_UNAVAILABLE') {
// Fallback to non-3DS payment if allowed
retryWithout3DS();
} else {
// Show error and reset form
showError(error.message);
resetForm();
}
}