(function($){
'use strict';
const EasyInvoicePayment={
initialized: false,
selectedGateway: null,
stripeInstance: null,
cardElement: null,
stripeClientSecret: null,
$paymentButton: null,
originalPayButtonText: '',
messageClearTimeout: null,
$paymentPanel: null,
$mainContentArea: null,
$openPaymentPanelButton: null,
$closePaymentPanelButton: null,
init: function(){
if(window.easyInvoicePaymentInitialized||this.initialized){
return;
}
this.initialized=true;
window.easyInvoicePaymentInitialized=true;
this.$paymentButton=$('.payment-button');
this.originalPayButtonText=this.$paymentButton.text()||'Pay Now';
this.$paymentPanel=$('#payment-slideout-panel');
this.$mainContentArea=$('.easy-invoice-main-content-area');
this.$openPaymentPanelButton=$('#open-payment-panel-button');
this.$closePaymentPanelButton=$('#close-payment-panel-button');
this.setupEventListeners();
const $defaultMethod=$('input[name="payment_method_radio"]:checked');
if($defaultMethod.length){
$defaultMethod.trigger('change');
}},
setupEventListeners: function(){
$('input[name="payment_method_radio"]').on('change', this.handlePaymentMethodChange.bind(this));
this.$paymentButton.on('click', this.handlePaymentButtonClick.bind(this));
this.$openPaymentPanelButton.on('click', this.handleOpenPanel.bind(this));
this.$closePaymentPanelButton.on('click', this.handleClosePanel.bind(this));
$(document).on('keydown', this.handleKeyDown.bind(this));
},
handlePaymentMethodChange: function(e){
this.selectedGateway=$(e.currentTarget).val();
const $selectedEntry=$(e.currentTarget).closest('.payment-method-entry');
$('.payment-method-entry').removeClass('selected-gateway border-indigo-600 shadow-lg')
.addClass('border-gray-300 shadow-sm');
$selectedEntry.addClass('selected-gateway border-indigo-600 shadow-lg')
.removeClass('border-gray-300 shadow-sm');
$('.easy-invoice-stripe-card-area').addClass('hidden');
if(this.cardElement){
const $cardErrorsDiv=$('#card-errors');
if($cardErrorsDiv.length){
$cardErrorsDiv.text('');
}}
this.$paymentButton.text(this.originalPayButtonText);
if(this.selectedGateway==='stripe'){
this.initializeStripePayment($selectedEntry);
}else{
this.$paymentButton.prop('disabled', false);
}},
initializeStripePayment: function($selectedEntry){
const $stripeCardArea=$selectedEntry.find('#stripe-card-area');
if(!$stripeCardArea.length){
this.showPaymentMessage('Stripe UI setup error. Please try refreshing.', 'error');
return;
}
$stripeCardArea.removeClass('hidden').html('<p class="text-sm text-gray-600 p-2">Loading card details...</p>');
const invoiceId=this.$paymentButton.data('invoice-id');
if(!invoiceId){
$stripeCardArea.html('<p class="text-red-600 p-2">Error: Invoice ID missing.</p>');
return;
}
$.ajax({
url: easy_invoice_vars.ajax_url,
type: 'POST',
data: {
action: 'easy_invoice_process_payment',
invoice_id: invoiceId,
payment_method: 'stripe',
payment_nonce: easy_invoice_vars.nonce
},
success: (response)=> {
if(response.success&&response.data.client_secret){
this.stripeClientSecret=response.data.client_secret;
$stripeCardArea.html(`
<label for="card-element" class="block text-sm font-medium text-gray-700 mb-1">Credit or debit card</label>
<div id="card-element" class="mt-1 p-3 border border-gray-300 rounded-md shadow-sm bg-white"></div>
<div id="card-errors" role="alert" class="mt-2 text-sm text-red-600"></div>
`);
if(this.initializeStripeElements(this.stripeClientSecret, $selectedEntry)){
this.$paymentButton.text('Pay with Card');
const currentCardState=this.cardElement._empty ? false:this.cardElement._complete;
this.$paymentButton.prop('disabled', !currentCardState);
}else{
this.$paymentButton.prop('disabled', true);
}}else{
const errorMsg=response.data&&response.data.message
? response.data.message
: 'Could not initialize Stripe payment.';
$stripeCardArea.html(`<p class="text-red-600 p-2">${errorMsg}</p>`);
this.$paymentButton.prop('disabled', true);
}},
error: (xhr)=> {
$stripeCardArea.html('<p class="text-red-600 p-2">Server error during Stripe setup. Please try again.</p>');
this.$paymentButton.prop('disabled', true);
}});
},
initializeStripeElements: function(clientSecret, $stripeMethodEntry){
if(!this.stripeInstance){
this.stripeInstance=Stripe(easy_invoice_vars.stripe_public_key);
}
const elements=this.stripeInstance.elements({
clientSecret: clientSecret
});
if(this.cardElement){
this.cardElement.destroy();
}
this.cardElement=elements.create('card');
const $stripeCardArea=$stripeMethodEntry.find('#stripe-card-area');
const $cardElementDiv=$stripeCardArea.find('#card-element');
const $cardErrorsDiv=$stripeCardArea.find('#card-errors');
if(!$cardElementDiv.length){
$stripeCardArea.removeClass('hidden')
.html('<p class="text-red-600">Stripe card input cannot be displayed. Element missing.</p>');
this.$paymentButton.prop('disabled', true);
return false;
}
$cardElementDiv.empty();
this.cardElement.mount($cardElementDiv.get(0));
$stripeCardArea.removeClass('hidden');
this.cardElement.on('change', (event)=> {
if(event.error){
$cardErrorsDiv.text(event.error.message);
this.$paymentButton.prop('disabled', true);
}else{
$cardErrorsDiv.text('');
this.$paymentButton.prop('disabled', !event.complete);
}});
return true;
},
handlePaymentButtonClick: function(e){
e.preventDefault();
this.$paymentButton.prop('disabled', true);
if(!this.selectedGateway){
this.showPaymentMessage('Please select a payment method first.', 'error');
this.$paymentButton.prop('disabled', false);
return;
}
this.$paymentButton.html('<i class="fas fa-circle-notch fa-spin mr-2"></i>Processing...');
if(this.selectedGateway==='stripe'){
this.processStripePayment();
return;
}
this.processStandardPayment();
},
processStripePayment: function(){
if(!this.stripeInstance||!this.cardElement||!this.stripeClientSecret){
this.showPaymentMessage('Stripe payment setup is incomplete. Please refresh and try again.', 'error');
this.$paymentButton.html(this.originalPayButtonText).prop('disabled', false);
return;
}
this.stripeInstance.confirmCardPayment(this.stripeClientSecret, {
payment_method: {
card: this.cardElement
}})
.then((result)=> {
if(result.error){
this.showPaymentMessage(result.error.message, 'error');
this.$paymentButton.html(this.originalPayButtonText).prop('disabled', false);
}else if(result.paymentIntent&&result.paymentIntent.status==='succeeded'){
this.createPaymentRecord(result.paymentIntent.id);
}else{
this.showPaymentMessage('Payment processing failed. Please try again.', 'error');
this.$paymentButton.html(this.originalPayButtonText).prop('disabled', false);
}})
.catch((error)=> {
this.showPaymentMessage('Payment processing error. Please try again.', 'error');
this.$paymentButton.html(this.originalPayButtonText).prop('disabled', false);
});
},
processStandardPayment: function(){
const invoiceId=this.$paymentButton.data('invoice-id');
if(!invoiceId){
this.showPaymentMessage('Invoice ID is missing. Please refresh the page.', 'error');
this.$paymentButton.html(this.originalPayButtonText).prop('disabled', false);
return;
}
$.ajax({
url: easy_invoice_vars.ajax_url,
type: 'POST',
data: {
action: 'easy_invoice_process_payment',
invoice_id: invoiceId,
payment_method: this.selectedGateway,
payment_nonce: easy_invoice_vars.nonce
},
success: (response)=> {
if(response.success){
this.handlePaymentSuccess(response.data);
}else{
const errorMsg=response.data&&response.data.message
? response.data.message
: 'Payment processing failed.';
this.showPaymentMessage(errorMsg, 'error');
this.$paymentButton.html(this.originalPayButtonText).prop('disabled', false);
}},
error: (xhr)=> {
this.showPaymentMessage('Server error during payment. Please try again.', 'error');
this.$paymentButton.html(this.originalPayButtonText).prop('disabled', false);
}});
},
createPaymentRecord: function(transactionId){
const invoiceId=this.$paymentButton.data('invoice-id');
$.ajax({
url: easy_invoice_vars.ajax_url,
type: 'POST',
data: {
action: 'easy_invoice_create_payment_record',
invoice_id: invoiceId,
payment_method: 'stripe',
transaction_id: transactionId,
payment_nonce: easy_invoice_vars.nonce
},
success: (response)=> {
if(response.success){
this.handlePaymentSuccess(response.data);
}else{
const errorMsg=response.data&&response.data.message
? response.data.message
: 'Payment recorded but failed to update records.';
this.showPaymentMessage(errorMsg, 'warning');
this.$paymentButton.html('Payment Recorded').prop('disabled', true);
setTimeout(()=> {
window.location.reload();
}, 3000);
}},
error: (xhr)=> {
this.showPaymentMessage('Payment successful but failed to update records. The administrator has been notified.', 'warning');
this.$paymentButton.html('Payment Recorded').prop('disabled', true);
setTimeout(()=> {
window.location.reload();
}, 3000);
}});
},
handlePaymentSuccess: function(data){
this.$paymentButton.html('<i class="fas fa-check-circle mr-2"></i>Payment Successful!').prop('disabled', true);
this.showPaymentMessage(data.message||'Payment successful!', 'success');
if(data.redirect_url){
setTimeout(()=> {
window.location.href=data.redirect_url;
}, 2000);
return;
}
setTimeout(()=> {
window.location.reload();
}, 2000);
},
showPaymentMessage: function(message, type){
const $messageArea=$('#payment-message-area');
if(!$messageArea.length){
return;
}
if(this.messageClearTimeout){
clearTimeout(this.messageClearTimeout);
}
let bgClass, textClass, icon;
switch (type){
case 'success':
bgClass='bg-green-100 border-green-400';
textClass='text-green-700';
icon='fa-check-circle';
break;
case 'warning':
bgClass='bg-yellow-100 border-yellow-400';
textClass='text-yellow-700';
icon='fa-exclamation-triangle';
break;
case 'error':
default:
bgClass='bg-red-100 border-red-400';
textClass='text-red-700';
icon='fa-times-circle';
break;
}
const $messageHtml=$(`
<div class="rounded-md border px-4 py-3 ${bgClass} mb-4 animate-fade-in">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas ${icon} ${textClass}"></i>
</div>
<div class="ml-3">
<p class="text-sm ${textClass}">${message}</p>
</div>
</div>
</div>
`);
$messageArea.html($messageHtml);
if(type!=='success'){
this.messageClearTimeout=setTimeout(()=> {
$messageHtml.fadeOut(300, function(){
$(this).remove();
});
}, 6000);
}},
handleOpenPanel: function(e){
e.preventDefault();
this.$paymentPanel.addClass('is-open');
$('body').addClass('body-panel-active');
this.$closePaymentPanelButton.focus();
},
handleClosePanel: function(e){
e.preventDefault();
this.$paymentPanel.removeClass('is-open');
$('body').removeClass('body-panel-active');
this.$openPaymentPanelButton.focus();
},
handleKeyDown: function(event){
if(event.key==="Escape"&&this.$paymentPanel.hasClass('is-open')){
this.handleClosePanel(event);
}}
};
$(document).ready(function(){
EasyInvoicePayment.init();
});
})(jQuery);