/** * 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('
' + fileInfo + '
'); // Show preview with loading state var $preview = $('#screenshot-preview'); $preview.html('

Loading preview...

'); var reader = new FileReader(); reader.onload = function(e) { var previewHtml = '
' + 'Payment Screenshot' + '' + '
'; $preview.html(previewHtml); // Remove preview handler $('.remove-preview').on('click', function() { $('#screenshot-input').val(''); $preview.empty(); $('#confirm-upload-btn').hide(); $('#file-info').remove(); self.showMessage('Screenshot removed.', 'info'); }); // Show confirm button with animation $('#confirm-upload-btn').show().addClass('pulse-animation'); setTimeout(function() { $('#confirm-upload-btn').removeClass('pulse-animation'); }, 2000); // Add zoom functionality to preview $preview.find('img').on('click', function() { self.showImageModal(e.target.result); }); self.showMessage('Preview loaded successfully. Click to upload.', 'success'); }; reader.onerror = function() { self.showMessage('Failed to load preview. You can still upload the file.', 'warning'); $('#confirm-upload-btn').show(); }; reader.readAsDataURL(file); return true; }, /** * Upload screenshot - FIXED VERSION with optimizations */ uploadScreenshot: function(file) { var self = this; if (self.uploadInProgress) { console.log('Upload already in progress'); return; } self.uploadInProgress = true; // Prepare form data var formData = new FormData(); formData.append('action', 'upi_instant_upload_screenshot'); formData.append('nonce', upi_instant_params.nonce); formData.append('order_id', self.orderId); formData.append('order_key', self.orderKey); formData.append('screenshot', file); // Show loading state immediately self.showLoading('Uploading screenshot...'); // Disable buttons $('#upload-screenshot-btn, #confirm-upload-btn').prop('disabled', true); // Upload via AJAX with enhanced options $.ajax({ url: upi_instant_params.ajax_url, type: 'POST', data: formData, processData: false, contentType: false, cache: false, timeout: 30000, // 30 seconds timeout xhr: function() { var xhr = new window.XMLHttpRequest(); // Upload progress xhr.upload.addEventListener('progress', function(evt) { if (evt.lengthComputable) { var percentComplete = Math.round((evt.loaded / evt.total) * 100); self.updateLoadingProgress(percentComplete); // Show processing message when upload completes if (percentComplete >= 100) { $('.loading-message').text('Processing upload... Please wait'); } } }, false); return xhr; }, success: function(response) { console.log('Upload response:', response); // Hide loading immediately self.hideLoading(); self.uploadInProgress = false; if (response && response.success) { // Show success message self.showMessage(response.data.message || 'Screenshot uploaded successfully!', 'success'); // Show success animation self.showSuccessAnimation(); // Track event self.trackEvent('screenshot_upload_success', { order_id: self.orderId, file_size: file.size, file_type: file.type }); // Debug log the redirect URL console.log('Redirecting to:', response.data.redirect); // Redirect after delay - ENSURE THIS HAPPENS setTimeout(function() { if (response.data && response.data.redirect) { // Force redirect with location.replace for better reliability window.location.replace(response.data.redirect); } else { // Fallback to order received page console.log('No redirect URL, using fallback'); window.location.href = '/checkout/order-received/' + self.orderId + '/?key=' + self.orderKey; } }, 1500); // Reduced delay for faster redirect } else { // Show error var errorMsg = (response && response.data) ? response.data : 'Upload failed. Please try again.'; self.showMessage(errorMsg, 'error'); // Re-enable buttons $('#upload-screenshot-btn, #confirm-upload-btn').prop('disabled', false); self.trackEvent('screenshot_upload_error', { error: errorMsg, order_id: self.orderId }); } }, error: function(xhr, status, error) { console.error('Upload error:', error, 'Status:', status, 'Response:', xhr.responseText); self.hideLoading(); self.uploadInProgress = false; var errorMsg = 'Upload failed. '; if (status === 'timeout') { errorMsg += 'Request timed out. Please check your connection and try again.'; } else if (xhr.status === 413) { errorMsg += 'File too large. Please select a smaller image.'; } else if (xhr.status === 500) { errorMsg += 'Server error. Please try again later.'; } else { errorMsg += 'Please check your connection and try again.'; } self.showMessage(errorMsg, 'error'); $('#upload-screenshot-btn, #confirm-upload-btn').prop('disabled', false); self.trackEvent('screenshot_upload_error', { error: errorMsg, status: status, http_status: xhr.status, order_id: self.orderId }); } }); }, /** * Complete payment without screenshot - Enhanced */ completePayment: function(hasScreenshot) { var self = this; var formData = new FormData(); formData.append('action', 'upi_instant_complete_payment'); formData.append('nonce', upi_instant_params.nonce); formData.append('order_id', self.orderId); formData.append('order_key', self.orderKey); formData.append('has_screenshot', hasScreenshot ? '1' : '0'); self.showLoading('Processing payment...'); $.ajax({ url: upi_instant_params.ajax_url, type: 'POST', data: formData, processData: false, contentType: false, timeout: 30000, success: function(response) { if (response.success) { self.trackEvent('payment_completed', { order_id: self.orderId, has_screenshot: hasScreenshot }); self.showMessage('Payment processed successfully!', 'success'); setTimeout(function() { window.location.href = response.data.redirect; }, 1000); } else { self.showMessage(response.data || 'An error occurred', 'error'); } }, error: function() { self.showMessage('Failed to process payment. Please contact support.', 'error'); }, complete: function() { self.hideLoading(); } }); }, /** * Setup payment app buttons - Enhanced UPI Intent */ setupPaymentButtons: function() { var self = this; var upiLink = $('#qr_text').val(); $('.payment-app-btn').off('click').on('click', function(e) { e.preventDefault(); var appScheme = $(this).data('app'); var appLink = upiLink; var $btn = $(this); // Add loading state $btn.addClass('loading').prop('disabled', true); // Track click self.trackEvent('payment_app_clicked', { app: appScheme, order_id: self.orderId }); // Modify link based on app with proper intent schemes switch(appScheme) { case 'gpay': if (self.isMobile() && /android/i.test(navigator.userAgent)) { appLink = 'intent://pay' + upiLink.substring(6) + '#Intent;scheme=upi;package=com.google.android.apps.nbu.paisa.user;end'; } else { appLink = upiLink.replace('upi://', 'tez://upi/'); } break; case 'phonepe': if (self.isMobile() && /android/i.test(navigator.userAgent)) { appLink = 'intent://pay' + upiLink.substring(6) + '#Intent;scheme=upi;package=com.phonepe.app;end'; } else { appLink = upiLink.replace('upi://', 'phonepe://'); } break; case 'paytm': if (self.isMobile() && /android/i.test(navigator.userAgent)) { appLink = 'intent://pay' + upiLink.substring(6) + '#Intent;scheme=upi;package=net.one97.paytm;end'; } else { appLink = upiLink.replace('upi://', 'paytmmp://'); } break; case 'whatsapp': var merchantName = $('#merchant_name').val() || 'Online Store'; var amount = $('.payment-amount').text() || ''; var message = 'UPI Payment Request\n' + 'Merchant: ' + merchantName + '\n' + 'Amount: ' + amount + '\n' + 'UPI Link: ' + upiLink; appLink = 'https://wa.me/?text=' + encodeURIComponent(message); break; case 'bhim': if (self.isMobile() && /android/i.test(navigator.userAgent)) { appLink = 'intent://pay' + upiLink.substring(6) + '#Intent;scheme=upi;package=in.org.npci.upiapp;end'; } else { appLink = upiLink; } break; default: appLink = upiLink; } // Try to open the app if (self.isMobile()) { if (appScheme === 'whatsapp') { window.open(appLink, '_blank'); } else { window.location.href = appLink; // Enhanced fallback with retry option setTimeout(function() { if (!document.hidden) { var fallbackMsg = 'App didn\'t open? '; fallbackMsg += ' '; fallbackMsg += 'or scan the QR code manually.'; self.showMessage(fallbackMsg, 'warning'); $('.retry-app-btn').on('click', function() { window.location.href = $(this).data('link'); }); } }, 2500); } } else { self.showMessage('Please scan the QR code with your mobile device or enter the UPI ID manually in your UPI app.', 'info'); } // Remove loading state setTimeout(function() { $btn.removeClass('loading').prop('disabled', false); }, 1000); }); }, /** * Copy to clipboard - Enhanced */ copyToClipboard: function(text) { var self = this; // Modern clipboard API if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(function() { self.showMessage('UPI ID copied to clipboard!', 'success'); self.trackEvent('upi_id_copied', { method: 'modern', order_id: self.orderId }); }).catch(function(err) { self.fallbackCopy(text); }); } else { // Fallback method self.fallbackCopy(text); } }, /** * Fallback copy method - Enhanced */ fallbackCopy: function(text) { var self = this; var temp = $('