Plugin Directory

Changeset 3414265


Ignore:
Timestamp:
12/08/2025 12:20:21 PM (4 months ago)
Author:
SplitIt
Message:

release version 6.1.0

Location:
splitit-installment-payments
Files:
610 added
7 edited

Legend:

Unmodified
Added
Removed
  • splitit-installment-payments/trunk/CHANGELOG.md

    r3357903 r3414265  
    33All notable changes to this project will be documented in this file
    44-
     5
     6### 6.1.0
     7* The authorization process on the plugin settings page has been optimized
     8* Improved behavior of On-Site Messaging display based on the total amount range in the plugin settings
     9* Code improvements and bug fixes
    510
    611### 6.0.0
  • splitit-installment-payments/trunk/assets/js/splitit-flex-form.js

    r3357903 r3414265  
    5959
    6060        if ( $block.length ) {
    61             $('html, body').animate(
     61            $( 'html, body' ).animate(
    6262                {
    6363                    scrollTop: ( $block.offset().top - 100 )
     
    6666            );
    6767        } else {
    68             console.warn('scrollTopToBlock: Element not found', block);
    69         }
     68            console.warn( 'scrollTopToBlock: Element not found', block );
     69        }
     70    }
     71
     72    function getInputValue(name, selectorType = 'input') {
     73        return $( `${selectorType}[name = "${name}"]` ).val() || '';
     74    }
     75
     76    window.collectSplititBillingPayload = function () {
     77        const firstName = getInputValue( 'billing_first_name' );
     78        const lastName  = getInputValue( 'billing_last_name' );
     79
     80        return {
     81            billingAddress: {
     82                AddressLine:  getInputValue( 'billing_address_1' ),
     83                AddressLine2: getInputValue( 'billing_address_2' ),
     84                City:         getBillingAddressValue( 'billing_city' ) || '',
     85                State:        getInputValue( 'billing_state', 'select' ),
     86                Country:      getInputValue( 'billing_country', 'select' ),
     87                Zip:          getBillingAddressValue( 'billing_postcode' ) || ''
     88            },
     89            consumerData: {
     90                FullName: `${firstName} ${lastName}`.trim(),
     91                Email:    getInputValue( 'billing_email' ),
     92                PhoneNumber: getInputValue( 'billing_phone' ),
     93                CultureName: culture
     94            }
     95        };
     96    };
     97
     98    function debounce(fn, wait) {
     99        let t;
     100        return function () {
     101            const args = arguments;
     102            clearTimeout( t );
     103            t = setTimeout( () => fn.apply( this, args ), wait );
     104        };
    70105    }
    71106
    72107    window.getBillingAddressValue = function (code) {
    73         // In some cases city or state is optional, but it required in Splitit.
     108        // In some cases some field is optional, but it required in Splitit.
    74109        let value = $( '[name="' + code + '"]' ).val();
    75110        if ( ! value) {
     
    125160        }
    126161
     162        const firstName = getInputValue( 'billing_first_name' );
     163        const lastName  = getInputValue( 'billing_last_name' );
     164
    127165        flexFieldsInstance.updateDetails(
    128166            {
    129167                billingAddress: {
    130                     addressLine: $( 'input[name="billing_address_1"]' ).val(),
    131                     addressLine2: $( 'input[name="billing_address_2"]' ).val(),
    132                     city: getBillingAddressValue( 'billing_city' ),
    133                     state: getBillingAddressValue( 'billing_state' ),
    134                     country: $( '[name="billing_country"]' ).val(),
    135                     zip: getBillingAddressValue( 'billing_postcode' )
     168                    addressLine: getInputValue( 'billing_address_1' ),
     169                    addressLine2: getInputValue( 'billing_address_2' ),
     170                    city: getBillingAddressValue( 'billing_city' ) || '',
     171                    state: getInputValue( 'billing_state', 'select' ),
     172                    country: getInputValue( 'billing_country', 'select' ),
     173                    zip: getBillingAddressValue( 'billing_postcode' ) || ''
    136174                },
    137175                consumerData: {
    138                     fullName: $( 'input[name="billing_first_name"]' ).val() + ' ' + $( 'input[name="billing_last_name"]' ).val(),
    139                     email: $( 'input[name="billing_email"]' ).val(),
    140                     phoneNumber: $( 'input[name="billing_phone"]' ).val(),
     176                    fullName: `${firstName} ${lastName}`.trim(),
     177                    email: getInputValue( 'billing_email' ),
     178                    phoneNumber: getInputValue( 'billing_phone' ),
    141179                    cultureName: culture
    142180                }
     
    191229                            }
    192230
    193                             if (window.location.href.includes( "hobfurniture" ) || window.location.href.includes( "bioflexwave" )) {
     231                            if (window.location.href.includes( "hobfurniture" ) || window.location.href.includes( "bioflexwave" ) || window.location.href.includes( "camelliarts" )) {
    194232                                flexFieldsInstance.pay();
    195233                            } else {
     
    279317    );
    280318
     319    const handleBillingChange = debounce(
     320        function () {
     321            try {
     322                const ipn = localStorage.getItem( 'ipn' ) ?? false;
     323                if (isSplititPaymentSelected() && typeof flexFieldsInstance !== 'undefined' && ipn) {
     324                    updateFlexFieldsTotal( ipn );
     325                } else if (isSplititPaymentSelected() && ! localStorage.getItem( 'ipn' )) {
     326                    firstInitFlexFieldsInstance();
     327                }
     328            } catch (e) {
     329                console.warn( 'splitit billing change handler error', e );
     330            }
     331        },
     332        600
     333    );
     334
     335    $( document ).on( 'input change', 'input[name^="billing_"], select[name^="billing_"]', handleBillingChange );
     336
    281337    $( 'form[name="checkout"]' ).on(
    282338        'checkout_place_order' ,
     
    385441
    386442    function firstInitFlexFieldsInstance() {
     443        const billingPayload = collectSplititBillingPayload();
     444
    387445        $.ajax(
    388446            {
     
    394452                    order_id: order_id,
    395453                    numberOfInstallments: '',
    396                     currency: getCurrencyCode()
     454                    currency: getCurrencyCode(),
     455                    billing: JSON.stringify( billingPayload )
    397456                },
    398457                success: function ( data ) {
     
    490549    function updateFlexFieldsTotal( planNumber ) {
    491550        if ( undefined !== flexFieldsInstance ) {
     551
     552            const billingPayload = collectSplititBillingPayload();
     553
    492554            $.ajax(
    493555                {
     
    499561                        'numberOfInstallments': '',
    500562                        'function': 'updateFlexFieldsTotal',
    501                         'currency': getCurrencyCode()
     563                        'currency': getCurrencyCode(),
     564                        'billing': JSON.stringify( billingPayload )
    502565                    },
    503566                    method: "POST",
  • splitit-installment-payments/trunk/assets/js/splitit-payment.js

    r3278936 r3414265  
    22    (function ($) {
    33        "use strict";
     4
     5        if (typeof wp !== 'undefined' && wp.data) {
     6            const { subscribe, select } = wp.data;
     7
     8            subscribe(() => {
     9                try {
     10                    const cartStore = select('wc/store/cart');
     11
     12                    if (cartStore && cartStore.getCustomerData) {
     13                        const currentAddressData = cartStore.getCustomerData();
     14                        if (currentAddressData) {
     15                            window.splititCurrentAddressData = currentAddressData;
     16                        }
     17                    }
     18                } catch (e) {
     19                    console.warn("splitit subscribe error", e);
     20                }
     21            });
     22        }
    423
    524        let billingData  = props.billing.billingAddress;
     
    5271            function () {
    5372                let ipn = localStorage.getItem( 'ipn' ) ?? false;
    54                 if ( isSplititPaymentSelectedBlocks() && flexFieldsInstance && ipn) {
    55                     updateFlexFieldsTotalBlocks( ipn );
     73
     74                if ( isSplititPaymentSelectedBlocks() ) {
     75                    if ( 'undefined' === typeof flexFieldsInstance ) {
     76                        cleanupWoocommerceErrorMessageBlocks();
     77                        $( '#custom_splitit_error' ).remove();
     78                        firstInitFlexFieldsInstanceBlocks();
     79                    } else if ( ipn ) {
     80                        updateFlexFieldsTotalBlocks( ipn );
     81                    }
    5682                }
    5783            }
     
    106132                console.warn('scrollTopToBlock: Element not found', block);
    107133            }
     134        }
     135
     136        function collectSplititBillingPayloadBlocks() {
     137            const billingAddressCurrentData = (window.splititCurrentAddressData && window.splititCurrentAddressData.billingAddress)
     138                ? window.splititCurrentAddressData.billingAddress
     139                : props.billing.billingAddress;
     140
     141            return {
     142                billingAddress: {
     143                    AddressLine: billingAddressCurrentData.address_1,
     144                    AddressLine2: billingAddressCurrentData.address_2,
     145                    City: getBillingAddressValueBlocks('city', billingAddressCurrentData),
     146                    State: billingAddressCurrentData.state,
     147                    Country: billingAddressCurrentData.country,
     148                    Zip: getBillingAddressValueBlocks('postcode', billingAddressCurrentData)
     149                },
     150                consumerData: {
     151                    FullName: billingAddressCurrentData.first_name + ' ' + billingAddressCurrentData.last_name,
     152                    Email: billingAddressCurrentData.email,
     153                    PhoneNumber: billingAddressCurrentData.phone,
     154                    CultureName: culture
     155                }
     156            };
    108157        }
    109158
     
    174223                        addressLine2: billingDataUpdated.address_2,
    175224                        city: getBillingAddressValueBlocks( 'city', billingDataUpdated ),
    176                         state: getBillingAddressValueBlocks( 'state', billingDataUpdated ),
     225                        state: billingDataUpdated.state,
    177226                        country: billingDataUpdated.country,
    178227                        zip: getBillingAddressValueBlocks( 'postcode', billingDataUpdated )
     
    234283                                return false;
    235284                            } else {
    236                                 if (window.location.href.includes( "hobfurniture" ) || window.location.href.includes( "bioflexwave" )) {
     285                                if (window.location.href.includes( "hobfurniture" ) || window.location.href.includes( "bioflexwave" ) || window.location.href.includes( "camelliarts" )) {
    237286                                    flexFieldsInstance.pay();
    238287                                } else {
     
    328377
    329378        window.firstInitFlexFieldsInstanceBlocks = function() {
     379            const billingPayload = collectSplititBillingPayloadBlocks();
     380
    330381            $.ajax(
    331382                {
     
    337388                        order_id: orderId,
    338389                        numberOfInstallments: '',
    339                         currency: getCurrencyCodeBlocks()
     390                        currency: getCurrencyCodeBlocks(),
     391                        billing: JSON.stringify(billingPayload)
    340392                    },
    341393                    success: function ( data ) {
     
    424476        window.updateFlexFieldsTotalBlocks = function( planNumber ) {
    425477            if ( undefined !== flexFieldsInstance ) {
     478                const billingPayload = collectSplititBillingPayloadBlocks();
     479
    426480                $.ajax(
    427481                    {
     
    433487                            'numberOfInstallments': '',
    434488                            'function': 'updateFlexFieldsTotal',
    435                             'currency': getCurrencyCodeBlocks()
     489                            'currency': getCurrencyCodeBlocks(),
     490                            'billing': JSON.stringify(billingPayload)
    436491                        },
    437492                        method: "POST",
  • splitit-installment-payments/trunk/changelog.txt

    r3357903 r3414265  
    11*** Splitit Changelog ***
     2
     32025-12-08 - version 6.1.0
     4* The authorization process on the plugin settings page has been optimized
     5* Improved behavior of On-Site Messaging display based on the total amount range in the plugin settings
     6* Code improvements and bug fixes
    27
    382025-09-08 - version 6.0.0
  • splitit-installment-payments/trunk/classes/traits/splitit-flexfields-payment-plugin-upstream-messaging-trait.php

    r3357903 r3414265  
    253253
    254254                                        let installment = getInstallmentByPrice(price, umOptions, settings);
     255
     256                                        if (!installment) {
     257                                            return false;
     258                                        }
     259
    255260                                        let um = generateUM(umType, umOptions, price, installment);
    256261
     
    291296
    292297                                        let installment = getInstallmentByPrice(price, umOptions, settings);
     298
     299                                        if (!installment) {
     300                                            return false;
     301                                        }
     302
    293303                                        let um = generateUM(umType, umOptions, price, installment);
    294304
     
    319329                        if (isNaN(price)) return false;
    320330
    321                         let customInstallments = checkCustomInstallmentsSettings( pageConfig, settings );
    322                         if (customInstallments !== null) {
    323                             return customInstallments;
    324                         }
     331                        let foundInRange = false;
     332                        let matchedInstallments = null;
    325333
    326334                        for (let i = 0; i < installmentsSettings.ic_from.length; i++) {
     
    332340                                    .split(',')
    333341                                    .map(Number);
    334                                 return Math.max(...installments);
     342                                matchedInstallments = Math.max(...installments);
     343                                foundInRange = true;
     344                                break;
    335345                            }
    336346                        }
    337                         return 4; // Default value
     347
     348                        if (foundInRange) {
     349                            let customInstallments = checkCustomInstallmentsSettings(pageConfig, settings);
     350                            if (customInstallments !== null) {
     351                                return customInstallments;
     352                            }
     353                            return matchedInstallments;
     354                        }
     355
     356                        return null;
    338357                    }
    339358
    340359                    function checkCustomInstallmentsSettings(pageConfig, splititSettings) {
    341                         let umType = '';
    342 
    343                         if (pageConfig.strip && pageConfig.strip.enable_strip == 1) {
    344                             umType = 'strip';
    345                         } else if (pageConfig.banner && pageConfig.banner.enable_banner == 1) {
    346                             umType = 'banner';
    347                         } else if (pageConfig.logo && pageConfig.logo.enable_logo == 1) {
    348                             umType = 'logo';
    349                         } else if (pageConfig.one_liner && pageConfig.one_liner.enable_one_liner == 1) {
    350                             umType = 'one_liner';
    351                         }
    352 
    353                         let customInstallments = pageConfig[umType] && pageConfig[umType].installments &&
    354                         pageConfig[umType].installments.trim() !== ''
    355                             ? parseInt( pageConfig[umType].installments, 10 )
     360
     361                        let customInstallments = pageConfig && pageConfig.installments &&
     362                        pageConfig.installments.trim() !== ''
     363                            ? parseInt( pageConfig.installments, 10 )
    356364                            : null;
    357365
     
    378386
    379387                        Object.entries(options).forEach(function ([key, val]) {
    380                             if ( '' !== key && 'regular' !== key && 'sale' !== key ) {
     388                            if ( '' !== key && 'regular' !== key && 'sale' !== key && 'installments' !== key ) {
    381389                                if (( 'hide_learn_more' === key || 'hide_icon' === key ) && '1' == val ) {
    382390                                    val = true;
     
    11161124            $current_order_total_in_range = $this->check_if_sum_in_range( $this->get_current_order_total() );
    11171125            $is_allowed_um                = $this->is_allowed_um_per_products_for_card_and_checkout_pages();
    1118             $hide_upstream_message        = empty( $installments ) || ! $total_in_range || ! $current_order_total_in_range || ! $is_allowed_um ? ' style="display:none"' : '';
     1126            $hide_upstream_message        = empty( $installments ) || ! $current_order_total_in_range || ! $is_allowed_um ? ' style="display:none"' : '';
    11191127
    11201128            if ( isset( $installments ) ) {
  • splitit-installment-payments/trunk/readme.txt

    r3357903 r3414265  
    66Requires PHP: 7.0
    77WC requires at least: 6.0
    8 WC tested up to: 10.1.1
    9 Stable tag: 6.0.0
     8WC tested up to: 10.3.6
     9Stable tag: 6.1.0
    1010License: GPLv3
    1111License URI: http://www.gnu.org/licenses/gpl-3.0.html
     
    8080== Changelog ==
    8181
     82= 6.1.0 - 2025-12-08
     83The authorization process on the plugin settings page has been optimized
     84Improved behavior of On-Site Messaging display based on the total amount range in the plugin settings
     85Code improvements and bug fixes
     86
    8287= 6.0.0 - 2025-09-08
    83 * Revert version with new SDK
     88Revert version with new SDK
    8489
    8590= 5.0.0 - 2025-09-03 =
  • splitit-installment-payments/trunk/splitIt-flexfields-payment-gateway.php

    r3357903 r3414265  
    1111 * Author URI: https://www.splitit.com/
    1212 * License: GPLv3
    13  * Version: 6.0.0
     13 * Version: 6.1.0
    1414 * Requires Plugins: woocommerce
    1515 * Requires at least: 5.6
    1616 * Tested up to: 6.8
    1717 * WC requires at least: 6.0
    18  * WC tested up to: 10.1.1
     18 * WC tested up to: 10.3.6
    1919 * Requires PHP: 7.0
    2020 */
     
    3131
    3232global $plugin_version;
    33 $plugin_version = '6.0.0';
     33$plugin_version = '6.1.0';
    3434
    3535global $required_splitit_php_version;
     
    838838            $callback_uri  = get_site_url() . '/splitit-auth/callback';
    839839
    840             if ( ! session_id() ) {
    841                 session_start(); }
    842             $verifier = isset( $_SESSION['code_verifier'] ) ? sanitize_text_field( $_SESSION['code_verifier'] ) : '';
     840            $verifier = get_transient( 'splitit_code_verifier_' . get_current_user_id() );
    843841
    844842            if ( '' === $verifier ) {
    845                 echo '<div style="width: 500px; margin: 15px auto; text-align: center; font-size: 16px; background-color: bisque; padding: 10px; border-radius: 10px;">An issue with session access has been detected.</br>Please ensure that your server settings allow session functionality</br>and that no additional site settings block session operation.</div>';
     843                SplitIt_FlexFields_Payment_Plugin_Log::save_log_info(
     844                    array(
     845                        'user_id' => null,
     846                        'method'  => __( 'get_access_token() Splitit', 'splitit-installment-payments' ),
     847                    ),
     848                    'get_access_token() - Error: splitit_code_verifier is empty',
     849                    'error'
     850                );
     851                echo '<div style="width: 500px; margin: 15px auto; text-align: center; font-size: 16px; background-color: bisque; padding: 10px; border-radius: 10px;">Something happened while trying to log in. </br>Please try again later or contact Splitit support.</div>';
    846852            }
    847853
     
    888894            $id_token = $response_data->id_token ?? null;
    889895            if ( $id_token ) {
    890                 if ( ! session_id() ) {
    891                     session_start();
    892                 }
    893                 $_SESSION['id_token'] = $id_token;
     896                set_transient( 'splitit_id_token_' . get_current_user_id(), $id_token, 10 * MINUTE_IN_SECONDS );
    894897            }
    895898
     
    11041107         * Method that generating drop-down with list of merchants
    11051108         *
    1106          * @param $list
     1109         * @param $merchants_list
    11071110         * @param $user_data
    11081111         * @param $env
    1109          * @param null      $url
    1110          * @param null      $token
    1111          */
    1112         public function generate_merchants_list_dropdown( $list, $user_data, $env, $url = null, $token = null ) {
    1113             if ( 'production' === $env ) {
    1114                 $merchants_list = json_decode( $list, true )['MerchantList'];
    1115 
     1112         * @param false          $limited_search_enable
     1113         * @param null           $url
     1114         * @param null           $token
     1115         */
     1116        public function generate_merchants_list_dropdown( $merchants_list, $user_data, $env, $limited_search_enable = false, $url = null, $token = null ) {
     1117            if ( 'production' === $env || ! $limited_search_enable ) {
    11161118                usort(
    11171119                    $merchants_list,
     
    11241126                );
    11251127
    1126                 if ( ! session_id() ) {
    1127                     session_start(); }
    1128                 $_SESSION['merchants_list'] = $merchants_list;
     1128                set_transient( 'splitit_merchants_list_' . get_current_user_id(), $merchants_list, 10 * MINUTE_IN_SECONDS );
    11291129            }
    11301130
     
    11651165                                        <option value="" disabled selected>Merchant Account</option>
    11661166                                        <?php
    1167                                         if ( 'production' === $env ) {
     1167                                        if ( 'production' === $env || ! $limited_search_enable ) {
    11681168                                            foreach ( $merchants_list as $item ) {
    11691169                                                echo '<option value="' . esc_attr( $item['Id'] ) . '">' . esc_html( $item['Code'] ) . '</option>';
     
    12141214                        jQuery(function ($) {
    12151215                            let env = '<?php echo esc_html( $env ); ?>';
    1216 
    1217                             if ( 'sandbox' === env ) {
     1216                            let limitedSearchEnable = '<?php echo esc_html( $limited_search_enable ); ?>';
     1217
     1218                            if ( 'sandbox' === env && limitedSearchEnable ) {
    12181219                                $('#merchants_list_dropdown').select2({
    12191220                                    placeholder: 'Enter the merchant name',
     
    12611262                            })
    12621263
    1263                             const id_token = '<?php echo esc_js( sanitize_text_field( $_SESSION['id_token'] ?? '' ) ); ?>';
     1264                            const id_token = '<?php echo esc_js( sanitize_text_field( get_transient( 'splitit_id_token_' . get_current_user_id() ) ?? '' ) ); ?>';
    12641265                            const environment   = localStorage.getItem( 'environment' );
    12651266
     
    15421543         */
    15431544        public function generate_terminals_list_dropdown( $terminals_list, $credentials_list, $merchant_id, $user_data, $env ) {
    1544             if ( ! session_id() ) {
    1545                 session_start(); }
    1546             $_SESSION['terminals_list']   = $terminals_list;
    1547             $_SESSION['credentials_list'] = $credentials_list;
    1548             $merchants_list               = isset( $_SESSION['merchants_list'] ) ? map_deep( $_SESSION['merchants_list'], 'sanitize_text_field' ) : array();
     1545            set_transient( 'splitit_terminals_list_' . get_current_user_id(), $terminals_list, 10 * MINUTE_IN_SECONDS );
     1546            set_transient( 'splitit_credentials_list_' . get_current_user_id(), $credentials_list, 10 * MINUTE_IN_SECONDS );
     1547
     1548            $merchants_list = get_transient( 'splitit_merchants_list_' . get_current_user_id() ) ? map_deep( get_transient( 'splitit_merchants_list_' . get_current_user_id() ), 'sanitize_text_field' ) : array();
    15491549            ?>
    15501550
     
    17481748                            })
    17491749
    1750                             const id_token = '<?php echo esc_js( sanitize_text_field( $_SESSION['id_token'] ?? '' ) ); ?>';
     1750                            const id_token = '<?php echo esc_js( sanitize_text_field( get_transient( 'splitit_id_token_' . get_current_user_id() ) ?? '' ) ); ?>';
    17511751                            const environment   = localStorage.getItem( 'environment' );
    17521752
     
    18281828                        $access_token = $this->get_access_token( $data['code'] );
    18291829                        if ( $access_token ) {
    1830                             if ( ! session_id() ) {
    1831                                 session_start(); }
    1832                             $_SESSION['access_token'] = $access_token;
    1833                             $user_data                = $this->get_user_data( 'https://id.' . $env . '.splitit.com/api/user/profile', $access_token );
     1830                            set_transient( 'splitit_access_token_' . get_current_user_id(), $access_token, 10 * MINUTE_IN_SECONDS );
     1831                            $user_data = $this->get_user_data( 'https://id.' . $env . '.splitit.com/api/user/profile', $access_token );
    18341832
    18351833                            update_option( 'splitit_logged_user_data', $user_data );
    18361834
    1837                             if ( 'sandbox' === $env ) {
    1838                                 $this->generate_merchants_list_dropdown( array(), $user_data, $env, 'https://pluginproxy.' . $env . '.splitit.com/api/v1/merchant/ref-list?forceRefresh=true&Statuses=Live', $access_token );
     1835                            $limit_param           = 'sandbox' === $env ? 'LimitResults=100&' : '';
     1836                            $merchant_ref_list     = $this->get_list( 'https://pluginproxy.' . $env . '.splitit.com/api/v1/merchant/ref-list?' . $limit_param . 'forceRefresh=true&Statuses=Live', $access_token );
     1837                            $merchants_list        = json_decode( $merchant_ref_list, true )['MerchantList'];
     1838                            $limited_search_enable = 100 === count( $merchants_list );
     1839
     1840                            if ( 'sandbox' === $env && $limited_search_enable ) {
     1841                                $this->generate_merchants_list_dropdown( array(), $user_data, $env, $limited_search_enable, 'https://pluginproxy.' . $env . '.splitit.com/api/v1/merchant/ref-list?forceRefresh=true&Statuses=Live', $access_token );
    18391842                            } else {
    1840                                 $merchant_ref_list = $this->get_list( 'https://pluginproxy.' . $env . '.splitit.com/api/v1/merchant/ref-list?forceRefresh=true&Statuses=Live', $access_token );
    1841                                 $this->generate_merchants_list_dropdown( $merchant_ref_list, $user_data, $env );
     1843                                $this->generate_merchants_list_dropdown( $merchants_list, $user_data, $env, $limited_search_enable );
    18421844                            }
    18431845                        }
     
    18621864                        );
    18631865
    1864                         if ( ! session_id() ) {
    1865                             session_start(); }
    1866                         $_SESSION['merchants_list'] = $merchants_list;
    1867 
     1866                        set_transient( 'splitit_merchants_list_' . get_current_user_id(), $merchants_list, 10 * MINUTE_IN_SECONDS );
    18681867                        wp_send_json_success( $merchants_list );
    18691868                    }
     
    18761875                        $merchant_id = isset( $_POST['merchant_id'] ) ? sanitize_text_field( wp_unslash( $_POST['merchant_id'] ) ) : null;
    18771876
    1878                         if ( ! session_id() ) {
    1879                             session_start(); }
    1880                         $access_token      = isset( $_SESSION['access_token'] ) ? sanitize_text_field( $_SESSION['access_token'] ) : null;
     1877                        $access_token = get_transient( 'splitit_access_token_' . get_current_user_id() ) ? sanitize_text_field( get_transient( 'splitit_access_token_' . get_current_user_id() ) ) : null;
     1878
    18811879                        $user_data         = get_option( 'splitit_logged_user_data' );
    18821880                        $merchant_settings = $this->get_merchant_settings( 'https://pluginproxy.' . $env . '.splitit.com/api/v1/merchant/extended-info', $access_token, $merchant_id );
     
    19001898                        $client_id   = isset( $_POST['client_id'] ) ? sanitize_text_field( wp_unslash( $_POST['client_id'] ) ) : null;
    19011899
    1902                         if ( ! session_id() ) {
    1903                             session_start(); }
    1904                         $merchants_list = isset( $_SESSION['merchants_list'] ) ? map_deep( $_SESSION['merchants_list'], 'sanitize_text_field' ) : array();
    1905                         $terminals_list = isset( $_SESSION['terminals_list'] ) ? map_deep( $_SESSION['terminals_list'], 'sanitize_text_field' ) : array();
    1906                         $access_token   = isset( $_SESSION['access_token'] ) ? sanitize_text_field( $_SESSION['access_token'] ) : null;
     1900                        $merchants_list = get_transient( 'splitit_merchants_list_' . get_current_user_id() ) ? map_deep( get_transient( 'splitit_merchants_list_' . get_current_user_id() ), 'sanitize_text_field' ) : array();
     1901                        $terminals_list = get_transient( 'splitit_terminals_list_' . get_current_user_id() ) ? map_deep( get_transient( 'splitit_terminals_list_' . get_current_user_id() ), 'sanitize_text_field' ) : array();
     1902                        $access_token   = get_transient( 'splitit_access_token_' . get_current_user_id() ) ? sanitize_text_field( get_transient( 'splitit_access_token_' . get_current_user_id() ) ) : null;
    19071903
    19081904                        $selected_merchant = null;
     
    20042000                $code_verifier = isset( $_POST['code_verifier'] ) ? sanitize_text_field( wp_unslash( $_POST['code_verifier'] ) ) : null;
    20052001                if ( $code_verifier ) {
    2006                     if ( ! session_id() ) {
    2007                         session_start(); }
    2008                     $_SESSION['code_verifier'] = $code_verifier;
     2002                    set_transient( 'splitit_code_verifier_' . get_current_user_id(), $code_verifier, 10 * MINUTE_IN_SECONDS );
    20092003                }
    20102004            }
     
    21542148            if ( $order && $order->get_id() ) {
    21552149                $order_info = SplitIt_FlexFields_Payment_Plugin_Log::get_splitit_info_by_order_id( $order->get_id() );
    2156             }
    2157             if ( isset( $order_info ) && ! empty( $order_info ) ) {
    2158 
    2159                 $env                = get_option( 'splitit_environment' ) ? get_option( 'splitit_environment' ) : $this->settings['splitit_environment'];
    2160                 $splitit_order_info = $this->get_splitit_order_info( 'https://web-api-v3.' . $env . '.splitit.com/api/installmentplans/' . $order_info->installment_plan_number . '/legal' );
    2161 
    2162                 $decoded_info = json_decode( $splitit_order_info );
    2163 
    2164                 $terms_conditions = '';
    2165                 $privacy_policy   = '';
    2166                 $provider         = 'SPLITIT';
    2167 
    2168                 if ( $decoded_info && json_last_error() === JSON_ERROR_NONE ) {
    2169                     if ( isset( $decoded_info->TermsAndConditions ) ) {
    2170                         $terms_conditions = $decoded_info->TermsAndConditions;
    2171                     }
    2172                     if ( isset( $decoded_info->PrivacyPolicy ) ) {
    2173                         $privacy_policy = $decoded_info->PrivacyPolicy;
    2174                     }
    2175                     if ( isset( $decoded_info->Provider ) ) {
    2176                         $provider = $decoded_info->Provider;
    2177                     }
    2178                 } else {
    2179                     $log_data = array(
    2180                         'user_id' => null,
    2181                         'method'  => __( 'splitit_add_installment_plan_number_data_thank_you_title() Splitit', 'splitit-installment-payments' ),
    2182                     );
    2183                     SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $log_data, 'splitit_add_installment_plan_number_data_thank_you_title() - error: ' . json_last_error(), 'error' );
    2184                 }
    2185 
    2186                 if ( 'VIS' === $provider ) {
    2187 
    2188                     $api      = new SplitIt_FlexFields_Payment_Plugin_API( $this->settings );
    2189                     $ipn_info = $api->get_ipn_info( $order_info->installment_plan_number );
    2190 
    2191                     $sub_total   = $order->get_subtotal();
    2192                     $total_tax   = $order->get_total_tax();
    2193                     $total_price = $order->get_total();
    2194 
    2195                     $currency_code   = $ipn_info->getCurrency();
    2196                     $currency_symbol = get_woocommerce_currency_symbol( $currency_code );
    2197 
    2198                     $payment_method = $ipn_info->getPaymentMethod();
    2199                     $card           = $payment_method->getCard();
    2200                     $card_number    = $card->getCardNumber();
    2201                     $card_brand     = $card->getCardBrand();
    2202 
    2203                     $links = $ipn_info->getLinks();
    2204 
    2205                     $number_of_installments = count( $ipn_info->getInstallments() );
    2206 
    2207                     $monthly_payment = round( $total_price / $number_of_installments, 2 );
    2208 
    2209                     $thank_you_title = '
    2210                     <table class="woocommerce-table woocommerce-table--order-details shop_table order_details">
    2211                         <thead>
    2212                             <tr class="woocommerce-table__line-item order_item">
    2213 
    2214                                 <th class="woocommerce-table__product-name product-name">
    2215                                     Purchase Amount:
    2216                                 </th>
    2217 
    2218                                 <td class="woocommerce-table__product-total product-total">
    2219                                     <strong><span class="woocommerce-Price-amount amount"><bdi>' . $sub_total . '&nbsp;<span class="woocommerce-Price-currencySymbol">' . $currency_symbol . '</span></bdi></span></strong>
    2220                                 </td>
    2221                            
    2222                             </tr>
    2223                            
    2224                             <tr class="woocommerce-table__line-item order_item">
    2225 
    2226                                 <th class="woocommerce-table__product-name product-name">
    2227                                     Payment:
    2228                                 </th>
    2229 
    2230                                 <td class="woocommerce-table__product-total product-total">
    2231                                     <div style="display: flex">
    2232                                         <div title="visa" style="margin-top: -12px; min-width: 25px; width: 4%; background: url(\'https://cdn.visa.com/v2/assets/images/logos/visa/blue/logo.png\') no-repeat center center; background-size: contain;">
    2233                                         </div>
    2234                                         <strong style="margin-left: 5px;">
    2235                                             ' . substr( $card_number, -8 ) . '
    2236                                         </strong>
    2237                                     </div>
    2238                                 </td>
    2239                            
    2240                             </tr>
    2241                            
    2242                         </thead>
    2243                        
    2244                         <tbody>
    2245                            
    2246                             <tr class="woocommerce-table__line-item order_item">
    2247 
    2248                                 <th class="woocommerce-table__product-name product-name">
    2249                                     Monthly Payment:
    2250                                 </th>
    2251 
    2252                                 <td class="woocommerce-table__product-total product-total">
    2253                                     <strong><span class="woocommerce-Price-amount amount"><bdi>' . $monthly_payment . '&nbsp;<span class="woocommerce-Price-currencySymbol">' . $currency_symbol . '</span></bdi></span></strong>
    2254                                 </td>
    2255                            
    2256                             </tr>
    2257                            
    2258                             <tr class="woocommerce-table__line-item order_item">
    2259 
    2260                                 <th class="woocommerce-table__product-name product-name">
    2261                                     Number of Installments:
    2262                                 </th>
    2263 
    2264                                 <td class="woocommerce-table__product-total product-total">
    2265                                     <strong>' . $number_of_installments . '</strong>
    2266                                 </td>
    2267                            
    2268                             </tr>
    2269                            
    2270                             <tr class="woocommerce-table__line-item order_item">
    2271 
    2272                                 <th class="woocommerce-table__product-name product-name">
    2273                                     Total Fees:
    2274                                 </th>
    2275 
    2276                                 <td class="woocommerce-table__product-total product-total">
    2277                                     <strong><span class="woocommerce-Price-amount amount"><bdi>' . $total_tax . '&nbsp;<span class="woocommerce-Price-currencySymbol">' . $currency_symbol . '</span></bdi></span>&nbsp;(APR:0%)</strong>
    2278                                 </td>
    2279                            
    2280                             </tr>
    2281                            
    2282                             <tr class="woocommerce-table__line-item order_item">
    2283 
    2284                                 <th class="woocommerce-table__product-name product-name">
    2285                                     Total Amount:
    2286                                 </th>
    2287 
    2288                                 <td class="woocommerce-table__product-total product-total">
    2289                                     <strong><span class="woocommerce-Price-amount amount"><bdi>' . $total_price . '&nbsp;<span class="woocommerce-Price-currencySymbol">' . $currency_symbol . '</span></bdi></span></strong>
    2290                                 </td>
    2291                            
    2292                             </tr>
    2293                         </tbody>
    2294                        
    2295                         <tfoot>
    2296                             <tr class="woocommerce-table__line-item order_item">
    2297                                 <td colspan="2">
    2298                                     <div style="color: #1434CB;">
    2299                                         <div style="display: flex">
    2300                                             Installments enabled by
    2301                                             <div title="visa" style="margin-left: 5px; margin-top: -12px; min-width: 25px; width: 4%; background: url(\'https://cdn.visa.com/v2/assets/images/logos/visa/blue/logo.png\') no-repeat center center; background-size: contain;">
    2302                                             </div>
    2303                                         </div>
    2304                                     </div>
    2305                                     <br>
    2306                                     <div>
    2307                                         ' . $terms_conditions . '
    2308                                     </div>
    2309                                 </td>
    2310                             </tr>
    2311                         </tfoot>
    2312                     </table>
    2313                 ';
    2314 
    2315                 } else {
    2316 
     2150                if ( ! empty( $order_info ) ) {
    23172151                    $thank_you_title = '<p><strong>' . __( 'Installment plan number', 'splitit-installment-payments' ) . ':</strong> ' . esc_html( $order_info->installment_plan_number ) . '</p> <p><strong>' . __( 'Number of installments', 'splitit-installment-payments' ) . ':</strong> ' . esc_html( $order_info->number_of_installments ) . '</p>';
    2318 
    23192152                }
    23202153            }
     
    24632296
    24642297        function displaying_custom_admin_notice() {
    2465             if ( ! session_id() ) {
    2466                 session_start(); }
    2467             if ( isset( $_SESSION['cancelled_order_message'] ) ) {
     2298            if ( get_transient( 'splitit_cancelled_order_message_' . get_current_user_id() ) ) {
    24682299                ?>
    24692300                <div class='notice notice-error is-dismissible'>
    2470                     <?php $cancelled_order_message = isset( $_SESSION['cancelled_order_message'] ) ? sanitize_text_field( $_SESSION['cancelled_order_message'] ) : ''; ?>
     2301                    <?php $cancelled_order_message = sanitize_text_field( get_transient( 'splitit_cancelled_order_message_' . get_current_user_id() ) ); ?>
    24712302                    <p><?php echo esc_html( $cancelled_order_message ); ?></p>
    24722303                </div>
    24732304                <?php
    2474                 unset( $_SESSION['cancelled_order_message'] );
     2305                delete_transient( 'splitit_cancelled_order_message_' . get_current_user_id() );
    24752306            }
    24762307        }
     
    25032334                                    SplitIt_FlexFields_Payment_Plugin_Settings::update_order_status_to_old( $order );
    25042335
    2505                                     if ( ! session_id() ) {
    2506                                         session_start(); }
    2507                                     $_SESSION['cancelled_order_message'] = $cancel_message;
     2336                                    set_transient( 'splitit_cancelled_order_message_' . get_current_user_id(), $cancel_message, 10 * MINUTE_IN_SECONDS );
    25082337
    25092338                                } else {
     
    58335662                    }
    58345663                }
     5664
     5665                // if billing payload came from client as JSON.
     5666                if ( isset( $_POST['billing'] ) && ! empty( $_POST['billing'] ) ) {
     5667                    $billing_payload_raw = sanitize_text_field( wp_unslash( $_POST['billing'] ) );
     5668                    $billing_payload     = json_decode( $billing_payload_raw, true );
     5669                    if ( is_array( $billing_payload ) ) {
     5670                        $data['clientBillingPayload'] = $billing_payload;
     5671                    }
     5672                }
    58355673            }
    58365674
     
    58565694                    $data['consumerData']['PhoneNumber'] = $order_data['billing']['phone'];
    58575695                    $data['consumerData']['CultureName'] = str_replace( '_', '-', get_locale() );
     5696                }
     5697            }
     5698
     5699            // if client provided billing payload (from checkout fields), use it as priority.
     5700            if ( empty( $data['order_id'] ) && isset( $data['clientBillingPayload'] ) ) {
     5701                $cp = $data['clientBillingPayload'];
     5702
     5703                if ( isset( $cp['billingAddress'] ) ) {
     5704                    $ba                                     = $cp['billingAddress'];
     5705                    $data['billingAddress']['AddressLine']  = isset( $ba['AddressLine'] ) ? sanitize_text_field( $ba['AddressLine'] ) : '';
     5706                    $data['billingAddress']['AddressLine2'] = isset( $ba['AddressLine2'] ) ? sanitize_text_field( $ba['AddressLine2'] ) : '';
     5707                    $data['billingAddress']['City']         = isset( $ba['City'] ) ? sanitize_text_field( $ba['City'] ) : '';
     5708                    $data['billingAddress']['State']        = isset( $ba['State'] ) ? sanitize_text_field( $ba['State'] ) : '';
     5709                    $data['billingAddress']['Country']      = isset( $ba['Country'] ) ? sanitize_text_field( $ba['Country'] ) : '';
     5710                    $data['billingAddress']['Zip']          = isset( $ba['Zip'] ) ? sanitize_text_field( $ba['Zip'] ) : '';
     5711                }
     5712
     5713                if ( isset( $cp['consumerData'] ) ) {
     5714                    $cd                                  = $cp['consumerData'];
     5715                    $data['consumerData']['FullName']    = isset( $cd['FullName'] ) ? sanitize_text_field( $cd['FullName'] ) : '';
     5716                    $data['consumerData']['Email']       = isset( $cd['Email'] ) ? sanitize_email( $cd['Email'] ) : '';
     5717                    $data['consumerData']['PhoneNumber'] = isset( $cd['PhoneNumber'] ) ? sanitize_text_field( $cd['PhoneNumber'] ) : '';
     5718                    $data['consumerData']['CultureName'] = isset( $cd['CultureName'] ) ? sanitize_text_field( $cd['CultureName'] ) : str_replace( '_', '-', get_locale() );
    58585719                }
    58595720            }
     
    59225783        public function checkout_validate() {
    59235784            if ( isset( $_POST ) ) {
    5924                 $_POST     = stripslashes_deep( $_POST );
    5925                 $errors    = array();
     5785                $_POST  = stripslashes_deep( $_POST );
     5786                $errors = array();
     5787
     5788                if ( strpos( DOMAIN, 'alphabiolabsusa' ) !== false ) {
     5789                    // compatibility with reCaptcha for WooCommerce plugin.
     5790                    if ( class_exists( 'I13_Woo_Recpatcha' ) ) {
     5791                        try {
     5792                            $old_request = array(
     5793                                'i13_checkout_token' => isset( $_REQUEST['i13_checkout_token'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['i13_checkout_token'] ) ) : null,
     5794                                'fallback_i13_checkout_token' => isset( $_REQUEST['fallback_i13_checkout_token'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['fallback_i13_checkout_token'] ) ) : null,
     5795                                'woocommerce-process-checkout-nonce' => isset( $_REQUEST['woocommerce-process-checkout-nonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['woocommerce-process-checkout-nonce'] ) ) : null,
     5796                                'payment_method'     => isset( $_REQUEST['payment_method'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['payment_method'] ) ) : null,
     5797                            );
     5798
     5799                            $old_post = array(
     5800                                'i13_checkout_token' => isset( $_POST['i13_checkout_token'] ) ? sanitize_text_field( wp_unslash( $_POST['i13_checkout_token'] ) ) : null,
     5801                                'fallback_i13_checkout_token' => isset( $_POST['fallback_i13_checkout_token'] ) ? sanitize_text_field( wp_unslash( $_POST['fallback_i13_checkout_token'] ) ) : null,
     5802                                'woocommerce-process-checkout-nonce' => isset( $_POST['woocommerce-process-checkout-nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['woocommerce-process-checkout-nonce'] ) ) : null,
     5803                                'payment_method'     => isset( $_POST['payment_method'] ) ? sanitize_text_field( wp_unslash( $_POST['payment_method'] ) ) : null,
     5804                            );
     5805
     5806                            if ( isset( $_POST['fields']['i13_checkout_token'] ) ) {
     5807                                $_REQUEST['i13_checkout_token'] = sanitize_text_field( wp_unslash( $_POST['fields']['i13_checkout_token'] ) );
     5808                                $_POST['i13_checkout_token']    = sanitize_text_field( wp_unslash( $_POST['fields']['i13_checkout_token'] ) );
     5809                            }
     5810                            if ( isset( $_POST['fields']['fallback_i13_checkout_token'] ) ) {
     5811                                $_REQUEST['fallback_i13_checkout_token'] = sanitize_text_field( wp_unslash( $_POST['fields']['fallback_i13_checkout_token'] ) );
     5812                                $_POST['fallback_i13_checkout_token']    = sanitize_text_field( wp_unslash( $_POST['fields']['fallback_i13_checkout_token'] ) );
     5813                            }
     5814                            if ( isset( $_POST['fields']['woocommerce-process-checkout-nonce'] ) ) {
     5815                                $_REQUEST['woocommerce-process-checkout-nonce'] = sanitize_text_field( wp_unslash( $_POST['fields']['woocommerce-process-checkout-nonce'] ) );
     5816                                $_POST['woocommerce-process-checkout-nonce']    = sanitize_text_field( wp_unslash( $_POST['fields']['woocommerce-process-checkout-nonce'] ) );
     5817                            }
     5818                            if ( isset( $_POST['fields']['payment_method'] ) ) {
     5819                                $_REQUEST['payment_method'] = sanitize_text_field( wp_unslash( $_POST['fields']['payment_method'] ) );
     5820                                $_POST['payment_method']    = sanitize_text_field( wp_unslash( $_POST['fields']['payment_method'] ) );
     5821                            }
     5822
     5823                            $recaptcha        = new I13_Woo_Recpatcha();
     5824                            $recaptcha_errors = $recaptcha->i13_woocomm_validate_checkout_captcha( array(), new WP_Error() );
     5825
     5826                            foreach ( $old_request as $key => $value ) {
     5827                                if ( $value === null ) {
     5828                                    unset( $_REQUEST[ $key ] );
     5829                                } else {
     5830                                    $_REQUEST[ $key ] = $value;
     5831                                }
     5832                            }
     5833
     5834                            foreach ( $old_post as $key => $value ) {
     5835                                if ( $value === null ) {
     5836                                    unset( $_POST[ $key ] );
     5837                                } else {
     5838                                    $_POST[ $key ] = $value;
     5839                                }
     5840                            }
     5841
     5842                            if ( is_wp_error( $recaptcha_errors ) && $recaptcha_errors->get_error_code() ) {
     5843                                foreach ( $recaptcha_errors->get_error_messages() as $message ) {
     5844                                    $errors[] = '<li>' . $message . '</li>';
     5845                                }
     5846                            }
     5847                        } catch ( \Throwable $e ) {
     5848                            SplitIt_FlexFields_Payment_Plugin_Log::save_log_info(
     5849                                array(
     5850                                    'user_id' => null,
     5851                                    'method'  => __( 'checkout_validate() Splitit', 'splitit-installment-payments' ),
     5852                                ),
     5853                                'checkout_validate() - I13_Woo_Recpatcha Error: ' . $e->getMessage(),
     5854                                'error'
     5855                            );
     5856                        }
     5857                    }
     5858
     5859                    if ( ! empty( $errors ) ) {
     5860                        wp_send_json(
     5861                            array(
     5862                                'result'   => 'failure',
     5863                                'messages' => implode( '', array_unique( $errors ) ),
     5864                            )
     5865                        );
     5866                    }
     5867                }
     5868
    59265869                $countries = new WC_Countries();
    59275870
     
    59315874                $billing_country  = isset( $_POST['fields']['billing_country'] ) ? wc_clean( wp_unslash( $_POST['fields']['billing_country'] ) ) : $countries->get_base_country(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Input is sanitized using wc_clean() after wp_unslash().
    59325875                $shipping_country = isset( $_POST['fields']['shipping_country'] ) ? wc_clean( wp_unslash( $_POST['fields']['shipping_country'] ) ) : $countries->get_base_country(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Input is sanitized using wc_clean() after wp_unslash().
     5876
     5877                if ( in_array( $billing_country, array( 'US', 'UM' ), true ) ) {
     5878                    if ( ! isset( $_POST['fields']['billing_state'] ) || empty( $_POST['fields']['billing_state'] ) ) {
     5879                        $errors[] = '<li>' . sprintf(
     5880                            __( 'For shoppers in the U.S., the %s field is mandatory.', 'splitit-installment-payments' ),
     5881                            '<strong>' . __( 'State', 'splitit-installment-payments' ) . '</strong>'
     5882                        ) . '</li>';
     5883                    }
     5884                }
    59335885
    59345886                $billings_fields = $countries->get_address_fields( $billing_country, 'billing_' );
     
    60355987                        case 'billing_state':
    60365988                            $valid_states = WC()->countries->get_states( WC()->customer->get_billing_country() );
     5989
     5990                            if ( strpos( DOMAIN, 'jonesandtomlin' ) !== false ) {
     5991                                $is_required = isset( $wc_fields[ $key ]['required'] ) && $wc_fields[ $key ]['required'];
     5992
     5993                                if ( $is_required && empty( $value ) ) {
     5994                                    $errors[] = '<li><strong>' . $wc_fields[ $key ]['label'] . '</strong> is a required field.</li>';
     5995                                    break;
     5996                                }
     5997                            }
     5998
    60375999                            if ( ! empty( $valid_states ) && is_array( $valid_states ) ) {
    60386000                                if ( empty( $value ) || ! array_key_exists( $value, $valid_states ) ) {
     
    60446006                        case 'shipping_state':
    60456007                            $valid_shipping_states = WC()->countries->get_states( WC()->customer->get_shipping_country() );
     6008
     6009                            if ( strpos( DOMAIN, 'jonesandtomlin' ) !== false ) {
     6010                                $is_required = isset( $wc_fields[ $key ]['required'] ) && $wc_fields[ $key ]['required'];
     6011
     6012                                if ( $is_required && empty( $value ) ) {
     6013                                    $errors[] = '<li><strong>' . $wc_fields[ $key ]['label'] . '</strong> is a required field.</li>';
     6014                                    break;
     6015                                }
     6016                            }
     6017
    60466018                            if ( ! empty( $valid_shipping_states ) && is_array( $valid_shipping_states ) ) {
    60476019                                if ( empty( $value ) || ! array_key_exists( $value, $valid_shipping_states ) ) {
Note: See TracChangeset for help on using the changeset viewer.