Plugin Directory

Changeset 3401525


Ignore:
Timestamp:
11/24/2025 04:13:25 AM (4 months ago)
Author:
easypayment
Message:

tags/9.0.51

Location:
woo-paypal-gateway
Files:
1060 added
5 edited

Legend:

Unmodified
Added
Removed
  • woo-paypal-gateway/trunk/ppcp/includes/class-ppcp-paypal-checkout-for-woocommerce-request.php

    r3396892 r3401525  
    32213221
    32223222            // Check if we have the necessary session data
    3223             $reference_id = ppcp_get_session('ppcp_reference_id');
     3223            $reference_id    = ppcp_get_session('ppcp_reference_id');
    32243224            $paypal_order_id = ppcp_get_session('ppcp_paypal_order_id');
    32253225
     
    32293229            }
    32303230
    3231             $patch_request = array();
     3231            $patch_request         = array();
    32323232            $update_amount_request = array();
    32333233
     
    32373237            // Shipping or Billing Address from customer
    32383238            $shipping_address_request = array();
    3239             $customer = WC()->customer;
     3239            $customer                 = WC()->customer;
    32403240
    32413241            if ($customer && is_a($customer, 'WC_Customer')) {
     
    32433243                    $shipping_address_1 = $customer->get_shipping_address();
    32443244                    $shipping_address_2 = $customer->get_shipping_address_2();
    3245                     $shipping_city = $customer->get_shipping_city();
    3246                     $shipping_state = $customer->get_shipping_state();
    3247                     $shipping_postcode = $customer->get_shipping_postcode();
    3248                     $shipping_country = $customer->get_shipping_country();
     3245                    $shipping_city      = $customer->get_shipping_city();
     3246                    $shipping_state     = $customer->get_shipping_state();
     3247                    $shipping_postcode  = $customer->get_shipping_postcode();
     3248                    $shipping_country   = $customer->get_shipping_country();
    32493249                } else {
    32503250                    $shipping_address_1 = $customer->get_billing_address_1();
    32513251                    $shipping_address_2 = $customer->get_billing_address_2();
    3252                     $shipping_city = $customer->get_billing_city();
    3253                     $shipping_state = $customer->get_billing_state();
    3254                     $shipping_postcode = $customer->get_billing_postcode();
    3255                     $shipping_country = $customer->get_billing_country();
     3252                    $shipping_city      = $customer->get_billing_city();
     3253                    $shipping_state     = $customer->get_billing_state();
     3254                    $shipping_postcode  = $customer->get_billing_postcode();
     3255                    $shipping_country   = $customer->get_billing_country();
    32563256                }
    32573257
     
    32603260                        'address_line_1' => $shipping_address_1 ?: '',
    32613261                        'address_line_2' => $shipping_address_2 ?: '',
    3262                         'admin_area_2' => $shipping_city ?: '',
    3263                         'admin_area_1' => $shipping_state ?: '',
    3264                         'postal_code' => $shipping_postcode ?: '',
    3265                         'country_code' => $shipping_country ?: '',
     3262                        'admin_area_2'   => $shipping_city ?: '',
     3263                        'admin_area_1'   => $shipping_state ?: '',
     3264                        'postal_code'    => $shipping_postcode ?: '',
     3265                        'country_code'   => $shipping_country ?: '',
    32663266                    );
    32673267                }
     
    32723272                $update_amount_request['item_total'] = array(
    32733273                    'currency_code' => get_woocommerce_currency(),
    3274                     'value' => ppcp_round($cart['total_item_amount'], $this->decimals)
     3274                    'value'         => ppcp_round($cart['total_item_amount'], $this->decimals)
    32753275                );
    32763276            }
     
    32813281                    $update_amount_request['discount'] = array(
    32823282                        'currency_code' => get_woocommerce_currency(),
    3283                         'value' => ppcp_round($cart['discount'], $this->decimals)
     3283                        'value'         => ppcp_round($cart['discount'], $this->decimals)
    32843284                    );
    32853285                }
     
    32873287                    $update_amount_request['shipping'] = array(
    32883288                        'currency_code' => get_woocommerce_currency(),
    3289                         'value' => ppcp_round($cart['shipping'], $this->decimals)
     3289                        'value'         => ppcp_round($cart['shipping'], $this->decimals)
    32903290                    );
    32913291                }
     
    32933293                    $update_amount_request['shipping_discount'] = array(
    32943294                        'currency_code' => get_woocommerce_currency(),
    3295                         'value' => ppcp_round($cart['ship_discount_amount'], $this->decimals),
     3295                        'value'         => ppcp_round($cart['ship_discount_amount'], $this->decimals),
    32963296                    );
    32973297                }
     
    32993299                    $update_amount_request['tax_total'] = array(
    33003300                        'currency_code' => get_woocommerce_currency(),
    3301                         'value' => ppcp_round($cart['order_tax'], $this->decimals)
     3301                        'value'         => ppcp_round($cart['order_tax'], $this->decimals)
    33023302                    );
    33033303                }
     
    33113311            // Patch request for updating the order amount
    33123312            $patch_request[] = array(
    3313                 'op' => 'add',
     3313                'op'   => 'add',
    33143314                'path' => "/purchase_units/@reference_id=='$reference_id'/amount",
    33153315                'value' => array(
    33163316                    'currency_code' => get_woocommerce_currency(),
    3317                     'value' => ppcp_round($cart['order_total'], $this->decimals),
    3318                     'breakdown' => $update_amount_request
     3317                    'value'         => ppcp_round($cart['order_total'], $this->decimals),
     3318                    'breakdown'     => $update_amount_request
    33193319                ),
    33203320            );
     3321
     3322            // =========================
     3323            // NEW: shipping options block
     3324            // =========================
     3325            $shipping_options = array();
     3326
     3327            $packages       = WC()->shipping()->get_packages();
     3328            $chosen_methods = WC()->session->get('chosen_shipping_methods', array());
     3329
     3330            if (!empty($packages)) {
     3331                foreach ($packages as $package_index => $package) {
     3332
     3333                    if (empty($package['rates']) || !is_array($package['rates'])) {
     3334                        continue;
     3335                    }
     3336
     3337                    $chosen_for_package = isset($chosen_methods[$package_index]) ? $chosen_methods[$package_index] : '';
     3338
     3339                    foreach ($package['rates'] as $rate_id => $rate) {
     3340
     3341                        // Base cost
     3342                        $rate_cost = (float) $rate->get_cost();
     3343
     3344                        // Include taxes if present
     3345                        $taxes = $rate->get_taxes();
     3346                        if (!empty($taxes) && is_array($taxes)) {
     3347                            $rate_cost += array_sum($taxes);
     3348                        }
     3349
     3350                        $shipping_options[] = array(
     3351                            'id'       => $rate_id, // e.g. flat_rate:1
     3352                            'label'    => $rate->get_label(),
     3353                            'type'     => 'SHIPPING',
     3354                            'selected' => ($rate_id === $chosen_for_package),
     3355                            'amount'   => array(
     3356                                'currency_code' => get_woocommerce_currency(),
     3357                                'value'         => ppcp_round($rate_cost, $this->decimals),
     3358                            ),
     3359                        );
     3360                    }
     3361
     3362                    // Typically only one package is sent to PayPal
     3363                    break;
     3364                }
     3365            }
     3366
     3367            $this->ppcp_log('Shipping options sent to PayPal: ' . wc_print_r($shipping_options, true));
     3368
     3369            if (!empty($shipping_options)) {
     3370                $patch_request[] = array(
     3371                    'op'   => 'add',
     3372                    'path' => "/purchase_units/@reference_id=='$reference_id'/shipping/options",
     3373                    'value' => $shipping_options,
     3374                );
     3375            }
     3376            // =========================
     3377            // END shipping options block
     3378            // =========================
    33213379
    33223380            // Update shipping address if available
    33233381            if (!empty($shipping_address_request) && !empty(array_filter($shipping_address_request))) {
    33243382                $patch_request[] = array(
    3325                     'op' => 'add',
     3383                    'op'   => 'add',
    33263384                    'path' => "/purchase_units/@reference_id=='$reference_id'/shipping/address",
    33273385                    'value' => $shipping_address_request
     
    33313389            // Update invoice ID for cart (using reference ID since no order number exists yet)
    33323390            $patch_request[] = array(
    3333                 'op' => 'add',
     3391                'op'   => 'add',
    33343392                'path' => "/purchase_units/@reference_id=='$reference_id'/invoice_id",
    33353393                'value' => $reference_id
     
    33393397            $update_custom_id = wp_json_encode(
    33403398                array(
    3341                     'order_id' => $reference_id,
     3399                    'order_id'  => $reference_id,
    33423400                    'order_key' => $reference_id,
    33433401                )
    33443402            );
    33453403            $patch_request[] = array(
    3346                 'op' => 'add',
     3404                'op'   => 'add',
    33473405                'path' => "/purchase_units/@reference_id=='$reference_id'/custom_id",
    33483406                'value' => $update_custom_id
     
    33643422            // Send the request to PayPal
    33653423            $response = wp_remote_request($this->paypal_order_api . $paypal_order_id, array(
    3366                 'timeout' => 60,
    3367                 'method' => 'PATCH',
     3424                'timeout'     => 60,
     3425                'method'      => 'PATCH',
    33683426                'redirection' => 5,
    33693427                'httpversion' => '1.1',
    3370                 'blocking' => true,
    3371                 'headers' => array(
    3372                     'Content-Type' => 'application/json',
    3373                     'Authorization' => "Bearer " . $this->access_token,
    3374                     "prefer" => "return=representation",
     3428                'blocking'    => true,
     3429                'headers'     => array(
     3430                    'Content-Type'                  => 'application/json',
     3431                    'Authorization'                 => "Bearer " . $this->access_token,
     3432                    "prefer"                        => "return=representation",
    33753433                    'PayPal-Partner-Attribution-Id' => 'MBJTechnolabs_SI_SPB',
    3376                     'PayPal-Request-Id' => $this->generate_request_id()
     3434                    'PayPal-Request-Id'             => $this->generate_request_id()
    33773435                ),
    3378                 'body' => $patch_request_json,
     3436                'body'    => $patch_request_json,
    33793437                'cookies' => array()
    33803438            ));
     
    33863444                return false;
    33873445            } else {
    3388                 $response_code = wp_remote_retrieve_response_code($response);
     3446                $response_code    = wp_remote_retrieve_response_code($response);
    33893447                $response_message = wp_remote_retrieve_response_message($response);
    3390                 $api_response = json_decode(wp_remote_retrieve_body($response), true);
     3448                $api_response     = json_decode(wp_remote_retrieve_body($response), true);
    33913449
    33923450                $this->ppcp_log('Response Code: ' . $response_code);
  • woo-paypal-gateway/trunk/ppcp/public/class-ppcp-paypal-checkout-for-woocommerce-button-manager.php

    r3396892 r3401525  
    882882                                    $address[$key] = $address_value;
    883883                                }
    884 
     884                                $address['ship_to_different_address'] = json_encode(array_map('strtolower', $billing_address)) !== json_encode(array_map('strtolower', $shipping_address)) ? '1' : '0';
    885885                                $_POST = $address;
    886886                                ppcp_set_session('wpg_ppcp_block_checkout_post', $address);
     
    957957                            if ($order_id) {
    958958                                $details = $request_obj->ppcp_get_details_from_order($order_id);
    959                                 $order = wc_get_order($order_id);
     959                                $order   = wc_get_order($order_id);
    960960                            } else {
    961961                                $details = $request_obj->ppcp_get_details_from_cart();
    962                                 $order = null;
     962                                $order   = null;
    963963                            }
    964964                            $order_total = isset($details['order_total']) ? (float) $details['order_total'] : (float) WC()->cart->total;
    965                             $tax_total = isset($details['order_tax']) ? (float) $details['order_tax'] : (float) WC()->cart->get_total_tax();
    966                             $shipping = isset($details['shipping']) ? (float) $details['shipping'] : (float) WC()->cart->get_shipping_total();
    967                             $discounts = (float) ($details['discount'] ?? 0);
    968                             $ship_disc = (float) ($details['ship_discount_amount'] ?? 0);
     965                            $tax_total   = isset($details['order_tax'])   ? (float) $details['order_tax']  : (float) WC()->cart->get_total_tax();
     966                            $shipping    = isset($details['shipping'])    ? (float) $details['shipping']    : (float) WC()->cart->get_shipping_total();
     967                            $discounts      = (float) ($details['discount'] ?? 0);
     968                            $ship_disc      = (float) ($details['ship_discount_amount'] ?? 0);
    969969                            $discount_total = $discounts + $ship_disc;
    970970                            if ($order) {
     
    973973                                $needs_shipping = WC()->cart->needs_shipping() ? '1' : '0';
    974974                            }
    975                             $items = array();
     975                            $items     = array();
    976976                            $sub_items = (isset($details['items']) && is_array($details['items'])) ? $details['items'] : array();
    977977                            if (!empty($sub_items)) {
    978978                                foreach ($sub_items as $li) {
    979                                     $name = isset($li['name']) ? $li['name'] : __('Item', 'ppcp');
    980                                     $amount = isset($li['amount']) ? (float) $li['amount'] : 0.0;
     979                                    $name     = isset($li['name']) ? $li['name'] : __('Item', 'ppcp');
     980                                    $amount   = isset($li['amount']) ? (float) $li['amount'] : 0.0;
    981981                                    $quantity = isset($li['quantity']) ? (int) $li['quantity'] : 1;
    982982                                    $items[] = array(
    983                                         'name' => $name,
    984                                         'price' => wc_format_decimal($amount, 2, false),
     983                                        'name'     => $name,
     984                                        'price'    => wc_format_decimal($amount, 2, false),
    985985                                        'quantity' => max(1, $quantity),
    986986                                        'subtotal' => wc_format_decimal($amount * max(1, $quantity), 2, false),
     
    991991                                if ($total_item_amount > 0) {
    992992                                    $items[] = array(
    993                                         'name' => __('Items', 'ppcp'),
    994                                         'price' => wc_format_decimal($total_item_amount, 2, false),
     993                                        'name'     => __('Items', 'ppcp'),
     994                                        'price'    => wc_format_decimal($total_item_amount, 2, false),
    995995                                        'quantity' => 1,
    996996                                        'subtotal' => wc_format_decimal($total_item_amount, 2, false),
     
    998998                                }
    999999                            }
     1000                            $shipping_methods = array();
     1001                            if ('1' === $needs_shipping && WC()->cart) {
     1002                                if (null === WC()->cart || !WC()->cart->get_cart_contents_count()) {
     1003                                    wc_load_cart();
     1004                                }
     1005                                WC()->cart->calculate_shipping();
     1006                                $packages       = WC()->shipping()->get_packages();
     1007                                $chosen_methods = WC()->session->get('chosen_shipping_methods', array());
     1008                                if (!empty($packages)) {
     1009                                    foreach ($packages as $pkg_index => $pkg) {
     1010                                        if (empty($pkg['rates'])) {
     1011                                            continue;
     1012                                        }
     1013                                        if (empty($chosen_methods[$pkg_index])) {
     1014                                            $first_rate_id              = key($pkg['rates']);
     1015                                            $chosen_methods[$pkg_index] = $first_rate_id;
     1016                                        }
     1017                                        foreach ($pkg['rates'] as $rate_id => $rate) {
     1018                                            $shipping_methods[] = array(
     1019                                                'id'          => $rate_id,
     1020                                                'label'       => $rate->label,
     1021                                                'amount'      => wc_format_decimal($rate->cost, 2, false),
     1022                                                'is_selected' => (
     1023                                                    isset($chosen_methods[$pkg_index]) &&
     1024                                                    $chosen_methods[$pkg_index] === $rate_id
     1025                                                ),
     1026                                            );
     1027                                        }
     1028                                    }
     1029                                    WC()->session->set('chosen_shipping_methods', $chosen_methods);
     1030                                }
     1031                            }
    10001032                            return array(
    1001                                 'currency' => get_woocommerce_currency(),
    1002                                 'cart_total' => wc_format_decimal($order_total, 2, false),
    1003                                 'needs_shipping' => $needs_shipping,
    1004                                 'shipping_total' => wc_format_decimal($shipping, 2, false),
    1005                                 'tax_total' => wc_format_decimal($tax_total, 2, false),
    1006                                 'discount_total' => wc_format_decimal(max(0, $discount_total), 2, false),
    1007                                 'cart_items' => $items,
     1033                                'currency'        => get_woocommerce_currency(),
     1034                                'cart_total'      => wc_format_decimal($order_total, 2, false),
     1035                                'needs_shipping'  => $needs_shipping,
     1036                                'shipping_total'  => wc_format_decimal($shipping, 2, false),
     1037                                'tax_total'       => wc_format_decimal($tax_total, 2, false),
     1038                                'discount_total'  => wc_format_decimal(max(0, $discount_total), 2, false),
     1039                                'cart_items'      => $items,
     1040                                'shipping_methods'=> $shipping_methods,
    10081041                            );
    10091042                        }
     
    10181051                        if (!empty($_POST)) {
    10191052                            if (isset($_POST['radio-control-wc-payment-method-options'])) {
    1020                                 $address = array();
    1021                                 $address['radio-control-wc-payment-method-options'] = wc_clean(wp_unslash($_POST['radio-control-wc-payment-method-options']));
    1022                                 $address['payment_method'] = $address['radio-control-wc-payment-method-options'];
    1023                                 $billing_address = json_decode(stripslashes($_POST['billing_address'] ?? ''), true);
     1053                                $address                                               = array();
     1054                                $address['radio-control-wc-payment-method-options']    = wc_clean(wp_unslash($_POST['radio-control-wc-payment-method-options']));
     1055                                $address['payment_method']                             = $address['radio-control-wc-payment-method-options'];
     1056                                $billing_address                                       = json_decode(stripslashes($_POST['billing_address'] ?? ''), true);
    10241057                                if (is_array($billing_address)) {
    10251058                                    foreach ($billing_address as $key => $val) {
     
    10461079                                WC()->session->set('reload_checkout', true);
    10471080                                $error_messages_data = wc_get_notices('error');
    1048                                 $error_messages = array();
     1081                                $error_messages      = array();
    10491082                                foreach ($error_messages_data as $value) {
    10501083                                    $error_messages[] = $value['notice'];
     
    16151648    }
    16161649
    1617     function set_customer_data($data) {
     1650   
     1651    public function set_customer_data($data) {
    16181652        try {
    16191653            $customer = WC()->customer;
    16201654            $billing_first_name = empty($data['billing_first_name']) ? '' : wc_clean($data['billing_first_name']);
    1621             $billing_last_name = empty($data['billing_last_name']) ? '' : wc_clean($data['billing_last_name']);
    1622             $billing_country = empty($data['billing_country']) ? '' : wc_clean($data['billing_country']);
    1623             $billing_address_1 = empty($data['billing_address_1']) ? '' : wc_clean($data['billing_address_1']);
    1624             $billing_address_2 = empty($data['billing_address_2']) ? '' : wc_clean($data['billing_address_2']);
    1625             $billing_city = empty($data['billing_city']) ? '' : wc_clean($data['billing_city']);
    1626             $billing_state = empty($data['billing_state']) ? '' : wc_clean($data['billing_state']);
    1627             $billing_postcode = empty($data['billing_postcode']) ? '' : wc_clean($data['billing_postcode']);
    1628             $billing_phone = empty($data['billing_phone']) ? '' : wc_clean($data['billing_phone']);
    1629             $billing_email = empty($data['billing_email']) ? '' : wc_clean($data['billing_email']);
    1630             if (isset($data['ship_to_different_address'])) {
     1655            $billing_last_name  = empty($data['billing_last_name']) ? '' : wc_clean($data['billing_last_name']);
     1656            $billing_country    = empty($data['billing_country']) ? '' : wc_clean($data['billing_country']);
     1657            $billing_address_1  = empty($data['billing_address_1']) ? '' : wc_clean($data['billing_address_1']);
     1658            $billing_address_2  = empty($data['billing_address_2']) ? '' : wc_clean($data['billing_address_2']);
     1659            $billing_city       = empty($data['billing_city']) ? '' : wc_clean($data['billing_city']);
     1660            $billing_state      = empty($data['billing_state']) ? '' : wc_clean($data['billing_state']);
     1661            $billing_postcode   = empty($data['billing_postcode']) ? '' : wc_clean($data['billing_postcode']);
     1662            $billing_phone      = empty($data['billing_phone']) ? '' : wc_clean($data['billing_phone']);
     1663            $billing_email      = empty($data['billing_email']) ? '' : wc_clean($data['billing_email']);
     1664            $has_valid_shipping = (
     1665                !empty($data['shipping_first_name']) ||
     1666                !empty($data['shipping_last_name']) ||
     1667                !empty($data['shipping_country']) ||
     1668                !empty($data['shipping_address_1']) ||
     1669                !empty($data['shipping_city']) ||
     1670                !empty($data['shipping_postcode'])
     1671            );
     1672            if ( isset($data['ship_to_different_address']) || $has_valid_shipping ) {
    16311673                $shipping_first_name = empty($data['shipping_first_name']) ? '' : wc_clean($data['shipping_first_name']);
    1632                 $shipping_last_name = empty($data['shipping_last_name']) ? '' : wc_clean($data['shipping_last_name']);
    1633                 $shipping_country = empty($data['shipping_country']) ? '' : wc_clean($data['shipping_country']);
    1634                 $shipping_address_1 = empty($data['shipping_address_1']) ? '' : wc_clean($data['shipping_address_1']);
    1635                 $shipping_address_2 = empty($data['shipping_address_2']) ? '' : wc_clean($data['shipping_address_2']);
    1636                 $shipping_city = empty($data['shipping_city']) ? '' : wc_clean($data['shipping_city']);
    1637                 $shipping_state = empty($data['shipping_state']) ? '' : wc_clean($data['shipping_state']);
    1638                 $shipping_postcode = empty($data['shipping_postcode']) ? '' : wc_clean($data['shipping_postcode']);
     1674                $shipping_last_name  = empty($data['shipping_last_name']) ? '' : wc_clean($data['shipping_last_name']);
     1675                $shipping_country    = empty($data['shipping_country']) ? '' : wc_clean($data['shipping_country']);
     1676                $shipping_address_1  = empty($data['shipping_address_1']) ? '' : wc_clean($data['shipping_address_1']);
     1677                $shipping_address_2  = empty($data['shipping_address_2']) ? '' : wc_clean($data['shipping_address_2']);
     1678                $shipping_city       = empty($data['shipping_city']) ? '' : wc_clean($data['shipping_city']);
     1679                $shipping_state      = empty($data['shipping_state']) ? '' : wc_clean($data['shipping_state']);
     1680                $shipping_postcode   = empty($data['shipping_postcode']) ? '' : wc_clean($data['shipping_postcode']);
    16391681            } else {
    16401682                $shipping_first_name = $billing_first_name;
    1641                 $shipping_last_name = $billing_last_name;
    1642                 $shipping_country = $billing_country;
    1643                 $shipping_address_1 = $billing_address_1;
    1644                 $shipping_address_2 = $billing_address_2;
    1645                 $shipping_city = $billing_city;
    1646                 $shipping_state = $billing_state;
    1647                 $shipping_postcode = $billing_postcode;
    1648             }
    1649             $customer->set_shipping_country($shipping_country);
    1650             $customer->set_shipping_address($shipping_address_1);
    1651             $customer->set_shipping_address_2($shipping_address_2);
    1652             $customer->set_shipping_city($shipping_city);
    1653             $customer->set_shipping_state($shipping_state);
    1654             $customer->set_shipping_postcode($shipping_postcode);
    1655             $customer->set_shipping_first_name($shipping_first_name);
    1656             $customer->set_shipping_last_name($shipping_last_name);
    1657             $customer->set_billing_first_name($billing_first_name);
    1658             $customer->set_billing_last_name($billing_last_name);
    1659             $customer->set_billing_country($billing_country);
    1660             $customer->set_billing_address_1($billing_address_1);
    1661             $customer->set_billing_address_2($billing_address_2);
    1662             $customer->set_billing_city($billing_city);
    1663             $customer->set_billing_state($billing_state);
    1664             $customer->set_billing_postcode($billing_postcode);
    1665             $customer->set_billing_phone($billing_phone);
    1666             $customer->set_billing_email($billing_email);
    1667         } catch (Exception $ex) {
    1668            
     1683                $shipping_last_name  = $billing_last_name;
     1684                $shipping_country    = $billing_country;
     1685                $shipping_address_1  = $billing_address_1;
     1686                $shipping_address_2  = $billing_address_2;
     1687                $shipping_city       = $billing_city;
     1688                $shipping_state      = $billing_state;
     1689                $shipping_postcode   = $billing_postcode;
     1690            }
     1691            $customer->set_shipping_country( $shipping_country );
     1692            $customer->set_shipping_address( $shipping_address_1 );
     1693            $customer->set_shipping_address_2( $shipping_address_2 );
     1694            $customer->set_shipping_city( $shipping_city );
     1695            $customer->set_shipping_state( $shipping_state );
     1696            $customer->set_shipping_postcode( $shipping_postcode );
     1697            $customer->set_shipping_first_name( $shipping_first_name );
     1698            $customer->set_shipping_last_name( $shipping_last_name );
     1699            $customer->set_billing_first_name( $billing_first_name );
     1700            $customer->set_billing_last_name( $billing_last_name );
     1701            $customer->set_billing_country( $billing_country );
     1702            $customer->set_billing_address_1( $billing_address_1 );
     1703            $customer->set_billing_address_2( $billing_address_2 );
     1704            $customer->set_billing_city( $billing_city );
     1705            $customer->set_billing_state( $billing_state );
     1706            $customer->set_billing_postcode( $billing_postcode );
     1707            $customer->set_billing_phone( $billing_phone );
     1708            $customer->set_billing_email( $billing_email );
     1709            $customer->save();
     1710
     1711        } catch ( Exception $ex ) {
     1712
    16691713        }
    16701714    }
     
    18091853
    18101854    public function validate_checkout($country, $state, $sec) {
    1811         $state_value = '';
    1812         $valid_states = WC()->countries->get_states(isset($country) ? $country : ( 'billing' === $sec ? WC()->customer->get_country() : WC()->customer->get_shipping_country() ));
     1855        $state_value  = '';
     1856        $valid_states = WC()->countries->get_states(
     1857            isset($country)
     1858                ? $country
     1859                : ( 'billing' === $sec ? WC()->customer->get_country() : WC()->customer->get_shipping_country() )
     1860        );
     1861
    18131862        if (!empty($valid_states) && is_array($valid_states)) {
    18141863            $valid_state_values = array_flip(array_map('strtolower', $valid_states));
     
    18201869            return $state;
    18211870        }
    1822         if (!empty($valid_states) && is_array($valid_states) && sizeof($valid_states) > 0) {
    1823             if (!in_array($state, array_keys($valid_states))) {
     1871
     1872        if (!empty($valid_states) && is_array($valid_states) && count($valid_states) > 0) {
     1873            if (!in_array($state, array_keys($valid_states), true)) {
     1874                $fuzzy = $this->fuzzy_match_state_label($state, $valid_states);
     1875                if ($fuzzy) {
     1876                    return $fuzzy;
     1877                }
    18241878                return false;
    18251879            } else {
     
    18301884        return $state_value;
    18311885    }
     1886
     1887    protected function fuzzy_match_state_label($state, $valid_states) {
     1888        $state    = trim((string) $state);
     1889        $state_lc = strtolower($state);
     1890
     1891        if ($state_lc === '') {
     1892            return false;
     1893        }
     1894
     1895        foreach ($valid_states as $code => $label) {
     1896            if (strtolower($label) === $state_lc) {
     1897                return $code;
     1898            }
     1899        }
     1900
     1901        foreach ($valid_states as $code => $label) {
     1902            $label_lc = strtolower($label);
     1903            if (strpos($label_lc, $state_lc) !== false || strpos($state_lc, $label_lc) !== false) {
     1904                return $code;
     1905            }
     1906        }
     1907
     1908        $state_soundex = soundex($state_lc);
     1909        foreach ($valid_states as $code => $label) {
     1910            if (soundex(strtolower($label)) === $state_soundex) {
     1911                return $code;
     1912            }
     1913        }
     1914
     1915        $closest_code      = false;
     1916        $shortest_distance = null;
     1917
     1918        foreach ($valid_states as $code => $label) {
     1919            $label_lc = strtolower($label);
     1920            $distance = levenshtein($state_lc, $label_lc);
     1921            if ($shortest_distance === null || $distance < $shortest_distance) {
     1922                $shortest_distance = $distance;
     1923                $closest_code      = $code;
     1924            }
     1925        }
     1926
     1927        if ($shortest_distance !== null && $shortest_distance <= 3) {
     1928            return $closest_code;
     1929        }
     1930
     1931        return false;
     1932    }
     1933
    18321934
    18331935    public function ppcp_update_checkout_field_details() {
     
    21682270    }
    21692271
     2272
     2273    protected function normalize_state_code( $country, $state ) {
     2274        $country = strtoupper( (string) $country );
     2275        $state   = trim( (string) $state );
     2276        $state_lc = strtolower( $state );
     2277        if ( $country === '' || $state === '' ) {
     2278            return $state;
     2279        }
     2280        $valid_states = WC()->countries->get_states( $country );
     2281        if ( empty( $valid_states ) || ! is_array( $valid_states ) ) {
     2282            return $state;
     2283        }
     2284        foreach ( $valid_states as $code => $label ) {
     2285            if ( strtolower( $code ) === $state_lc ) {
     2286                return $code;
     2287            }
     2288        }
     2289        foreach ( $valid_states as $code => $label ) {
     2290            if ( strtolower( $label ) === $state_lc ) {
     2291                return $code;
     2292            }
     2293        }
     2294        foreach ( $valid_states as $code => $label ) {
     2295            if ( strpos( strtolower( $label ), $state_lc ) !== false ||
     2296                 strpos( $state_lc, strtolower( $label ) ) !== false ) {
     2297                return $code;
     2298            }
     2299        }
     2300        foreach ( $valid_states as $code => $label ) {
     2301            if ( soundex( $state_lc ) === soundex( strtolower( $label ) ) ) {
     2302                return $code;
     2303            }
     2304        }
     2305        $closest_code = $state;
     2306        $shortest_distance = 5; // max allowed edits
     2307        foreach ( $valid_states as $code => $label ) {
     2308            $distance = levenshtein( $state_lc, strtolower( $label ) );
     2309            if ( $distance < $shortest_distance ) {
     2310                $shortest_distance = $distance;
     2311                $closest_code      = $code;
     2312            }
     2313        }
     2314        if ( $closest_code !== $state ) {
     2315            return $closest_code;
     2316        }
     2317        return $state;
     2318    }
     2319
    21702320    public function ppcp_prepare_order_data() {
    2171         // Use generic session key names
    2172         $session_billing = WC()->session->get('ppcp_billing_address');
     2321        $session_billing  = WC()->session->get('ppcp_billing_address');
    21732322        $session_shipping = WC()->session->get('ppcp_shipping_address');
    2174 
    2175         // Load fallback checkout details if session is empty
    21762323        if (empty($this->checkout_details)) {
    21772324            $this->checkout_details = ppcp_get_session('ppcp_paypal_transaction_details', false);
    2178 
    21792325            if (empty($this->checkout_details)) {
    21802326                $ppcp_order_id = ppcp_get_session('ppcp_paypal_order_id');
     
    21832329                }
    21842330            }
    2185 
    21862331            if (!empty($this->checkout_details)) {
    21872332                ppcp_set_session('ppcp_paypal_transaction_details', $this->checkout_details);
    21882333            }
    21892334        }
    2190 
    2191         // Final source: priority → session > checkout_details
    2192         $billing_address = is_array($session_billing) && !empty($session_billing) ? $session_billing : $this->get_mapped_billing_address($this->checkout_details ?? [], !$this->set_billing_address);
    2193 
    2194         $shipping_address = is_array($session_shipping) && !empty($session_shipping) ? $session_shipping : $this->get_mapped_shipping_address($this->checkout_details ?? []);
    2195 
    2196         // Build order data
     2335        $billing_address = is_array($session_billing) && !empty($session_billing)
     2336            ? $session_billing
     2337            : $this->get_mapped_billing_address($this->checkout_details ?? [], !$this->set_billing_address);
     2338
     2339        $shipping_address = is_array($session_shipping) && !empty($session_shipping)
     2340            ? $session_shipping
     2341            : $this->get_mapped_shipping_address($this->checkout_details ?? []);
     2342
     2343        if (!empty($billing_address['state']) && !empty($billing_address['country'])) {
     2344            $billing_address['state'] = $this->normalize_state_code(
     2345                $billing_address['country'],
     2346                $billing_address['state']
     2347            );
     2348            $validated = $this->validate_checkout(
     2349                $billing_address['country'],
     2350                $billing_address['state'],
     2351                'billing'
     2352            );
     2353            if ($validated !== false && $validated !== '') {
     2354                $billing_address['state'] = $validated;
     2355            }
     2356        }
     2357
     2358        if (!empty($shipping_address['state']) && !empty($shipping_address['country'])) {
     2359            $shipping_address['state'] = $this->normalize_state_code(
     2360                $shipping_address['country'],
     2361                $shipping_address['state']
     2362            );
     2363            $validated = $this->validate_checkout(
     2364                $shipping_address['country'],
     2365                $shipping_address['state'],
     2366                'shipping'
     2367            );
     2368            if ($validated !== false && $validated !== '') {
     2369                $shipping_address['state'] = $validated;
     2370            }
     2371        }
     2372
    21972373        $order_data = [
    2198             'terms' => 1,
    2199             'createaccount' => 0,
    2200             'payment_method' => 'wpg_paypal_checkout',
     2374            'terms'                     => 1,
     2375            'createaccount'             => 0,
     2376            'payment_method'            => 'wpg_paypal_checkout',
    22012377            'ship_to_different_address' => false,
    2202             'order_comments' => '',
    2203             'shipping_method' => '',
    2204             // Billing from selected source
     2378            'order_comments'            => '',
     2379            'shipping_method'           => '',
    22052380            'billing_first_name' => $billing_address['first_name'] ?? '',
    2206             'billing_last_name' => $billing_address['last_name'] ?? '',
    2207             'billing_email' => $billing_address['email'] ?? '',
    2208             'billing_company' => $billing_address['company'] ?? '',
    2209             'billing_address_1' => $billing_address['address_1'] ?? '',
    2210             'billing_address_2' => $billing_address['address_2'] ?? '',
    2211             'billing_city' => $billing_address['city'] ?? '',
    2212             'billing_state' => $billing_address['state'] ?? '',
    2213             'billing_postcode' => $billing_address['postcode'] ?? '',
    2214             'billing_country' => $billing_address['country'] ?? '',
    2215             'billing_phone' => $billing_address['phone'] ?? '',
    2216             // Shipping from selected source
     2381            'billing_last_name'  => $billing_address['last_name'] ?? '',
     2382            'billing_email'      => $billing_address['email'] ?? '',
     2383            'billing_company'    => $billing_address['company'] ?? '',
     2384            'billing_address_1'  => $billing_address['address_1'] ?? '',
     2385            'billing_address_2'  => $billing_address['address_2'] ?? '',
     2386            'billing_city'       => $billing_address['city'] ?? '',
     2387            'billing_state'      => $billing_address['state'] ?? '',
     2388            'billing_postcode'   => $billing_address['postcode'] ?? '',
     2389            'billing_country'    => $billing_address['country'] ?? '',
     2390            'billing_phone'      => $billing_address['phone'] ?? '',
    22172391            'shipping_first_name' => $shipping_address['first_name'] ?? '',
    2218             'shipping_last_name' => $shipping_address['last_name'] ?? '',
    2219             'shipping_company' => $shipping_address['company'] ?? '',
    2220             'shipping_address_1' => $shipping_address['address_1'] ?? '',
    2221             'shipping_address_2' => $shipping_address['address_2'] ?? '',
    2222             'shipping_city' => $shipping_address['city'] ?? '',
    2223             'shipping_state' => $shipping_address['state'] ?? '',
    2224             'shipping_postcode' => $shipping_address['postcode'] ?? '',
    2225             'shipping_country' => $shipping_address['country'] ?? '',
    2226             'shipping_phone' => $shipping_address['phone'] ?? '',
     2392            'shipping_last_name'  => $shipping_address['last_name'] ?? '',
     2393            'shipping_company'    => $shipping_address['company'] ?? '',
     2394            'shipping_address_1'  => $shipping_address['address_1'] ?? '',
     2395            'shipping_address_2'  => $shipping_address['address_2'] ?? '',
     2396            'shipping_city'       => $shipping_address['city'] ?? '',
     2397            'shipping_state'      => $shipping_address['state'] ?? '',
     2398            'shipping_postcode'   => $shipping_address['postcode'] ?? '',
     2399            'shipping_country'    => $shipping_address['country'] ?? '',
     2400            'shipping_phone'      => $shipping_address['phone'] ?? '',
    22272401        ];
    2228 
    2229         // Clean up sessions after use
    22302402        WC()->session->__unset('ppcp_billing_address');
    22312403        WC()->session->__unset('ppcp_shipping_address');
    2232 
    22332404        return $order_data;
    22342405    }
     
    23712542
    23722543    public function ppcp_get_updated_total() {
    2373     // Ensure cart is loaded
    2374     if ( null === WC()->cart || ! WC()->cart->get_cart_contents_count() ) {
    2375         wc_load_cart();
    2376     }
    2377 
    2378     // Parse input
    2379     $shipping_address = ! empty( $_POST['shipping_address'] )
    2380         ? json_decode( stripslashes( sanitize_textarea_field( wp_unslash( $_POST['shipping_address'] ) ) ), true )
    2381         : [];
    2382     $billing_address  = ! empty( $_POST['billing_address'] )
    2383         ? json_decode( stripslashes( sanitize_textarea_field( wp_unslash( $_POST['billing_address'] ) ) ), true )
    2384         : [];
    2385 
    2386     // Helper
    2387     $split_name = function ( $full_name ) {
    2388         if ( ! is_string( $full_name ) || trim( $full_name ) === '' ) {
    2389             return [ '', '' ];
    2390         }
    2391         $parts = preg_split( '/\s+/', trim( $full_name ) );
    2392         $first = $parts[0] ?? '';
    2393         $last  = isset( $parts[1] ) ? implode( ' ', array_slice( $parts, 1 ) ) : '';
    2394         return [ $first, $last ];
    2395     };
    2396 
    2397     // Clear any previously stored session addresses so WC recalculates cleanly
    2398     WC()->session->__unset( 'ppcp_billing_address' );
    2399     WC()->session->__unset( 'ppcp_shipping_address' );
    2400 
    2401     // ---- Apply SHIPPING address (if provided) ----
    2402     if ( ! empty( $shipping_address ) ) {
    2403         [ $s_first, $s_last ] = $split_name( $shipping_address['name'] ?? '' );
    2404         if ( empty( $s_last ) ) {
    2405             $s_last = $shipping_address['surname'] ?? '';
    2406         }
    2407 
    2408         // Set on WC customer
    2409         WC()->customer->set_shipping_first_name( $s_first );
    2410         WC()->customer->set_shipping_last_name( $s_last );
    2411         WC()->customer->set_shipping_address_1( $shipping_address['address1'] ?? '' );
    2412         WC()->customer->set_shipping_address_2( $shipping_address['address2'] ?? '' );
    2413         WC()->customer->set_shipping_city( $shipping_address['city'] ?? '' );
    2414         WC()->customer->set_shipping_postcode( $shipping_address['postcode'] ?? '' );
    2415         WC()->customer->set_shipping_state( $shipping_address['state'] ?? '' );
    2416         WC()->customer->set_shipping_country( $shipping_address['country'] ?? '' );
    2417 
    2418         // Also mirror to session so WC shipping zones trigger correctly
    2419         WC()->session->set( 'customer_shipping_country',  $shipping_address['country'] ?? '' );
    2420         WC()->session->set( 'customer_shipping_state',    $shipping_address['state'] ?? '' );
    2421         WC()->session->set( 'customer_shipping_postcode', $shipping_address['postcode'] ?? '' );
    2422         WC()->session->set( 'customer_shipping_city',     $shipping_address['city'] ?? '' );
    2423         WC()->session->set( 'customer_shipping_address',  $shipping_address['address1'] ?? '' );
    2424         WC()->session->set( 'customer_shipping_address_1', $shipping_address['address1'] ?? '' );
    2425         WC()->session->set( 'customer_shipping_address_2', $shipping_address['address2'] ?? '' );
    2426 
    2427         // Save a copy (optional)
    2428         WC()->session->set( 'ppcp_shipping_address', [
    2429             'first_name' => $s_first,
    2430             'last_name'  => $s_last,
    2431             'address_1'  => $shipping_address['address1'] ?? '',
    2432             'address_2'  => $shipping_address['address2'] ?? '',
    2433             'city'       => $shipping_address['city'] ?? '',
    2434             'postcode'   => $shipping_address['postcode'] ?? '',
    2435             'state'      => $shipping_address['state'] ?? '',
    2436             'country'    => $shipping_address['country'] ?? '',
    2437             'phone'      => $shipping_address['phoneNumber'] ?? '',
    2438         ] );
    2439     }
    2440 
    2441     // ---- Apply BILLING address (if provided) ----
    2442     if ( ! empty( $billing_address ) ) {
    2443         [ $b_first, $b_last ] = $split_name( $billing_address['name'] ?? '' );
    2444         if ( empty( $b_last ) ) {
    2445             $b_last = $billing_address['surname'] ?? '';
    2446         }
    2447 
    2448         WC()->customer->set_billing_first_name( $b_first );
    2449         WC()->customer->set_billing_last_name( $b_last );
    2450         WC()->customer->set_billing_address_1( $billing_address['address1'] ?? '' );
    2451         WC()->customer->set_billing_address_2( $billing_address['address2'] ?? '' );
    2452         WC()->customer->set_billing_city( $billing_address['city'] ?? '' );
    2453         WC()->customer->set_billing_postcode( $billing_address['postcode'] ?? '' );
    2454         WC()->customer->set_billing_state( $billing_address['state'] ?? '' );
    2455         WC()->customer->set_billing_country( $billing_address['country'] ?? '' );
    2456         WC()->customer->set_billing_email( $billing_address['emailAddress'] ?? '' );
    2457         WC()->customer->set_billing_phone( $billing_address['phoneNumber'] ?? '' );
    2458 
    2459         WC()->session->set( 'customer_billing_country',  $billing_address['country'] ?? '' );
    2460         WC()->session->set( 'customer_billing_state',    $billing_address['state'] ?? '' );
    2461         WC()->session->set( 'customer_billing_postcode', $billing_address['postcode'] ?? '' );
    2462         WC()->session->set( 'customer_billing_city',     $billing_address['city'] ?? '' );
    2463         WC()->session->set( 'customer_billing_address',  $billing_address['address1'] ?? '' );
    2464         WC()->session->set( 'customer_billing_address_1', $billing_address['address1'] ?? '' );
    2465         WC()->session->set( 'customer_billing_address_2', $billing_address['address2'] ?? '' );
    2466 
    2467         WC()->session->set( 'ppcp_billing_address', [
    2468             'first_name' => $b_first,
    2469             'last_name'  => $b_last,
    2470             'email'      => $billing_address['emailAddress'] ?? '',
    2471             'address_1'  => $billing_address['address1'] ?? '',
    2472             'address_2'  => $billing_address['address2'] ?? '',
    2473             'city'       => $billing_address['city'] ?? '',
    2474             'postcode'   => $billing_address['postcode'] ?? '',
    2475             'state'      => $billing_address['state'] ?? '',
    2476             'country'    => $billing_address['country'] ?? '',
    2477             'phone'      => $billing_address['phoneNumber'] ?? '',
    2478         ] );
    2479     }
    2480 
    2481     try {
    2482         // When address changes, nuke cached shipping rates so WC recomputes packages
    2483         $packages = WC()->cart->get_shipping_packages();
    2484         foreach ( $packages as $i => $pkg ) {
    2485             WC()->session->__unset( "shipping_for_package_{$i}" );
    2486         }
    2487 
    2488         // Force recalculation
    2489         WC()->customer->set_calculated_shipping( false );
    2490         WC()->customer->save();
    2491         WC()->cart->calculate_shipping();
    2492         WC()->cart->calculate_totals();
    2493 
    2494         // Build items for Google Pay sheet (subtotal per line, excl. shipping; taxes reported separately)
    2495         $cart_items = [];
    2496         foreach ( WC()->cart->get_cart() as $cart_item ) {
    2497             /** @var WC_Product $product */
    2498             $product   = $cart_item['data'];
    2499             $qty       = (int) $cart_item['quantity'];
    2500             $line_subtotal = isset( $cart_item['line_subtotal'] ) ? (float) $cart_item['line_subtotal'] : 0.0; // excl. tax
    2501             $unit_price    = $qty > 0 ? $line_subtotal / $qty : $line_subtotal;
    2502 
    2503             $cart_items[] = [
    2504                 'name'     => html_entity_decode( $product->get_name(), ENT_QUOTES ),
    2505                 'quantity' => $qty,
    2506                 // Either provide unit 'price' OR full line 'subtotal'; your JS handles both.
    2507                 'price'    => wc_format_decimal( $unit_price, 2 ),
    2508                 'subtotal' => wc_format_decimal( $line_subtotal, 2 ),
    2509             ];
    2510         }
    2511 
    2512         // Pull numeric totals from cart
    2513         $currency        = get_woocommerce_currency();
    2514         $total           = wc_format_decimal( (float) WC()->cart->get_total( 'edit' ), 2 );         // grand total (incl tax)
    2515         $shipping_total  = wc_format_decimal( (float) WC()->cart->get_shipping_total(), 2 );        // shipping excl tax
    2516         $tax_total       = wc_format_decimal( (float) WC()->cart->get_total_tax(), 2 );             // all taxes (items + shipping)
    2517         $discount_total  = wc_format_decimal( (float) WC()->cart->get_discount_total(), 2 );        // coupons etc.
    2518 
    2519         // Return a full breakdown so your JS can fill displayItems and totals (no "Placeholder text")
    2520         wp_send_json_success( [
    2521             'currency'       => $currency,
    2522             'cart_items'     => $cart_items,
    2523             'shipping_total' => (string) $shipping_total,  // excl. tax
    2524             'tax_total'      => (string) $tax_total,
    2525             'discount_total' => (string) $discount_total,
    2526             'total'          => (string) $total,           // grand total incl. tax
    2527         ] );
    2528     } catch ( Exception $e ) {
    2529         wp_send_json_error( [
    2530             'message' => 'Failed to update cart totals.',
    2531             'error'   => $e->getMessage(),
    2532         ] );
    2533     }
    2534 }
     2544        // Ensure cart is loaded
     2545        if ( null === WC()->cart || ! WC()->cart->get_cart_contents_count() ) {
     2546            wc_load_cart();
     2547        }
     2548
     2549        // Parse input
     2550        $shipping_address = ! empty( $_POST['shipping_address'] )
     2551            ? json_decode( stripslashes( sanitize_textarea_field( wp_unslash( $_POST['shipping_address'] ) ) ), true )
     2552            : [];
     2553        $billing_address  = ! empty( $_POST['billing_address'] )
     2554            ? json_decode( stripslashes( sanitize_textarea_field( wp_unslash( $_POST['billing_address'] ) ) ), true )
     2555            : [];
     2556
     2557        $selected_shipping_id = ! empty( $_POST['selected_shipping_id'] ) ? sanitize_text_field( wp_unslash( $_POST['selected_shipping_id'] ) ) : '';
     2558        // Helper
     2559        $split_name = function ( $full_name ) {
     2560            if ( ! is_string( $full_name ) || trim( $full_name ) === '' ) {
     2561                return [ '', '' ];
     2562            }
     2563            $parts = preg_split( '/\s+/', trim( $full_name ) );
     2564            $first = $parts[0] ?? '';
     2565            $last  = isset( $parts[1] ) ? implode( ' ', array_slice( $parts, 1 ) ) : '';
     2566            return [ $first, $last ];
     2567        };
     2568
     2569        // Clear any previously stored session addresses so WC recalculates cleanly
     2570        WC()->session->__unset( 'ppcp_billing_address' );
     2571        WC()->session->__unset( 'ppcp_shipping_address' );
     2572
     2573        // ---- Apply SHIPPING address (if provided) ----
     2574        if ( ! empty( $shipping_address ) ) {
     2575            [ $s_first, $s_last ] = $split_name( $shipping_address['name'] ?? '' );
     2576            if ( empty( $s_last ) ) {
     2577                $s_last = $shipping_address['surname'] ?? '';
     2578            }
     2579
     2580            // Set on WC customer
     2581            WC()->customer->set_shipping_first_name( $s_first );
     2582            WC()->customer->set_shipping_last_name( $s_last );
     2583            WC()->customer->set_shipping_address_1( $shipping_address['address1'] ?? '' );
     2584            WC()->customer->set_shipping_address_2( $shipping_address['address2'] ?? '' );
     2585            WC()->customer->set_shipping_city( $shipping_address['city'] ?? '' );
     2586            WC()->customer->set_shipping_postcode( $shipping_address['postcode'] ?? '' );
     2587            WC()->customer->set_shipping_state( $shipping_address['state'] ?? '' );
     2588            WC()->customer->set_shipping_country( $shipping_address['country'] ?? '' );
     2589
     2590            // Also mirror to session so WC shipping zones trigger correctly
     2591            WC()->session->set( 'customer_shipping_country',   $shipping_address['country'] ?? '' );
     2592            WC()->session->set( 'customer_shipping_state',     $shipping_address['state'] ?? '' );
     2593            WC()->session->set( 'customer_shipping_postcode',  $shipping_address['postcode'] ?? '' );
     2594            WC()->session->set( 'customer_shipping_city',      $shipping_address['city'] ?? '' );
     2595            WC()->session->set( 'customer_shipping_address',   $shipping_address['address1'] ?? '' );
     2596            WC()->session->set( 'customer_shipping_address_1', $shipping_address['address1'] ?? '' );
     2597            WC()->session->set( 'customer_shipping_address_2', $shipping_address['address2'] ?? '' );
     2598
     2599            // Save a copy (optional)
     2600            WC()->session->set( 'ppcp_shipping_address', [
     2601                'first_name' => $s_first,
     2602                'last_name'  => $s_last,
     2603                'address_1'  => $shipping_address['address1'] ?? '',
     2604                'address_2'  => $shipping_address['address2'] ?? '',
     2605                'city'       => $shipping_address['city'] ?? '',
     2606                'postcode'   => $shipping_address['postcode'] ?? '',
     2607                'state'      => $shipping_address['state'] ?? '',
     2608                'country'    => $shipping_address['country'] ?? '',
     2609                'phone'      => $shipping_address['phoneNumber'] ?? '',
     2610            ] );
     2611        }
     2612
     2613        // ---- Apply BILLING address (if provided) ----
     2614        if ( ! empty( $billing_address ) ) {
     2615            [ $b_first, $b_last ] = $split_name( $billing_address['name'] ?? '' );
     2616            if ( empty( $b_last ) ) {
     2617                $b_last = $billing_address['surname'] ?? '';
     2618            }
     2619
     2620            WC()->customer->set_billing_first_name( $b_first );
     2621            WC()->customer->set_billing_last_name( $b_last );
     2622            WC()->customer->set_billing_address_1( $billing_address['address1'] ?? '' );
     2623            WC()->customer->set_billing_address_2( $billing_address['address2'] ?? '' );
     2624            WC()->customer->set_billing_city( $billing_address['city'] ?? '' );
     2625            WC()->customer->set_billing_postcode( $billing_address['postcode'] ?? '' );
     2626            WC()->customer->set_billing_state( $billing_address['state'] ?? '' );
     2627            WC()->customer->set_billing_country( $billing_address['country'] ?? '' );
     2628            WC()->customer->set_billing_email( $billing_address['emailAddress'] ?? '' );
     2629            WC()->customer->set_billing_phone( $billing_address['phoneNumber'] ?? '' );
     2630
     2631            WC()->session->set( 'customer_billing_country',   $billing_address['country'] ?? '' );
     2632            WC()->session->set( 'customer_billing_state',     $billing_address['state'] ?? '' );
     2633            WC()->session->set( 'customer_billing_postcode',  $billing_address['postcode'] ?? '' );
     2634            WC()->session->set( 'customer_billing_city',      $billing_address['city'] ?? '' );
     2635            WC()->session->set( 'customer_billing_address',   $billing_address['address1'] ?? '' );
     2636            WC()->session->set( 'customer_billing_address_1', $billing_address['address1'] ?? '' );
     2637            WC()->session->set( 'customer_billing_address_2', $billing_address['address2'] ?? '' );
     2638
     2639            WC()->session->set( 'ppcp_billing_address', [
     2640                'first_name' => $b_first,
     2641                'last_name'  => $b_last,
     2642                'email'      => $billing_address['emailAddress'] ?? '',
     2643                'address_1'  => $billing_address['address1'] ?? '',
     2644                'address_2'  => $billing_address['address2'] ?? '',
     2645                'city'       => $billing_address['city'] ?? '',
     2646                'postcode'   => $billing_address['postcode'] ?? '',
     2647                'state'      => $billing_address['state'] ?? '',
     2648                'country'    => $billing_address['country'] ?? '',
     2649                'phone'      => $billing_address['phoneNumber'] ?? '',
     2650            ] );
     2651        }
     2652
     2653        try {
     2654            // 🔹 If Google Pay passed a selected shipping method, apply it to Woo session
     2655            if ( ! empty( $selected_shipping_id ) && WC()->cart ) {
     2656                $chosen_methods = WC()->session->get( 'chosen_shipping_methods', [] );
     2657
     2658                // Typical stores have 1 package; apply to all packages just in case
     2659                $packages = WC()->shipping()->get_packages();
     2660                if ( empty( $packages ) ) {
     2661                    // make sure packages exist before using chosen method
     2662                    WC()->cart->calculate_shipping();
     2663                    $packages = WC()->shipping()->get_packages();
     2664                }
     2665
     2666                if ( ! empty( $packages ) ) {
     2667                    foreach ( $packages as $index => $pkg ) {
     2668                        $chosen_methods[ $index ] = $selected_shipping_id;
     2669                    }
     2670                    WC()->session->set( 'chosen_shipping_methods', $chosen_methods );
     2671                }
     2672            }
     2673
     2674            // When address / shipping changes, nuke cached shipping rates so WC recomputes packages
     2675            $packages = WC()->cart->get_shipping_packages();
     2676            foreach ( $packages as $i => $pkg ) {
     2677                WC()->session->__unset( "shipping_for_package_{$i}" );
     2678            }
     2679
     2680            // Force recalculation
     2681            WC()->customer->set_calculated_shipping( false );
     2682            WC()->customer->save();
     2683            WC()->cart->calculate_shipping();
     2684            WC()->cart->calculate_totals();
     2685
     2686            // Re-fetch packages after calculation
     2687            $packages       = WC()->shipping()->get_packages();
     2688            $chosen_methods = WC()->session->get( 'chosen_shipping_methods', [] );
     2689
     2690            // Build items for Google Pay sheet (subtotal per line, excl. shipping; taxes reported separately)
     2691            $cart_items = [];
     2692            foreach ( WC()->cart->get_cart() as $cart_item ) {
     2693                /** @var WC_Product $product */
     2694                $product   = $cart_item['data'];
     2695                $qty       = (int) $cart_item['quantity'];
     2696                $line_subtotal = isset( $cart_item['line_subtotal'] ) ? (float) $cart_item['line_subtotal'] : 0.0; // excl. tax
     2697                $unit_price    = $qty > 0 ? $line_subtotal / $qty : $line_subtotal;
     2698
     2699                $cart_items[] = [
     2700                    'name'     => html_entity_decode( $product->get_name(), ENT_QUOTES ),
     2701                    'quantity' => $qty,
     2702                    // Either provide unit 'price' OR full line 'subtotal'; your JS handles both.
     2703                    'price'    => wc_format_decimal( $unit_price, 2 ),
     2704                    'subtotal' => wc_format_decimal( $line_subtotal, 2 ),
     2705                ];
     2706            }
     2707
     2708            // Pull numeric totals from cart
     2709            $currency        = get_woocommerce_currency();
     2710            $total           = wc_format_decimal( (float) WC()->cart->get_total( 'edit' ), 2 );         // grand total (incl tax)
     2711            $shipping_total  = wc_format_decimal( (float) WC()->cart->get_shipping_total(), 2 );        // shipping excl tax
     2712            $tax_total       = wc_format_decimal( (float) WC()->cart->get_total_tax(), 2 );             // all taxes (items + shipping)
     2713            $discount_total  = wc_format_decimal( (float) WC()->cart->get_discount_total(), 2 );        // coupons etc.
     2714            $needs_shipping  = WC()->cart->needs_shipping() ? '1' : '0';
     2715
     2716            // 🔹 Build shipping_methods list (same structure you saw in get_transaction_info)
     2717            $shipping_methods = [];
     2718            if ( '1' === $needs_shipping && ! empty( $packages ) ) {
     2719                foreach ( $packages as $pkg_index => $pkg ) {
     2720                    if ( empty( $pkg['rates'] ) ) {
     2721                        continue;
     2722                    }
     2723
     2724                    // Ensure there is a chosen method for this package
     2725                    if ( empty( $chosen_methods[ $pkg_index ] ) ) {
     2726                        $first_rate_id              = key( $pkg['rates'] );
     2727                        $chosen_methods[ $pkg_index ] = $first_rate_id;
     2728                    }
     2729
     2730                    foreach ( $pkg['rates'] as $rate_id => $rate ) {
     2731                        $shipping_methods[] = [
     2732                            'id'          => $rate_id,
     2733                            'label'       => $rate->label,
     2734                            'amount'      => wc_format_decimal( $rate->cost, 2, false ),
     2735                            'is_selected' => (
     2736                                isset( $chosen_methods[ $pkg_index ] ) &&
     2737                                $chosen_methods[ $pkg_index ] === $rate_id
     2738                            ),
     2739                        ];
     2740                    }
     2741                }
     2742                // Persist chosen methods so Woo uses them consistently
     2743                WC()->session->set( 'chosen_shipping_methods', $chosen_methods );
     2744            }
     2745
     2746            // Return a full breakdown so your JS can fill displayItems and totals
     2747            wp_send_json_success( [
     2748                'currency'        => $currency,
     2749                'cart_items'      => $cart_items,
     2750                'shipping_total'  => (string) $shipping_total,  // excl. tax
     2751                'tax_total'       => (string) $tax_total,
     2752                'discount_total'  => (string) $discount_total,
     2753                'total'           => (string) $total,           // grand total incl. tax
     2754                'cart_total'      => (string) $total,           // for your JS convenience
     2755                'needs_shipping'  => $needs_shipping,
     2756                'shipping_methods'=> $shipping_methods,
     2757            ] );
     2758        } catch ( Exception $e ) {
     2759            wp_send_json_error( [
     2760                'message' => 'Failed to update cart totals.',
     2761                'error'   => $e->getMessage(),
     2762            ] );
     2763        }
     2764    }
    25352765
    25362766
     
    26422872    public function ppcp_validate_shipping_address() {
    26432873        try {
    2644             if (!wp_verify_nonce($_POST['security'], 'ppcp_ajax_nonce')) {
    2645                 wp_send_json_error('Security verification failed');
     2874            // Security check (slightly hardened)
     2875            if ( ! isset( $_POST['security'] ) || ! wp_verify_nonce( $_POST['security'], 'ppcp_ajax_nonce' ) ) {
     2876                wp_send_json_error( 'Security verification failed' );
    26462877                return;
    26472878            }
    2648             if (!isset($_POST['shipping_address'])) {
    2649                 wp_send_json_error('No shipping address data received');
     2879
     2880            if ( ! isset( $_POST['shipping_address'] ) ) {
     2881                wp_send_json_error( 'No shipping address data received' );
    26502882                return;
    26512883            }
    2652             $shipping_address = json_decode(wp_unslash($_POST['shipping_address']), true);
    2653             if (!$shipping_address || empty($shipping_address['countryCode'])) {
    2654                 wp_send_json_error('Invalid shipping address');
     2884
     2885            $shipping_address = json_decode( wp_unslash( $_POST['shipping_address'] ), true );
     2886            if ( ! $shipping_address || empty( $shipping_address['countryCode'] ) ) {
     2887                wp_send_json_error( 'Invalid shipping address' );
    26552888                return;
    26562889            }
     2890
    26572891            $countries = WC()->countries->get_shipping_countries();
    2658             if (!isset($countries[$shipping_address['countryCode']])) {
    2659                 wp_send_json_error('We do not ship to this country');
     2892            if ( ! isset( $countries[ $shipping_address['countryCode'] ] ) ) {
     2893                wp_send_json_error( 'We do not ship to this country' );
    26602894                return;
    26612895            }
    2662             if (empty($shipping_address['postalCode']) || empty($shipping_address['city'])) {
    2663                 wp_send_json_error('Please complete all required fields');
     2896
     2897            if ( empty( $shipping_address['postalCode'] ) || empty( $shipping_address['city'] ) ) {
     2898                wp_send_json_error( 'Please complete all required fields' );
    26642899                return;
    26652900            }
     2901
     2902            // NEW: read selected shipping from PayPal (if provided)
     2903            $selected_shipping_id = isset( $_POST['selected_shipping_id'] )
     2904                ? wc_clean( wp_unslash( $_POST['selected_shipping_id'] ) )
     2905                : '';
     2906
    26662907            $customer = WC()->customer;
    2667             if ($customer && is_a($customer, 'WC_Customer')) {
     2908            if ( $customer && is_a( $customer, 'WC_Customer' ) ) {
    26682909                try {
    2669                     $customer->set_shipping_country($shipping_address['countryCode'] ?? '');
    2670                     $customer->set_shipping_state($shipping_address['state'] ?? '');
    2671                     $customer->set_shipping_postcode($shipping_address['postalCode'] ?? '');
    2672                     $customer->set_shipping_city($shipping_address['city'] ?? '');
    2673                     if (isset($shipping_address['address_line_1'])) {
    2674                         $customer->set_shipping_address_1($shipping_address['address_line_1']);
     2910                    $country_code = $shipping_address['countryCode'] ?? '';
     2911
     2912                    $customer->set_shipping_country( $country_code );
     2913                    $customer->set_shipping_postcode( $shipping_address['postalCode'] ?? '' );
     2914                    $customer->set_shipping_city( $shipping_address['city'] ?? '' );
     2915
     2916                    // -----------------------------
     2917                    // State normalization + validate
     2918                    // -----------------------------
     2919                    $raw_state = $shipping_address['state'] ?? '';
     2920
     2921                    if ( $raw_state !== '' && $country_code !== '' ) {
     2922                        // 1. Normalize state from any format (name/code, any case) → WC code
     2923                        $normalized_state = $this->normalize_state_code( $country_code, $raw_state );
     2924
     2925                        // 2. Let existing system-wide validation run (unchanged logic)
     2926                        $validated_state = $this->validate_checkout(
     2927                            $country_code,
     2928                            $normalized_state,
     2929                            'shipping'
     2930                        );
     2931
     2932                        if ( $validated_state !== false && $validated_state !== '' ) {
     2933                            $customer->set_shipping_state( $validated_state );
     2934                        } else {
     2935                            // Fall back to normalized value if validate_checkout can't resolve
     2936                            $customer->set_shipping_state( $normalized_state );
     2937                        }
     2938                    } else {
     2939                        // Some countries don't use states
     2940                        $customer->set_shipping_state( '' );
    26752941                    }
    2676                     if (isset($shipping_address['address_line_2'])) {
    2677                         $customer->set_shipping_address_2($shipping_address['address_line_2']);
     2942
     2943                    if ( isset( $shipping_address['address_line_1'] ) ) {
     2944                        $customer->set_shipping_address_1( $shipping_address['address_line_1'] );
    26782945                    }
    2679                     if (isset($shipping_address['name']['full_name'])) {
    2680                         $name_parts = explode(' ', $shipping_address['name']['full_name'], 2);
    2681                         $customer->set_shipping_first_name($name_parts[0] ?? '');
    2682                         $customer->set_shipping_last_name($name_parts[1] ?? '');
     2946                    if ( isset( $shipping_address['address_line_2'] ) ) {
     2947                        $customer->set_shipping_address_2( $shipping_address['address_line_2'] );
    26832948                    }
     2949                    if ( isset( $shipping_address['name']['full_name'] ) ) {
     2950                        $name_parts = explode( ' ', $shipping_address['name']['full_name'], 2 );
     2951                        $customer->set_shipping_first_name( $name_parts[0] ?? '' );
     2952                        $customer->set_shipping_last_name( $name_parts[1] ?? '' );
     2953                    }
     2954
    26842955                    $customer->save();
    2685                     if (WC()->cart && !WC()->cart->is_empty()) {
    2686                         WC()->cart->calculate_shipping();
    2687                         WC()->cart->calculate_totals();
     2956                } catch ( Exception $e ) {
     2957                    error_log( 'Error updating customer shipping address: ' . $e->getMessage() );
     2958                }
     2959            }
     2960
     2961            // Build & calculate shipping packages + set chosen_shipping_methods
     2962            if ( WC()->cart && ! WC()->cart->is_empty() ) {
     2963
     2964                $packages = WC()->cart->get_shipping_packages();
     2965                WC()->shipping()->calculate_shipping( $packages );
     2966
     2967                // Now WC()->shipping()->get_packages() has the rates
     2968                $packages = WC()->shipping()->get_packages();
     2969
     2970                if ( ! empty( $selected_shipping_id ) && ! empty( $packages ) ) {
     2971                    $chosen_methods = WC()->session->get( 'chosen_shipping_methods', array() );
     2972
     2973                    foreach ( $packages as $package_index => $package ) {
     2974                        if ( ! empty( $package['rates'] ) && isset( $package['rates'][ $selected_shipping_id ] ) ) {
     2975                            $chosen_methods[ $package_index ] = $selected_shipping_id;
     2976                            break; // found matching rate; no need to continue
     2977                        }
    26882978                    }
    2689                 } catch (Exception $e) {
    2690                     error_log('Error updating customer shipping address: ' . $e->getMessage());
    2691                 }
    2692             }
     2979
     2980                    WC()->session->set( 'chosen_shipping_methods', $chosen_methods );
     2981                }
     2982
     2983                // Recalculate totals using new address + chosen method
     2984                WC()->cart->calculate_totals();
     2985            }
     2986
     2987            // Keep original behavior: update PayPal order from cart
    26932988            $this->request->ppcp_update_order_from_cart();
    2694             wp_send_json_success('Address is valid');
    2695         } catch (Exception $e) {
    2696             wp_send_json_error('Unable to validate address');
     2989
     2990            wp_send_json_success( 'Address is valid' );
     2991        } catch ( Exception $e ) {
     2992            wp_send_json_error( 'Unable to validate address' );
    26972993        }
    26982994    }
  • woo-paypal-gateway/trunk/ppcp/public/js/ppcp-paypal-checkout-for-woocommerce-public.js

    r3396892 r3401525  
    1111            this.pageContext = 'unknown';
    1212            this.ppcp_used_payment_method = null;
     13            this.googleSelectedShippingId = '';
     14            this.googleShippingIdMap = {};
     15            this.appleSelectedShippingId = '';
     16            this.appleShippingIdMap = {};
    1317            this.init();
    1418            this.ppcp_cart_css();
     
    373377            }
    374378
    375             // Simple address mapping
     379            // Simple address mapping (same as before)
    376380            const shippingAddress = {
    377381                city: data.shipping_address.city || '',
     
    381385            };
    382386
    383             // Validate address via AJAX (this will call ppcp_update_order)
     387            // NEW: detect which shipping method was selected in the PayPal UI
     388            const selectedOption =
     389                data.selected_shipping_option ||
     390                data.selectedShippingOption ||
     391                null;
     392
     393            const selectedShippingId =
     394                selectedOption && selectedOption.id ? selectedOption.id : '';
     395
     396            // Validate address via AJAX (this will also update PayPal order)
    384397            return fetch(this.ppcp_manager.ajax_url, {
    385398                method: 'POST',
     
    388401                    action: 'ppcp_validate_shipping_address',
    389402                    security: this.ppcp_manager.ajax_nonce,
    390                     shipping_address: JSON.stringify(shippingAddress)
     403                    shipping_address: JSON.stringify(shippingAddress),
     404                    // NEW: send selected shipping method id to PHP
     405                    selected_shipping_id: selectedShippingId
    391406                })
    392407            })
    393                     .then(res => res.json())
    394                     .then(result => {
    395                         if (result.success) {
    396                             return actions.resolve();
    397                         } else {
    398                             return actions.reject();
    399                         }
    400                     })
    401                     .catch(() => {
     408                .then(res => res.json())
     409                .then(result => {
     410                    if (result && result.success) {
     411                        return actions.resolve();
     412                    } else {
    402413                        return actions.reject();
    403                     });
    404         }
     414                    }
     415                })
     416                .catch(() => {
     417                    return actions.reject();
     418                });
     419        }
     420
    405421
    406422        renderSmartButton() {
     
    865881                    $("#wpg_paypal_checkout_cc-card-cvc").empty();
    866882                    numberField.render("#wpg_paypal_checkout_cc-card-number");
    867                     numberField.setAttribute("placeholder", "4111 1111 1111 1111");
     883                    numberField.setAttribute("placeholder", "1234 1234 1234 1234");
    868884                    cardFields.ExpiryField().render("#wpg_paypal_checkout_cc-card-expiry");
    869885                    cardFields.CVVField().render("#wpg_paypal_checkout_cc-card-cvc");
     
    11281144        async onPaymentDataChanged(intermediatePaymentData) {
    11291145            try {
    1130                 const {callbackTrigger, shippingAddress} = intermediatePaymentData || {};
     1146                const {callbackTrigger, shippingAddress, shippingOptionData} = intermediatePaymentData || {};
    11311147                const mapAddr = (addr = {}) => ({
    1132                         address1: addr.address1 || '',
    1133                         address2: addr.address2 || '',
    1134                         city: addr.locality || '',
    1135                         state: addr.administrativeArea || '',
    1136                         postcode: addr.postalCode || '',
    1137                         country: addr.countryCode || ''
    1138                     });
     1148                    address1: addr.address1 || '',
     1149                    address2: addr.address2 || '',
     1150                    city: addr.locality || '',
     1151                    state: addr.administrativeArea || '',
     1152                    postcode: addr.postalCode || '',
     1153                    country: addr.countryCode || ''
     1154                });
    11391155                if (callbackTrigger === 'INITIALIZE') {
    11401156                    if (shippingAddress) {
     
    11481164                    const tx = this.getGoogleTransactionInfo();
    11491165                    tx.totalPriceStatus = 'ESTIMATED';
    1150                     return {newTransactionInfo: tx};
     1166                    const shippingOptionParameters = this.getGoogleShippingOptionParameters();
     1167                    const result = {newTransactionInfo: tx};
     1168                    if (shippingOptionParameters) {
     1169                        result.newShippingOptionParameters = shippingOptionParameters;
     1170                    }
     1171                    return result;
    11511172                }
    11521173                if (callbackTrigger === 'SHIPPING_ADDRESS' && shippingAddress) {
     
    11541175                    const tx = this.getGoogleTransactionInfo();
    11551176                    tx.totalPriceStatus = 'ESTIMATED';
    1156                     return {newTransactionInfo: tx};
     1177                    const shippingOptionParameters = this.getGoogleShippingOptionParameters();
     1178                    const result = {newTransactionInfo: tx};
     1179                    if (shippingOptionParameters) {
     1180                        result.newShippingOptionParameters = shippingOptionParameters;
     1181                    }
     1182                    return result;
     1183                }
     1184                if (callbackTrigger === 'SHIPPING_OPTION' && shippingOptionData && shippingOptionData.id) {
     1185                    const safeId = shippingOptionData.id;
     1186                    const internalId = (this.googleShippingIdMap && this.googleShippingIdMap[safeId]) ? this.googleShippingIdMap[safeId] : safeId; // fallback
     1187                    this.googleSelectedShippingId = internalId;
     1188                    const addr = shippingAddress ? mapAddr(shippingAddress) : {};
     1189                    await this.fetchUpdatedTotalFromBackend(addr, null, internalId);
     1190                    const tx = this.getGoogleTransactionInfo();
     1191                    tx.totalPriceStatus = 'ESTIMATED';
     1192                    const shippingOptionParameters = this.getGoogleShippingOptionParameters();
     1193                    const result = { newTransactionInfo: tx };
     1194                    if (shippingOptionParameters) {
     1195                        result.newShippingOptionParameters = shippingOptionParameters;
     1196                    }
     1197                    return result;
    11571198                }
    11581199                return {};
     
    13081349                    throw new Error("");
    13091350                }
    1310                 if (transactionInfo?.success) {
    1311                     const d = transactionInfo.data || {};
    1312                     this.ppcp_manager.cart_total = d.cart_total     ?? this.ppcp_manager.cart_total;
    1313                     this.ppcp_manager.cart_items = Array.isArray(d.cart_items) ? d.cart_items : [];
    1314                     this.ppcp_manager.shipping_total = d.shipping_total ?? "0.00";
    1315                     this.ppcp_manager.tax_total = d.tax_total      ?? "0.00";
    1316                     this.ppcp_manager.discount_total = d.discount_total ?? "0.00";
    1317                     this.ppcp_manager.currency = d.currency       ?? this.ppcp_manager.currency;
    1318                     this.ppcp_manager.needs_shipping = d.needs_shipping ?? this.ppcp_manager.needs_shipping ?? "0";
    1319                 }
    1320 
    1321                 // Build client that matches callbackIntents we’re about to request
     1351                if (transactionInfo?.success && transactionInfo.data) {
     1352                    if (typeof this.setTotalsFromResponse === 'function') {
     1353                        this.setTotalsFromResponse(transactionInfo.data);
     1354                    }
     1355                }
    13221356                const shippingRequired = this.ppcp_manager.needs_shipping === "1";
    13231357                const paymentsClient = this.getGooglePaymentsClient({requireOnPaymentDataChanged: shippingRequired});
    1324 
    13251358                const paymentDataRequest = await this.getGooglePaymentDataRequest();
     1359                console.log('GPay PaymentDataRequest:', paymentDataRequest); // 👈 debug
    13261360                await paymentsClient.loadPaymentData(paymentDataRequest);
    1327 
    13281361            } catch (error) {
     1362                console.error('[GPay] loadPaymentData error:', error);
    13291363                this.hideSpinner();
    13301364                if (error?.statusCode === "CANCELED") {
     
    13361370        }
    13371371
     1372        getGoogleShippingOptionParameters() {
     1373            const methods = Array.isArray(this.ppcp_manager.shipping_methods) ? this.ppcp_manager.shipping_methods : [];
     1374            if (!methods.length) {
     1375                return null;
     1376            }
     1377            this.googleShippingIdMap = {};
     1378            const formatPrice = (amount) => {
     1379                const n = parseFloat(String(amount || "0").replace(/,/g, "")) || 0;
     1380                const currency = this.ppcp_manager.currency || "USD";
     1381                const locale   = this.ppcp_manager.locale   || "en-US";
     1382                return new Intl.NumberFormat(locale, {
     1383                    style: "currency",
     1384                    currency
     1385                }).format(n);
     1386            };
     1387            let defaultInternalId = this.googleSelectedShippingId;
     1388            if (!defaultInternalId) {
     1389                const selected = methods.find(m => m.is_selected);
     1390                defaultInternalId = selected?.id || methods[0].id;
     1391            }
     1392            const options = methods.map(method => {
     1393                const internalId = method.id;
     1394                const safeId = internalId.replace(/[^a-zA-Z0-9 _-]/g, "_");
     1395                this.googleShippingIdMap[safeId] = internalId;
     1396                const formattedAmount = formatPrice(method.amount);
     1397                return {
     1398                    id: safeId,
     1399                    label: method.label || internalId,
     1400                    description: formattedAmount
     1401                };
     1402            });
     1403            let defaultSafeId = options[0].id;
     1404            const found = options.find(opt => this.googleShippingIdMap[opt.id] === defaultInternalId);
     1405            if (found) defaultSafeId = found.id;
     1406            return {
     1407                defaultSelectedOptionId: defaultSafeId,
     1408                shippingOptions: options
     1409            };
     1410        }
     1411
    13381412        async getGooglePaymentDataRequest() {
    13391413            const {allowedPaymentMethods, merchantInfo} = await this.getGooglePayConfig();
    13401414            const shippingRequired = this.ppcp_manager.needs_shipping === "1";
    1341 
     1415            const callbackIntents = ['PAYMENT_AUTHORIZATION'];
     1416            if (shippingRequired) {
     1417                callbackIntents.push('SHIPPING_ADDRESS', 'SHIPPING_OPTION');
     1418            }
    13421419            const paymentDataRequest = {
    13431420                apiVersion: 2,
     
    13471424                merchantInfo,
    13481425                emailRequired: true,
    1349                 callbackIntents: ['PAYMENT_AUTHORIZATION', ...(shippingRequired ? ['SHIPPING_ADDRESS'] : [])]
    1350             };
    1351 
     1426                callbackIntents
     1427            };
    13521428            if (shippingRequired) {
    13531429                paymentDataRequest.shippingAddressRequired = true;
     
    13551431                    phoneNumberRequired: true
    13561432                };
    1357             }
    1358 
     1433                const shippingOptionParameters = this.getGoogleShippingOptionParameters();
     1434                if (shippingOptionParameters && Array.isArray(shippingOptionParameters.shippingOptions) && shippingOptionParameters.shippingOptions.length > 0) {
     1435                    paymentDataRequest.shippingOptionRequired   = true;
     1436                    paymentDataRequest.shippingOptionParameters = shippingOptionParameters;
     1437                }
     1438            }
    13591439            return paymentDataRequest;
    13601440        }
     
    13951475                    method: 'POST',
    13961476                    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    1397                     body: data,
     1477                    body: data
    13981478                });
    13991479                return await response.json();
     
    14081488        getGoogleTransactionInfo() {
    14091489            const currency = this.ppcp_manager?.currency || "USD";
     1490            const country  = this.ppcp_manager?.country  || "US";
    14101491            const toNum = (v) => {
    14111492                const f = parseFloat((v ?? "").toString().replace(/,/g, ""));
     
    14131494            };
    14141495            const toStr = (v) => toNum(v).toFixed(2);
    1415 
    14161496            const displayItems = [];
    1417 
    14181497            const items = Array.isArray(this.ppcp_manager?.cart_items) ? this.ppcp_manager.cart_items : [];
    14191498            for (const it of items) {
    14201499                const name = it?.name ?? "Item";
    1421                 const qty = Math.max(1, parseInt(it?.quantity, 10) || 1);
     1500                const qty  = Math.max(1, parseInt(it?.quantity, 10) || 1);
    14221501                const lineTotal = ("subtotal" in it) ? toNum(it.subtotal) : toNum(it.price) * qty;
    1423 
    14241502                displayItems.push({
    14251503                    label: `${name}${qty > 1 ? ` × ${qty}` : ""}`,
    14261504                    type: "LINE_ITEM",
    1427                     price: toStr(lineTotal),
     1505                    price: toStr(lineTotal)
    14281506                });
    14291507            }
    1430 
    14311508            const needsShipping = String(this.ppcp_manager?.needs_shipping ?? "0") === "1";
    14321509            const shippingTotal = toNum(this.ppcp_manager?.shipping_total);
     
    14351512                    label: "Shipping",
    14361513                    type: "SHIPPING_OPTION",
    1437                     price: toStr(shippingTotal),
     1514                    price: toStr(shippingTotal)
    14381515                });
    14391516            }
    1440 
    14411517            const taxTotal = toNum(this.ppcp_manager?.tax_total);
    14421518            if (taxTotal > 0) {
     
    14441520                    label: "Tax",
    14451521                    type: "TAX",
    1446                     price: toStr(taxTotal),
     1522                    price: toStr(taxTotal)
    14471523                });
    14481524            }
    1449 
    14501525            const discountTotal = toNum(this.ppcp_manager?.discount_total);
    14511526            if (discountTotal > 0) {
     
    14531528                    label: "Discount",
    14541529                    type: "DISCOUNT",
    1455                     price: toStr(-discountTotal),
     1530                    price: toStr(-discountTotal)
    14561531                });
    14571532            }
    1458 
    14591533            const total = toNum(this.ppcp_manager?.cart_total);
    1460 
    14611534            return {
    14621535                displayItems,
    14631536                currencyCode: currency,
     1537                countryCode: country,
    14641538                totalPriceStatus: "ESTIMATED",
    14651539                totalPrice: toStr(total),
    14661540                totalPriceLabel: "Total",
     1541                checkoutOption: "DEFAULT"
    14671542            };
    14681543        }
     
    14711546            try {
    14721547                if (this.pageContext !== 'checkout') {
    1473                     const billingRaw = paymentData?.paymentMethodData?.info?.billingAddress || {};
     1548                    const billingRaw  = paymentData?.paymentMethodData?.info?.billingAddress || {};
    14741549                    const shippingRaw = paymentData?.shippingAddress || {};
    1475                     const email = paymentData?.email || '';
     1550                    const email       = paymentData?.email || '';
    14761551                    const billingAddress = {
    1477                         name: billingRaw.name || '',
    1478                         surname: '',
    1479                         address1: billingRaw.address1 || '',
    1480                         address2: billingRaw.address2 || '',
    1481                         city: billingRaw.locality || '',
    1482                         state: billingRaw.administrativeArea || '',
    1483                         postcode: billingRaw.postalCode || '',
    1484                         country: billingRaw.countryCode || '',
    1485                         phoneNumber: billingRaw.phoneNumber || '',
     1552                        name:         billingRaw.name || '',
     1553                        surname:      '',
     1554                        address1:     billingRaw.address1 || '',
     1555                        address2:     billingRaw.address2 || '',
     1556                        city:         billingRaw.locality || '',
     1557                        state:        billingRaw.administrativeArea || '',
     1558                        postcode:     billingRaw.postalCode || '',
     1559                        country:      billingRaw.countryCode || '',
     1560                        phoneNumber:  billingRaw.phoneNumber || '',
    14861561                        emailAddress: email || ''
    14871562                    };
    14881563                    const shippingAddress = {
    1489                         name: shippingRaw.name || '',
    1490                         surname: '',
    1491                         address1: shippingRaw.address1 || '',
    1492                         address2: shippingRaw.address2 || '',
    1493                         city: shippingRaw.locality || '',
    1494                         state: shippingRaw.administrativeArea || '',
    1495                         postcode: shippingRaw.postalCode || '',
    1496                         country: shippingRaw.countryCode || '',
     1564                        name:        shippingRaw.name || '',
     1565                        surname:     '',
     1566                        address1:    shippingRaw.address1 || '',
     1567                        address2:    shippingRaw.address2 || '',
     1568                        city:        shippingRaw.locality || '',
     1569                        state:       shippingRaw.administrativeArea || '',
     1570                        postcode:    shippingRaw.postalCode || '',
     1571                        country:     shippingRaw.countryCode || '',
    14971572                        phoneNumber: shippingRaw.phoneNumber || ''
    14981573                    };
    1499                     const updatedTotal = await this.fetchUpdatedTotalFromBackend({
    1500                         shipping_address: {
    1501                             address1: shippingAddress.address1 || '',
    1502                             address2: shippingAddress.address2 || '',
    1503                             city: shippingAddress.city || '',
    1504                             state: shippingAddress.state || '',
    1505                             postcode: shippingAddress.postcode || '',
    1506                             country: shippingAddress.country || '',
    1507                             name: shippingAddress.name || '',
    1508                             phoneNumber: shippingAddress.phoneNumber || ''
    1509                         },
    1510                         billing_address: {
    1511                             address1: billingAddress.address1 || '',
    1512                             address2: billingAddress.address2 || '',
    1513                             city: billingAddress.city || '',
    1514                             state: billingAddress.state || '',
    1515                             postcode: billingAddress.postcode || '',
    1516                             country: billingAddress.country || '',
    1517                             name: billingAddress.name || '',
    1518                             phoneNumber: billingAddress.phoneNumber || '',
    1519                             emailAddress: billingAddress.emailAddress || ''
    1520                         }
    1521                     });
    15221574                    await this.fetchUpdatedTotalFromBackend(shippingAddress, billingAddress);
    15231575                }
     
    15281580                const result = await wpg_paypal_sdk.Googlepay().confirmOrder({
    15291581                    orderId,
    1530                     paymentMethodData: paymentData.paymentMethodData,
     1582                    paymentMethodData: paymentData.paymentMethodData
     1583                }).catch(err => {
     1584                    console.error('[Google Pay] confirmOrder error:', err);
     1585                    throw err;
    15311586                });
    1532                 if (result.status === "PAYER_ACTION_REQUIRED") {
    1533                     await wpg_paypal_sdk.Googlepay().initiatePayerAction({orderId});
    1534                 }
    1535                 this.onApproveHandler({orderID: orderId}, 'google_pay');
    1536                 return {transactionState: "SUCCESS"};
     1587                if (result && result.status === "PAYER_ACTION_REQUIRED") {
     1588                    await wpg_paypal_sdk.Googlepay().initiatePayerAction({ orderId });
     1589                }
     1590                this.onApproveHandler({ orderID: orderId }, 'google_pay');
     1591                return { transactionState: "SUCCESS" };
    15371592            } catch (error) {
    15381593                this.showError(error.message || "Google Pay failed.");
     
    15491604
    15501605        setTotalsFromResponse(d = {}) {
    1551             if (!d)
     1606            if (!d) {
    15521607                return;
    1553             this.ppcp_manager.cart_total = d.total          ?? d.cart_total ?? this.ppcp_manager.cart_total;
    1554             this.ppcp_manager.cart_items = Array.isArray(d.cart_items) ? d.cart_items : (this.ppcp_manager.cart_items || []);
    1555             this.ppcp_manager.shipping_total = d.shipping_total ?? this.ppcp_manager.shipping_total ?? "0.00";
    1556             this.ppcp_manager.tax_total = d.tax_total      ?? this.ppcp_manager.tax_total      ?? "0.00";
    1557             this.ppcp_manager.discount_total = d.discount_total ?? this.ppcp_manager.discount_total ?? "0.00";
    1558             this.ppcp_manager.currency = d.currency       ?? this.ppcp_manager.currency;
    1559             this.ppcp_manager.needs_shipping = d.needs_shipping ?? this.ppcp_manager.needs_shipping ?? "0";
    1560         }
    1561 
    1562         async fetchUpdatedTotalFromBackend(shippingAddress, billingAddress = null) {
     1608            }
     1609
     1610            this.ppcp_manager.cart_total       = d.total          ?? d.cart_total ?? this.ppcp_manager.cart_total;
     1611            this.ppcp_manager.cart_items       = Array.isArray(d.cart_items) ? d.cart_items : (this.ppcp_manager.cart_items || []);
     1612            this.ppcp_manager.shipping_total   = d.shipping_total ?? this.ppcp_manager.shipping_total ?? "0.00";
     1613            this.ppcp_manager.tax_total        = d.tax_total      ?? this.ppcp_manager.tax_total      ?? "0.00";
     1614            this.ppcp_manager.discount_total   = d.discount_total ?? this.ppcp_manager.discount_total ?? "0.00";
     1615            this.ppcp_manager.currency         = d.currency       ?? this.ppcp_manager.currency;
     1616            this.ppcp_manager.needs_shipping   = d.needs_shipping ?? this.ppcp_manager.needs_shipping ?? "0";
     1617            this.ppcp_manager.shipping_methods = Array.isArray(d.shipping_methods)
     1618                ? d.shipping_methods
     1619                : (this.ppcp_manager.shipping_methods || []);
     1620
     1621            const selected = (this.ppcp_manager.shipping_methods || []).find(m => m.is_selected);
     1622            if (selected && selected.id) {
     1623                this.googleSelectedShippingId = selected.id;
     1624                this.appleSelectedShippingId  = selected.id;
     1625            }
     1626        }
     1627
     1628        async fetchUpdatedTotalFromBackend(shippingAddress, billingAddress = null, selectedShippingId = '') {
    15631629            try {
     1630                const params = new URLSearchParams({
     1631                    action: 'ppcp_get_updated_total',
     1632                    security: this.ppcp_manager.ajax_nonce,
     1633                    shipping_address: JSON.stringify(shippingAddress || {}),
     1634                    billing_address: billingAddress ? JSON.stringify(billingAddress) : '',
     1635                    context: this.pageContext
     1636                });
     1637                if (selectedShippingId) {
     1638                    params.append('selected_shipping_id', selectedShippingId);
     1639                }
    15641640                const res = await fetch(this.ppcp_manager.ajax_url, {
    15651641                    method: 'POST',
    15661642                    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    1567                     body: new URLSearchParams({
    1568                         action: 'ppcp_get_updated_total',
    1569                         security: this.ppcp_manager.ajax_nonce,
    1570                         shipping_address: JSON.stringify(shippingAddress || {}),
    1571                         billing_address: billingAddress ? JSON.stringify(billingAddress) : '',
    1572                         context: this.pageContext
    1573                     })
     1643                    body: params
    15741644                });
    15751645                const result = await res.json();
     
    16061676            const isApplePayEnabled = this.ppcp_manager?.enabled_apple_pay === 'yes';
    16071677            const isProductPage = document.querySelector('.apple-pay-container[data-context="product"]');
    1608             if (!isApplePayEnabled || !isProductPage)
     1678            if (!isApplePayEnabled || !isProductPage) {
    16091679                return;
    1610 
     1680            }
    16111681            const baseProductId = parseInt(this.ppcp_manager?.product_id || 0);
    16121682            const defaultQty = parseFloat(document.querySelector('input.qty')?.value || '1');
    16131683            this.fetchProductTotal(baseProductId, defaultQty);
    1614 
    16151684            jQuery('form.variations_form').on('found_variation', (event, variation) => {
    16161685                const variationId = variation.variation_id;
     
    16311700                headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    16321701                body: data.toString()
    1633             })
    1634                     .then(res => res.json())
    1635                     .then(response => {
    1636                         if (response.success && response.data?.combined_total) {
    1637                             this.ppcp_manager.cart_total = response.data.combined_total;
    1638                         }
    1639                     });
     1702            }).then(res => res.json())
     1703            .then(response => {
     1704                if (response.success && response.data?.combined_total) {
     1705                    this.ppcp_manager.cart_total = response.data.combined_total;
     1706                }
     1707            });
    16401708        }
    16411709
     
    17641832            });
    17651833        }
     1834       
     1835        getAppleShippingMethods() {
     1836            const methods = Array.isArray(this.ppcp_manager.shipping_methods) ? this.ppcp_manager.shipping_methods : [];
     1837            if (!methods.length) {
     1838                return [];
     1839            }
     1840            this.appleShippingIdMap = {};
     1841            let defaultInternalId = this.appleSelectedShippingId;
     1842            if (!defaultInternalId) {
     1843                const selected = methods.find(m => m.is_selected);
     1844                defaultInternalId = (selected && selected.id) ? selected.id : methods[0].id;
     1845            }
     1846            this.appleSelectedShippingId = defaultInternalId;
     1847
     1848            const appleMethods = methods.map(method => {
     1849                const internalId = method.id;
     1850                const safeId = internalId.replace(/[^a-zA-Z0-9 _-]/g, "_");
     1851                this.appleShippingIdMap[safeId] = internalId;
     1852
     1853                return {
     1854                    label: method.label || internalId,
     1855                    amount: this.formatAmount(method.amount),
     1856                    identifier: safeId,
     1857                    detail: method.description || ''
     1858                };
     1859            });
     1860
     1861            // Debug logs - you can remove these later
     1862            console.log('Apple Pay - Shipping methods processed:', appleMethods);
     1863            console.log('Apple Pay - Default selected shipping ID:', defaultInternalId);
     1864            console.log('Apple Pay - Shipping ID map:', this.appleShippingIdMap);
     1865
     1866            return appleMethods;
     1867        }
    17661868
    17671869        onApplePayButtonClicked(container) {
     
    17881890                    }
    17891891                };
     1892
     1893                // ✅ UPDATED: Enhanced shipping method handling
    17901894                if (needsShipping) {
    17911895                    paymentRequest.requiredShippingContactFields = ["postalAddress", "name", "phone", "email"];
     1896                    const methods = this.getAppleShippingMethods();
     1897                    if (methods.length) {
     1898                        paymentRequest.shippingMethods = methods;
     1899
     1900                        // Debug logs for shipping methods
     1901                        console.log('Apple Pay - Available shipping methods:', methods);
     1902                        if (this.appleSelectedShippingId) {
     1903                            console.log('Apple Pay - Default selected shipping ID:', this.appleSelectedShippingId);
     1904                        }
     1905                    }
    17921906                } else {
    17931907                    paymentRequest.requiredShippingContactFields = ["name", "phone", "email"];
    17941908                }
     1909
    17951910                const session = new ApplePaySession(4, paymentRequest);
    17961911                session.onvalidatemerchant = async (event) => {
     
    18091924
    18101925                if (needsShipping) {
    1811                     let newTotal = {
    1812                         label: this.ppcp_manager.store_label || "Store",
    1813                         amount: "0.00",
    1814                         type: "final"
    1815                     };
    18161926                    session.onshippingcontactselected = async (event) => {
    18171927                        try {
    18181928                            if (this.pageContext === 'product') {
    18191929                                const transactionInfo = await this.ppcpGettransactionInfo();
    1820                                 if (transactionInfo.success === false) {
     1930                                if (transactionInfo?.success === false) {
    18211931                                    const messages = transactionInfo.data?.messages ?? transactionInfo.data ?? ['Unknown error'];
    18221932                                    this.showError(messages);
    18231933                                    throw new Error(messages);
    18241934                                }
    1825                                 if (transactionInfo?.success) {
    1826                                     this.ppcp_manager.cart_total = transactionInfo.data?.cart_total || this.ppcp_manager.cart_total;
     1935                                if (transactionInfo?.success && transactionInfo.data && typeof this.setTotalsFromResponse === 'function') {
     1936                                    this.setTotalsFromResponse(transactionInfo.data);
    18271937                                }
    18281938                            }
     1939
    18291940                            const shipping = event.shippingContact;
    18301941                            if (!shipping || !shipping.countryCode || !shipping.postalCode) {
    18311942                                throw new Error("Shipping address is incomplete");
    18321943                            }
     1944
    18331945                            const updatedTotal = await this.fetchUpdatedTotalFromBackend({
    1834                                 city: shipping.locality || '',
    1835                                 state: shipping.administrativeArea || '',
     1946                                city:    shipping.locality || '',
     1947                                state:   shipping.administrativeArea || '',
    18361948                                postcode: shipping.postalCode || '',
    18371949                                country: shipping.countryCode || ''
    18381950                            });
    1839                             newTotal.amount = updatedTotal;
     1951
     1952                            const methods = this.getAppleShippingMethods();
    18401953                            const update = {
    1841                                 newTotal: newTotal
     1954                                newTotal: {
     1955                                    label: this.ppcp_manager.store_label || "Store",
     1956                                    amount: updatedTotal,
     1957                                    type: "final"
     1958                                }
    18421959                            };
     1960
     1961                            if (methods.length) {
     1962                                update.newShippingMethods = methods;
     1963                            }
     1964
    18431965                            session.completeShippingContactSelection(update);
    18441966                        } catch (error) {
    1845                             session.completeShippingContactSelection({});
     1967                            console.error('[ApplePay] onshippingcontactselected error:', error);
     1968                            session.completeShippingContactSelection({
     1969                                newTotal: {
     1970                                    label: this.ppcp_manager.store_label || "Store",
     1971                                    amount: this.formatAmount(this.ppcp_manager.cart_total),
     1972                                    type: "final"
     1973                                },
     1974                                newShippingMethods: this.getAppleShippingMethods()
     1975                            });
     1976                        }
     1977                    };
     1978
     1979                    session.onshippingmethodselected = async (event) => {
     1980                        try {
     1981                            const method = event.shippingMethod;
     1982                            if (!method || !method.identifier) {
     1983                                throw new Error('Invalid shipping method.');
     1984                            }
     1985
     1986                            // Map Apple safe ID back to Woo internal ID
     1987                            const internalId = this.appleShippingIdMap[method.identifier] || method.identifier;
     1988                            this.appleSelectedShippingId = internalId;
     1989
     1990                            const updatedTotal = await this.fetchUpdatedTotalFromBackend(
     1991                                {}, // shipping address unchanged
     1992                                null, // billing not needed here
     1993                                internalId
     1994                            );
     1995
     1996                            const update = {
     1997                                newTotal: {
     1998                                    label: this.ppcp_manager.store_label || "Store",
     1999                                    amount: updatedTotal,
     2000                                    type: "final"
     2001                                }
     2002                            };
     2003
     2004                            session.completeShippingMethodSelection(update);
     2005                        } catch (error) {
     2006                            console.error('[ApplePay] onshippingmethodselected error:', error);
     2007                            session.completeShippingMethodSelection({
     2008                                newTotal: {
     2009                                    label: this.ppcp_manager.store_label || "Store",
     2010                                    amount: this.formatAmount(this.ppcp_manager.cart_total),
     2011                                    type: "final"
     2012                                }
     2013                            });
    18462014                        }
    18472015                    };
     
    18612029                            }
    18622030                        }
     2031
    18632032                        const billingRaw = event.payment?.billingContact || {};
    18642033                        const shippingRaw = event.payment?.shippingContact || {};
     2034
    18652035                        if (this.pageContext !== 'checkout') {
    18662036                            const emailAddress = billingRaw.emailAddress || shippingRaw.emailAddress || billingRaw.email || shippingRaw.email || '';
     
    18882058                                phoneNumber: shippingRaw.phoneNumber || ''
    18892059                            };
    1890                             await this.fetchUpdatedTotalFromBackend(shippingAddress, billingAddress);
     2060
     2061                            await this.fetchUpdatedTotalFromBackend(
     2062                                shippingAddress,
     2063                                billingAddress,
     2064                                this.appleSelectedShippingId || ''
     2065                            );
    18912066                        }
     2067
    18922068                        const orderId = await this.googleapplecreateOrder();
    18932069                        if (!orderId) {
    18942070                            throw new Error("Order creation failed.");
    18952071                        }
     2072
    18962073                        const result = await applepay.confirmOrder({
    18972074                            orderId: orderId,
     
    18992076                            billingContact: event.payment.billingContact
    19002077                        });
     2078
    19012079                        const status = result?.approveApplePayPayment?.status;
    19022080                        if (status === "APPROVED") {
    19032081                            this.showSpinner();
    19042082                            const order_id = orderId;
    1905                             const payer_id = '';
    1906                             if (!order_id) {
    1907                                 console.error('[ApplePay] Missing order ID after approval.');
    1908                                 return;
    1909                             }
     2083
    19102084                            if (this.isCheckoutPage()) {
    19112085                                const url = `${this.ppcp_manager.cc_capture}&paypal_order_id=${encodeURIComponent(order_id)}&woocommerce-process-checkout-nonce=${this.ppcp_manager.woocommerce_process_checkout}`;
     
    19242098                                            this.showError(messages);
    19252099                                            this.hideSpinner();
    1926                                             return null;
    19272100                                        }
    19282101                                    }
    19292102                                });
    1930                                 return;
     2103                            } else {
     2104                                session.completePayment({
     2105                                    status: ApplePaySession.STATUS_SUCCESS
     2106                                });
     2107                                let redirectUrl = `${this.ppcp_manager.checkout_url}?paypal_order_id=${encodeURIComponent(order_id)}&from=${this.ppcp_manager.page}`;
     2108                                window.location.href = redirectUrl;
     2109                                this.hideSpinner();
    19312110                            }
    1932                             session.completePayment({
    1933                                 status: ApplePaySession.STATUS_SUCCESS
    1934                             });
    1935                             let redirectUrl = `${this.ppcp_manager.checkout_url}?paypal_order_id=${encodeURIComponent(order_id)}&from=${this.ppcp_manager.page}`;
    1936                             if (payer_id) {
    1937                                 redirectUrl += `&paypal_payer_id=${encodeURIComponent(payer_id)}`;
    1938                             }
    1939                             window.location.href = redirectUrl;
    1940                             this.hideSpinner();
    19412111                        } else {
    19422112                            throw new Error("Apple Pay confirmation returned non-APPROVED status.");
     
    19632133        }
    19642134    }
    1965 
    19662135    $(function () {
    19672136        $('.woocommerce #payment #place_order, .woocommerce-page #payment #place_order').hide();
  • woo-paypal-gateway/trunk/readme.txt

    r3396892 r3401525  
    44Requires at least: 3.3 
    55Tested up to: 6.8.3
    6 Stable tag: 9.0.50 
     6Stable tag: 9.0.51 
    77Requires PHP: 7.4 
    88License: GPLv3 
     
    102102== Changelog ==
    103103
     104= 9.0.51 - 2025-11-24 =
     105* Added - Support for selecting shipping methods directly within PayPal, Google Pay, and Apple Pay pop-ups.
     106
    104107= 9.0.50 - 2025-11-17 =
    105108* Added – Compatibility with YayCurrency Multi-Currency Switcher plugin for Express Checkout.
  • woo-paypal-gateway/trunk/woo-paypal-gateway.php

    r3396892 r3401525  
    66 * Plugin URI:        https://profiles.wordpress.org/easypayment
    77 * Description:       PayPal, Credit/Debit Cards, Google Pay, Apple Pay, Pay Later, Venmo, SEPA, iDEAL, Mercado Pago, Sofort, Bancontact & more - by an official PayPal Partner
    8  * Version:           9.0.50
     8 * Version:           9.0.51
    99 * Author:            easypayment
    1010 * Author URI:        https://profiles.wordpress.org/easypayment/
     
    2626
    2727if (!defined('WPG_PLUGIN_VERSION')) {
    28     define('WPG_PLUGIN_VERSION', '9.0.50');
     28    define('WPG_PLUGIN_VERSION', '9.0.51');
    2929}
    3030if (!defined('WPG_PLUGIN_PATH')) {
Note: See TracChangeset for help on using the changeset viewer.