/** * UPI Instant Payment Gateway - Complete Checkout JavaScript * File: assets/js/checkout.js * * Handles all frontend functionality including QR code generation, * timer management, screenshot upload, payment processing, and * mobile UPI ID display enhancements. * * @package UPI_Instant * @version 1.0.2 */ (function($) { 'use strict'; /** * UPI Instant Payment Handler - Complete Version */ var UPIInstant = { // Properties timerInterval: null, remainingTime: 0, orderId: null, orderKey: null, timerDuration: 60, qrCodeGenerated: false, uploadInProgress: false, statusCheckInterval: null, hasUserInteracted: false, /** * Initialize the payment handler */ init: function() { var self = this; // Check if we're on the payment page if ($('#upi-payment-container').length > 0) { self.initializePaymentPage(); } // Check if we're on checkout page if ($('form.checkout').length > 0) { self.initializeCheckoutPage(); } }, /** * Initialize payment page functionality - Enhanced */ initializePaymentPage: function() { var self = this; // Get order details self.orderId = $('#order_id').val(); self.orderKey = $('#order_key').val(); self.timerDuration = parseInt($('#timer_duration').val()) || 60; // Generate QR Code self.generateQRCode(); // Start timer self.startTimer(self.timerDuration); // Setup event handlers self.setupEventHandlers(); // Setup payment app buttons self.setupPaymentButtons(); // Mobile adjustments if (self.isMobile()) { self.setupMobileView(); self.setupMobileUPIDisplay(); } // Start status checking (optional) if ($('#check_status').val() === 'yes') { self.startStatusChecking(); } // Enhanced UPI display for all devices self.enhanceUPIDisplay(); }, /** * Initialize checkout page functionality - Enhanced */ initializeCheckoutPage: function() { var self = this; // Add styling to UPI options on checkout $(document).on('change', 'input[name="upi_instant_selected"]', function() { $('.upi-option-label').removeClass('selected'); $(this).closest('.upi-option-label').addClass('selected'); // Add animation $(this).closest('.upi-option-label').addClass('pulse-animation'); setTimeout(function() { $('.upi-option-label').removeClass('pulse-animation'); }, 1000); }); // Validate on checkout submission $('form.checkout').on('checkout_place_order_upi_instant', function() { return self.validateCheckoutFields(); }); // Enhanced UPI ID display on checkout if (self.isMobile()) { self.setupMobileUPIDisplay(); } }, /** * Generate QR Code - Enhanced */ generateQRCode: function() { var self = this; var qrText = $('#qr_text').val(); var qrContainer = document.getElementById('qrcode'); if (qrText && qrContainer && !self.qrCodeGenerated) { // Show loading state $('#qr-loading').show(); // Clear any existing QR code $(qrContainer).empty(); try { // Generate QR code with enhanced options new QRCode(qrContainer, { text: qrText, width: 280, height: 280, colorDark: "#000000", colorLight: "#ffffff", correctLevel: QRCode.CorrectLevel.H, margin: 4, backgroundImage: undefined, backgroundImageAlpha: 1.0, autoColor: false }); self.qrCodeGenerated = true; // Hide loading and show QR with animation setTimeout(function() { $('#qr-loading').hide(); $(qrContainer).hide().fadeIn(500); self.setupQRDownload(); // Add success message self.showMessage('QR Code generated successfully! Scan with any UPI app.', 'success'); }, 500); } catch(error) { console.error('QR Code generation failed:', error); $('#qr-loading').hide(); self.showMessage('Failed to generate QR code. Please use the UPI ID instead.', 'error'); // Show refresh button $('#refresh-qr').show(); } } }, /** * Setup QR code download functionality - Enhanced */ setupQRDownload: function() { var self = this; var canvas = $('#qrcode canvas')[0]; var img = $('#qrcode img')[0]; var element = canvas || img; if (element) { $('#download-qr, #refresh-qr').show(); // Download handler $('#download-qr').off('click').on('click', function(e) { e.preventDefault(); var filename = 'upi-payment-qr-order-' + $('#order_id').val() + '.png'; if (canvas) { // For canvas element var link = document.createElement('a'); link.download = filename; link.href = canvas.toDataURL('image/png', 1.0); document.body.appendChild(link); link.click(); document.body.removeChild(link); } else if (img) { // For img element var link = document.createElement('a'); link.download = filename; link.href = img.src; document.body.appendChild(link); link.click(); document.body.removeChild(link); } self.showMessage('QR Code downloaded successfully!', 'success'); // Add download animation $(this).addClass('downloaded'); setTimeout(function() { $('#download-qr').removeClass('downloaded'); }, 2000); }); // Refresh handler $('#refresh-qr').off('click').on('click', function(e) { e.preventDefault(); self.qrCodeGenerated = false; $(this).html(' Refreshing...').prop('disabled', true); setTimeout(function() { self.generateQRCode(); $('#refresh-qr').html(' Refresh QR').prop('disabled', false); }, 1000); }); } }, /** * Start countdown timer - Enhanced */ startTimer: function(duration) { var self = this; self.remainingTime = duration; // Update display immediately self.updateTimerDisplay(); // Start interval self.timerInterval = setInterval(function() { self.remainingTime--; self.updateTimerDisplay(); // Add visual feedback based on remaining time var $timer = $('#timer-display'); if (self.remainingTime <= 30 && self.remainingTime > 10) { $timer.addClass('timer-warning').removeClass('timer-urgent'); } else if (self.remainingTime <= 10 && self.remainingTime > 0) { $timer.removeClass('timer-warning').addClass('timer-urgent'); } else if (self.remainingTime > 30) { $timer.removeClass('timer-warning timer-urgent'); } // Timer completed if (self.remainingTime <= 0) { clearInterval(self.timerInterval); self.showContinueButton(); } }, 1000); // Add skip timer functionality (double-click) $('#timer-display').off('dblclick').on('dblclick', function() { if (confirm('Skip timer and continue to payment verification?')) { clearInterval(self.timerInterval); self.showContinueButton(); } }); // Add timer controls for better UX self.addTimerControls(); }, /** * Add timer controls - New */ addTimerControls: function() { var self = this; // Add pause/resume functionality (hidden, activated by triple-click) var clickCount = 0; $('#timer-display').on('click', function() { clickCount++; if (clickCount === 3) { if (self.timerInterval) { clearInterval(self.timerInterval); $(this).append(' (Paused)'); self.timerInterval = null; } else { $(this).find('small').remove(); self.startTimer(self.remainingTime); } clickCount = 0; } setTimeout(function() { clickCount = 0; }, 1000); }); }, /** * Update timer display - Enhanced */ updateTimerDisplay: function() { var minutes = Math.floor(this.remainingTime / 60); var seconds = this.remainingTime % 60; var display = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; $('#timer-text').html('Please wait: ' + display + ''); // Update document title with countdown if (this.remainingTime > 0) { document.title = '(' + display + ') UPI Payment - Order #' + this.orderId; } else { document.title = 'UPI Payment - Order #' + this.orderId; } // Add progress indication var progress = ((this.timerDuration - this.remainingTime) / this.timerDuration) * 100; $('.timer-progress').css('width', progress + '%'); }, /** * Show continue button - Enhanced */ showContinueButton: function() { var self = this; $('#timer-container').fadeOut(300, function() { $('#continue-container').fadeIn(300); // Auto-focus continue button $('#continue-btn').focus(); // Add animation and sound (if available) $('#continue-btn').addClass('pulse-animation'); // Browser notification (if permission granted) if (Notification.permission === 'granted') { new Notification('UPI Payment Timer Complete', { body: 'You can now continue with your payment verification.', icon: '/favicon.ico' }); } // Haptic feedback on mobile if ('vibrate' in navigator) { navigator.vibrate([200, 100, 200]); } }); }, /** * Setup event handlers - Enhanced */ setupEventHandlers: function() { var self = this; // Continue button click $('#continue-btn').off('click').on('click', function(e) { e.preventDefault(); $(this).removeClass('pulse-animation'); $('#continue-container').fadeOut(300, function() { $('#upload-container').fadeIn(300); $('#upload-screenshot-btn').focus(); }); }); // Upload screenshot button $('#upload-screenshot-btn').off('click').on('click', function(e) { e.preventDefault(); if (!self.uploadInProgress) { $('#screenshot-input').click(); } }); // Handle file selection - Enhanced $('#screenshot-input').off('change').on('change', function(e) { var file = this.files[0]; if (file) { self.handleFileSelection(file); } }); // Confirm upload button $('#confirm-upload-btn').off('click').on('click', function(e) { e.preventDefault(); if (!self.uploadInProgress) { var file = $('#screenshot-input')[0].files[0]; if (file) { self.uploadScreenshot(file); } } }); // Skip screenshot option $('#skip-screenshot').off('click').on('click', function(e) { e.preventDefault(); if (confirm('Are you sure you want to skip uploading the payment screenshot? Your order verification may be delayed.')) { self.completePayment(false); } }); // Copy UPI ID - Enhanced version $('#copy-upi-id, #copy-upi-id-alt').off('click').on('click', function(e) { e.preventDefault(); var upiId = $(this).data('upi'); self.enhancedCopyUPI(upiId, this); }); // Drag and drop for screenshot self.setupDragAndDrop(); // Keyboard shortcuts self.setupKeyboardShortcuts(); // Window focus/blur events for better UX self.setupWindowEvents(); }, /** * Setup keyboard shortcuts - New */ setupKeyboardShortcuts: function() { var self = this; $(document).on('keydown', function(e) { // ESC to close modals if (e.keyCode === 27) { $('.upi-image-modal').fadeOut(200, function() { $(this).remove(); }); } // Space bar to trigger continue (when visible) if (e.keyCode === 32 && $('#continue-btn').is(':visible') && !$('input, textarea').is(':focus')) { e.preventDefault(); $('#continue-btn').click(); } // Ctrl+C to copy UPI ID (when visible) if (e.ctrlKey && e.keyCode === 67 && $('#copy-upi-id').length) { e.preventDefault(); $('#copy-upi-id').click(); } }); }, /** * Setup window events - Updated to keep timer running */ setupWindowEvents: function() { var self = this; // Remove the pause/resume timer functionality for tab switching // Timer will continue running in background // Only warn before leaving page if upload in progress $(window).on('beforeunload', function() { if (self.uploadInProgress) { return 'Upload in progress. Are you sure you want to leave?'; } }); }, /** * Setup drag and drop functionality - Enhanced */ setupDragAndDrop: function() { var self = this; var dropZone = $('#upload-container'); // Prevent default drag behaviors $(document).on('dragover dragenter', function(e) { e.preventDefault(); e.stopPropagation(); }); // Highlight drop zone dropZone.on('dragover dragenter', function(e) { e.preventDefault(); e.stopPropagation(); $('.upload-area').addClass('drag-over'); $(this).addClass('drag-active'); }); // Remove highlight dropZone.on('dragleave dragend', function(e) { e.preventDefault(); e.stopPropagation(); $('.upload-area').removeClass('drag-over'); $(this).removeClass('drag-active'); }); // Handle drop dropZone.on('drop', function(e) { e.preventDefault(); e.stopPropagation(); $('.upload-area').removeClass('drag-over'); $(this).removeClass('drag-active'); var files = e.originalEvent.dataTransfer.files; if (files.length > 0) { var file = files[0]; if (file.type.match('image.*')) { $('#screenshot-input')[0].files = files; self.handleFileSelection(file); self.showMessage('File dropped successfully!', 'success'); } else { self.showMessage('Please select an image file (JPG, PNG, or GIF)', 'error'); } } }); }, /** * Handle file selection - Enhanced */ handleFileSelection: function(file) { var self = this; // Validate file type if (!file.type.match('image.*')) { self.showMessage('Please select an image file (JPG, PNG, or GIF)', 'error'); return false; } // Validate file size (5MB limit) if (file.size > 5000000) { self.showMessage('File size must be less than 5MB. Current size: ' + self.formatFileSize(file.size), 'error'); return false; } // Show file info with enhanced details var fileInfo = 'Selected: ' + file.name + ' (' + self.formatFileSize(file.size) + ', ' + file.type + ')'; $('#file-info').remove(); $('#upload-screenshot-btn').after('
Loading preview...