PageSourceSearch

https://freshlyfermented.co.uk/wp-content/plugins/autifydigital-ta…/js/google-pay-product.js?ver=1.1.2-p1

js freshlyfermented.co.uk collected 2026-10-04 02:52:20 UTC 20,019 bytes, 506 lines download raw bytes

1(function ($) {
2    'use strict';
3
4    const settings = window.taylrGooglePayData || {};
5    
6    if (!settings.enable_googlepay || settings.enable_googlepay !== 'yes') {
7        return;
8    }
9
10    let paymentsClient = null;
11    const googlepay_environment = settings.googlepay_environment || 'TEST';
12
13    // Shipping variables
14    let googleSelectedShippingId = "";
15    let googleShippingOptions = [];
16    let googleShippingCost = {};
17    let googleTax = 0;
18    let googleAmount = 0;
19    const googleCurrency = settings.currency_code || 'GBP';
20    const googleNeedsShipping = settings.needs_shipping || false;
21    const googleAllowedCardNetworks = ['AMEX', 'DISCOVER', 'INTERAC', 'JCB', 'MASTERCARD', 'VISA'];
22
23    // Get product ID - handle both simple and variable products
24    let product_id = $('.single_add_to_cart_button').val() || $('input[name="add-to-cart"]').val();
25
26    // Load Google Pay SDK
27    function loadGooglePaySDK(callback) {
28        if (window.google && window.google.payments && window.google.payments.api) {
29            callback();
30            return;
31        }
32        const script = document.createElement('script');
33        script.src = 'https://pay.google.com/gp/p/js/pay.js';
34        script.async = true;
35        script.onload = callback;
36        script.onerror = function() {};
37        document.head.appendChild(script);
38    }
39
40    // Get Google Pay PaymentsClient
41    // Note: Client is recreated if shipping callback is needed but wasn't included initially
42    function getPaymentsClient(needsShippingCallback = false) {
43        const shouldHaveShippingCallback = needsShippingCallback && googleShippingOptions.length > 0;
44
45        // Recreate client if we need shipping callback but don't have it
46        if (paymentsClient && shouldHaveShippingCallback && !paymentsClient._hasShippingCallback) {
47            paymentsClient = null;
48        }
49
50        if (!paymentsClient && window.google && window.google.payments && window.google.payments.api) {
51            paymentsClient = new google.payments.api.PaymentsClient({
52                environment: googlepay_environment,
53                paymentDataCallbacks: {
54                    onPaymentAuthorized: onPaymentAuthorized,
55                    ...(shouldHaveShippingCallback && { onPaymentDataChanged: onPaymentDataChanged })
56                },
57            });
58            paymentsClient._hasShippingCallback = shouldHaveShippingCallback;
59        }
60        return paymentsClient;
61    }
62
63    /**
64     * Fetch available shipping methods from WooCommerce
65     */
66    async function getGoogleShippingOptions() {
67        try {
68            const response = await $.ajax({
69                type: "POST",
70                url: settings.ajax_url,
71                data: {
72                    action: 'taylr_get_shipping_methods',
73                    page_name: 'product',
74                    security: settings.nonce,
75                },
76            });
77
78            if (response.success && response.data.shipping_options_google) {
79                googleShippingOptions = response.data.shipping_options_google;
80                googleShippingCost = response.data.shipping_cost || {};
81                googleSelectedShippingId = googleShippingOptions[0]?.id || "";
82            }
83        } catch (error) {
84            // Shipping options fetch failed
85        }
86    }
87
88    /**
89     * Handles GooglePay Shipping Data Updates
90     */
91    async function onPaymentDataChanged(paymentData) {
92        const callbackResponse = {};
93
94        if (paymentData.shippingAddress || paymentData.shippingOptionData) {
95            try {
96                const response = await $.ajax({
97                    type: "POST",
98                    url: settings.ajax_url,
99                    data: {
100                        action: 'taylr_validate_shipping_address',
101                        address: paymentData.shippingAddress,
102                        shipping_id: paymentData.shippingOptionData ? paymentData.shippingOptionData.id : "",
103                        security: settings.nonce,
104                    }
105                });
106
107                if (!response.success) {
108                    return {
109                        error: {
110                            reason: "SHIPPING_ADDRESS_UNSUPPORTED",
111                            message: response.data?.message || 'Invalid shipping address',
112                            intent: "SHIPPING_ADDRESS",
113                        }
114                    };
115                }
116                
117                // Update all shipping data from response
118                if (response.data.shipping_options_google && response.data.shipping_options_google.length > 0) {
119                    googleShippingOptions = response.data.shipping_options_google;
120                    // Only set new shipping options if address was changed
121                    if (paymentData.shippingAddress) {
122                        callbackResponse.newShippingOptionParameters = {
123                            defaultSelectedOptionId: response.data.chosen_method || googleShippingOptions[0]?.id,
124                            shippingOptions: googleShippingOptions
125                        };
126                    }
127                }
128                
129                if (response.data.shipping_costs) {
130                    googleShippingCost = response.data.shipping_costs;
131                }
132                
133                // Update amounts from backend
134                googleTax = response.data.tax_total || googleTax;
135                googleAmount = response.data.amount || googleAmount;
136                
137                // If shipping option was selected, use backend calculated shipping total
138                if (paymentData.shippingOptionData) {
139                    googleSelectedShippingId = paymentData.shippingOptionData.id;
140                }
141                
142                const shippingAmount = response.data.shipping_total || googleShippingCost[googleSelectedShippingId] || 0;
143                
144                // Update transaction info with backend calculated totals
145                callbackResponse.newTransactionInfo = {
146                    totalPriceStatus: "FINAL",
147                    totalPrice: (
148                        parseFloat(googleAmount) +
149                        parseFloat(shippingAmount) +
150                        parseFloat(googleTax)
151                    ).toFixed(2),
152                    currencyCode: googleCurrency,
153                };
154                
155            } catch (error) {
156                return {
157                    error: {
158                        reason: "SHIPPING_ADDRESS_ERROR",
159                        message: "Error validating shipping address.",
160                        intent: "SHIPPING_ADDRESS",
161                    }
162                };
163            }
164        }
165
166        return callbackResponse;
167    }
168
169    /**
170     * Add product to cart
171     */
172    async function addToCart() {
173        let product_id = $('[name="add-to-cart"]').val();
174        let variation_id = $('[name="variation_id"]').val();
175        let quantity = $('[name="quantity"]').val() || 1;
176        const form = $('form.cart');
177        const formDataArray = form.serializeArray();
178        const data = {
179            action: 'taylr_add_to_cart',
180            product_id: product_id,
181            variation_id: variation_id,
182            quantity: quantity,
183            clear_cart: true,
184            security: settings.nonce,
185        };
186
187        // Populate data object from form
188        formDataArray.forEach(function (item) {
189            data[item.name] = item.value;
190        });
191
192        const response = await $.ajax({
193            type: "POST",
194            url: settings.ajax_url,
195            data: data
196        });
197
198        if (response.success) {
199            googleAmount = response.data.amount.toString();
200            googleTax = response.data.tax_total || googleTax;
201            return true;
202        } else {
203            throw new Error(response.data?.message || 'Failed to add to cart');
204        }
205    }
206
207    // Render Google Pay button
208    function renderGooglePayButton() {
209        const container = document.getElementById('taylr-google-pay-product-btn');
210        if (!container) return;
211
212        const client = getPaymentsClient();
213        if (!client) {
214            container.style.display = 'none';
215            return;
216        }
217
218        container.style.display = 'block';
219        container.innerHTML = '';
220
221        const baseCardPaymentMethod = {
222            type: 'CARD',
223            parameters: {
224                allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
225                allowedCardNetworks: googleAllowedCardNetworks
226            }
227        };
228
229        const isReadyToPayRequest = {
230            apiVersion: 2,
231            apiVersionMinor: 0,
232            allowedPaymentMethods: [baseCardPaymentMethod]
233        };
234
235        client.isReadyToPay(isReadyToPayRequest)
236            .then(function(response) {
237                if (response.result) {
238                    const button = client.createButton({
239                        onClick: onGooglePaymentButtonClicked,
240                        buttonType: 'buy',
241                        buttonColor: 'black',
242                        buttonSizeMode: 'fill'
243                    });
244
245                    button.className = 'taylr-google-pay-button';
246                    container.appendChild(button);
247
248                    const errorDiv = document.createElement('div');
249                    errorDiv.className = 'taylr-google-pay-product-error';
250                    errorDiv.style.display = 'none';
251                    errorDiv.style.color = 'red';
252                    errorDiv.style.marginTop = '10px';
253                    container.appendChild(errorDiv);
254                } else {
255                    container.style.display = 'none';
256                }
257            })
258            .catch(function() {
259                container.style.display = 'none';
260            });
261    }
262
263    // Show error message
264    function showGooglePayError(msg) {
265        const errorDiv = document.querySelector('.taylr-google-pay-product-error');
266        if (errorDiv) {
267            errorDiv.innerText = msg;
268            errorDiv.style.display = 'block';
269        }
270    }
271
272    async function onGooglePaymentButtonClicked() {
273        if ($('.single_add_to_cart_button').hasClass('wc-variation-selection-needed')) {
274            alert("Please select some product options before adding this product to your cart.");
275            return;
276        }
277
278        let product_id = $('[name="add-to-cart"]').val();
279        let variation_id = $('[name="variation_id"]').val();
280        let quantity = $('[name="quantity"]').val() || 1;
281        const form = $('form.cart');
282        const formDataArray = form.serializeArray();
283        const data = {
284            action: 'taylr_add_to_cart',
285            product_id: product_id,
286            variation_id: variation_id,
287            quantity: quantity,
288            clear_cart: true,
289            security: settings.nonce,
290        };
291
292        formDataArray.forEach(function (item) {
293            data[item.name] = item.value;
294        });
295
296        if ($('.variations_form').length && !data['variation_id']) {
297            alert("Invalid variation selected.");
298            return;
299        }
300
301        try {
302            const response = await $.ajax({
303                type: "POST",
304                url: settings.ajax_url,
305                data: data
306            });
307
308            if (response.success) {
309                googleAmount = response.data.amount.toString();
310                googleTax = response.data.tax_total || googleTax;
311
312                // Refresh shipping options after adding to cart
313                if (googleNeedsShipping) {
314                    await getGoogleShippingOptions();
315                }
316
317                const googlePayConfig = {
318                    apiVersion: 2,
319                    apiVersionMinor: 0,
320                    allowedPaymentMethods: [{
321                        type: "CARD",
322                        parameters: {
323                            allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
324                            allowedCardNetworks: googleAllowedCardNetworks,
325                            billingAddressRequired: true,
326                            billingAddressParameters: { format: "FULL", phoneNumberRequired: true },
327                        },
328                        tokenizationSpecification: {
329                            type: "PAYMENT_GATEWAY",
330                            parameters: {
331                                gateway: 'crst', // STATIC as per GoogleSample reference
332                                gatewayMerchantId: settings.merchant_id || ''  // Taylr/Cardstream merchant ID
333                            },
334                        }
335                    }],
336                    transactionInfo: {
337                        totalPriceStatus: "FINAL",
338                        totalPrice: (
339                            parseFloat(googleAmount) +
340                            parseFloat(googleTax)
341                        ).toFixed(2),
342                        currencyCode: googleCurrency,
343                    },
344                    merchantInfo: {
345                        merchantName: settings.googlepay_merchant_name || settings.store_name || 'Store',
346                        // Only include merchantId if it's set (required for PRODUCTION, optional for TEST)
347                        ...(settings.googlepay_merchant_id && { merchantId: settings.googlepay_merchant_id }),
348                    },
349                    emailRequired: true,
350                    // Only include shipping intents if we have shipping options
351                    callbackIntents: [
352                        "PAYMENT_AUTHORIZATION",
353                        ...(googleNeedsShipping && googleShippingOptions.length > 0 ? ["SHIPPING_ADDRESS", "SHIPPING_OPTION"] : [])
354                    ],
355                    ...(googleNeedsShipping && googleShippingOptions.length > 0 && {
356                        shippingAddressRequired: true,
357                        shippingOptionRequired: true,
358                        shippingOptionParameters: {
359                            defaultSelectedOptionId: googleSelectedShippingId,
360                            shippingOptions: googleShippingOptions,
361                        },
362                    }),
363                };
364
365                // Get client with appropriate shipping callback setting
366                const paymentsClient = getPaymentsClient(googleNeedsShipping);
367                try {
368                    // loadPaymentData automatically triggers onPaymentAuthorized via paymentDataCallbacks
369                    await paymentsClient.loadPaymentData(googlePayConfig);
370                } catch (error) {
371                    if (error.statusCode !== 'CANCELED' && !error.message?.includes('User closed')) {
372                        showGooglePayError('Google Pay payment failed. Please try again.');
373                    }
374                }
375            } else {
376                alert("Error: " + (response.data?.message || 'Failed to add to cart'));
377            }
378        } catch (error) {
379            alert("An error occurred: " + error.message);
380        }
381    }
382
383    function onPaymentAuthorized(paymentData) {
384        return new Promise(function (resolve, reject) {
385            handleGooglePaySuccess(paymentData)
386                .then(function () {
387                    resolve({ transactionState: 'SUCCESS' });
388                })
389                .catch(function () {
390                    resolve({
391                        transactionState: 'ERROR',
392                        error: {
393                            intent: 'PAYMENT_AUTHORIZATION',
394                            message: 'Something went wrong',
395                            reason: 'PAYMENT_DATA_INVALID'
396                        }
397                    });
398                });
399        });
400    }
401
402    /**
403     * Handles successful GooglePay transaction - Lloyds-style clean request/response
404     */
405    function handleGooglePaySuccess(paymentData) {
406        return new Promise(function (resolve, reject) {
407            setTimeout(async function () {
408                try {
409                    $.blockUI({ message: null, baseZ: 0 });
410
411                    // Send payment data directly as JSON (Lloyds approach - no base64 encoding)
412                    const response = await $.ajax({
413                        type: 'POST',
414                        url: settings.ajax_url,
415                        data: {
416                            action: 'taylr_process_google_pay_order',
417                            paymentData: JSON.stringify(paymentData),
418                            addresses: JSON.stringify({
419                                shippingAddress: paymentData.shippingAddress,
420                                billingAddress: paymentData.paymentMethodData?.info?.billingAddress,
421                            }),
422                            email: paymentData.email,
423                            selectedShippingId: googleSelectedShippingId,
424                            security: settings.nonce,
425                        },
426                    });
427
428                    $.unblockUI();
429
430                    if (response.success) {
431                        resolve();
432                        setTimeout(function() {
433                            window.location.href = response.data.redirect;
434                        }, 500);
435                    } else {
436                        reject(new Error(response.data?.message || 'Transaction failed'));
437                    }
438                } catch (error) {
439                    $.unblockUI();
440                    reject(error);
441                }
442            }, 3000);
443        });
444    }
445
446    // Insert Google Pay button on product page
447    function insertProductGooglePayButton() {
448        const addToCartButton = document.querySelector('.single_add_to_cart_button') || 
449                               document.querySelector('button[name="add-to-cart"]') ||
450                               document.querySelector('.wp-block-woocommerce-product-button');
451        
452        if (addToCartButton && !document.getElementById('taylr-google-pay-product-btn')) {
453            const container = document.createElement('div');
454            container.id = 'taylr-google-pay-product-btn';
455            container.style.marginTop = '15px';
456            addToCartButton.parentNode.insertBefore(container, addToCartButton.nextSibling);
457        }
458    }
459
460    // Initialize Google Pay
461    async function initGooglePay() {
462        try {
463            insertProductGooglePayButton();
464            
465            // Pre-load shipping options if needed
466            if (googleNeedsShipping) {
467                await getGoogleShippingOptions();
468            }
469            
470            // Wait for Google Pay API
471            function waitForGooglePayAPI() {
472                if (window.google && window.google.payments && window.google.payments.api) {
473                    renderGooglePayButton();
474                } else {
475                    setTimeout(waitForGooglePayAPI, 100);
476                }
477            }
478            waitForGooglePayAPI();
479            
480            // Re-initialize when variation changes (for variable products)
481            $(document.body).on('found_variation', function(event, variation) {
482                // Variation changed, re-fetch shipping options if needed
483                if (googleNeedsShipping) {
484                    getGoogleShippingOptions().catch(function() {});
485                }
486            });
487        } catch (error) {
488            // Initialization failed silently
489        }
490    }
491
492    // Initialize on page load
493    $(document).ready(function() {
494        loadGooglePaySDK(function() {
495            function waitForGooglePayAPI() {
496                if (window.google && window.google.payments && window.google.payments.api) {
497                    initGooglePay();
498                } else {
499                    setTimeout(waitForGooglePayAPI, 100);
500                }
501            }
502            waitForGooglePayAPI();
503        });
504    });
505
506})(jQuery);

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.