Plugin Directory

Changeset 3320256


Ignore:
Timestamp:
06/30/2025 09:58:58 PM (8 months ago)
Author:
trustswiftly
Message:

Update block UI

Location:
trust-swiftly-verification
Files:
279 added
9 edited

Legend:

Unmodified
Added
Removed
  • trust-swiftly-verification/trunk/.gitignore

    r2960921 r3320256  
    11/vendor/
     2.DS_Store
  • trust-swiftly-verification/trunk/assets/css/app.css

    r3108811 r3320256  
    2121    display: block;   
    2222}
     23/* Defines the new, larger size for the verification button */
     24.ts-verify-link {
     25    height: 54px;
     26    width: 240px;
     27   
     28    /* Adds a smooth transition for the hover effect */
     29    transition: transform 0.2s ease-in-out;
     30}
     31
     32/* Optional but recommended: A subtle grow effect on hover */
     33.ts-verify-link:hover {
     34    transform: scale(1.03);
     35}
     36
     37/* The main call-out box for the verification step (base styles) */
     38.ts-verification-callout {
     39    border: 2px solid #0073e6;
     40    background-color: #f8f9fa;
     41    border-radius: 8px;
     42    padding: 20px;
     43    margin-top: 20px !important;
     44    text-align: center;
     45    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
     46    /* Transition for color changes and content fades */
     47    transition: background-color 0.4s ease, border-color 0.4s ease;
     48}
     49
     50/* The class that specifically triggers the pulse animation */
     51.ts-verification-callout.ts-pulse-animation {
     52    animation: ts-pulse-border 2.5s infinite;
     53}
     54
     55/* --- NEW: Success State Transformation --- */
     56/* When verification is successful, the box transforms to a green confirmation state */
     57.ts-verification-callout.ts-verification-success {
     58    border-color: #28a745; /* Green border */
     59    background-color: #f0f9f2; /* Lighter green background */
     60}
     61
     62/* The main instructional/status text */
     63.ts-verification-callout .ts-verification-message {
     64    font-size: 16px;
     65    font-weight: 600;
     66    color: #3c434a;
     67    margin: 0;
     68    line-height: 1.5;
     69    /* Transition for smooth fading */
     70    transition: opacity 0.3s ease;
     71}
     72
     73/* The success message gets a distinct color */
     74.ts-verification-callout.ts-verification-success .ts-verification-message {
     75    color: #155724; /* Dark green text for readability */
     76}
     77
     78/* Wrapper for the button and spinner for easy layout */
     79.ts-verification-callout .ts-verification-button-wrapper,
     80.ts-verification-callout .ts-verification-spinner {
     81    min-height: 45px; /* Ensures layout doesn't jump */
     82    display: flex;
     83    justify-content: center;
     84    align-items: center;
     85}
     86
     87/* Animation keyframes for the pulsing border */
     88@keyframes ts-pulse-border {
     89    0% {
     90        box-shadow: 0 0 0 0 rgba(0, 115, 230, 0.5);
     91    }
     92    70% {
     93        box-shadow: 0 0 0 10px rgba(0, 115, 230, 0);
     94    }
     95    100% {
     96        box-shadow: 0 0 0 0 rgba(0, 115, 230, 0);
     97    }
     98}
  • trust-swiftly-verification/trunk/assets/js/checkout.js

    r3131714 r3320256  
    1 function isEmailValid(email) {
    2     var pattern = new RegExp(
    3         /^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([ \t]*\r\n)?[ \t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([ \t]*\r\n)?[ \t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i
    4     );
    5     // eslint-disable-line max-len
    6 
    7     return pattern.test(email);
    8 }
     1/**
     2 * Trust Swiftly Checkout Verification Script
     3 *
     4 * Final Version for Modern WooCommerce Blocks:
     5 * - Uses the official `onCheckoutValidation` hook for Block Checkout.
     6 * - Classic Checkout is handled by server-side PHP hooks.
     7 * - Asynchronously updates a client-side verification status variable.
     8 * - Synchronously validates against that status when the user places the order.
     9 */
    910(function ($) {
    10     // Custom code here
    11 
    12     $(function () {
    13         window.TSVerification = {
    14             config: {
    15                 userEmail: TSCheckoutConfig.user_email,
    16                 ajaxUrl: TSCheckoutConfig.ajax_url,
    17                 nonce: TSCheckoutConfig.nonce,
    18                 verifyLinkText: TSCheckoutConfig.verify_link_text,
    19                 thankyouPage: TSCheckoutConfig.thank_you_page,
    20             },
    21 
    22             $billingEmail: null,
    23             $submitBtn: null,
    24 
    25             init: function () {
    26                 this.thankyouPage = TSCheckoutConfig.thank_you_page;
    27                 this.userEmail = TSCheckoutConfig.user_email;
    28 
    29                 if (this.thankyouPage !== "1") {
    30                     if (jQuery("#billing_email").length !== 0) {
    31                         this.$billingEmail = $("#billing_email");
     11    'use strict';
     12
     13    function isEmailValid(email) {
     14        var pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
     15        return pattern.test(email);
     16    }
     17
     18    var TSVerification = {
     19        config: null,
     20        $emailInput: null,
     21        debounceTimer: null,
     22        isInitialized: false,
     23        isBlockCheckout: false,
     24        // This state variable holds the current verification status. It is updated by the verifyUser() function.
     25        isClientSideVerified: false,
     26
     27        /**
     28         * Initializes the script, determines checkout type, and binds all necessary events.
     29         */
     30        init: function ($emailElement) {
     31            if (this.isInitialized) return;
     32            this.isInitialized = true;
     33            if (typeof TSCheckoutConfig === 'undefined') return console.error("Trust Swiftly: Config object not found.");
     34           
     35            this.config = TSCheckoutConfig;
     36            this.$emailInput = $emailElement;
     37            this.isBlockCheckout = this.$emailInput.attr('id') === 'email';
     38
     39            if (this.config.thank_you_page === "1") return;
     40
     41            this.setupVerificationUI();
     42            this.bindEvents();
     43
     44            // For Block Checkout, subscribe to the official validation hook.
     45            if (this.isBlockCheckout) {
     46                this.initCheckoutValidationSubscriber();
     47            }
     48
     49            // Perform an initial check on page load with the current email value.
     50            this.verifyUser(this.$emailInput.val());
     51            console.log("Trust Swiftly Verification Initialized for " + (this.isBlockCheckout ? "Block" : "Classic") + " Checkout.");
     52        },
     53       
     54        /**
     55         * Injects the verification UI elements into the page.
     56         */
     57        setupVerificationUI: function() {
     58            if ($('#ts-verification-container').length > 0) return;
     59            var $parent = this.isBlockCheckout ?
     60                this.$emailInput.closest('.wc-block-components-address-form__email, .wc-block-checkout__contact-fields') :
     61                this.$emailInput.closest('p.form-row');
     62            if (!$parent.length) $parent = this.$emailInput.parent();
     63            if (!$parent.length) return;
     64
     65            var spinnerImage = this.config.spinner_url || '/wp-includes/images/wpspin.gif';
     66            const messageText = this.config.checkout_message || 'To checkout, please complete the required verification.';
     67            const verificationContainerHTML = `
     68                <div id="ts-verification-container" class="ts-verification-callout" style="display: none;">
     69                    <p class="ts-verification-message">${messageText}</p>
     70                    <div class="ts-verification-spinner" style="display: none;">
     71                        <img src="${spinnerImage}" alt="Loading..." style="width: 20px; height: 20px; margin-right: 8px;">
     72                        <span>Verifying...</span>
     73                    </div>
     74                    <div class="ts-verification-button-wrapper"></div>
     75                </div>`;
     76            $parent.append(verificationContainerHTML);
     77        },
     78
     79        /**
     80         * Binds events for email input and payment method changes.
     81         */
     82        bindEvents: function() {
     83            // When the user types in the email field.
     84            $(document.body).on("input", '#billing_email, #email', (e) => {
     85                clearTimeout(this.debounceTimer);
     86                this.debounceTimer = setTimeout(() => this.verifyUser(e.target.value), 500);
     87            });
     88            // When the user changes the payment method.
     89            $(document.body).on('change', 'input[name="payment_method"], input[name="radio-control-wc-payment-method-options"]', this.handlePaymentMethodChange.bind(this));
     90        },
     91       
     92        /**
     93         * Wrapper to trigger verification on payment method change.
     94         */
     95        handlePaymentMethodChange: function() {
     96            if (!this.isInitialized || !this.$emailInput) return;
     97            this.verifyUser(this.$emailInput.val());
     98        },
     99       
     100        /**
     101         * The core logic. Makes an API call to check user status and updates the UI and the
     102         * isClientSideVerified state variable based on the response.
     103         */
     104        verifyUser: function (email) {
     105            const $container = $('#ts-verification-container');
     106            const $message = $container.find('.ts-verification-message');
     107            const $spinner = $container.find('.ts-verification-spinner');
     108            const $buttonWrapper = $container.find('.ts-verification-button-wrapper');
     109
     110            // Pre-flight checks
     111            const currentMethod = $('input[name="payment_method"]:checked').val() || $('input[name="radio-control-wc-payment-method-options"]:checked').val();
     112            const applicableMethods = this.config.applicable_payment_methods || [];
     113           
     114            // If verification isn't required by the current settings, set status to verified and hide the UI.
     115            if ((applicableMethods.length > 0 && $.inArray(currentMethod, applicableMethods) === -1) || !isEmailValid(email)) {
     116                this.isClientSideVerified = true;
     117                this.hideIt();
     118                return;
     119            }
     120
     121            // Prepare for AJAX Call
     122            $container.removeClass('ts-pulse-animation ts-verification-success');
     123            $buttonWrapper.hide().empty();
     124            $message.hide();
     125            $spinner.show();
     126
     127            // AJAX Call to our custom REST endpoint
     128            $.ajax({
     129                url: this.config.rest_url, method: 'POST', beforeSend: (xhr) => xhr.setRequestHeader('X-WP-Nonce', this.config.nonce),
     130                data: { email: email, payment_method: currentMethod }
     131            }).done((resp) => {
     132                $spinner.hide();
     133                if (resp.success) {
     134                    if (resp.data.type === "user_already_verified") {
     135                        this.isClientSideVerified = true; // Update state
     136                        $container.addClass('ts-verification-success');
     137                        $message.html('Verification Successful ✔').show();
     138                        this.showIt();
     139                    } else if (resp.data.type === "ok" && resp.data.method === "link") {
     140                        this.isClientSideVerified = false; // Update state
     141                        $container.addClass('ts-pulse-animation');
     142                        const messageText = this.config.checkout_message || 'To checkout, please complete the required verification.';
     143                        $message.text(messageText).show();
     144                        const $btn = $('<a>', { id: 'ts-verify-link', class: 'ts-verify-link', target: '_blank', href: resp.data.link })
     145                            .css({ "background-image": "url(" + this.config.btn_img + ")", "display": "block", "background-repeat": "no-repeat", "background-size": "contain", "margin": "10px auto 0" });
     146                        $buttonWrapper.append($btn).fadeIn();
     147                        this.showIt();
    32148                    } else {
    33                         if (jQuery("#email").length !== 0) {
    34                             this.$billingEmail = $("#email");
    35                         } else {
    36                             return 0;
    37                         }
     149                        // Unknown success type, treat as verified to not block user, hide UI.
     150                        this.isClientSideVerified = true;
     151                        this.hideIt();
    38152                    }
    39 
    40                     if (!this.$billingEmail.val()) {
    41                         let img2 = "'" + TSCheckoutConfig.btn_img + "'";
    42                         $checkBtn = $(
    43                             '<span class="ts_emb_verify_temp">' +
    44                                 '<div class="row" style="margin-top: 20px"> ' +
    45                                 '<div class="col-6" style="margin-top: 20px"> ' +
    46                                 '<div class="row" style="margin-top: 20px"> ' +
    47                                 '<p style="text-align:center;"><u>Input a Email to verify</u></p> <button id="ts-verify-link" disabled class="btn ts-verify-link col-8" style="border-radius:20px;opacity: 0.5;background-image: url(' +
    48                                 img2 +
    49                                 ');margin: auto"></button>' +
    50                                 "</div></div></div></span>"
    51                         );
    52                         this.$billingEmail.after($checkBtn);
    53                     }
    54 
    55                     if (jQuery("#email").length !== 0) {
    56                         $("#email").on(
    57                             "change",
    58                             this.maybeVerifyUser.bind(this)
    59                         );
    60                     } else {
    61                         if (jQuery("#billing_email").length !== 0) {
    62                             $("#billing_email").on(
    63                                 "change",
    64                                 this.maybeVerifyUser.bind(this)
    65                             );
    66                         } else {
    67                             return 0;
    68                         }
    69                     }
    70 
    71                     this.verifyUser(this.$billingEmail.val());
    72                    
    73153                } else {
    74                     this.verifyUser(this.userEmail);
     154                    // API call was not successful (e.g., verification not required by API logic).
     155                    // This means the API said "no verification needed", so the user is clear to proceed.
     156                    this.isClientSideVerified = true;
     157                    this.hideIt();
    75158                }
    76 
    77                 // this.initCheckoutFormSubmit();
    78             },
    79 
    80             // initCheckoutFormSubmit: function() {
    81             //     $(document).on('submit', '.woocommerce-checkout form', function(e) {
    82             //         this.checkVerify();
    83             //         console.log(123);
    84             //         return false;
    85             //     });
    86             // },
    87 
    88             maybeVerifyUser: function (e) {
    89                 var email = e.target.value;
    90                 this.verifyUser(email);
    91             },
    92 
    93             verifyUser: function (email) {
    94                 if (!email || !this.isEmailValid(email)) {
    95                     $("#ts-verify-link").remove();
    96                     $(".ts_emb_verify_temp").remove();
    97                     $(".ts_emb_verify").remove();
    98                     return;
     159            }).fail(() => {
     160                $spinner.hide();
     161                // On server error, block checkout to be safe.
     162                this.isClientSideVerified = false;
     163                this.hideIt();
     164            });
     165        },
     166
     167        /**
     168         * Subscribes our validation logic to the official WooCommerce Block Checkout validation hook.
     169         */
     170        initCheckoutValidationSubscriber: function() {
     171            // The wc.hooks object may not be available immediately on page load, so we wait for it.
     172            const interval = setInterval(() => {
     173                if (window.wc && window.wc.hooks) {
     174                    clearInterval(interval);
     175                    // Add our custom validation function to the checkout_validation hook.
     176                    window.wc.hooks.addAction('checkout_validation', this.handleCheckoutValidation.bind(this));
    99177                }
    100 
    101                 $('.ts_verification_successful').remove();
    102                 $("#ts-verify-link").remove();
    103                 $(".ts_emb_verify_temp").remove();
    104                 $(".ts_emb_verify").remove();
    105                 $(".ts-loader").remove();
    106 
    107                 var sURL = jQuery("#sURL").val();
    108                 var sSpinImage =
    109                     '<img class="ts-loader" src="' +
    110                     sURL +
    111                     '/wp-includes/images/wpspin.gif">';
    112                 // this.$billingEmail.after(sSpinImage)
    113                 $("#verify_div").append(sSpinImage);
    114                 let is_verified = 0;
    115                 let urlParams = new URLSearchParams(window.location.search);
    116                 if (urlParams.has("is_verified")) {
    117                     if (urlParams.get("is_verified") === "1") {
    118                         is_verified = 1;
    119                     }
    120                 }
    121 
    122                 if (TSCheckoutConfig.payment_method == "unknown") {
    123                     TSCheckoutConfig.payment_method = jQuery(
    124                         "input[name=payment_method]:checked"
    125                     ).val();
    126                 }
    127                 if (
    128                     TSCheckoutConfig.payment_method == "unknown" ||
    129                     typeof TSCheckoutConfig.payment_method === "undefined"
    130                 ) {
    131                     if (
    132                         jQuery(
    133                             "input[name=radio-control-wc-payment-method-options]:checked"
    134                         ).length !== 0
    135                     ) {
    136                         TSCheckoutConfig.payment_method = jQuery(
    137                             "input[name=radio-control-wc-payment-method-options]:checked"
    138                         ).val();
    139                     }
    140                 }
    141 
    142                 $.post(
    143                     this.config.ajaxUrl + location.search,
    144                     {
    145                         action: "ts_get_user_verification",
    146                         nonce: this.config.nonce,
    147                         email: email,
    148                         payment_method: TSCheckoutConfig.payment_method,
    149                         thank_you_page: this.thankyouPage,
    150                     },
    151                     function (resp) {
    152                         if (!resp.success) {
    153                             $(".ts_emb_verify").hide();
    154                             return;
    155                         }
    156 
    157                         if (resp.data.type == "ok") {
    158                             var method = resp.data.method;
    159                             let pre1 = "";
    160                             let pre2 = "";
    161                             if (this.thankyouPage === "1") {
    162                                 pre1 =
    163                                     '<div class="col-8" style="margin-top: 20px"> ' +
    164                                     '<div class="row" style="margin-top: 20px"> ';
    165                                 pre2 = "</div></div>";
    166                             }
    167                             if (method == "modal") {
    168                                 if (is_verified === 1) {
    169                                     let $checkBtn;
    170                                     $checkBtn = $(
    171                                         '<span class="ts_emb_verify">' +
    172                                             '<div class="row" style="margin-top: 20px"> ' +
    173                                             pre1 +
    174                                             resp.data.html +
    175                                             '<i class="fa fa-hourglass col-1" style="margin: auto;color:orange"></i>' +
    176                                             '<button id="ts-verify-check-button" class="ts-check-verify btn btn-small col-3" type="button">' +
    177                                             '<i class="fa fa-refresh"></i></button>' +
    178                                             pre2 +
    179                                             "</div></span>"
    180                                     );
    181                                     if (this.thankyouPage !== "1") {
    182                                         this.$billingEmail.after($checkBtn);
    183                                     } else {
    184                                         $("#verify_div").html($checkBtn);
    185                                     }
    186                                     $("#trustVerify").addClass("col-8");
    187                                     $("#ts-verify-check-button").on(
    188                                         "click",
    189                                         this.checkVerify.bind(this)
    190                                     );
    191                                 } else {
    192                                     if (this.thankyouPage !== "1") {
    193                                         this.$billingEmail.after(
    194                                             resp.data.html
    195                                         );
    196                                     } else {
    197                                         $("#verify_div").html(resp.data.html);
    198                                     }
    199                                 }
    200                                 trustVerify.configs = {
    201                                     embedKey: TSCheckoutConfig.embed_key,
    202                                     signature: resp.data.ts_user_signature,
    203                                     baseUrl: TSCheckoutConfig.base_url,
    204                                     type: "modal_link",
    205                                     verifyDivId: "trustVerify",
    206                                     userId: resp.data.ts_embed_user_id,
    207                                     moduleType: "wordpress",
    208                                 };
    209 
    210                                 trustVerify.load();
    211 
    212                                 return;
    213                             }
    214                             if (this.thankyouPage === "1") {
    215                                 pre1 =
    216                                     '<div class="col-6" style="margin-top: 20px"> ' +
    217                                     '<div class="row" style="margin-top: 20px"> ';
    218                             }
    219 
    220                             var url = resp.data.link;
    221                             img = TSCheckoutConfig.btn_img;
    222                             jQuery(".ts-loader").remove();
    223                             if (is_verified === 1) {
    224                                 jQuery("#ts-verify-link").remove();
    225                                 let $checkBtn;
    226                                 img = "'" + img + "'";
    227                                 $checkBtn = $(
    228                                     '<span class="ts_emb_verify">' +
    229                                         '<div class="row" style="margin-top: 20px"> ' +
    230                                         pre1 +
    231                                         '<a id="ts-verify-link" class="ts-verify-link col-8" style="background-image: url(' +
    232                                         img +
    233                                         ');margin: auto" target="_blank" href="' +
    234                                         url +
    235                                         '"></a>' +
    236                                         '<i class="fa fa-hourglass col-1" style="margin: auto;color:orange"></i>' +
    237                                         '<button id="ts-verify-check-button" class="ts-check-verify btn btn-small col-3" type="button">' +
    238                                         '<i class="fa fa-refresh"></i></button>' +
    239                                         pre2 +
    240                                         "</div></span>"
    241                                 );
    242                                 if (this.thankyouPage !== "1") {
    243                                     this.$billingEmail.after($checkBtn);
    244                                 } else {
    245                                     $("#verify_div").html($checkBtn);
    246                                 }
    247                                 $("#ts-verify-check-button").on(
    248                                     "click",
    249                                     this.checkVerify.bind(this)
    250                                 );
    251                             } else {
    252                                 jQuery("#ts-verify-link").remove();
    253                                 var $btn = $(
    254                                     '<a id="ts-verify-link" class="ts-verify-link" target="_blank" href="' +
    255                                         url +
    256                                         '"></a>'
    257                                 );
    258                                 $btn = $btn.css({
    259                                     "background-image": "url(" + img + ")",
    260                                 });
    261                                 if (this.thankyouPage !== "1") {
    262                                     this.$billingEmail.after($btn);
    263                                 } else {
    264                                     $("#verify_div").html($btn);
    265                                 }
    266                             }
    267                         } else if (resp.data.type == "user_already_verified") {
    268                             $(".ts_emb_verify").hide();
    269                             let $btn =
    270                                 "<span class='ts_verification_successful' style='color:green;" +
    271                                 (jQuery(
    272                                     ".wc-block-components-address-form__email"
    273                                 ).length !== 0
    274                                     ? "display:block"
    275                                     : "") +
    276                                 "'>Verification Successful <i class='fa fa-check' style='color:green'></i></span>";
    277                             if (this.thankyouPage !== "1") {
    278                                 this.$billingEmail.after($btn);
    279                             } else {
    280                                 $("#verify_div").html($btn);
    281                             }
    282                         }
    283                     }.bind(this)
    284                 ).done(function (resp) {
    285                     $(".ts-loader").remove();
    286                 });
    287             },
    288             isEmailValid: function (email) {
    289                 var pattern = new RegExp(
    290                     /^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([ \t]*\r\n)?[ \t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([ \t]*\r\n)?[ \t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i
    291                 );
    292                 // eslint-disable-line max-len
    293 
    294                 return pattern.test(email);
    295             },
    296             checkVerify: function (e) {
    297                 $.post(
    298                     this.config.ajaxUrl,
    299                     {
    300                         action: "ts_check_user_verification",
    301                         nonce: this.config.nonce,
    302                     },
    303                     function (resp) {
    304                         if (!resp.success) {
    305                             return;
    306                         }
    307 
    308                         if (resp.data.is_verified == "1") {
    309                             $(".ts_emb_verify").hide();
    310                             let $btn =
    311                                 "<span style='color:green'>Verification Successful <i class='fa fa-check' style='color:green'></i></span>";
    312                             if (this.thankyouPage !== "1") {
    313                                 this.$billingEmail.after($btn);
    314                             } else {
    315                                 $("#verify_div").html($btn);
    316                             }
    317                         }
    318                     }.bind(this)
    319                 );
    320             },
    321             hideIt: function (e) {
    322                 $(".ts_emb_verify").hide();
    323                 $(".ts-verify-link").hide();
    324             },
    325         };
     178            }, 100);
     179        },
     180
     181        /**
     182         * This is the callback for the 'checkout_validation' hook. It runs when the user clicks "Place Order".
     183         * It must be SYNCHRONOUS and immediately return a value.
     184         */
     185        handleCheckoutValidation: function() {
     186            // It simply reads the state variable we've been maintaining.
     187            if (this.isClientSideVerified) {
     188                return true; // Return true to allow checkout to proceed.
     189            }
     190            // If not verified, return an error object. WooCommerce will display the message.
     191            return {
     192                errorMessage: 'Please complete the Trust Swiftly verification before placing your order.'
     193            };
     194        },
     195
     196        hideIt: function () { $('#ts-verification-container').slideUp(); },
     197        showIt: function () { $('#ts-verification-container').slideDown(); }
     198    };
     199
     200    /**
     201     * Finds the email input field on the page and initializes the script.
     202     * Retries for a few seconds to handle cases where the field is rendered late.
     203     */
     204    $(document).ready(function() {
     205        const checkInterval = 1000;
     206        const maxChecks = 15;
     207        let checks = 0;
     208        const observerInterval = setInterval(() => {
     209            // Find the first visible email input for either classic or block checkout.
     210            const $emailInput = $('#billing_email:visible, #email:visible').first();
     211            if ($emailInput.length > 0) {
     212                clearInterval(observerInterval);
     213                TSVerification.init($emailInput);
     214            }
     215            checks++;
     216            if (checks >= maxChecks) {
     217                clearInterval(observerInterval);
     218            }
     219        }, checkInterval);
    326220    });
    327     var fInterval = 0;
    328     var bIsVerified = 0;
    329     var funcAreAllItemsLoaded = function () {
    330         /*if(jQuery('input[name=payment_method]').length!==0){
    331             //return ;
    332         }*/
    333         if (
    334             jQuery("#email").length !== 0 ||
    335             jQuery("#billing_email").length !== 0
    336         ) {
    337             if (TSCheckoutConfig.thank_you_page !== "1") {
    338                 window.TSVerification.init();
    339             }
    340 
    341             jQuery(".wc-block-components-checkout-place-order-button").click(
    342                 function (event) {
    343                     if (bIsVerified) {
    344                         return true;
    345                     }
    346                     event.preventDefault();
    347                     var email = "";
    348                     if (jQuery("#email").length !== 0) {
    349                         email = jQuery("#email").val();
    350                     }
    351                     if (jQuery("#billing_email").length !== 0) {
    352                         email = jQuery("#billing_email").val();
    353                     }
    354 
    355                     if (!email || !isEmailValid(email)) {
    356                         return;
    357                     }
    358 
    359                     var payment_method = "unknown";
    360                     if (
    361                         jQuery(
    362                             "input[name=radio-control-wc-payment-method-options]:checked"
    363                         ).length !== 0
    364                     ) {
    365                         payment_method = jQuery(
    366                             "input[name=radio-control-wc-payment-method-options]:checked"
    367                         ).val();
    368                     }
    369 
    370                     if (payment_method == "unknown") {
    371                         payment_method = jQuery(
    372                             "input[name=payment_method]:checked"
    373                         ).val();
    374                     }
    375                     if (typeof payment_method === "undefined") {
    376                         payment_method = "unknown";
    377                     }
    378                     jQuery.ajax({
    379                         type: "POST",
    380                         url: TSCheckoutConfig.ajax_url,
    381                         data: {
    382                             action: "ts_verifyVerification",
    383                             email: email,
    384                             payment_method: payment_method,
    385                         },
    386                         success: function (data) {
    387                             if (data.message != "") {
    388                                 jQuery(
    389                                     ".ts-err-verification-required"
    390                                 ).remove();
    391                                 jQuery(
    392                                     ".wp-block-woocommerce-checkout-actions-block"
    393                                 ).prepend(
    394                                     '<div class="woocommerce-error ts-err-verification-required" role="alert">' +
    395                                         data.message +
    396                                         "</div>"
    397                                 );
    398                             } else {
    399                                 bIsVerified = 1;
    400                                 jQuery(
    401                                     ".wc-block-components-checkout-place-order-button"
    402                                 ).click();
    403                             }
    404                         },
    405                         error: function (data) {},
    406                     });
    407 
    408                     return false;
    409                 }
    410             );
    411         }
    412         if (
    413             jQuery("#email").length !== 0 ||
    414             jQuery("#billing_email").length !== 0
    415         ) {
    416             clearInterval(fInterval);
    417         }
    418     };
    419     fInterval = window.setInterval(funcAreAllItemsLoaded, 1000);
     221
    420222})(jQuery);
  • trust-swiftly-verification/trunk/readme.txt

    r3238884 r3320256  
    1 === Top Identity Verifications for WooCommerce | Trust Swiftly ===
     1=== Identity Verification for WooCommerce | Trust Swiftly ===
    22Contributors: trustswiftly
    3 Tags: id verification, fraud prevention, verify id, woocommerce, age verify
     3Tags: id verification, identity verification, fraud prevention, age verification, woocommerce, kyc, aml, identity proofing, verify id
    44Requires at least: 6.7
    5 Tested up to: 6.7.1
     5Tested up to: 6.8.1
    66Requires PHP: 8.2
    7 Stable tag: 1.1.13
     7Stable tag: 1.1.14
    88License: GPLv2 or later
    99
    10 Flexible, secure, and accurate identity verifications for WooCommerce stores
     10The most flexible, secure, and accurate identity verification platform for WooCommerce.
    1111
    1212== Description ==
    1313
    14 Trust Swiftly provides flexible and accurate identity verifications with over 15 different verification methods. Trust Swiftly is easily customizable through our dashboard and configurable with Woocommerce to optimize your customer's experience with frictionless verifications.
     14Trust Swiftly provides the most flexible and accurate identity verification platform for WooCommerce. Protect your store from fraud, prevent chargebacks, and comply with age-restricted sales regulations using over 15 customizable verification methods. Our plugin is easily configurable to optimize your customer's experience with frictionless verifications.
    1515
    16 Trust Swiftly helps businesses adaptively fight fraud by applying the right friction when needed. From SMS verification to ID, Banking, Voice, Signatures and Credit Cards, you can be sure all checks are covered.
     16Trust Swiftly helps businesses adaptively fight fraud by applying the right level of friction when needed. From simple SMS verification to robust document, banking, voice, and signature checks, you can be sure all your compliance and security needs are covered.
    1717
    18 * Verify age for restricted products (Alcohol, cannabis and vaping)
    19 * Prevent fraud from risky transactions (Chargebacks and high value goods)
    20 * Verify all country IDs, Driver License, Passports, State IDs, and more
    21 * Adhere to KYC and AML regulations
     18**Key Use Cases:**
     19
     20*   **Verify Age:** For restricted products like alcohol, cannabis, and vaping.
     21*   **Prevent Fraud:** Reduce chargebacks on risky transactions and high-value goods.
     22*   **Global ID Verification:** Verify government-issued IDs, Driver's Licenses, Passports, and more from hundreds of countries.
     23*   **Regulatory Compliance:** Adhere to Know Your Customer (KYC) and Anti-Money Laundering (AML) regulations.
    2224
    2325[youtube https://www.youtube.com/watch?v=9aUsSZgLXOo]
    2426
    25 Trust Swiftly supports all types of identity documents from hundreds of different countries. Supported documents include:
     27**Supported Document Types Include:**
    2628
    27 * Driver’s Licenses
    28 * Passports
    29 * Insurance Cards
    30 * Concealed Carry Licenses
    31 * State IDs
    32 * Country IDs
    33 * National ID Cards
     29*   Driver’s Licenses
     30*   Passports
     31*   Insurance Cards
     32*   Concealed Carry Licenses
     33*   State IDs & National ID Cards
    3434
    35 Start using the best and top identity verification plugin for WooCommerce.
     35Secure your business and build trust with your customers today.
    3636
    3737== Installation ==
    3838
    39 **Prerequisite:** A Trust Swiftly account. Sign up for free [https://app.trustswiftly.com/create](https://app.trustswiftly.com/create)
     39**Prerequisite:** A Trust Swiftly account is required. [**Sign up for a free account here**](https://app.trustswiftly.com/create).
    4040
    41 = Setup in Trust Swiftly and WP Plugin =
     41Follow these steps to configure the integration.
    4242
    43 To configure the integration with Trust Swiftly, follow the steps below.
     431.  **Get Your API Key & Base URL:**
     44    *   Log in to your [Trust Swiftly Dashboard](https://app.trustswiftly.com/login).
     45    *   Your **Base URL** is your unique, branded site name (e.g., `https://your-company.trustswiftly.com`).
     46    *   Navigate to <kbd>Developer</kbd> -> <kbd>API</kbd> to get your **API Key**.
    4447
    45 1. Log in to the Trust Swiftly dashboard. Your **base url** is your branded site name you login i.e. `https://EXAMPLE.trustswiftly.com`
    46 2. **Create a Template** in the Trust Swiftly dashboard to configure the user verification steps. Read the documentation on managing templates for more information. [How to create templates?](https://support.trustswiftly.com/how-to-manage-templates.html)
    47 3. Navigate to the **Developer > API** page to create an API key. i.e. `https://EXAMPLE.trustswiftly.com/settings/developer`
    48 4. Navigate to the **Developer > Webhooks** page to create an IPN which notifies your store about completed verifications incase of redirect problems. Click add Webhook at enter the IPN listed on your TS settings page. i.e. `https://example.com/wp-json/ts/v1/ipn` Afterwards make sure to copy the secret from the button to input later.
    49 5. Update in your Trust Swiftly dashboard settings the **Completion Redirect URL**, Go to `https://EXAMPLE.trustswiftly.com/settings` copy and paste the url given on the Wordpress TS settings page and update. i.e. `https://example.com/wp-json/ts/v1/return/?order_id=[order_id]&email=[user_email]`
    50 6. You should now have the **API Key, Secret, and Embed Key** then update in the Wordpress settings of Trust Swiftly. The webhoook secret should also be added if that feature is enabled.
    51 7. Click Save to test the API connection. Once it is confirmed you can then select the remaining settings like the Verification template or trigger location of the verification (Before or after checkout).
     482.  **Connect to WordPress:**
     49    *   In your WordPress admin, go to the Trust Swiftly settings page.
     50    *   Enter your **Base URL** and **API Key**.
     51    *   Click **Save Changes**. The plugin will test the connection.
     52
     533.  **Create a Verification Template:**
     54    *   In the Trust Swiftly dashboard, go to <kbd>Templates</kbd> and create a new template to define the verification steps you want users to follow.
     55    *   For more details, read our guide on [how to manage templates](https://support.trustswiftly.com/how-to-manage-templates.html).
     56
     574.  **Set Up Webhooks (IPN):**
     58    *   A webhook ensures verification updates are always received, even if a user has redirect issues.
     59    *   In your Trust Swiftly dashboard, go to <kbd>Developer</kbd> -> <kbd>Webhooks</kbd> and click **Add Webhook**.
     60    *   Copy the IPN URL from your WordPress plugin settings page (e.g., `https://your-site.com/wp-json/ts/v1/ipn`) and paste it into the webhook form.
     61    *   After creating it, click **Show Secret**, copy the **Webhook Secret**, and paste it into the corresponding field in your WordPress plugin settings.
     62
     635.  **Configure Final Settings:**
     64    *   Back in WordPress, select your newly created **Verification Template** from the dropdown menu.
     65    *   Choose where to trigger the verification (e.g., before or after checkout) and configure any other desired options.
     66    *   Save your settings, and you're ready to go!
    5267
    5368== Frequently Asked Questions ==
    5469
    55 = How does it work?
     70= How does it work? =
    5671
    57 Trust Swiftly uses AI to automatically detect and verify identities. Driver license, passports, and more can be checked for age and fraud related checks. You can learn how the customer receives the email, link, or SMS to complete the verification. [https://trustswiftly.com/features/](https://trustswiftly.com/features/)
     72Trust Swiftly uses AI to automatically detect and verify identities. Customers receive a secure link via email, QR code, or SMS to complete the verification on any device. You can learn more about our verification methods on our [Features page](https://trustswiftly.com/features/).
    5873
    59 = What countries and types of identity documents does your system support?
     74= What countries and types of identity documents do you support? =
    6075
    61 Trust Swiftly supports all types of ID documents from hundreds of different countries. Supported documents include:
     76We support all major types of ID documents from hundreds of countries. Supported documents include:
    6277
    63 * Driver’s Licenses
    64 * Passports
    65 * Insurance Cards
    66 * Concealed Carry Licenses
    67 * State IDs
    68 * Country IDs
    69 * National ID Cards
     78*   Driver’s Licenses
     79*   Passports
     80*   Insurance Cards
     81*   Concealed Carry Licenses
     82*   State IDs
     83*   Country IDs
     84*   National ID Cards
    7085
    71 If you find an ID that doesn't work let us know. We’d be happy to help by adding it to our system.
     86If you encounter an ID that isn't supported, let us know! We are always expanding our coverage.
    7287
    73 = How much does this cost?
     88= How much does this cost? =
    7489
    75 Depending on the verification we offer different pricing. As low as $0.01 per verify. No montly commitments with pay as you go pricing. You can learn more here [https://trustswiftly.com/#pricing](https://trustswiftly.com/#pricing)
     90We offer flexible, pay-as-you-go pricing with no monthly commitments, starting as low as $0.01 per verification. You can find detailed pricing information here: [Trust Swiftly Pricing](https://trustswiftly.com/#pricing).
    7691
    7792= What is Trust Swiftly? =
    7893
    79 Trust Swiftly is a cloud-based identity verification software designed to help businesses verify their customers through over 15 different security capabilities. Adding this plugin, gives anyone the ability to step up identity proofing on Wordpress. The plugin will allow you to verify IDs, selfies, credit cards, addresses, SSNs, phone numbers, signatures and much more. Use Trust Swiftly to verify your customers to prevent chargebacks or when they purchase age restricted products.
     94Trust Swiftly is a cloud-based identity verification platform designed to help businesses secure their operations. Our plugin for WordPress allows you to easily integrate over 15 different verification capabilities, including ID checks, selfie biometrics, address validation, SSN lookups, and more, to prevent fraud and meet compliance standards.
    8095
    81 See [https://trustswiftly.com/features/](https://trustswiftly.com/features/) for more info.
     96= Does this work with any other WooCommerce plugins? =
    8297
    83 = Does this work with any other WooCommerce plugins?
     98Yes, Trust Swiftly integrates perfectly with the official [WooCommerce Anti-Fraud](https://woo.com/document/woocommerce-anti-fraud/) plugin. Combine our identity verification with their AI-powered fraud detection for comprehensive, multi-layered security.
    8499
    85 Yes Trust Swiftly works with the top rated Anti-Fraud plugin. Combining our identity verifications with Anti-Fraud AI fraud detection will secure your site. You can learn more here [https://woo.com/document/woocommerce-anti-fraud/](https://woo.com/document/woocommerce-anti-fraud/)
     100= Need help getting started or have feedback? =
    86101
    87 = Need help getting started or changes?
    88 
    89 We are always looking to improve our plugin and any feedback is appreciated. Contact us at [email protected] for support or feedback. Our support site also has useful tips on preventing fraud [https://support.trustswiftly.com/](https://support.trustswiftly.com/)
     102We are always looking to improve our plugin. Please contact us at `[email protected]` for assistance or feedback. Our support site also has useful tips on preventing fraud: [support.trustswiftly.com](https://support.trustswiftly.com/).
    90103
    91104== Changelog ==
    92105
    93 = 1.0.0 =
    94 Initial release.
     106= 1.1.14 =
     107*   **Tweak:** Improved the checkout user experience by making the verification button appear instantly as a valid email is typed.
     108*   **Tweak:** Added a loading spinner and instructional text during the verification process for better user feedback and a more polished feel.
     109*   **Update:** Updated and improved the plugin's `readme.txt` for better clarity and easier setup.
     110*   **Update:** Compatibility confirmed for WordPress 6.8.1 and WooCommerce 6.8.1.
    95111
    96 = 1.0.1 =
    97 * Permission fix
    98 * Updated readme
     112= 1.1.13 =
     113*   **Tweak:** Simplified plugin setup. The Secret and Embed Key are no longer required. Removed the legacy modal option in favor of a more streamlined verification flow.
    99114
    100 = 1.0.2 =
    101 * Updated readme
     115= 1.1.12 =
     116*   Fix: Resolved an issue with the verification status on the customer's return to the site.
    102117
    103 = 1.0.3 =
    104 * Updated css assets for buttons
     118= 1.1.11 =
     119*   Fix: Addressed a bug related to the block-based checkout.
    105120
    106 = 1.0.4 =
    107 * Version fix
     121= 1.1.10 =
     122*   Feature: Added support for the new WooCommerce block-based checkout.
    108123
    109 = 1.0.5 =
    110 * Fix icons loading
     124= 1.1.6 =
     125*   Feature: Added enhancements for identity verification options. Tested with WordPress 6.4.
     126
     127= 1.1.4 =
     128*   Feature: Added an option to notify users via email.
     129
     130= 1.1.3 =
     131*   Fix: Resolved an API warning.
     132
     133= 1.0.8 =
     134*   Feature: Added the ability to require verification only for specific payment methods.
    111135
    112136= 1.0.7 =
    113 * Fix bugs
     137*   Fix: General bug fixes and stability improvements.
    114138
    115 = 1.0.8 =
    116 * Added feature to limit payment methods that require verification
    117 
    118 = 1.1.3 =
    119 * Fixed API warning.
    120 
    121 = 1.1.4 =
    122 * Add email notify option
    123 
    124 = 1.1.6 =
    125 * Added support for 6.4 and identity verification option enhancements
    126 
    127 = 1.1.10 =
    128 * Added support for WooCommerce Block checkout
    129 
    130 = 1.1.11 =
    131 * Fix block checkout bug
    132 
    133 = 1.1.12 =
    134 * Fix verification status on return
    135 
    136 = 1.1.13 =
    137 * Removed modal option, secret and embed key no longer needed.
     139= 1.0.0 =
     140*   Initial release.
  • trust-swiftly-verification/trunk/src/Settings/Settings.php

    r3238884 r3320256  
    161161            'verification_method',
    162162            'allow_guest_checkout_verify',
     163            'checkout_message', // Added this line
    163164            'custom_css'
    164165        ];
    165166
    166167        foreach ($sanitizeValues as $field) {
    167             if (is_array($input[$field])) {
    168                 $sanitaryValues[$field] = array_map('esc_attr', $input[$field]);
    169             }
    170             else {
    171                 $sanitaryValues[$field] = sanitize_text_field($input[$field]);
     168            // --- THE FIX IS HERE ---
     169            // First, check if the key was actually submitted in the form.
     170            if (isset($input[$field])) {
     171                // If it was, sanitize it.
     172                // If the key was submitted, sanitize it.
     173                if (is_array($input[$field])) {
     174                    $sanitaryValues[$field] = array_map('esc_attr', $input[$field]);
     175                } else {
     176                    $sanitaryValues[$field] = sanitize_text_field($input[$field]);
     177                }
     178            } else {
     179                // If the key was NOT submitted (e.g., an empty multi-select or unchecked checkbox),
     180                // we must explicitly set it to an empty value to clear it in the database.
     181                // This is the fix.
     182                $sanitaryValues[$field] = '';
    172183            }
    173184        }
     
    201212            case 'url':
    202213            case 'password':
    203                 $html .= '<input id="' . esc_attr($field['id']) . '" type="' . $field['type'] . '" name="' . esc_attr($optionName) . '" placeholder="' . esc_attr(ts_array_get($field, 'placeholder', '')) . '" value="' . $data . '"/>' . "\n";
     214                $html .= '<input id="' . esc_attr($field['id']) . '" type="' . $field['type'] . '" name="' . esc_attr($optionName) . '" placeholder="' . esc_attr(ts_array_get($field, 'placeholder', '')) . '" value="' . $data . '"' . (isset($field['autocomplete']) ? ' autocomplete="' . esc_attr($field['autocomplete']) . '"' : '') . '/>' . "\n";
    204215                if (ts_array_get($field, 'toggle') == 1) {
    205216                    $html .= '<a href="#" class="js-toggle-password-visibility">' . __('Show', 'trustswifly-verification') . '</a>';
     
    459470                    'type'          => 'password',
    460471                    'toggle'        => 1,
     472                    'autocomplete'  => 'off', // Add this line
    461473                    'default'       => '',
    462474                    'required'      => 'true',
     
    615627                    'default'       => '',
    616628                    'description'   => __('Select the button style of Trust Swiftly to display on your checkout page or order complete.', 'trustswiftly-verifications')
     629                ],
     630                [
     631                    'id'            => 'checkout_message',
     632                    'label'         => __('Checkout Message' , 'trustswiftly-verification'),
     633                    'type'          => 'textarea',
     634                    'default'       => __('To checkout, please complete the required verification.', 'trustswiftly-verification'),
     635                    'placeholder'   => __('To checkout, please complete the required verification.', 'trustswiftly-verification'),
     636                    'description'   => __('The message displayed to users on the checkout page when verification is required.', 'trustswiftly-verifications')
    617637                ],
    618638                [
  • trust-swiftly-verification/trunk/src/TrustVerifyPlugin.php

    r3238884 r3320256  
    77use TrustswiftlyVerification\WooCommerce\ReturnHandler;
    88use TrustswiftlyVerification\WooCommerce\Verification;
     9
    910class TrustVerifyPlugin
    1011{
     
    2223     */
    2324    protected static $singletons = [];
    24 
     25    /**
     26     * Required plugins
     27     *
     28     * @var array
     29     */
     30    protected static $requiredPlugins = [
     31        'woocommerce/woocommerce.php' => 'WooCommerce'
     32    ];
    2533    /**
    2634     * Plugin components
     
    3644
    3745    /**
    38      * Required plugins
    39      *
    40      * @var array
    41      */
    42     protected static $requiredPlugins = [
    43         'woocommerce/woocommerce.php' => 'WooCommerce'
    44     ];
    45 
    46     /**
    4746     * Constructor
    4847     */
     
    5049    {
    5150        add_action('admin_init', [$this, 'checkRequirements']);
    52         if( !function_exists('is_plugin_active') ) {
    53             include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
    54         }
     51
     52        if (!function_exists('is_plugin_active')) {
     53            include_once(ABSPATH . 'wp-admin/includes/plugin.php');
     54        }
     55
     56        // Only initialize components and add hooks if the required plugins are active.
     57        // The 'checkRequirements' hook will handle notices if they are not.
    5558        if (is_plugin_active('trust-swiftly-verification/trustswiftly-verification.php') && is_plugin_active('woocommerce/woocommerce.php')) {
    5659            $this->initComponents();
    5760            add_action('wp_enqueue_scripts', [$this, 'addAssets']);
    5861            add_action('admin_enqueue_scripts', [$this, 'addAdminAssets']);
    59         }
     62            add_action('rest_api_init', [$this, 'register_rest_routes']);
     63        }
     64    }
     65
     66    /**
     67     * Init plugin components
     68     * @return void
     69     */
     70    public function initComponents()
     71    {
     72        foreach ($this->components as $component) {
     73            new $component();
     74        }
     75    }
     76
     77    /**
     78     * Get a singleton servce
     79     * @param string $class
     80     * @return mixed
     81     */
     82    public static function get($class)
     83    {
     84        if (!class_exists($class) || !in_array($class, static::$singletons)) {
     85            return null;
     86        }
     87        if (!isset(static::$container[$class])) {
     88            static::$container[$class] = new $class;
     89        }
     90        return static::$container[$class];
     91    }
     92
     93    /**
     94     * Get Plugin Path
     95     * @return string
     96     */
     97    public static function pluginDir()
     98    {
     99        return plugin_dir_path(TS_PLUGIN_BASE_PATH);
     100    }
     101
     102    /**
     103     * --- ADDED ---
     104     * Registers the custom REST API routes for the plugin.
     105     */
     106    public function register_rest_routes()
     107    {
     108        register_rest_route('trustswiftly/v1', '/verify-user', [
     109            'methods' => \WP_REST_Server::CREATABLE, // This corresponds to POST
     110            'callback' => [Verification::class, 'handle_rest_get_user_verification'],
     111            'permission_callback' => '__return_true', // Public endpoint, security is handled by nonce
     112        ]);
     113    }
     114
     115    /**
     116     * Check if all required plugins are active
     117     * @return boolean
     118     */
     119    public function checkRequirements()
     120    {
     121        return self::activate();
    60122    }
    61123
     
    68130
    69131        foreach (self::$requiredPlugins as $key => $name) {
    70 
    71             if (! is_plugin_active($key)) {
     132            if (!is_plugin_active($key)) {
    72133                $missingPlugins[$key] = $name;
    73134            }
     
    78139        }
    79140
    80         add_action('admin_notices', function() use ($missingPlugins) {
     141        add_action('admin_notices', function () use ($missingPlugins) {
    81142            $names = rtrim(implode(', ', $missingPlugins), ', ');
    82 
    83143            printf(
    84144                '<div class="notice notice-error is-dismissible"><p>%s</p></div>',
    85145                __("Trustswiftly Verification requires <strong>{$names}</strong> to be active", 'trustswiftly-verification')
    86146            );
    87 
    88147        });
    89148        deactivate_plugins('trust-swiftly-verification/trustswiftly-verification.php');
     
    92151
    93152    /**
    94      * Check if all required plugins are active
    95      *
    96      * @return boolean
    97      */
    98     public function checkRequirements()
    99     {
    100         return self::activate();
    101     }
    102 
    103     /**
    104153     * Add plugin assets
    105      *
    106154     * @return void
    107155     */
    108156    public function addAssets()
    109157    {
    110         $loadScripts = Verification::shouldVerifyBeforeCheckoutAtAssets();
    111         if (!$loadScripts){
    112             $loadScripts = ts_get_option('verify_on', Verification::VERIFY_AFTER_CHECKOUT) == Verification::VERIFY_AFTER_CHECKOUT;
    113         }
    114         $verificationMethod = ts_get_option('verification_method', Settings::defaultVerificationMethod());
     158        // This function now only handles the Classic Checkout and the settings object.
     159        // Block checkout assets are handled separately.
     160        $verificationMethod = ts_get_option('verification_method', 'link');
    115161        $baseUrl = ts_get_option('base_url');
    116162
    117         $recievedPage=is_wc_endpoint_url( 'order-received' );
    118         if ($loadScripts && (is_wc_endpoint_url( 'order-received' ) || is_checkout())) {
     163        if (is_checkout()) {
     164            // Enqueue assets for checkout pages.
    119165            $currentUser = wp_get_current_user();
    120             $jsDependecies = [
    121                 'jquery',
    122             ];
    123 
    124             wp_enqueue_script('ts-checkout-js', static::pluginUrl() . 'assets/js/checkout.js', $jsDependecies, '1.0');
    125             wp_enqueue_script('ts-checkout-helper-js', static::pluginUrl() . 'assets/js/checkout-helper.js', $jsDependecies);
     166            $jsDependecies = ['jquery'];
     167            $plugin_version = defined('TS_PLUGIN_VERSION') ? TS_PLUGIN_VERSION : time();
     168
     169            // Enqueue script for both checkouts, but it will have logic to differentiate
     170            wp_enqueue_script('ts-checkout-js', static::pluginUrl() . 'assets/js/checkout.js', $jsDependecies, $plugin_version, true);
     171            wp_enqueue_script('ts-checkout-block-js', static::pluginUrl() . 'assets/js/checkout-block.js', $jsDependecies, $plugin_version, true);
    126172
    127173            wp_localize_script('ts-checkout-js', 'TSCheckoutConfig', [
    128                 'is_checkout' => is_checkout(),
    129                 'nonce' => wp_create_nonce('ts_action'),
     174                'nonce' => wp_create_nonce('wp_rest'), // For REST API calls
     175                'ajax_nonce' => wp_create_nonce('ts_action'), // For admin-ajax calls
     176                'rest_url' => get_rest_url(null, 'trustswiftly/v1/verify-user'),
    130177                'ajax_url' => admin_url('admin-ajax.php'),
    131                 'verify_link_text' => __('Verify Your Account', 'trustswiftly-verification'),
    132                 'is_user_verified' => ts_is_user_verified($currentUser->ID),
    133                 'verify_before_checkout' => ts_get_option('verify_on', Verification::VERIFY_AFTER_CHECKOUT) == Verification::VERIFY_BEFORE_CHECKOUT,
    134178                'btn_img' => self::pluginUrl() . 'assets/img/' . ts_get_option('btn_img', Settings::defaultBtnImg()),
    135                 'verification_method' => $verificationMethod,
    136                 'thank_you_page' => $recievedPage,
    137                 'payment_method' => "unknown",
    138                 'base_url' => $baseUrl,
     179                'applicable_payment_methods' => (array)ts_get_option('applicable_payment_methods', []),
     180                'checkout_message' => ts_get_option('checkout_message', __('To checkout, please complete the required verification.', 'trustswiftly-verification')),
     181                'user_email' => $currentUser->user_email, // Added for the old JS
     182                'thank_you_page' => is_wc_endpoint_url('order-received') ? '1' : '0', // Added for the old JS
    139183            ]);
    140             wp_enqueue_style('ts-app-css', static::pluginUrl() . 'assets/css/app.css');
    141             wp_add_inline_style('ts-app-css',".ts_emb_verify{".ts_get_option('custom_css')."}");
    142         }
    143     }
    144 
     184            wp_enqueue_style('ts-app-css', static::pluginUrl() . 'assets/css/app.css', [], $plugin_version);
     185            wp_add_inline_style('ts-app-css', ".ts_emb_verify{" . ts_get_option('custom_css') . "}");
     186        }
     187    }
     188
     189    /**
     190     * Get Plugin Url
     191     * @return string
     192     */
     193    public static function pluginUrl()
     194    {
     195        return plugin_dir_url(TS_PLUGIN_BASE_PATH);
     196    }
    145197
    146198    /**
    147199     * Add admin assets
    148      *
    149200     * @return void
    150201     */
    151202    public function addAdminAssets()
    152203    {
    153         // Add admin assets
    154204        $screen = get_current_screen();
    155 
    156205        if ($screen && $screen->id == 'toplevel_page_ts-settings') {
    157            
    158206            wp_enqueue_style('ts-admin-css', static::pluginUrl() . 'assets/css/admin.css');
    159207            wp_enqueue_style('ts-select2', static::pluginUrl() . 'assets/css/select2.min.css');
    160    
    161             wp_enqueue_script('ts-select2',  static::pluginUrl() . 'assets/js/select2.min.js', [
    162                 'jquery'
    163             ]);
    164    
    165             wp_enqueue_script('ts-admin-js',  static::pluginUrl() . 'assets/js/admin.js', [
    166                 'jquery',
    167                 'ts-select2'
    168             ]);
     208            wp_enqueue_script('ts-select2', static::pluginUrl() . 'assets/js/select2.min.js', ['jquery']);
     209            wp_enqueue_script('ts-admin-js', static::pluginUrl() . 'assets/js/admin.js', ['jquery', 'ts-select2']);
    169210            $cm_settings['codeEditor'] = wp_enqueue_code_editor(array('type' => 'text/css'));
    170211            wp_localize_script('jquery', 'cm_settings', $cm_settings);
    171 
    172212            wp_enqueue_script('wp-theme-plugin-editor');
    173213            wp_enqueue_style('wp-codemirror');
    174214        }
    175215    }
    176 
    177     /**
    178      * Init plugin components
    179      *
    180      * @return void
    181      */
    182     public function initComponents()
    183     {
    184         foreach ($this->components as $component) {
    185             new $component();
    186         }
    187     }
    188 
    189         /**
    190      * Get a singleton servce
    191      *
    192      * @param string $class
    193      * @return mixed
    194      */
    195     public static function get($class)
    196     {
    197         if (! class_exists($class) || ! in_array($class, static::$singletons)) {
    198             return null;
    199         }
    200 
    201         if (! isset(static::$container[$class])) {
    202             static::$container[$class] = new $class;
    203         }
    204 
    205         return static::$container[$class];
    206     }
    207 
    208     /**
    209      * Get Plugin Url
    210      *
    211      * @return string
    212      */
    213     public static function pluginUrl()
    214     {
    215         return plugin_dir_url(TS_PLUGIN_BASE_PATH);
    216     }
    217 
    218     /**
    219      * Get Plugin Path
    220      *
    221      * @return string
    222      */
    223     public static function pluginDir()
    224     {
    225         return plugin_dir_path(TS_PLUGIN_BASE_PATH);
    226     }
    227216}
  • trust-swiftly-verification/trunk/src/WooCommerce/Verification.php

    r3238884 r3320256  
    99use TrustswiftlyVerification\Settings\Settings;
    1010
    11 class Verification 
     11class Verification
    1212{
    1313    const USER_ALREADY_VERIFIED = 'user_already_verified';
    1414    const OK = 'ok';
    15 
    1615    const VERIFY_BEFORE_CHECKOUT = 'before_checkout';
    1716    const VERIFY_AFTER_CHECKOUT = 'after_checkout';
     
    1918    public function __construct()
    2019    {
    21         add_filter( 'query_vars', [$this,'add_query_vars_filter'] );
     20        // --- FINALIZED HOOKS ---
     21        add_filter('query_vars', [$this, 'add_query_vars_filter']);
    2222        add_action('wp_ajax_ts_get_user_verification', [$this, 'getUserVerification']);
    2323        add_action('wp_ajax_nopriv_ts_get_user_verification', [$this, 'getUserVerification']);
    24         add_action('wp_ajax_nopriv_ts_verifyVerification', [$this, 'verifyVerification_forBlocks']);
    25         add_action('wp_ajax_ts_verifyVerification', [$this, 'verifyVerification_forBlocks']);
    2624        add_action('wp_ajax_ts_check_user_verification', [$this, 'checkUserVerification']);
    2725        add_action('wp_ajax_nopriv_ts_check_user_verification', [$this, 'checkUserVerification']);
    28 
    2926        add_action('woocommerce_thankyou', [$this, 'maybeRenderVerification']);
    3027        add_action('woocommerce_admin_order_data_after_shipping_address', [$this, 'displayCustomOrderData']);
    31 
    32         add_action('woocommerce_after_checkout_validation', [$this, 'verifyVerification'], 10, 2);
    33 
    34         add_filter( 'manage_edit-shop_order_columns', [$this,'ts_add_new_order_admin_list_column'] );
    35 
    36         add_action( 'manage_shop_order_posts_custom_column', [$this,'ts_add_new_order_admin_list_column_content'] );
    37 
    38         add_action( 'woocommerce_review_order_before_payment', [$this,'refresh_payment_methods'] );
     28        add_filter('manage_edit-shop_order_columns', [$this, 'ts_add_new_order_admin_list_column']);
     29        add_action('manage_shop_order_posts_custom_column', [$this, 'ts_add_new_order_admin_list_column_content']);
     30        add_action('woocommerce_review_order_before_payment', [$this, 'refresh_payment_methods']);
    3931        add_action('wp_footer', [$this, 'ExtraFooter'], 9999);
    40 
    41         // add_action('init', function() {
    42         //     $v = ts_is_user_verified(17);
    43         //     var_dump($v);
    44         //     die;
    45         // });
     32        add_action('wp_ajax_ts_verifyVerification', [$this, 'verifyVerification_forBlocks']);
     33        add_action('wp_ajax_nopriv_ts_verifyVerification', [$this, 'verifyVerification_forBlocks']);
     34
     35        // Server-side validation for Classic Checkout
     36        add_action('woocommerce_after_checkout_validation', [$this, 'validate_classic_checkout'], 10, 2);
     37
     38        // Server-side validation for Block Checkout
     39        add_action('woocommerce_blocks_checkout_process', [$this, 'validate_block_checkout'], 10, 2);
     40    }
     41
     42    public function ExtraFooter(){
     43        echo '<input type="hidden" id="sURL" value="'.esc_url(get_site_url()).'">';
     44    }
     45
     46    public static function handle_rest_get_user_verification(\WP_REST_Request $request) {
     47        $email = sanitize_email($request->get_param('email'));
     48        $paymentMethod = sanitize_text_field($request->get_param('payment_method'));
     49        $self = new self();
     50
     51        if (empty($email) || !is_email($email)) {
     52            return new \WP_REST_Response(['success' => false, 'data' => ['message' => 'Email is required']], 400);
     53        }
     54        if (!self::shouldVerifyBeforeCheckout($paymentMethod)) {
     55            return new \WP_REST_Response(['success' => false, 'data' => ['reason' => 'not_required']], 200);
     56        }
     57
     58        if ( (is_user_logged_in() && ts_is_user_verified(get_current_user_id())) ||
     59             (!is_user_logged_in() && ts_get_option('allow_guest_checkout_verify') && $self->is_verification_complete($email, $paymentMethod)) ) {
     60            return new \WP_REST_Response(['success' => true, 'data' => ['type' => self::USER_ALREADY_VERIFIED]]);
     61        }
    4662       
    47 
    48         // add_action('init', function() {
    49         //     if (isset($_GET['delete_ts_user'])) {
    50         //         $user = wp_get_current_user();
    51            
    52         //         if (! $user) {
    53         //             return;
    54         //         }
    55 
    56         //         $userId = get_user_meta($user->ID, '_trust_user_id', true);
    57 
    58         //         delete_user_meta($user->ID, '_trust_user_id');
    59         //         delete_user_meta($user->ID, '_trust_data');
    60         //         delete_user_meta($user->ID, '_trust_embed_user_id');
    61 
    62         //         if ($userId) {
    63         //             $api = ts_api();
    64         //             $api->userClient()->deleteUser($userId);
    65         //         }
    66         //     }
    67            
    68         // });
    69     }
    70     public function ExtraFooter(){
    71         echo'<input type="hidden" id="sURL" value="'.get_site_url().'">';
    72     }
     63        $userIds = $self->createOrFindTsUser(is_user_logged_in() ? get_current_user_id() : null, $email);
     64        if (!$userIds) {
     65            return new \WP_REST_Response(['success' => false, 'data' => ['message' => 'API Error']], 500);
     66        }
     67        if (is_user_logged_in()) {
     68            update_user_meta(get_current_user_id(), '_trust_user_id', $userIds[0]);
     69        }
     70        $link = $self->getMagicLink($userIds[0]);
     71        if (!$link) {
     72            return new \WP_REST_Response(['success' => false, 'data' => ['message' => 'Could not generate link']], 500);
     73        }
     74        return new \WP_REST_Response(['success' => true, 'data' => ['type' => self::OK, 'method' => 'link', 'link' => $link]]);
     75    }
     76   
     77    /**
     78     * --- NEW: SERVER-SIDE VALIDATION FOR BLOCK CHECKOUT ---
     79     * Hooks into the native block process to add a validation error.
     80     */
     81    public function validate_block_checkout($data, $errors) {
     82        $email = isset($data['billing_address']['email']) ? sanitize_email($data['billing_address']['email']) : '';
     83        $payment_method = isset($data['payment_method']) ? sanitize_text_field($data['payment_method']) : '';
     84
     85        if (!$this->is_verification_complete($email, $payment_method)) {
     86            $errors->add('ts_verification_required', __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification'));
     87        }
     88    }
     89
     90    /**
     91     * --- RENAMED: SERVER-SIDE VALIDATION FOR CLASSIC CHECKOUT ---
     92     * Includes a "run-once" guard to prevent duplicate errors.
     93     */
     94    public function validate_classic_checkout($fields, $errors) {
     95        static $has_run = false;
     96        if ($has_run) {
     97            return;
     98        }
     99        $has_run = true;
     100       
     101        $email = isset($fields['billing_email']) ? sanitize_email($fields['billing_email']) : '';
     102        $payment_method = isset($fields['payment_method']) ? sanitize_text_field($fields['payment_method']) : '';
     103
     104        if (!$this->is_verification_complete($email, $payment_method)) {
     105            $errors->add('validation', __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification'));
     106        }
     107    }
     108
     109    /**
     110     * --- NEW: CENTRALIZED VALIDATION LOGIC ---
     111     * A single function to check if verification is complete.
     112     * Returns true if verification is complete or not required.
     113     * Returns false if verification is required and not complete.
     114     */
     115    private function is_verification_complete($email, $payment_method) {
     116        if (ts_get_option('verify_on', self::VERIFY_BEFORE_CHECKOUT) !== self::VERIFY_BEFORE_CHECKOUT) {
     117            return true;
     118        }
     119        if (!self::shouldVerifyBeforeCheckout($payment_method)) {
     120            return true;
     121        }
     122        if (is_user_logged_in()) {
     123            return ts_is_user_verified(get_current_user_id());
     124        }
     125        if (ts_get_option('allow_guest_checkout_verify') && !empty($email)) {
     126            $user = $this->tryFindUser($email);
     127            return ($user && !empty($user['is_verified']));
     128        }
     129        // If guest checkout is disabled and user is not logged in, fail open (let WooCommerce handle it)
     130        if (!ts_get_option('allow_guest_checkout_verify')) {
     131            return true;
     132        }
     133        // If guest but no email, it's not complete
     134        return false;
     135    }
     136
     137
     138    public function verifyVerification_forBlocks() {
     139        check_ajax_referer('ts_action', 'nonce');
     140
     141        $response = ['message' => ''];
     142        $email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
     143        $payment_method = isset($_POST['payment_method']) ? sanitize_text_field($_POST['payment_method']) : '';
     144
     145        if (ts_get_option('verify_on', self::VERIFY_BEFORE_CHECKOUT) !== self::VERIFY_BEFORE_CHECKOUT || !self::shouldVerifyBeforeCheckout($payment_method)) {
     146            wp_send_json($response);
     147            return;
     148        }
     149
     150        $is_verified = false;
     151        if (is_user_logged_in()) {
     152            $is_verified = ts_is_user_verified(get_current_user_id());
     153        } elseif (ts_get_option('allow_guest_checkout_verify') && !empty($email)) {
     154            $user = $this->tryFindUser($email);
     155            $is_verified = ($user && !empty($user['is_verified']));
     156        }
     157       
     158        if (!$is_verified) {
     159            $response['message'] = __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification');
     160        }
     161
     162        wp_send_json($response);
     163    }
     164   
     165    // --- NOTE: THE DUPLICATED CODE BLOCK THAT WAS HERE HAS BEEN REMOVED TO FIX THE PARSE ERROR ---
     166
    73167    function refresh_payment_methods(){
    74168        $loadScripts = Verification::shouldVerifyBeforeCheckoutAtAssets();
    75169        if ($loadScripts){
    76             $chosen_payment_method = WC()->session->get('chosen_payment_method');
    77             $applicableMethodsData=ts_get_option('applicable_payment_methods');
    78170            ?>
    79171            <script type="text/javascript">
    80                 var j_method = <?='"'. $chosen_payment_method .'"'?>;
    81                 var availableArray = <?= json_encode($applicableMethodsData) ?>;
    82172                (function($){
    83                     $( 'form.checkout' ).on( 'change', 'input[name^="payment_method"]', function(event) {
     173                    function handlePaymentMethodChange() {
     174                        if (typeof TSCheckoutConfig === 'undefined') {
     175                            return;
     176                        }
    84177                        var payment_method = $('form.checkout').find('input[name^="payment_method"]:checked').val();
    85                         if(jQuery.inArray(payment_method, availableArray) !== -1)
    86                         {
     178                        var applicableMethods = TSCheckoutConfig.applicable_payment_methods || [];
     179                        if(applicableMethods.length === 0 || $.inArray(payment_method, applicableMethods) !== -1) {
    87180                            TSCheckoutConfig.payment_method = payment_method;
    88                             window.TSVerification.init();
    89                         }else{
    90                             window.TSVerification.hideIt();
     181                            if (window.TSVerification && typeof window.TSVerification.showIt === 'function') {
     182                                window.TSVerification.showIt();
     183                                window.TSVerification.verifyUser($('#billing_email').val());
     184                            }
     185                        } else {
     186                            if (window.TSVerification && typeof window.TSVerification.hideIt === 'function') {
     187                                window.TSVerification.hideIt();
     188                            }
    91189                        }
    92                         //set new value
    93                         j_method = $('form.checkout').find('input[name^="payment_method"]:checked').val();
     190                    }
     191                    $(document).ready(function() {
     192                        $('form.checkout').on('change', 'input[name^="payment_method"]', handlePaymentMethodChange);
     193                        handlePaymentMethodChange();
    94194                    });
    95195                })(jQuery);
     
    104204            $order = wc_get_order($post->ID);
    105205            $user = $order->get_user();
     206            if (!$user) {
     207                echo "Guest";
     208                return;
     209            }
    106210            $tsUserId = get_user_meta($user->ID, '_trust_user_id', true);
    107 
    108211            if (!$tsUserId) {
    109212                echo "Unknown";
    110             }
    111 
    112             $url = $this->getTSUserShowUrl($tsUserId);
    113             if (!$url) {
    114                 echo "Unknown";
    115             }
    116 
     213                return;
     214            }
    117215            $isVerified = ts_is_user_verified($user->ID);
    118 
    119             if ($isVerified) {
    120                 echo "Verified";
    121             } else {
    122                 echo "Unverified";
    123             }
    124         }
    125 
     216            echo $isVerified ? "Verified" : "Unverified";
     217        }
    126218    }
    127219
    128220    public function ts_add_new_order_admin_list_column($columns){
    129221        $reordered_columns = array();
    130         // Inserting columns to a specific location
    131222        foreach( $columns as $key => $column){
    132223            $reordered_columns[$key] = $column;
    133224            if( $key ==  'order_status' ){
    134                 // Inserting after "Status" column
    135225                $reordered_columns['ts_verify_status'] = 'Verify Status';
    136226            }
     
    138228        return $reordered_columns;
    139229    }
     230
    140231    public function add_query_vars_filter( $vars ){
    141232        $vars[] = "is_verified";
    142233        return $vars;
    143234    }
    144     public function verifyVerification_forBlocks(){
    145         $aAnswer = ['not_verified'=>0,'skip'=>0,'message'=>''];
    146         $email = sanitize_text_field($_POST['email']);
     235
     236    /**
     237     * --- CORRECTED & FINAL ---
     238     * This hook now ONLY handles validation for the Classic Checkout.
     239     * It includes a "run-once" guard to prevent duplicate errors caused by themes or other plugins.
     240     * It also explicitly ignores Block Checkout submissions.
     241     */
     242    public function verifyVerification($fields, $errors)
     243    {
     244        // --- FIX: START ---
     245        // "Run-once" guard. This prevents the function from executing more than once per request,
     246        // solving the duplicate error issue on Classic Checkout.
     247        static $has_run = false;
     248        if ($has_run) {
     249            return $errors;
     250        }
     251        $has_run = true;
    147252       
    148         $payment_method = sanitize_text_field($_POST['payment_method'] );
     253        // This "firewall" prevents this function from ever running during a Block Checkout submission.
     254        if (function_exists('wc_is_rest_api_request') && wc_is_rest_api_request('wc/store/v1')) {
     255            return $errors;
     256        }
     257        // --- FIX: END ---
    149258       
    150259        if (!ts_get_option('allow_guest_checkout_verify') && !is_user_logged_in()) {
    151             $aAnswer['skip'] = 2;
    152         }
    153         $verifyAfterCheckout = ts_get_option('verify_on', static::VERIFY_BEFORE_CHECKOUT) == static::VERIFY_AFTER_CHECKOUT;
    154         if ($verifyAfterCheckout){
    155             $aAnswer['skip'] = 3;
    156         }
    157        
    158         if (is_user_logged_in()) {
    159            
    160             $user = wp_get_current_user();
    161             $isVerified = ts_is_user_verified($user->ID);
    162             // $isVerified = true;
    163 
    164             if (!$isVerified) {
    165                 if (self::shouldVerifyBeforeCheckout($payment_method)) {
    166                     $aAnswer['message'] = __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification');
    167                 } else {
    168                     $aAnswer['skip'] = 4;
    169                 }
    170             }
    171         }else if (ts_get_option('allow_guest_checkout_verify') && ($email)){
    172             if($email){
    173                 $user = $this->tryFindUser($email);
    174             }
    175 
    176             if ($user){
    177                 $isVerified = ts_array_get($user, 'is_verified', false);
    178                 if (!$isVerified) {
    179                    
    180                     if (self::shouldVerifyBeforeCheckout($payment_method)) {
    181                         $aAnswer['message'] = __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification');
    182                     } else {
    183                         $aAnswer['skip'] = 1;
    184                     }
    185                    
    186                 }
    187             }
    188         }
    189         $aJSONAnswer = json_encode($aAnswer);
    190         header('Content-type: application/json');
    191         echo($aJSONAnswer);
    192         die();
    193     }
    194     public function verifyVerification($fields, $errors)
    195     {
    196         if (!ts_get_option('allow_guest_checkout_verify') && !is_user_logged_in()) {
    197260            return $errors;
    198261        }
    199         $verifyAfterCheckout = ts_get_option('verify_on', static::VERIFY_BEFORE_CHECKOUT) == static::VERIFY_AFTER_CHECKOUT;
    200         if ($verifyAfterCheckout){
     262        if (ts_get_option('verify_on', static::VERIFY_BEFORE_CHECKOUT) == static::VERIFY_AFTER_CHECKOUT){
    201263            return $errors;
    202264        }
     265
    203266        if (is_user_logged_in()) {
    204267            $user = wp_get_current_user();
    205             $isVerified = ts_is_user_verified($user->ID);
    206 
    207             if (!$isVerified) {
    208                 if (self::shouldVerifyBeforeCheckout()) {
    209                     $errors->add(
    210                         'validation',
    211                         __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification')
    212                     );
    213                 } else {
    214                     return $errors;
     268            if (!ts_is_user_verified($user->ID) && self::shouldVerifyBeforeCheckout()) {
     269                $errors->add('validation', __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification'));
     270            }
     271        } else if (ts_get_option('allow_guest_checkout_verify') && !empty($fields['billing_email'])) {
     272            $user = $this->tryFindUser($fields['billing_email']);
     273            if ($user) {
     274                if (empty($user['is_verified']) && self::shouldVerifyBeforeCheckout()) {
     275                    $errors->add('validation', __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification'));
    215276                }
    216277            }
    217         }else if (ts_get_option('allow_guest_checkout_verify') && ($fields['billing_email'] || $fields['email'])){
    218             if($fields['email']){
    219                 $user = $this->tryFindUser($fields['email']);
    220             }
    221             if($fields['billing_email']){
    222                 $user = $this->tryFindUser($fields['billing_email']);
    223             }
    224            
    225             if ($user){
    226                 $isVerified = $user['is_verified'];
    227                 if (!$isVerified) {
    228                     if (self::shouldVerifyBeforeCheckout()) {
    229                         $errors->add(
    230                             'validation',
    231                             __('Please complete the verification requirements by clicking the 🛡️ Trust Swiftly button prior to paying', 'trustswiftly-verification')
    232                         );
    233                     } else {
    234                         return $errors;
    235                     }
    236                 }
    237             }
    238 
    239278        }
    240279
     
    250289        }
    251290        $tsUserId = get_user_meta($user->ID, '_trust_user_id', true);
    252 
    253291        if (! $tsUserId) {
    254292            return;
    255293        }
    256 
    257294        $url = $this->getTSUserShowUrl($tsUserId);
    258295        if (! $url) {
    259296            return;
    260297        }
    261 
    262298        $msg = __('View User', 'trustswiftly-verifications');
    263299        $isVerified = ts_is_user_verified($user->ID);
    264 
    265300        printf(
    266301            '<h3>%s</h3><a href="%s" target="_blank">%s</a> - %s',
     
    271306        );
    272307    }
     308
    273309    protected function CreateTheUserIfNotExists($orderId){
    274310        if(!is_user_logged_in()){
     
    285321                $order->set_customer_id($userId);
    286322                $order->save();
    287 
    288323            }else{
    289324                if(empty($order->get_user_id())){
     
    306341                 }
    307342            }
    308            
    309         }
    310     }
     343        }
     344    }
     345
    311346    public function maybeRenderVerification($orderId)
    312347    { 
    313         $verifyAfterCheckout = ts_get_option('verify_on', static::VERIFY_BEFORE_CHECKOUT) == static::VERIFY_AFTER_CHECKOUT;
    314         if (! $verifyAfterCheckout) {
     348        if (ts_get_option('verify_on', static::VERIFY_BEFORE_CHECKOUT) == static::VERIFY_BEFORE_CHECKOUT) {
    315349            $this->CreateTheUserIfNotExists($orderId);
    316350            return;
     
    319353            return;
    320354        }
    321 
    322355        if (! static::shouldVerifyAfterCheckout($orderId)) {
    323356            return;
    324357        }
    325         if(!is_user_logged_in()){
    326             $order = wc_get_order($orderId);
     358        $order = wc_get_order($orderId);
     359        $user = $order->get_user();
     360        if (!$user) {
    327361            $email = $order->get_billing_email();
    328             $userId = email_exists($email);
    329             if(empty($userId )){
    330                 $username = sanitize_user(current(explode('@', $email)), true);
     362            $user_id = $order->get_customer_id();
     363            if ($user_id === 0) {
     364                 $user_id = email_exists($email);
     365            }
     366            if (!$user_id) {
     367                 $username = sanitize_user(current(explode('@', $email)), true);
    331368                if (username_exists($username)) {
    332                     $username = $username . '_' . wp_generate_password(4, false, false);
     369                    $username .= '_' . wp_generate_password(4, false, false);
    333370                }
    334371                $password = wp_generate_password();
    335                 $userId = wp_create_user($username, $password, $email);
    336                 $order->set_customer_id($userId);
     372                $user_id = wp_create_user($username, $password, $email);
     373                $order->set_customer_id($user_id);
    337374                $order->save();
    338 
    339             }
    340             $user = get_user_by( 'id', $userId );
    341             if(isset($user->data)){
    342                 $user = $user->data;
    343             }
    344         }else{
    345             $user = wp_get_current_user();
    346         }
    347        
     375            }
     376            $user = get_user_by('id', $user_id);
     377        }
     378        if (!$user) { return; }
     379
    348380        $isUserAlreadyVerified = ts_is_user_verified($user->ID);
    349        
    350381        if ($isUserAlreadyVerified) {
    351             echo ts_render('post-order-verification.php', [
    352                 'btnImg' => TrustVerifyPlugin::pluginUrl() . 'assets/img/' . ts_get_option('btn_img', Settings::defaultBtnImg()),
    353                 'isVerified' => true,
    354                 'link' => null
    355             ]);
    356 
    357             return;
    358         }
    359 
    360         $trustUserId = get_user_meta($user->ID, '_trust_user_id', true);
    361         $trustEmbedUserId = get_user_meta($user->ID, '_trust_embed_user_id', true);
    362         if (! $trustUserId || !$trustEmbedUserId) {
    363             $userId = $this->createOrFindTsUser($user->ID, $user->user_email);
    364 
    365             if (! $userId) {
    366                 return;
    367             }
    368 
    369             $trustUserId = $userId[0];
    370             $trustEmbedUserId = $userId[1];
    371             if ($trustUserId) {
    372                 update_user_meta($user->ID, '_trust_user_id', $trustUserId);
    373             }
    374             if ($trustEmbedUserId) {
    375                 update_user_meta($user->ID, '_trust_embed_user_id', $trustEmbedUserId);
    376             }
    377         }
    378 
     382            echo ts_render('post-order-verification.php', ['isVerified' => true]);
     383            return;
     384        }
     385        $userIds = $this->createOrFindTsUser($user->ID, $user->user_email);
     386        if (!$userIds) { return; }
     387        $trustUserId = $userIds[0];
     388        $trustEmbedUserId = $userIds[1];
     389        update_user_meta($user->ID, '_trust_user_id', $trustUserId);
     390        update_user_meta($user->ID, '_trust_embed_user_id', $trustEmbedUserId);
    379391        $this->updateOrderId($trustUserId, $orderId);
    380 
    381392        $link = $this->getMagicLink($trustUserId);
    382 
    383393        echo ts_render('post-order-verification.php', [
    384             'btnImg' => TrustVerifyPlugin::pluginUrl() . 'assets/img/' . ts_get_option('btn_img', Settings::defaultBtnImg()),
    385394            'isVerified' => false,
    386395            'link' => $link,
    387             'user_email'=>$user->user_email
     396            'user_email' => $user->user_email
    388397        ]);
    389398    }
    390399
    391     public function checkUserVerification(){
    392 
    393         $user = wp_get_current_user();
    394 
    395         $isUserAlreadyVerified = ts_is_user_verified($user->ID);
    396 
    397         if ($isUserAlreadyVerified) {
    398             return wp_send_json_success([
    399                 'is_verified' => 1,
    400             ]);
    401         }
    402 
    403         return wp_send_json_success([
    404             'is_verified' => 0,
    405         ]);
    406     }
    407 
    408400    public function getUserVerification()
    409401    {
    410402        check_ajax_referer('ts_action', 'nonce');
    411 
    412403        $email = sanitize_email(ts_array_get($_POST, 'email'));
    413         $thank_you_page = sanitize_text_field(ts_array_get($_POST, 'thank_you_page'));
    414 
    415         if (! $email) {
    416             return wp_send_json_error([
    417                 'message' => __('Email is required'. 'trustswiftly-verification')
    418             ]);
    419         }
    420 
    421         $paymentMethod = sanitize_text_field(ts_array_get($_POST, 'payment_method'));
    422        
    423         if ($thank_you_page!=="1") {
    424             if ($paymentMethod!=="unknown") {
    425                 if (!static::shouldVerifyBeforeCheckout($paymentMethod)) {
    426                     return wp_send_json_error();
    427                 }
    428             }else{
    429                 if (!static::shouldVerifyBeforeCheckout()) {
    430                     return wp_send_json_error();
    431                 }
    432             }
    433         }
    434 
     404        if (!$email) {
     405            wp_send_json_error(['message' => __('Email is required', 'trustswiftly-verification')]);
     406        }
     407        if (ts_array_get($_POST, 'thank_you_page') !== "1") {
     408            $paymentMethod = sanitize_text_field(ts_array_get($_POST, 'payment_method'));
     409            if (!static::shouldVerifyBeforeCheckout($paymentMethod)) {
     410                wp_send_json_error();
     411            }
     412        }
    435413        if (is_user_logged_in()) {
    436 
    437414            $user = wp_get_current_user();
    438             $trustUserId = null;
    439             $isUserAlreadyVerified = ts_is_user_verified($user->ID);
    440 
    441             if ($isUserAlreadyVerified) {
    442                 return wp_send_json_success([
    443                     'type' => static::USER_ALREADY_VERIFIED,
    444                 ]);
    445             }
    446 
    447             $trustUserId = get_user_meta($user->ID, '_trust_user_id', true);
    448             $trustEmbedUserId = get_user_meta($user->ID, '_trust_embed_user_id', true);
    449             /*check whether the user exists to prevent exclusion when the user is deleted from the portal*/
    450             if(!empty($trustUserId)&&!$this->isUserExists($trustUserId)){
    451                 $trustUserId = 0;
    452                 $trustEmbedUserId = 0;
    453                 update_user_meta($user->ID, '_trust_user_id', 0);
    454                 update_user_meta($user->ID, '_trust_embed_user_id', 0);
    455             }
    456            
    457             if (! $trustUserId || ! $trustEmbedUserId) {
    458                
    459                 $userId = $this->createOrFindTsUser($user->ID, $email);
    460                 if (! $userId) {
    461                     return wp_send_json_error([
    462                         'message' => __('Error occurred', 'trustswiftly-verification')
    463                     ]);
    464                 }
    465 
    466                 $trustUserId = $userId[0];
    467                 $trustEmbedUserId = $userId[1];
    468 
    469                 if ($trustUserId) {
    470                     update_user_meta($user->ID, '_trust_user_id', $trustUserId);
    471                 }
    472                 if ($trustEmbedUserId) {
    473                     update_user_meta($user->ID, '_trust_embed_user_id', $trustEmbedUserId);
    474                 }
    475                 if (isset($userId[2]) && $userId[2]) {
    476                     $alreadyVerified = ts_array_get($userId[2], 'is_verified', false);
    477                     if ($alreadyVerified) {
    478                         return wp_send_json_success([
    479                             'type' => static::USER_ALREADY_VERIFIED,
    480                         ]);
    481                     }
    482                 }
    483             }else{
    484                 $tsUser = $this->tryFindUser($email);
    485                 $isUserAlreadyVerified = $tsUser ? ts_array_get($tsUser, 'is_verified', false) : false;
    486                 if ($isUserAlreadyVerified) {
    487                     return wp_send_json_success([
    488                         'type' => static::USER_ALREADY_VERIFIED,
    489                     ]);
    490                 }
    491             }
    492         } else if (ts_get_option('allow_guest_checkout_verify')) {
     415            if (ts_is_user_verified($user->ID)) {
     416                wp_send_json_success(['type' => static::USER_ALREADY_VERIFIED]);
     417            }
     418            $userIds = $this->createOrFindTsUser($user->ID, $email);
     419            if (!$userIds) { wp_send_json_error(['message' => 'API Error']); }
     420            $trustUserId = $userIds[0];
     421            update_user_meta($user->ID, '_trust_user_id', $trustUserId);
     422        } elseif (ts_get_option('allow_guest_checkout_verify')) {
    493423            $tsUser = $this->tryFindUser($email);
    494             $isUserAlreadyVerified = $tsUser ? ts_array_get($tsUser, 'is_verified', false) : false;
    495             // echo '<pre>';
    496             // var_dump($isUserAlreadyVerified);
    497             // die;
    498             if ($isUserAlreadyVerified) {
    499                 return wp_send_json_success([
    500                     'type' => static::USER_ALREADY_VERIFIED,
    501                 ]);
     424            if ($tsUser && !empty($tsUser['is_verified'])) {
     425                wp_send_json_success(['type' => static::USER_ALREADY_VERIFIED]);
    502426            }
    503427            $userIds = $this->createOrFindTsUser(null, $email);
    504             if (! $userIds) {
    505                 return wp_send_json_error([
    506                     'message' => __('Error occurred', 'trustswiftly-verification')
    507                 ]);
    508             }
    509 
     428            if (!$userIds) { wp_send_json_error(['message' => 'API Error']); }
    510429            $trustUserId = $userIds[0];
    511             $trustEmbedUserId = $userIds[1];
    512 
    513         }else{
    514             return wp_send_json_error();
    515         }
    516          
     430        } else {
     431            wp_send_json_error();
     432        }
    517433        $link = $this->getMagicLink($trustUserId);
    518        
    519         return wp_send_json_success([
    520             'ts_user_id' => $trustUserId,
    521             'ts_embed_user_id' => $trustEmbedUserId,
    522             'method' => 'link',
    523             'type' => static::OK,
    524             // 'type' => static::USER_ALREADY_VERIFIED,
    525             'link' => $link
    526         ]);
     434        wp_send_json_success(['ts_user_id' => $trustUserId, 'method' => 'link', 'type' => static::OK, 'link' => $link]);
    527435    }
    528436
     
    538446                return false;
    539447            }
    540             $params = [
    541                 'email' => $email,
    542                 'template_id' => ts_get_option('template_id', Settings::getDefaultTemplateId()),
    543                 'reference_id' => $userId
    544             ];
     448            $params = ['email' => $email, 'template_id' => ts_get_option('template_id', Settings::getDefaultTemplateId()), 'reference_id' => $userId];
    545449            if (ts_get_option('send_verify_link',false)){
    546450                $params['send_link']=true;
    547451            }
    548 
    549452            $response = $api->userClient()->createUser($params);
    550 
    551453            if (! $response instanceof UserCreateResponse || ts_array_get($response->createArray, 'status') !== 'success') {
    552454                return false;
    553455            }
    554    
    555456            $userId = ts_array_get($response->createArray, 'id');
    556457            $embedUserId = ts_array_get($response->createArray, 'user_id');
    557 
    558458            return [$userId, $embedUserId,null];
    559    
    560459        } catch (ApiException $e) {
    561            
    562460            return null;
    563461        }
    564462    }
     463
    565464    public function isUserExists($user_id){
    566465        $api = ts_api();
    567466        return $api->userClient()->isUserExists($user_id);
    568467    }
     468
    569469    public function getMagicLink($userId)
    570470    {
     
    572472        try {
    573473            $link = $api->userClient()->getMagicLink($userId);
    574             if (! $link instanceof UserMagicLinkResponse) {
    575                 return null;
    576             }
    577    
     474            if (! $link instanceof UserMagicLinkResponse) { return null; }
    578475            return $link->magicArray['full_url'];
    579476        } catch (ApiException $e) {
     
    585482    {
    586483        $api = ts_api();
    587 
    588         $users = $api->userClient()->getAllUsers([
    589             'filter' => [
    590                 // 'search' => $email
    591                 'email' => $email
    592             ]
    593         ]);
    594         // return ts_array_get($users, 'data', 0);
     484        $users = $api->userClient()->getAllUsers(['filter' => ['email' => $email]]);
    595485        return ts_array_get($users['data'], 0);
    596486    }
     
    599489    {
    600490        $baseUrl = ts_get_option('base_url');
    601         if (! $baseUrl) {
    602             return '';
    603         }
    604 
    605         $baseUrl = untrailingslashit($baseUrl) . "/user/{$tsUserId}/show";
    606 
    607         return $baseUrl;
     491        if (! $baseUrl) { return ''; }
     492        return untrailingslashit($baseUrl) . "/user/{$tsUserId}/show";
    608493    }
    609494
    610495    public static function shouldVerifyBeforeCheckoutAtAssets(){
    611         $verifyBeforeCheckout = ts_get_option('verify_on', Verification::VERIFY_BEFORE_CHECKOUT) == Verification::VERIFY_BEFORE_CHECKOUT;
    612 
    613         if (! $verifyBeforeCheckout) {
     496        if (ts_get_option('verify_on', Verification::VERIFY_BEFORE_CHECKOUT) !== Verification::VERIFY_BEFORE_CHECKOUT) {
    614497            return false;
    615498        }
    616 
    617499        $categories = (array)ts_get_option('product_categories', []);
    618500        $comparePriceMode = ts_get_option('compare_price_mode');
    619501        $comparePrice = ts_get_option('compare_price');
    620502        $isPriceChecked = true;
    621 
    622         if ($comparePriceMode && $comparePrice>0) {
     503        if ($comparePriceMode && $comparePrice > 0 && function_exists('WC') && WC()->cart) {
    623504            $cartTotalPrice = (double) WC()->cart->get_total('edit');
    624505            $comparePrice = doubleval($comparePrice);
    625             $isPriceChecked = false;
    626 
    627             if ($comparePriceMode == 'less_than') {
    628                 $isPriceChecked = $comparePrice > $cartTotalPrice;
    629             }
    630             else {
    631                 $isPriceChecked = $comparePrice < $cartTotalPrice;
    632             }
    633         }
    634 
    635         return (empty($categories) || ts_cart_has_products_in_categories($categories))
    636             && $isPriceChecked
    637             ;
    638     }
    639 
    640     public static function shouldVerifyBeforeCheckout($paymentMethod = null)
    641     {
    642         $verifyBeforeCheckout = ts_get_option('verify_on', Verification::VERIFY_BEFORE_CHECKOUT) == Verification::VERIFY_BEFORE_CHECKOUT;
    643 
    644         if (! $verifyBeforeCheckout) {
     506            $isPriceChecked = ($comparePriceMode == 'less_than') ? ($comparePrice > $cartTotalPrice) : ($comparePrice < $cartTotalPrice);
     507        }
     508        return (empty($categories) || ts_cart_has_products_in_categories($categories)) && $isPriceChecked;
     509    }
     510
     511public static function shouldVerifyBeforeCheckout($paymentMethod = null)
     512{
     513    if (ts_get_option('verify_on', self::VERIFY_BEFORE_CHECKOUT) !== self::VERIFY_BEFORE_CHECKOUT) {
     514        return false;
     515    }
     516    $applicableMethods = (array) ts_get_option('applicable_payment_methods', []);
     517    if (!empty($applicableMethods) && function_exists('WC')) {
     518       
     519        // --- THIS IS THE CRITICAL FIX FOR STATELESS VALIDATION ---
     520        $chosen_payment_method = $paymentMethod; // Rely ONLY on the passed-in argument.
     521
     522        if (!$chosen_payment_method || !in_array($chosen_payment_method, $applicableMethods)) {
    645523            return false;
    646524        }
    647 
    648         $categories = (array)ts_get_option('product_categories', []);
     525    }
     526        $categories = (array) ts_get_option('product_categories', []);
     527        if (!empty($categories) && function_exists('ts_cart_has_products_in_categories') && !ts_cart_has_products_in_categories($categories)) {
     528            return false;
     529        }
     530        $comparePriceMode = ts_get_option('compare_price_mode');
     531        $comparePrice = ts_get_option('compare_price');
     532        if ($comparePriceMode && !empty($comparePrice) && $comparePrice > 0 && function_exists('WC') && WC()->cart) {
     533            $cartTotalPrice = (double) WC()->cart->get_total('edit');
     534            $comparePrice = doubleval($comparePrice);
     535            if (($comparePriceMode == 'less_than' && $cartTotalPrice >= $comparePrice) || ($comparePriceMode == 'more_than' && $cartTotalPrice <= $comparePrice)) {
     536                return false;
     537            }
     538        }
     539        return true;
     540    }
     541
     542    public static function shouldVerifyAfterCheckout($orderId)
     543    {
     544        if (ts_get_option('verify_on', Verification::VERIFY_AFTER_CHECKOUT) !== Verification::VERIFY_AFTER_CHECKOUT) {
     545            return false;
     546        }
     547        $order = wc_get_order($orderId);
     548        if (! $order) { return false; }
     549        $orderPaymentMethod = $order->get_payment_method();
     550        $applicableMethodsData=ts_get_option('applicable_payment_methods');
     551        $paymentMethodRestrictionFlag = !($applicableMethodsData && count($applicableMethodsData)>0 && !in_array($orderPaymentMethod,$applicableMethodsData));
    649552        $comparePriceMode = ts_get_option('compare_price_mode');
    650553        $comparePrice = ts_get_option('compare_price');
    651554        $isPriceChecked = true;
    652         $applicableMethodsData=ts_get_option('applicable_payment_methods');
    653         if ($paymentMethod!==null){
    654             $chosen_payment_method = $paymentMethod;
    655         }else{
    656             $chosen_payment_method = WC()->session->get('chosen_payment_method');
    657         }
    658 
    659         $paymentMethodRestrictionFlag = true;
    660 
    661         if ($applicableMethodsData && count($applicableMethodsData)>0 && !in_array($chosen_payment_method,$applicableMethodsData))
    662         {
    663 
    664             $paymentMethodRestrictionFlag = false;
    665         }
    666 
    667         if ($comparePriceMode && $comparePrice>0) {
    668             $cartTotalPrice = (double) WC()->cart->get_total('edit');
    669             $comparePrice = doubleval($comparePrice);
    670             $isPriceChecked = false;
    671 
    672             if ($comparePriceMode == 'less_than') {
    673                 $isPriceChecked = $comparePrice > $cartTotalPrice;
    674             }
    675             else {
    676                 $isPriceChecked = $comparePrice < $cartTotalPrice;
    677             }
    678         }
    679 
    680         return (empty($categories) || ts_cart_has_products_in_categories($categories))
    681             && $isPriceChecked && $paymentMethodRestrictionFlag
    682         ;
    683     }
    684 
    685     public static function shouldVerifyAfterCheckout($orderId)
    686     {
    687         $verifyAfterCheckout = ts_get_option('verify_on', Verification::VERIFY_AFTER_CHECKOUT) == Verification::VERIFY_AFTER_CHECKOUT;
    688 
    689         if (! $verifyAfterCheckout) {
    690             return false;
    691         }
    692 
    693         $order = wc_get_order($orderId);
    694         if (! $order) {
    695             return false;
    696         }
    697 
    698         $orderPaymentMethod = $order->get_payment_method();
    699         $applicableMethodsData=ts_get_option('applicable_payment_methods');
    700 
    701         $paymentMethodRestrictionFlag = true;
    702 
    703         if ($applicableMethodsData && count($applicableMethodsData)>0 && !in_array($orderPaymentMethod,$applicableMethodsData))
    704         {
    705             $paymentMethodRestrictionFlag = false;
    706         }
    707 
    708         $comparePriceMode = ts_get_option('compare_price_mode');
    709         $comparePrice = ts_get_option('compare_price');
    710         $isPriceChecked = true;
    711 
    712         if ($comparePriceMode && $comparePrice>0) {
     555        if ($comparePriceMode && $comparePrice > 0) {
    713556            $orderTotalPrice = (double) $order->get_total('edit');
    714557            $comparePrice = doubleval($comparePrice);
    715             $isPriceChecked = false;
    716 
    717             if ($comparePriceMode == 'less_than') {
    718                 $isPriceChecked = $comparePrice > $orderTotalPrice;
    719             }
    720             else {
    721                 $isPriceChecked = $comparePrice < $orderTotalPrice;
    722             }
    723         }
    724 
     558            $isPriceChecked = ($comparePriceMode == 'less_than') ? ($comparePrice > $orderTotalPrice) : ($comparePrice < $orderTotalPrice);
     559        }
    725560        $categories = ts_get_option('product_categories', []);
    726 
    727         return (empty($categories) || ts_order_has_products_in_categories($order, $categories))
    728             && $isPriceChecked && $paymentMethodRestrictionFlag
    729         ;
     561        return (empty($categories) || ts_order_has_products_in_categories($order, $categories)) && $isPriceChecked && $paymentMethodRestrictionFlag;
    730562    }
    731563
     
    733565    {
    734566        $api = ts_api();
    735 
    736567        try {
    737             $response = $api->userClient()->updateUser($userId, [
    738                 'order_id' => $orderId
    739             ]);
    740 
     568            $api->userClient()->updateUser($userId, ['order_id' => $orderId]);
    741569            return true;
    742         }
    743         catch (ApiException $e) {
     570        } catch (ApiException $e) {
    744571            return false;
    745572        }
  • trust-swiftly-verification/trunk/trustswiftly-verification.php

    r3238884 r3320256  
    99 * Author:            Trust Swiftly
    1010 * Author URI:        https://www.trustswiftly.com
    11  * Version:           1.1.13
    12  * Text Domain:       Trust Swiftly Verification
     11 * Version:           1.1.14
     12 * Text Domain:       trust-swiftly-verification
    1313 */
    1414
     
    1919    exit;
    2020}
     21
     22// Define a constant for the main plugin file path.
     23// This is crucial for other parts of the plugin, like WooCommerce block compatibility.
     24if ( ! defined( 'TS_PLUGIN_BASE_PATH' ) ) {
     25    define( 'TS_PLUGIN_BASE_PATH', __FILE__ );
     26}
     27
     28if ( ! defined( 'TS_PLUGIN_VERSION' ) ) {
     29    define( 'TS_PLUGIN_VERSION', '1.1.14' ); // Make sure this matches the version in the header
     30}
    2131require_once __DIR__ . '/bootstrap.php';
    2232
    23 $aPlugins = apply_filters( 'active_plugins', get_option( 'active_plugins' ));
    24 if(empty($aPlugins)){
    25     $aPlugins = [];
     33/**
     34 * Begins execution of the plugin.
     35 *
     36 * Load the plugin only after all other plugins are loaded to ensure compatibility.
     37 */
     38add_action(
     39    'plugins_loaded',
     40    function () {
     41        new TrustVerifyPlugin();
     42    }
     43);
     44
     45register_activation_hook( __FILE__, array( 'TrustswiftlyVerification\TrustVerifyPlugin', 'activate' ) );
     46
     47/**
     48 * Adds a settings link to the plugin's action links on the plugins page.
     49 *
     50 * @param array $links An array of plugin action links.
     51 * @return array An array of plugin action links.
     52 */
     53function ts_add_plugin_page_settings_link( $links ) {
     54    $links[] = '<a href="' . admin_url( 'admin.php?page=ts-settings' ) . '">' . __( 'Settings', 'trust-swiftly-verification' ) . '</a>';
     55    return $links;
    2656}
    27 if ( in_array( 'woocommerce/woocommerce.php',  $aPlugins)&& file_exists(WP_PLUGIN_DIR.'/woocommerce/woocommerce.php') ) {
    28     register_activation_hook(__FILE__, array('TrustswiftlyVerification\TrustVerifyPlugin', 'activate'));
    29     new TrustVerifyPlugin();
    30     add_filter('plugin_action_links_'.plugin_basename(__FILE__), 'ts_add_plugin_page_settings_link');
    31     function ts_add_plugin_page_settings_link( $links ) {
    32         $links[] = '<a href="' .
    33             admin_url( 'admin.php?page=ts-settings' ) .
    34             '">' . __('Settings') . '</a>';
    35         return $links;
    36     }
    37 }
     57add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'ts_add_plugin_page_settings_link' );
  • trust-swiftly-verification/trunk/views/post-order-verification.php

    r2794742 r3320256  
     1<?php
     2/**
     3 * Template for displaying the verification UI on the "Thank You" (order confirmation) page.
     4 * FINAL VERSION: This template now uses the REST API for verification checks, ensuring
     5 * consistency with the main checkout page and correctly handling the "already verified" state.
     6 */
     7?>
     8
    19<?php if (! $isVerified): ?>
    2     <p id="verify_div">
    3         <?php _e('Please verify your account: ', 'trustswiftly-verifications'); ?>
    4     </p>
     10    <div class="ts-verification-callout ts-pulse-animation" id="ts-thank-you-verification">
     11        <p class="ts-verification-message" id="ts-thank-you-message">
     12            <?php _e('Please complete the verification to process your order:', 'trust-swiftly-verification'); ?>
     13        </p>
     14        <div id="verify_div" class="ts-verification-button-wrapper">
     15            <!-- The verification UI will be injected here by JavaScript -->
     16        </div>
     17    </div>
    518<?php else: ?>
    6     <p class="ts-verification-status">
    7         <img src="data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='96px' height='96px' viewBox='0 0 96 96' enable-background='new 0 0 96 96' xml:space='preserve'%3E%3Cg%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' fill='%236BBE66' d='M48,0c26.51,0,48,21.49,48,48S74.51,96,48,96S0,74.51,0,48 S21.49,0,48,0L48,0z M26.764,49.277c0.644-3.734,4.906-5.813,8.269-3.79c0.305,0.182,0.596,0.398,0.867,0.646l0.026,0.025 c1.509,1.446,3.2,2.951,4.876,4.443l1.438,1.291l17.063-17.898c1.019-1.067,1.764-1.757,3.293-2.101 c5.235-1.155,8.916,5.244,5.206,9.155L46.536,63.366c-2.003,2.137-5.583,2.332-7.736,0.291c-1.234-1.146-2.576-2.312-3.933-3.489 c-2.35-2.042-4.747-4.125-6.701-6.187C26.993,52.809,26.487,50.89,26.764,49.277L26.764,49.277z'/%3E%3C/g%3E%3C/svg%3E" alt="" width="30">
    8         <?php esc_html_e('Verifications Completed', 'trustswiftly-verification'); ?>
    9     </p>
     19    <div class="ts-verification-callout ts-verification-success">
     20        <p class="ts-verification-message">
     21            <img src="data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 96'%3E%3Cpath fill='%2328a745' d='M48,0c26.5,0,48,21.5,48,48s-21.5,48-48,48S0,74.5,0,48S21.5,0,48,0z M42.9,63.1l-16-16c-1.2-1.2-1.2-3.1,0-4.2s3.1-1.2,4.2,0l13.8,13.8l27.2-27.2c1.2-1.2,3.1-1.2,4.2,0s1.2,3.1,0,4.2L47.1,63.1C46.5,63.7,45.8,64,45,64S43.5,63.7,42.9,63.1z'/%3E%3C/svg%3E" alt="Success" width="24" style="vertical-align: middle; margin-right: 8px;">
     22            <?php esc_html_e('Verification Completed', 'trust-swiftly-verification'); ?>
     23        </p>
     24    </div>
    1025<?php endif; ?>
    11 <script>
    12     (function($) {
    13         // Custom code here
    14         $(function () {
    15             TSCheckoutConfig.user_email='<?php echo $user_email?>';
    16             window.TSVerification.init();
     26
     27<?php if (! $isVerified && ! empty($user_email)): ?>
     28<script type="text/javascript">
     29(function($) {
     30    $(document).ready(function() {
     31        var userEmail = <?php echo wp_json_encode($user_email); ?>;
     32        var $container = $('#ts-thank-you-verification');
     33        var $placeholder = $('#verify_div');
     34        var $message = $('#ts-thank-you-message');
     35
     36        if (!userEmail || $container.length === 0) {
     37            return;
     38        }
     39
     40        var config = window.TSCheckoutConfig;
     41        if (typeof config === 'undefined') {
     42            console.error('Trust Swiftly: Config object not found on Thank You page.');
     43            return;
     44        }
     45
     46        // --- FINAL FIX: Use the REST API for consistency ---
     47        // This mirrors the logic from checkout.js to get a reliable status.
     48       
     49        $placeholder.html('<div class="ts-verification-spinner" style="display: flex; justify-content: center; align-items: center;"><img src="/wp-includes/images/wpspin.gif" alt="Loading..." style="width: 20px; height: 20px;"></div>');
     50
     51        $.ajax({
     52            url: config.rest_url, // Use the modern REST API URL
     53            method: 'POST',
     54            beforeSend: function(xhr) {
     55                xhr.setRequestHeader('X-WP-Nonce', config.nonce); // Use the REST API nonce
     56            },
     57            data: {
     58                email: userEmail,
     59                // Provide context; backend might not need it, but it's good practice
     60                payment_method: 'thank_you_page'
     61            }
     62        }).done(function(resp) {
     63            $placeholder.empty();
     64
     65            if (resp.success) {
     66                if (resp.data.type === 'user_already_verified') {
     67                    // CORRECTLY handle the "already verified" state.
     68                    $container.removeClass('ts-pulse-animation').addClass('ts-verification-success');
     69                    $message.html('<img src="data:image/svg+xml,%3Csvg version=\'1.1\' xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 96 96\'%3E%3Cpath fill=\'%2328a745\' d=\'M48,0c26.5,0,48,21.5,48,48s-21.5,48-48,48S0,74.5,0,48S21.5,0,48,0z M42.9,63.1l-16-16c-1.2-1.2-1.2-3.1,0-4.2s3.1-1.2,4.2,0l13.8,13.8l27.2-27.2c1.2-1.2,3.1-1.2,4.2,0s1.2,3.1,0,4.2L47.1,63.1C46.5,63.7,45.8,64,45,64S43.5,63.7,42.9,63.1z\'/%3E%3C/svg%3E" alt="Success" width="24" style="vertical-align: middle; margin-right: 8px;"> <?php esc_html_e('Verification Completed', 'trust-swiftly-verification'); ?>');
     70                    $placeholder.remove();
     71
     72                } else if (resp.data.type === 'ok' && resp.data.link) {
     73                    // If verification is needed, show the button.
     74                    var $btn = $('<a>', {
     75                        id: 'ts-verify-link',
     76                        class: 'ts-verify-link',
     77                        target: '_blank',
     78                        href: resp.data.link
     79                    }).css({
     80                        "background-image": "url(" + config.btn_img + ")",
     81                        "display": "block",
     82                        "background-repeat": "no-repeat",
     83                        "background-size": "contain",
     84                        "margin": "10px auto 0"
     85                    });
     86                    $placeholder.append($btn);
     87
     88                } else {
     89                    $container.hide();
     90                }
     91            } else {
     92                // If the API call fails or says verification is not required for this option
     93                $container.hide();
     94            }
     95        }).fail(function() {
     96            $placeholder.empty();
     97            $message.text('A server error occurred. Please try refreshing the page.');
    1798        });
    18     })(jQuery);
     99    });
     100})(jQuery);
    19101</script>
     102<?php endif; ?>
Note: See TracChangeset for help on using the changeset viewer.