PageSourceSearch

https://resonancerecords.org/wp-content/plugins/peachpay-for-wooco…/js/convesiopay-unified-blocks.js?ver=1.0.0

js resonancerecords.org collected 2026-10-02 05:34:04 UTC 55,802 bytes, 1,364 lines download raw bytes

1/**
2 * ConvesioPay Unified WooCommerce Blocks Integration
3 * Handles both card and BTCPay payments based on detected type
4 */
5
6(function() {
7    'use strict';
8
9    // Use the proper WooCommerce Blocks API with error handling
10    const { registerPaymentMethod } = window.wc?.wcBlocksRegistry || {};
11    const { createElement, useEffect, useState } = wp?.element || {};
12    const { __ } = wp?.i18n || {};
13    const getSetting = (window.wc?.wcSettings?.getSetting) || function(key, defaultValue) { return defaultValue; };
14
15    // Check if required APIs are available
16    if (!registerPaymentMethod) {
17        return;
18    }
19    // Get ConvesioPay settings
20    const settings = getSetting('peachpay_convesiopay_unified_data', {});
21
22    // Global state management
23    let convesioPayComponent = null;
24    let isComponentMounted = false;
25    let btcPaySession = null;
26    let isSessionCreated = false;
27    let paymentConfirmedData = null;
28    let currentPaymentMethod = null;
29    let lastKnownCartTotal = 0;
30
31    /**
32     * Reset session state for new checkout
33     */
34    const resetSessionState = () => {
35        convesioPayComponent = null;
36        isComponentMounted = false;
37        btcPaySession = null;
38        isSessionCreated = false;
39        paymentConfirmedData = null;
40        currentPaymentMethod = null;
41    };
42
43    /**
44     * Label component for ConvesioPay
45     */
46    const ConvesioPayLabel = () => {
47        return createElement('span', { 
48            className: 'wc-block-components-payment-method-label' 
49        }, 
50            ''
51        );
52    };
53
54    /**
55     * Detect payment method based on ConvesioPay events and validation data
56     *
57     * @param {Object} event ConvesioPay component event
58     * @return {string} Detected payment method (card, btcpay, applepay, etc.)
59     */
60    const detectPaymentMethod = (event) => {
61        // Method 1: Check explicit payment method in event
62        if (event.type) {
63            return event.type;
64        }
65        // Default fallback
66        return 'unknown';
67    };
68
69    /**
70     * Create PeachPay transaction for ConvesioPay (unified version)
71     * Exact copy of createPeachPayTransaction from checkout.js
72     */
73    const createPeachPayTransactionUnified = () => {
74        // Check if we have the AJAX configuration
75        if (typeof window.peachpayConvesioPayAjax === 'undefined') {
76            return;
77        }
78
79        // Prepare transaction data for centralized PeachPay endpoint
80        const transactionData = {
81            gateway_id: 'peachpay_convesiopay_card',
82            checkout_location: 'checkout-blocks' // Always blocks for unified
83        };
84
85        // Debug: Check what URL we're using
86        const transactionUrl = window.peachpayConvesioPayAjax.create_transaction_url || window.peachpayConvesioPayAjax.ajax_url;
87
88        // Prepare FormData based on endpoint type
89        const formData = new FormData();
90
91        // Check if we're using the centralized endpoint or falling back to AJAX
92        if (window.peachpayConvesioPayAjax.create_transaction_url &&
93            !window.peachpayConvesioPayAjax.create_transaction_url.includes('admin-ajax.php')) {
94
95            // Use centralized PeachPay endpoint
96            formData.append('gateway_id', transactionData.gateway_id);
97            formData.append('checkout_location', transactionData.checkout_location);
98
99        } else {
100            // Fallback to standard AJAX with action parameter
101            formData.append('action', 'pp-create-transaction');
102            formData.append('gateway_id', transactionData.gateway_id);
103            formData.append('checkout_location', transactionData.checkout_location);
104            formData.append('nonce', window.peachpayConvesioPayAjax.nonce);
105        }
106
107        // Send the request
108        fetch(transactionUrl, {
109            method: 'POST',
110            body: formData
111        })
112        .then(response => response.json())
113        .then(data => {
114            if (data.success) {
115            } else {
116            }
117        })
118        .catch(error => {
119        });
120    };
121
122    /**
123     * Store payment token in form (unified blocks version)
124     */
125    const storePaymentTokenUnified = (token) => {
126        // Store in hidden field for traditional checkout
127        const tokenField = document.getElementById('convesiopay-payment-token');
128        if (tokenField) {
129            tokenField.value = token;
130        }
131
132        // Store globally for WooCommerce Blocks
133        window.convesiopayPaymentToken = token;
134
135    };
136
137    /**
138     * Enable submit button (unified blocks version)
139     */
140    const enableSubmitButtonUnified = () => {
141        // For WooCommerce Blocks
142        const submitButton = document.querySelector('.wc-block-components-checkout-place-order-button');
143        if (submitButton) {
144            submitButton.disabled = false;
145        }
146
147        // For traditional checkout (fallback)
148        const checkoutForm = document.querySelector('form.checkout');
149        if (checkoutForm) {
150            const submitBtn = checkoutForm.querySelector('#place_order');
151            if (submitBtn) {
152                submitBtn.disabled = false;
153            }
154        }
155    };
156
157    /**
158     * Disable submit button (unified blocks version)
159     */
160    const disableSubmitButtonUnified = () => {
161        // For WooCommerce Blocks
162        const submitButton = document.querySelector('.wc-block-components-checkout-place-order-button');
163        // if (submitButton) {
164        //     submitButton.disabled = true;
165        // }
166
167        // For traditional checkout (fallback)
168        const checkoutForm = document.querySelector('form.checkout');
169        if (checkoutForm) {
170            const submitBtn = checkoutForm.querySelector('#place_order');
171            // if (submitBtn) {
172            //     submitBtn.disabled = true;
173            // }
174        }
175    };
176
177    /**
178     * Show error message (unified blocks version)
179     */
180    const showErrorUnified = (message) => {
181
182        // Skip WordPress notices system - it's causing compatibility issues
183        // Show error directly in checkout validation area
184        try {
185            const checkoutContainer = document.querySelector('.wc-block-checkout') ||
186                                      document.querySelector('.wp-block-woocommerce-checkout') ||
187                                      document.querySelector('#convesiopay-unified-component-container');
188
189            if (checkoutContainer) {
190                // Remove existing error
191                const existingError = checkoutContainer.querySelector('.convesiopay-unified-error');
192                if (existingError) {
193                    existingError.remove();
194                }
195
196                // Add new error message
197                const errorDiv = document.createElement('div');
198                errorDiv.className = 'convesiopay-unified-error wc-block-components-notice-banner is-error';
199                errorDiv.style.cssText = 'margin: 10px 0; padding: 12px; background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; border-radius: 4px; font-family: inherit;';
200                errorDiv.innerHTML = `<strong>Payment Error:</strong> ${message}`;
201
202                // Insert at top of checkout container
203                checkoutContainer.insertBefore(errorDiv, checkoutContainer.firstChild);
204
205                // Auto-remove after 8 seconds
206                setTimeout(() => {
207                    if (errorDiv && errorDiv.parentNode) {
208                        errorDiv.remove();
209                    }
210                }, 8000);
211            }
212        } catch (error) {
213            // Last resort: alert (for critical errors)
214            if (message.includes('token') || message.includes('payment')) {
215                alert('Payment Error: ' + message);
216            }
217        }
218    };
219
220    /**
221     * Hide error message (unified blocks version)
222     */
223    const hideErrorUnified = () => {
224        // Skip WordPress notices system - it's causing compatibility issues
225        // Just clear our custom error messages
226        try {
227            const customErrors = document.querySelectorAll('.convesiopay-unified-error');
228            customErrors.forEach(error => {
229                if (error.parentNode) {
230                    error.remove();
231                }
232            });
233        } catch (error) {
234        }
235    };
236
237    /**
238     * Handle card payment using peachpay_convesiopay_card functions (when event.type === 'card')
239     * This mimics what happens when peachpay_convesiopay_card is selected
240     */
241    const handleCardPaymentWithPeachPayFunctions = async (event, component) => {
242
243        // STEP 1: Call createPeachPayTransaction() (what peachpay_convesiopay_card does)
244        createPeachPayTransactionUnified();
245
246        // STEP 2: Handle component change validation (like checkout.js handleComponentChange)
247        if (event.isValid) {
248            // All component fields are valid and we can get the token
249            try {
250                const tokenResult = component.createToken();
251
252                // Handle both Promise and direct string return
253                let token;
254                if (tokenResult && typeof tokenResult.then === 'function') {
255                    // It's a Promise, await it
256                    token = await tokenResult;
257                } else {
258                    // It's a direct value
259                    token = tokenResult;
260                }
261
262                if (token) {
263                    // Store payment token (exactly like checkout.js storePaymentToken function)
264                    storePaymentTokenUnified(token);
265                }
266
267                // Get order data
268                const orderData = getOrderData();
269
270                if (!orderData.isValid) {
271                    throw new Error('Invalid order data');
272                }
273
274                // Store payment data for blocks processing
275                paymentConfirmedData = {
276                    paymentId: 'card_' + Date.now(),
277                    status: 'succeeded',
278                    paymentMethod: 'card',
279                    amount: orderData.amount,
280                    currency: orderData.currency,
281                    orderNumber: orderData.orderNumber,
282                    token: token
283                };
284
285                currentPaymentMethod = 'card';
286
287                // Component is valid, enable checkout (exactly like checkout.js enableSubmitButton)
288                enableSubmitButtonUnified();
289                hideErrorUnified();
290
291
292            } catch (error) {
293                showErrorUnified('Failed to create payment token');
294                disableSubmitButtonUnified();
295            }
296        } else {
297            // Handle errors, one or more component fields are not valid (like checkout.js)
298            showErrorUnified('Please complete all required payment fields');
299            disableSubmitButtonUnified();
300        }
301    };
302
303    /**
304     * Handle card payment using component.createToken() and ConvesioPay API
305     */
306    const handleCardPayment = async (component, event) => {
307        try {
308            
309            // Create payment token using component (following checkout.js pattern)
310            const tokenResult = component.createToken();
311            
312            // Handle both Promise and direct string return
313            let token;
314            if (tokenResult && typeof tokenResult.then === 'function') {
315                // It's a Promise, await it
316                token = await tokenResult;
317            } else {
318                // It's a direct value
319                token = tokenResult;
320            }
321            
322            if (!token) {
323                throw new Error('Failed to create payment token');
324            }
325            
326            
327            // Get order data
328            const orderData = getOrderData();
329            
330            if (!orderData.isValid) {
331                throw new Error('Invalid order data');
332            }
333                
334                // Store payment data but DON'T auto-click - let user click Place Order
335                paymentConfirmedData = {
336                    paymentId: 'card_' + Date.now(),
337                    status: 'succeeded',
338                    paymentMethod: 'card',
339                    amount: orderData.amount,
340                    currency: orderData.currency,
341                    orderNumber: orderData.orderNumber,
342                    token: token
343                };
344            
345            currentPaymentMethod = 'card';
346            
347            // Show success message instead of auto-clicking
348            
349        } catch (error) {
350        }
351    };
352
353    /**
354     * Handle BTCPay payment (following BTCPay blocks pattern)
355     */
356    const handleBTCPayPayment = (event) => {
357        
358        // For BTCPay, only proceed if status is 'Processing' (not 'Pending')
359        if (event.paymentData && event.paymentData.status === 'Processing') {
360            
361            // Store payment data and trigger auto-click
362            paymentConfirmedData = {
363                paymentId: event.paymentData.paymentId,
364                invoiceId: event.paymentData.paymentId,
365                status: event.paymentData.status,
366                amount: event.paymentData.amount,
367                currency: event.paymentData.currency,
368                orderNumber: event.paymentData.orderNumber,
369                paymentMethod: 'btcpay'
370            };
371            
372            currentPaymentMethod = 'btcpay';
373            
374            setTimeout(() => {
375                autoClickPlaceOrderButton();
376            }, 500);
377        } else if (event.paymentData) {
378        }
379    };
380
381    /**
382     * Handle Apple Pay payment success with auto-submit (NEW - following ConvesioPay docs)
383     */
384    const handleApplePayPaymentSuccess = async (component, event) => {
385        try {
386            // Get order data
387            const orderData = getOrderData();
388
389            if (!orderData.isValid) {
390                showErrorUnified('Invalid order data');
391                return;
392            }
393
394            // Try to create a payment token if component provides createToken method
395            let paymentToken = null;
396            if (component && typeof component.createToken === 'function') {
397                try {
398                    paymentToken = await component.createToken();
399                } catch (tokenError) {
400                    // Token creation failed, continue without token
401                }
402            }
403
404            // Try multiple possible locations for payment ID
405            const possiblePaymentId = event.paymentData?.paymentId ||
406                                     event.paymentData?.id ||
407                                     event.paymentId ||
408                                     event.id ||
409                                     event.payment_id ||
410                                     event.transactionId ||
411                                     'applepay_' + Date.now();
412
413            paymentConfirmedData = {
414                paymentId: possiblePaymentId,
415                status: event.paymentData?.status || event.status || 'succeeded',
416                paymentMethod: 'applepay',
417                amount: event.paymentData?.amount || orderData.amount,
418                currency: event.paymentData?.currency || orderData.currency,
419                orderNumber: event.paymentData?.orderNumber || orderData.orderNumber,
420                token: paymentToken || event.token || event.paymentData?.token
421            };
422
423            currentPaymentMethod = 'applepay';
424
425            // Enable the Place Order button and auto-click it
426            setTimeout(() => {
427                let placeOrderButton = document.querySelector('.wc-block-components-checkout-place-order-button') ||
428                                      document.querySelector('.wp-element-button.wc-block-components-checkout-place-order-button') ||
429                                      document.querySelector('button[type="submit"]');
430
431                if (placeOrderButton) {
432                    placeOrderButton.disabled = false;
433                    placeOrderButton.removeAttribute('aria-disabled');
434                    placeOrderButton.style.opacity = '1';
435                    placeOrderButton.style.cursor = 'pointer';
436                    placeOrderButton.style.pointerEvents = 'auto';
437
438                    // Force a reflow to ensure styles are applied
439                    placeOrderButton.offsetHeight;
440
441                    // Auto-click the button to complete the order
442                    placeOrderButton.click();
443                }
444            }, 500);
445
446        } catch (error) {
447            showErrorUnified('Apple Pay failed: ' + error.message);
448        }
449    };
450
451    /**
452     * Handle Apple Pay payment (LEGACY - requires manual click)
453     */
454    const handleApplePayPayment = async (component, event) => {
455        try {
456
457            // Store payment data and trigger auto-click
458            paymentConfirmedData = {
459                paymentId: event.paymentData.paymentId,
460                status: event.paymentData.status,
461                paymentMethod: 'applepay',
462                amount: event.paymentData.amount,
463                currency: event.paymentData.currency,
464                orderNumber: event.paymentData.orderNumber
465            };
466
467            currentPaymentMethod = 'applepay';
468
469            setTimeout(() => {
470                autoClickPlaceOrderButton();
471            }, 500);
472
473        } catch (error) {
474        }
475    };
476
477    /**
478     * Listen for postMessage events from BTCPay iframe
479     */
480    const setupBTCPayMessageListener = () => {
481        window.addEventListener('message', (event) => {
482            // Verify the origin is from BTCPay
483            if (!event.origin.includes('btcpay') && !event.origin.includes('convesiopay')) {
484                return;
485            }
486            
487            try {
488                let messageData;
489                
490                // Handle both string and object message data
491                if (typeof event.data === 'string') {
492                    try {
493                        messageData = JSON.parse(event.data);
494                    } catch (e) {
495                        messageData = { type: event.data };
496                    }
497            } else {
498                    messageData = event.data;
499                }
500                
501                // Check for payment status messages - 'Processing' means payment received
502                if (messageData.status === 'Processing') {
503                    
504                    // Extract payment details from the message
505                    const extractedData = {
506                        invoiceId: messageData.invoiceId,
507                        status: 'Processing',
508                        paymentId: messageData.invoiceId,
509                        paymentMethod: 'btcpay'
510                    };
511                    
512                    paymentConfirmedData = extractedData;
513                    
514                    // Trigger auto-click of place order button
515                    setTimeout(() => {
516                        autoClickPlaceOrderButton();
517                    }, 500);
518            }
519            
520        } catch (error) {
521            }
522        });
523    };
524
525    /**
526     * Auto-click the place order button for WooCommerce Blocks checkout
527     */
528    const autoClickPlaceOrderButton = () => {
529
530        // Use stored payment data
531        if (!paymentConfirmedData) {
532
533            // Disable the place order button
534            const placeOrderButton = document.querySelector('.components-button.wc-block-components-button.wp-element-button.wc-block-components-checkout-place-order-button.contained');
535            if (placeOrderButton) {
536                // placeOrderButton.disabled = true;
537                placeOrderButton.style.opacity = '0.5';
538                placeOrderButton.style.cursor = 'not-allowed';
539            }
540            return;
541        }
542
543
544        // Reset session state after successful payment
545        setTimeout(() => {
546            resetSessionState();
547        }, 5000);
548
549        // Find the place order button
550        let placeOrderButton = document.querySelector('.components-button.wc-block-components-button.wp-element-button.wc-block-components-checkout-place-order-button.contained');
551
552        if (!placeOrderButton) {
553            // Try alternative selectors
554            placeOrderButton = document.querySelector('.wc-block-components-checkout-place-order-button') ||
555                              document.querySelector('[data-block-name="woocommerce/checkout-order-summary-block"] button') ||
556                              document.querySelector('button[type="submit"]');
557        }
558
559        if (placeOrderButton) {
560
561            // IMPORTANT: Enable the button before clicking
562            placeOrderButton.disabled = false;
563            // placeOrderButton.removeAttribute('aria-disabled');
564            placeOrderButton.style.opacity = '1';
565            placeOrderButton.style.cursor = 'pointer';
566            placeOrderButton.style.pointerEvents = 'auto';
567
568            // Force reflow
569            placeOrderButton.offsetHeight;
570
571            // Click the button
572            placeOrderButton.click();
573
574        } else {
575            console.log('ERROR: No place order button found!');
576        }
577    };
578
579    /**
580     * Get current cart total from WooCommerce Blocks store
581     * @returns {number} Current cart total in cents
582     */
583    const getCurrentCartTotal = () => {
584        // Try to get cart total from blocks store
585        if (window.wp && window.wp.data && window.wp.data.select) {
586            try {
587                const store = window.wp.data.select('wc/store/cart');
588                const cartTotals = store.getCartTotals();
589
590                if (cartTotals && cartTotals.total_price) {
591                    return parseInt(cartTotals.total_price);
592                }
593            } catch (error) {
594                // Fallback to DOM
595            }
596        }
597
598        // Fallback: Try to get from DOM
599        const totalElement = document.querySelector('.wc-block-components-totals-item__value');
600        if (totalElement) {
601            const totalText = totalElement.textContent.replace(/[^\d.]/g, '');
602            const amount = parseFloat(totalText);
603            if (!isNaN(amount)) {
604                return Math.round(amount * 100); // Convert to cents
605            }
606        }
607
608        return 0;
609    };
610
611    /**
612     * Monitor cart total changes and update component amount for ApplePay/GooglePay
613     */
614    const setupCartTotalMonitoring = () => {
615        // Listen for WooCommerce Blocks cart updates
616        if (window.wp && window.wp.data && window.wp.data.subscribe) {
617            let previousTotal = getCurrentCartTotal();
618
619            window.wp.data.subscribe(() => {
620                const currentTotal = getCurrentCartTotal();
621
622                // Check if total has changed
623                if (currentTotal !== previousTotal && currentTotal > 0) {
624                    previousTotal = currentTotal;
625                    lastKnownCartTotal = currentTotal;
626
627                    // Update component amount if component exists
628                    if (convesioPayComponent) {
629                        updateComponentAmount(currentTotal);
630                    }
631                }
632            });
633        }
634
635        // Also monitor checkout form changes as a fallback
636        const observer = new MutationObserver(() => {
637            const currentTotal = getCurrentCartTotal();
638
639            if (currentTotal !== lastKnownCartTotal && currentTotal > 0) {
640                lastKnownCartTotal = currentTotal;
641
642                // Update component amount if component exists
643                if (convesioPayComponent) {
644                    updateComponentAmount(currentTotal);
645                }
646            }
647        });
648
649        // Observe the totals section for changes
650        const totalsElement = document.querySelector('.wc-block-components-totals-wrapper');
651        if (totalsElement) {
652            observer.observe(totalsElement, {
653                childList: true,
654                subtree: true,
655                characterData: true
656            });
657        }
658    };
659
660    /**
661     * Update component amount when cart total changes
662     * Recreates the ApplePay session with new amount
663     */
664    const updateComponentAmount = async (newAmountInCents) => {
665        try {
666
667            // Step 1: Unmount and destroy the old component
668            if (convesioPayComponent) {
669                try {
670                    // Unmount the component if it has an unmount method
671                    if (typeof convesioPayComponent.unmount === 'function') {
672                        await convesioPayComponent.unmount();
673                    }
674
675                    // Destroy the component if it has a destroy method
676                    if (typeof convesioPayComponent.destroy === 'function') {
677                        convesioPayComponent.destroy();
678                    }
679                } catch (unmountError) {
680                    console.log('[ConvesioPay Blocks] Error during unmount/destroy:', unmountError);
681                }
682
683                // Clear the component reference
684                convesioPayComponent = null;
685                isComponentMounted = false;
686
687                // Clear the container
688                const componentContainer = document.querySelector('#convesiopay-unified-component-container');
689                if (componentContainer) {
690                    componentContainer.innerHTML = '';
691                }
692            }
693
694            // Step 2: Clear session state
695            isSessionCreated = false;
696            const hadBtcPaySession = btcPaySession !== null;
697            btcPaySession = null;
698
699            // Step 3: Create and mount new component
700            const componentResult = await createAndMountConvesioPayComponent();
701
702            if (!componentResult.success) {
703                console.error('[ConvesioPay Blocks] Failed to create new component:', componentResult.message);
704                return;
705            }
706
707            // Mount the component in the container
708            const componentContainer = document.querySelector('#convesiopay-unified-component-container');
709            if (componentContainer) {
710                componentContainer.innerHTML = '';
711                await convesioPayComponent.mount('#convesiopay-unified-component-container');
712            }
713
714            // Step 4: Get fresh order data with updated amount
715            const orderData = getOrderData();
716
717            // Override with the new amount (in case getOrderData() hasn't updated yet)
718            orderData.amount = newAmountInCents;
719
720            // Step 5: Create ApplePay session with new amount
721            try {
722                await convesioPayComponent.createApplePaySession({
723                    integration: settings.integration_name || window.peachpayConvesioPayUnified?.integration_name || 'PeachPay',
724                    returnUrl: window.location.href,
725                    orderNumber: orderData.orderNumber,
726                    amount: orderData.amount,
727                    currency: orderData.currency,
728                    email: orderData.email,
729                    name: orderData.name
730                });
731            } catch (applePayError) {
732                console.log('[ConvesioPay Blocks] ApplePay session creation failed (might not be available):', applePayError);
733            }
734
735            // Step 6: Create BTCPay session and intent if it was previously active
736            if (hadBtcPaySession) {
737                try {
738
739                    // Create new BTCPay session with updated amount
740                    const sessionResult = await createBTCPaySession();
741
742                    if (sessionResult.success && sessionResult.session) {
743                        // Update the global session variable
744                        btcPaySession = sessionResult.session;
745
746                        // Prepare BTCPay intent data
747                        const btcPayIntentData = {
748                            session: btcPaySession
749                        };
750
751                        // Create the BTCPay intent with the new session
752                        await convesioPayComponent.createBTCPayIntent(btcPayIntentData);
753
754                        // Re-setup the message listener
755                        setupBTCPayMessageListener();
756                    }
757                } catch (btcPayError) {
758                    console.error('[ConvesioPay Blocks] BTCPay session/intent recreation failed:', btcPayError);
759                }
760            }
761
762        } catch (error) {
763            console.error('[ConvesioPay Blocks] Error updating component amount:', error);
764        }
765    };
766
767    /**
768     * Get order data for ConvesioPay including required address fields
769     */
770    const getOrderData = () => {
771        const orderData = {
772            isValid: false,
773            orderNumber: getOrderNumber(),
774            amount: 0,
775            currency: 'USD',
776            email: '',
777            name: 'Customer',
778            // Required address fields for ConvesioPay API
779            billingAddress: {
780                street: '123 Default Street', // Must be at least 1 character
781                city: 'Default City',
782                state: 'CA',
783                postalCode: '90210',
784                country: 'US'
785            },
786            shippingAddress: {
787                street: '123 Default Street', // Must be at least 1 character
788                city: 'Default City',
789                state: 'CA',
790                postalCode: '90210',
791                country: 'US'
792            }
793        };
794
795        // Try to get cart total from blocks store
796        if (window.wp && window.wp.data && window.wp.data.select) {
797            try {
798                const store = window.wp.data.select('wc/store/cart');
799                const cartTotals = store.getCartTotals();
800
801                if (cartTotals && cartTotals.total_price) {
802                    orderData.amount = parseInt(cartTotals.total_price);
803                }
804
805                if (cartTotals && cartTotals.currency_code) {
806                    orderData.currency = cartTotals.currency_code;
807                }
808
809                // Try to get billing address from checkout store
810                try {
811                    const checkoutStore = window.wp.data.select('wc/store/checkout');
812                    if (checkoutStore) {
813                        const billingData = checkoutStore.getBillingData();
814                        const shippingData = checkoutStore.getShippingData();
815
816                        if (billingData) {
817                            orderData.email = billingData.email || orderData.email;
818                            orderData.name = (billingData.first_name + ' ' + billingData.last_name).trim() || orderData.name;
819
820                            if (billingData.address_1 && billingData.address_1.trim()) {
821                                orderData.billingAddress = {
822                                    street: billingData.address_1.trim() + (billingData.address_2 ? ' ' + billingData.address_2.trim() : ''),
823                                    city: billingData.city || 'Default City',
824                                    state: billingData.state || 'CA',
825                                    postalCode: billingData.postcode || '90210',
826                                    country: billingData.country || 'US'
827                                };
828                            }
829                        }
830
831                        if (shippingData && shippingData.address_1 && shippingData.address_1.trim()) {
832                            orderData.shippingAddress = {
833                                street: shippingData.address_1.trim() + (shippingData.address_2 ? ' ' + shippingData.address_2.trim() : ''),
834                                city: shippingData.city || 'Default City',
835                                state: shippingData.state || 'CA',
836                                postalCode: shippingData.postcode || '90210',
837                                country: shippingData.country || 'US'
838                            };
839                        } else {
840                            // Use billing address as shipping if shipping is empty
841                            orderData.shippingAddress = { ...orderData.billingAddress };
842                        }
843                    }
844                } catch (checkoutError) {
845                }
846            } catch (error) {
847            }
848        }
849
850        // Try to get amount from DOM if store failed
851        if (orderData.amount === 0) {
852            const totalElement = document.querySelector('.wc-block-components-totals-item__value');
853            if (totalElement) {
854                const totalText = totalElement.textContent.replace(/[^\d.]/g, '');
855                const amount = parseFloat(totalText);
856                if (!isNaN(amount)) {
857                    orderData.amount = Math.round(amount * 100); // Convert to cents
858                }
859            }
860        }
861
862        // Try to get customer data from checkout form fields if store failed
863        if (orderData.email === '') {
864            const emailField = document.querySelector('input[type="email"]');
865            if (emailField && emailField.value) {
866                orderData.email = emailField.value;
867            } else {
868                orderData.email = '[email protected]';
869            }
870        }
871
872        // Ensure we have valid data and valid street addresses (ConvesioPay requirement)
873        orderData.isValid = orderData.amount > 0;
874
875        // Final validation - ensure street addresses are never empty
876        if (!orderData.billingAddress.street || orderData.billingAddress.street.trim().length === 0) {
877            orderData.billingAddress.street = '123 Default Street';
878        }
879        if (!orderData.shippingAddress.street || orderData.shippingAddress.street.trim().length === 0) {
880            orderData.shippingAddress.street = '123 Default Street';
881        }
882
883
884        return orderData;
885    };
886
887    /**
888     * Get order number
889     */
890    const getOrderNumber = () => {
891        // Try to get order number from blocks checkout store
892        if (window.wp && window.wp.data && window.wp.data.select) {
893            try {
894                const checkoutStore = window.wp.data.select('wc/store/checkout');
895
896                if (checkoutStore) {
897                    const orderId = checkoutStore.getOrderId();
898
899                    if (orderId) {
900                        return orderId.toString();
901                    }
902                }
903            } catch (e) {
904                console.log('[ConvesioPay Blocks] Error accessing checkout store:', e);
905            }
906        }
907
908        // Fallback: Try to get from URL parameters
909        const urlParams = new URLSearchParams(window.location.search);
910        const orderId = urlParams.get('order');
911
912        if (orderId) {
913            return orderId;
914        }
915
916        // Generate session-based identifier
917        const timestamp = Date.now();
918        const random = Math.floor(Math.random() * 10000);
919        return `SESSION-${timestamp}-${random}`;
920    };
921
922    /**
923     * Create BTCPay session via AJAX call
924     */
925    const createBTCPaySession = async () => {
926        if (isSessionCreated && btcPaySession) {
927            return { success: true, session: btcPaySession };
928        }
929        
930        try {
931            const orderData = getOrderData();
932            if (!orderData.isValid) {
933                throw new Error('Unable to get order details');
934            }
935            
936            const requestData = {
937                action: 'peachpay_convesiopay_create_btcpay_session',
938                nonce: settings.nonce || '',
939                integration: settings.integration_name,
940                returnUrl: window.location.href,
941                orderNumber: orderData.orderNumber,
942                amount: orderData.amount,
943                currency: orderData.currency,
944                email: orderData.email,
945                name: orderData.name,
946                // Include required address fields for ConvesioPay API
947                billingAddress: JSON.stringify(orderData.billingAddress),
948                shippingAddress: JSON.stringify(orderData.shippingAddress)
949            };
950
951            
952            const response = await fetch(settings.ajax_url || '/wp-admin/admin-ajax.php', {
953                method: 'POST',
954                headers: {
955                    'Content-Type': 'application/x-www-form-urlencoded',
956                },
957                body: new URLSearchParams(requestData).toString()
958            });
959            
960            const sessionResponse = await response.json();
961            
962            if (!sessionResponse.success || !sessionResponse.data?.session) {
963                throw new Error(sessionResponse.message || 'Failed to create BTCPay session');
964            }
965            
966            // Store session
967            btcPaySession = sessionResponse.data.session;
968            isSessionCreated = true;
969            
970            return { success: true, session: btcPaySession };
971            
972        } catch (error) {
973            return { success: false, message: error.message };
974        }
975    };
976
977    /**
978     * Create and mount ConvesioPay component
979     */
980    const createAndMountConvesioPayComponent = async () => {
981        if (isComponentMounted && convesioPayComponent) {
982            return { success: true, component: convesioPayComponent };
983        }
984        
985        try {
986            // Check if ConvesioPay SDK is available
987            if (typeof window.ConvesioPay === 'undefined') {
988                throw new Error('ConvesioPay SDK not available');
989            }
990
991            // Get ConvesioPay configuration
992            const config = settings.convesiopay_config || {};
993            const clientKey = config.clientKey;
994            const apiKey = config.apiKey;
995            const integrationName = settings.integration_name;
996            
997            if (!apiKey) {
998                throw new Error('ConvesioPay api key not available');
999            }
1000            
1001            // Create ConvesioPay instance
1002            const cpay = window.ConvesioPay(apiKey);
1003            
1004            // Get order data for customer email
1005            const orderData = getOrderData();
1006            // Initialize component
1007            const component = cpay.component({
1008                environment: config.api_url?.includes('qa') ? 'test' : 'live',
1009                clientKey: clientKey,
1010                customerEmail: orderData.email || '[email protected]',
1011                integration: settings.integration_name,
1012                theme: settings.checkout_theme || 'light'
1013            });
1014            
1015            // Store globally
1016            convesioPayComponent = component;
1017            isComponentMounted = true;
1018            
1019            // Set up event listeners
1020            component.on('change', (event) => {
1021
1022                // Check if this is a card payment event based on event.type
1023                if (event.type === 'card') {
1024                    handleCardPaymentWithPeachPayFunctions(event, component);
1025                } else if (event.type === 'btcpay') {
1026                    // Handle BTCPay payments directly when event.type is btcpay
1027                    handleBTCPayPayment(event);
1028                } else if (event.type === 'applepay') {
1029                    // Handle ApplePay payments with auto-submit when successful
1030                    if (event.isSuccessful) {
1031                        // ApplePay payment intent was successfully authorized
1032                        handleApplePayPaymentSuccess(component, event);
1033                    } else if (event.errors) {
1034                        // Handle ApplePay errors
1035                        const errorMessage = event.errors[0]?.message || 'ApplePay payment failed';
1036                        showErrorUnified(errorMessage);
1037                    }
1038                } else {
1039                    // Detect payment method type and handle accordingly for other methods
1040                    const paymentMethod = detectPaymentMethod(event);
1041
1042                    // Only process payment when user explicitly submits/confirms, not on input change
1043                    if (paymentMethod === 'card' && event.isValid === true && event.isSuccessful === true) {
1044                        handleCardPayment(component, event);
1045                    } else if (paymentMethod === 'btcpay' && event.isValid === true && event.isSuccessful === true) {
1046                        handleBTCPayPayment(event);
1047                    } else if (paymentMethod === 'applepay' && event.isValid === true && event.isSuccessful === true) {
1048                        handleApplePayPayment(component, event);
1049                    } else {
1050                    }
1051                }
1052            });
1053            
1054            return { success: true, component: component };
1055            
1056        } catch (error) {
1057            return { success: false, message: error.message };
1058        }
1059    };
1060
1061    /**
1062     * Mount ConvesioPay component directly in the content div (following BTCPay blocks pattern)
1063     */
1064    const mountConvesioPayComponent = async () => {
1065        // Check if component is already mounted in the content
1066        const existingComponent = document.querySelector('#convesiopay-unified-component-container .convesiopay-component');
1067        if (existingComponent) {
1068            return;
1069        }
1070        
1071        try {
1072            // Create and mount the component
1073            const componentResult = await createAndMountConvesioPayComponent();
1074            
1075            if (componentResult.success) {
1076                // Find the content container
1077                const contentContainer = document.querySelector('.wc-convesiopay-unified-blocks-content');
1078                if (!contentContainer) {
1079                    throw new Error('Content container not found');
1080                }
1081                
1082                // The component container already exists in the content div
1083                const componentContainer = contentContainer.querySelector('#convesiopay-unified-component-container');
1084                if (!componentContainer) {
1085                    throw new Error('Component container not found in content');
1086                }
1087                
1088                // Clear the loading content
1089                componentContainer.innerHTML = '';
1090                
1091                // Mount the component in the container
1092                await convesioPayComponent.mount('#convesiopay-unified-component-container');
1093                
1094                // Ensure the mounted ConvesioPay component takes full width
1095                const mountedComponent = document.querySelector('#convesiopay-unified-component-container .convesiopay-component, #convesiopay-unified-component-container iframe, #convesiopay-unified-component-container > div');
1096                if (mountedComponent) {
1097                    mountedComponent.style.width = '100%';
1098                    mountedComponent.style.maxWidth = '100%';
1099                    mountedComponent.style.minWidth = '100%';
1100                }
1101                
1102                // Add CSS to ensure ConvesioPay component takes full width
1103                const styleId = 'convesiopay-full-width-styles';
1104                if (!document.getElementById(styleId)) {
1105                    const style = document.createElement('style');
1106                    style.id = styleId;
1107                    style.textContent = `
1108                        #convesiopay-unified-component-container {
1109                            width: 100% !important;
1110                        }
1111                        #convesiopay-unified-component-container .convesiopay-component,
1112                        #convesiopay-unified-component-container iframe,
1113                        #convesiopay-unified-component-container > div {
1114                            width: 100% !important;
1115                            max-width: 100% !important;
1116                            min-width: 100% !important;
1117                        }
1118                        .convesiopay-component-container {
1119                            width: 100% !important;
1120                        }
1121                        /* Hide radio button when ConvesioPay is the only payment method */
1122                        .wc-block-components-radio-control-accordion-option:only-child .wc-block-components-radio-control__option {
1123                            display: none !important;
1124                        }
1125                        /* Remove padding from accordion content */
1126                        .wc-block-components-radio-control-accordion-content {
1127                            padding: 0 !important;
1128                        }
1129                    `;
1130                    document.head.appendChild(style);
1131                }
1132                
1133                const orderData = getOrderData();
1134
1135                // Prepare the request body for createBTCPayIntent
1136                const btcPayIntentData = {
1137                    session: btcPaySession,
1138                    integration: settings.integration_name,
1139                    returnUrl: window.location.href,
1140                    orderNumber: orderData.orderNumber,
1141                    amount: orderData.amount,
1142                    currency: orderData.currency,
1143                    email: orderData.email,
1144                    name: orderData.name,
1145                    billingAddress: orderData.billingAddress,
1146                    shippingAddress: orderData.shippingAddress
1147                };
1148
1149                const applePaySession = await convesioPayComponent.createApplePaySession({
1150                    integration: settings.integration_name,
1151                    returnUrl: window.location.href,
1152                    orderNumber: orderData.orderNumber,
1153                    amount: orderData.amount,
1154                    currency: orderData.currency,
1155                    email: orderData.email,
1156                    name: orderData.name
1157                });
1158
1159
1160                // Create BTCPay intent using the mounted component (following BTCPay blocks pattern)
1161                // Include required address fields for ConvesioPay API
1162                await convesioPayComponent.createBTCPayIntent(btcPayIntentData);
1163                
1164                // Set up postMessage listener for iframe communication
1165                setupBTCPayMessageListener();
1166                
1167                
1168            } else {
1169                throw new Error(componentResult.message || 'Failed to create component');
1170            }
1171            
1172        } catch (error) {
1173            console.error('Failed to create ConvesioPay payment:', error.message)
1174            // // Show error in the content area
1175            // const contentContainer = document.querySelector('.wc-convesiopay-unified-blocks-content');
1176            // if (contentContainer) {
1177            //     const errorDiv = document.createElement('div');
1178            //     errorDiv.style.cssText = `
1179            //         color: red;
1180            //         font-size: 14px;
1181            //         text-align: center;
1182            //         padding: 20px;
1183            //         border: 1px solid #ff0000;
1184            //         border-radius: 5px;
1185            //         background: #fff5f5;
1186            //         margin: 0 0 10px 0;
1187            //     `;
1188            //     errorDiv.innerHTML = `
1189            //         <div>❌ Error</div>
1190            //         <small>Failed to create ConvesioPay payment: ${error.message}</small>
1191            //     `;
1192
1193            //     // Insert error message at the top
1194            //     contentContainer.insertBefore(errorDiv, contentContainer.firstChild);
1195            // }
1196        }
1197    };
1198
1199    /**
1200     * Content component for ConvesioPay Unified (following BTCPay blocks pattern)
1201     */
1202    const ConvesioPayUnifiedContent = (props) => {
1203        const { eventRegistration, emitResponse } = props;
1204
1205        // Create session and mount component when radio button is selected (following BTCPay pattern)
1206        useEffect(() => {
1207            // Create BTCPay session immediately when radio button is selected
1208            createBTCPaySession().then(sessionResult => {
1209                if (sessionResult.success) {
1210                    // After session is created, mount the ConvesioPay component
1211                    setTimeout(() => {
1212                        mountConvesioPayComponent();
1213                    }, 100); // Small delay to ensure DOM is ready
1214                } else {
1215                }
1216            }).catch(error => {
1217            });
1218        }, []);
1219
1220        // Handle payment setup (when Place Order is clicked) - USE EXISTING SESSION
1221        useEffect(() => {
1222            if (eventRegistration && eventRegistration.onPaymentSetup) {
1223                const unsubscribe = eventRegistration.onPaymentSetup(async () => {
1224
1225                    // For ApplePay and Card, we don't need btcPaySession - only check for BTCPay
1226                    if (!btcPaySession && currentPaymentMethod === 'btcpay') {
1227                        return {
1228                            type: emitResponse.responseTypes.ERROR,
1229                            message: 'BTCPay session not ready. Please try again.',
1230                        };
1231                    }
1232
1233                    // For ApplePay and Card, if no session exists, create a fake one
1234                    if (!btcPaySession && (currentPaymentMethod === 'applepay' || currentPaymentMethod === 'card')) {
1235                        btcPaySession = 'fake-session-' + Date.now();
1236                    }
1237
1238                    // Prepare payment method data based on payment type
1239                    // Ensure all values are strings or booleans to avoid REST API validation errors
1240                    const paymentMethodData = {
1241                        peachpay_transaction_id: String('convesio-tx-' + Date.now()),
1242                        payment_method_type: String(currentPaymentMethod || paymentConfirmedData?.paymentMethod || 'btcpay')
1243                    };
1244
1245                    // Include payment data based on payment method
1246                    if (paymentConfirmedData) {
1247                        // Add common payment data that PHP expects (use lowercase for WooCommerce Blocks compatibility)
1248                        // Convert all values to strings to avoid REST API validation errors
1249                        paymentMethodData.paymentid = String(paymentConfirmedData.paymentId || '');
1250                        paymentMethodData.paymentmethod = String(paymentConfirmedData.paymentMethod || '');
1251                        paymentMethodData.status = String(paymentConfirmedData.status || '');
1252                        paymentMethodData.amount = String(paymentConfirmedData.amount || '0');
1253                        paymentMethodData.currency = String(paymentConfirmedData.currency || '');
1254                        paymentMethodData.ordernumber = String(paymentConfirmedData.orderNumber || '');
1255
1256                        // Also add in camelCase format for PHP compatibility
1257                        paymentMethodData.paymentId = String(paymentConfirmedData.paymentId || '');
1258                        paymentMethodData.paymentMethod = String(paymentConfirmedData.paymentMethod || '');
1259
1260                        if (paymentConfirmedData.paymentMethod === 'card') {
1261                            // Card payment data
1262                            paymentMethodData.convesiopay_payment_token = String(paymentConfirmedData.token || '');
1263                        } else if (paymentConfirmedData.paymentMethod === 'applepay') {
1264                            // Apple Pay payment data
1265                            paymentMethodData.convesiopay_payment_token = String(paymentConfirmedData.token || '');
1266                            paymentMethodData.convesiopay_payment_id = String(paymentConfirmedData.paymentId || '');
1267                            paymentMethodData.paymentId = String(paymentConfirmedData.paymentId || '');
1268                            paymentMethodData.applepay_payment_id = String(paymentConfirmedData.paymentId || '');
1269                        } else if (paymentConfirmedData.paymentMethod === 'btcpay') {
1270                            // BTCPay payment data
1271                            paymentMethodData.convesiopay_payment_token = String('btc-session-' + btcPaySession);
1272                            paymentMethodData.btcpay_session_id = String(btcPaySession || '');
1273                            paymentMethodData.btcpay_payment_id = String(paymentConfirmedData.paymentId || '');
1274                            paymentMethodData.btcpay_invoice_id = String(paymentConfirmedData.invoiceId || '');
1275                            paymentMethodData.btcpay_status = String(paymentConfirmedData.status || '');
1276                        }
1277                    } else {
1278                        // Fallback for BTCPay session
1279                        paymentMethodData.paymentid = String('btc-session-' + btcPaySession);
1280                        paymentMethodData.paymentmethod = String('btcpay');
1281                        paymentMethodData.status = String('pending');
1282                        paymentMethodData.convesiopay_payment_token = String('btc-session-' + btcPaySession);
1283                        paymentMethodData.btcpay_session_id = String(btcPaySession || '');
1284                    }
1285
1286                    // Return success with payment data for WooCommerce to process
1287                    return {
1288                        type: emitResponse.responseTypes.SUCCESS,
1289                        meta: {
1290                            paymentMethodData: paymentMethodData,
1291                        },
1292                    };
1293                });
1294                return unsubscribe;
1295            }
1296        }, [eventRegistration, emitResponse]);
1297
1298        return createElement(
1299            'div',
1300            {
1301                className: 'wc-convesiopay-unified-blocks-content',
1302                style: {
1303                    width: '100%',
1304                    padding: '0',
1305                    margin: '0'
1306                }
1307            },
1308            createElement(
1309                'div',
1310                {
1311                    id: 'convesiopay-unified-component-container',
1312                    className: 'convesiopay-component-container',
1313                    style: {
1314                        border: '1px solid #e0e0e0',
1315                        borderRadius: '5px',
1316                        background: '#f8f8f8',
1317                        margin: '0',
1318                        minHeight: '200px',
1319                        display: 'flex',
1320                        alignItems: 'center',
1321                        justifyContent: 'center',
1322                        width: '100%'
1323                    }
1324                }
1325            )
1326        );
1327    };
1328
1329    /**
1330     * ConvesioPay Unified payment method configuration
1331     */
1332    const ConvesioPayUnifiedPaymentMethod = {
1333        name: 'peachpay_convesiopay_unified',
1334        label: createElement(ConvesioPayLabel),
1335        content: createElement(ConvesioPayUnifiedContent),
1336        edit: createElement(ConvesioPayUnifiedContent),
1337        canMakePayment: () => {
1338            return true;
1339        },
1340        ariaLabel: 'ConvesioPay',
1341        supports: {
1342            features: settings.supports || ['products']
1343        },
1344        placeOrderButtonLabel: __('Complete Order', 'peachpay-for-woocommerce'),
1345    };
1346
1347    // Reset session state when the page loads
1348    document.addEventListener('DOMContentLoaded', function() {
1349        setTimeout(() => {
1350            resetSessionState();
1351            setupCartTotalMonitoring();
1352
1353            // Initialize last known cart total
1354            lastKnownCartTotal = getCurrentCartTotal();
1355        }, 1000);
1356    });
1357
1358    // Register the payment method
1359    if (typeof registerPaymentMethod === 'function') {
1360        registerPaymentMethod(ConvesioPayUnifiedPaymentMethod);
1361    } else {
1362    }
1363
1364})(); // End IIFE

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.