Announcement

Collapse
No announcement yet.

Add Invisible reCaptcha v2 to PROD page for AJAX Add to Cart Form

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Add Invisible reCaptcha v2 to PROD page for AJAX Add to Cart Form

    We get a lot of bot activity spamming our PROD page attributes which creates carts with spammy links and information.

    So, to combat that, I would like to add the invisible recaptcha v2 to our PROD page for the Add to Cart form.

    We are on the most current update for Miva running the Suivant ReadyTheme.

    Here is what I have done so far using google's guide and some other more generic online guides:

    Code Added to Head Tag:

    Code:
        <script type="text/javascript">
            var onSubmit = function(response) {
                document.getElementById("js-purchase-product").submit(); // send response to your backend service
            };
        </script>
    Code for Add to Cart Form:

    Code:
            <form method="post" action="&mvte:urls:BASK:auto;" name="add" id="js-purchase-product">
                <input type="hidden" name="Old_Screen" value="&mvte:global:Screen;" />
                <input type="hidden" name="Old_Search" value="&mvte:global:Search;" />
                <input type="hidden" name="Action" value="ADPR" />
                <input type="hidden" name="Product_Code" value="&mvte:product:code;" />
                <input type="hidden" name="Category_Code" value="&mvte:global:category_code;" />
                <input type="hidden" name="Offset" value="&mvte:global:Offset;" />
                <input type="hidden" name="AllOffset" value="&mvte:global:AllOffset;" />
                <input type="hidden" name="CatListingOffset" value="&mvte:global:CatListingOffset;" />
                <input type="hidden" name="RelatedOffset" value="&mvte:global:RelatedOffset;" />
                <input type="hidden" name="SearchOffset" value="&mvte:global:SearchOffset;" />
                <div class="row">
                    <mvt:if expr="l.settings:product:inv_active OR l.settings:attributemachine:product:inv_active">
                        <div id="js-inventory-message" class="column whole print-hide">
                            <p>&mvt:product:inv_long;</p>
                        </div>
                    </mvt:if>
                    <div class="column whole">
                        <mvt:item name="customfields" param="Read_Product_Code( l.settings:product:code, 'familytree', g.familytree )" />
                        <mvt:if expr="NOT ISNULL g.familytree">
                            <div id="family-tree">&mvt:global:familytree;</div>
                        </mvt:if>
                        <mvt:item name="customfields" param="Read_Product_Code( l.settings:product:code, 'productbulletpoints', g.productbulletpoints )" />
                        <mvt:if expr="NOT ISNULL g.productbulletpoints">
                            <div>&mvt:global:productbulletpoints;</div>
                        </mvt:if>
                        <mvt:if expr="l.settings:product:code EQ 'EFTGH' OR l.settings:product:code EQ 'EFTSH'">
                            <div class="align-right">
                                <p class="italic">Done Adding Hearts to Your Tree?</p>
                                <a href="https://&mvt:global:domain:name;/&mvte:product:code;.html#RelatedProducts" rel="nofollow" class="button corners bg-sky mbm1-25" style="white-space:normal;height:auto;line-height:1;padding:0.5rem">Click Here to Add Discs</a>
                            </div>
                        <mvt:elseif expr="l.settings:product:code EQ 'EFTGD' OR l.settings:product:code EQ 'EFTSD' OR l.settings:product:code EQ 'EFTDD' OR l.settings:product:code EQ 'EFTDA' OR l.settings:product:code EQ 'EFTADB' OR l.settings:product:code EQ 'EFTPD'">
                            <div class="align-right">
                                <p class="italic">Done Adding Discs to Your Tree?</p>
                                <a href="https://&mvt:global:domain:name;/&mvte:product:code;.html#RelatedProducts" rel="nofollow" class="button corners bg-sky mbm1-25" style="white-space:normal;height:auto;line-height:1;padding:0.5rem">Click Here to Add Hearts</a>
                            </div>
                        </mvt:if>
                        <div id="js-purchase-message" class="message message-warning purchase-message"></div>
                    </div>
                    <div id="js-product-attributes" class="column whole product-attributes mtp1-25 mbm1-25">
                        <!--[if gte IE 9]><!-->
                            <script src="&mvte:global:theme_path;/js/jquery-3.4.0.min.js"></script>
                        <!--<![endif]-->
                        <mvt:item name="product_attributes" param="product:id" />
                    </div>
                    <mvt:if expr="g.hasSwatches">
                        <div class="column whole product-swatches">
                            <label>&mvte:global:swatch_prompt;</label>
                            <span id="js-swatch-name" class="bold swatch-name">&nbsp;</span>
                            <hr noshade />
                            <div id="js-swatches"></div>
                        </div>
                    </mvt:if>
    
    
                    <div class="column whole np">
                        <div class="g-recaptcha" data-sitekey="SITE-KEY-XXXXXXXXXXXX" data-callback="onSubmit" data-badge="inline" data-size="invisible"></div>
                        <script src="https://www.google.com/recaptcha/api.js" async defer></script>
                    </div>
                    <!-- end invisible recaptcha -->
    
    
                    <a id="AddToCart"></a>
                </div>
                <div class="row corners add-to-cart-wrap bg-lt-gray">
                    <div class="column whole align-center">
                        <div class="bold">Price Subtotal<br /><span class="small normal">(with options added)</span></div>
                        <mvt:if expr="l.settings:product:price GT 0">
                            <div id="js-price-value" class="h3 charcoal nm" data-base-price="&mvt:product:price;">&mvt:product:formatted_price;</div>
                        <mvt:else>
                            <div id="js-price-value" class="all-hidden" data-base-price="&mvt:product:price;"></div>
                        </mvt:if>
                        <br />
                    </div>
                    <div class="column whole small-half medium-whole large-two-fifths x-large-three-tenths print-hide">
                        <div class="row quantity-wrap align-center">
                            <div class="column whole np">
                                <span id="js-decrease-quantity" class="bg-gray corners-left decrease-quantity" unselectable="on"
                                 data-rt-icon="&#xf068;"></span>
                                <input type="tel" name="Quantity" value="1" id="l-quantity" class="align-center" style="font-size:1rem" />
                                <span id="js-increase-quantity" class="bg-gray corners-right increase-quantity" unselectable="on"
                                 data-rt-icon="&#xf067;"></span>
                            </div>
                        </div>
                        <mvt:if expr="NOT l.settings:subscription:mandatory">
                            <div class="row align-center">
                                <div class="column whole np">
                                    <span data-mmnodisable="true">
                                        <span onClick="document.forms.add.action = '&mvtj:urls:WISH:secure;'; document.forms.add.elements.Action.value = 'ATWL';" class="small" data-rt-icon="&#xf004;">
                                            <mvt:item name="buttons" param="AddToWishList" />
                                        </span>
                                    </span>
                                </div>
                            </div>
                        </mvt:if>
                    </div>
                    <div class="column whole small-half medium-whole large-three-fifths x-large-seven-tenths print-hide">
                        <div class="breaker small-all-hidden medium-all-shown large-all-hidden"></div>
    
    <mvt:comment> ========== for Attribute Magic module ========== </mvt:comment>
    <mvt:if expr="g.ATMG_Edit_Item">
                        <span onclick="document.forms.add.action = '&mvtj:urls:BASK:auto;'; document.forms.add.elements.Action.value = 'RGRP,ADPR';">
                            <input type="submit" value="Replace Item In Cart" data-value="Replace Item In Cart" id="add-to-cart" class="button button-block corners uppercase add-to-cart black bg-yellow bold" />
                            <input type="hidden" name="Basket_Group" value="&mvte:global:Basket_Group;" />
                        </span>
    <mvt:else>
                        <span onclick="document.forms.add.action = '&mvtj:urls:BASK:auto;'; document.forms.add.elements.Action.value = 'ADPR';">
                            <input type="submit" value="Add to Cart" data-value="Add to Cart" id="js-add-to-cart" class="button button-block corners uppercase add-to-cart black bg-sky bold" />
                        </span>
    </mvt:if>
                    </div>
                </div>
            </form>
    Code for AJAX Add to Cart in scripts.js file

    Code:
            // ---- AJAX Add To Cart ---- //
    
            function addToCart () {
    
                $('#js-add-to-cart').on('click', function (e) {
    
                    var purchaseForm = $('#js-purchase-product');
    
                    // Check the form is not currently submitting
    
                    if (purchaseForm.data('formstatus') !== 'submitting') {
    
                        // Set up variables
    
                        var form = purchaseForm,
    
                            formData = form.serialize(),
    
                            randomNo = Math.ceil(Math.random() * 1000000), // IE Hack: Creating random number to refresh ajax call
    
                            formUrl = form.attr('action'),
    
                            formMethod = form.attr('method'),
    
                            responseMessage = $('#js-purchase-message'),
    
                            miniBasket = $('#js-mini-basket-container'),
    
                            processingImage = $('#js-processing-purchase'),
    
                            purchaseButton = $(this),
    
                            purchaseButtonText = purchaseButton.val();
    
    
    
                        if (/\?/.test(formUrl)) {
    
                            formUrl = formUrl + '&v=' + randomNo;
    
                        }
    
                        else {
    
                            formUrl = formUrl + '?v=' + randomNo;
    
                        };
    
    
    
                        // Add status data to form
    
                        form.data('formstatus', 'submitting');
    
    
    
                        // Show processing message
    
                        processingImage.show();
    
                        purchaseButton.toggleDisabled().val('Processing...');
    
                        responseMessage.html('').hide();
    
    
    
                        // Send data to server for validation
    
                        $.ajax({
    
                            url: formUrl,
    
                            type: formMethod,
    
                            data: formData,
    
                            success: function(data, textStatus, jqXHR) {
    
                                if (data.search(/id="js-BASK"/i) != -1) {
    
                                    $('html, body').animate({scrollTop: '0px'}, 250);
    
                                    var responseMiniBasket = $(data).find('#js-mini-basket-container'),
    
                                        miniBasketCount = responseMiniBasket.contents()[1].getAttribute('data-itemcount'),
    
                                        miniBasketSubtotal = ' ' + responseMiniBasket.contents()[1].getAttribute('data-subtotal'),
    
                                        miniBasketLinkCount = $('#js-mini-basket-count, #js-mobile-basket-button .notification'),
    
                                        miniBasketLinkSubtotal = $('#js-mini-basket-subtotal');
    
    
    
                                    miniBasketLinkCount.text(miniBasketCount); // Update basket quantity (display only)
    
                                    miniBasketLinkSubtotal.text(miniBasketSubtotal); // Update basket subtotal (display only)
    
    
    
                                    miniBasket.html(responseMiniBasket.contents()).addClass('open');
                                    $('.mini-basket-table-wrap').scrollTop($('.mini-basket-table-wrap')[0].scrollHeight);
    
                                    setTimeout(function () {
    
                                        miniBasket.removeClass('open');
    
                                        if ( $( "#family-tree" ).length ) {
    
                                            $(document).ready(function(){
                                                var url = "/family-tree-hearts-discs.html";
                                                $(location).attr('href',url);
                                            });
    
                                        };
    
                                        if ( $( "#wholesale-family-tree" ).length ) {
    
                                            $(document).ready(function(){
                                                var url = "/wholesale-family-tree-hearts-discs.html";
                                                $(location).attr('href',url);
                                            });
    
                                        };
    
                                    }, 3000);
    
                                    // Re-Initialize Attribute Machine (if it is active)
    
                                    if (typeof attrMachCall !== 'undefined') {
    
                                        attrMachCall.Initialize();
    
                                    };
    
                                }
    
                                else if(data.search(/id="js-PATR"/i) != -1) {
    
                                    var missingAttrs = [];
    
                                    form.find('.required').each(function () {
    
                                        missingAttrs.push(' ' + $(this).attr('title'));
    
                                    });
    
                                    responseMessage.html('All <em class="red">Required</em> options have not been selected.<br />Please review the following options: <span class="red">' + missingAttrs + '</span>.').fadeIn();
                                        $('html, body').animate({scrollTop: ($('#js-purchase-message').offset().top - 100)},500);
    
                                }
    
                                else if(data.search(/id="js-PLMT"/i) != -1) {
    
                                    responseMessage.html('We do not have enough of the Size/Color you have selected.<br />Please adjust your quantity.').fadeIn().delay(3000).fadeOut();
                                        $('html, body').animate({scrollTop: ($('#js-inventory-message').offset().top - 100)},500);
    
                                }
    
                                else if(data.search(/id="js-POUT"/i) != -1) {
    
                                    responseMessage.html('The Size/Color you have selected is out of stock.<br />Please review your options or check back later.').fadeIn().delay(3000).fadeOut();
                                        $('html, body').animate({scrollTop: ($('#js-inventory-message').offset().top - 100)},500); 
    
                                }
    
                                else {
    
                                    responseMessage.html('Please review options.').fadeIn().delay(3000).fadeOut();
                                        $('html, body').animate({scrollTop: ($('#js-purchase-message').offset().top - 100)},500);
    
                                };
    
    
    
                                // Hide processing message and reset formstatus
    
                                processingImage.hide();
    
                                purchaseButton.toggleDisabled().val(purchaseButtonText);
    
                                form.data('formstatus', 'idle');
    
                            },
    
                            error: function (jqXHR, textStatus, errorThrown) {
    
                            }
    
                        });
    
                    };
    
                    // Prevent form from submitting
    
                    e.stopImmediatePropagation();
                    e.preventDefault();
    
                });
    
            };
    
            var addToCart = new addToCart;

    The trouble I am having is:


    Where do I add grecaptcha.execute();?


    I want to keep the functionality of the AJAX Add to Cart and Form Validation.


    Here is a link to a product page for reference: https://www.loveisarose.com/ILYR11.html



    Thanks in advance for any help or light shed on the situation.
    Nick Harkins
    www.loveisarose.com

    #2
    I'd think the most obvious point would be at the start. And if not true, just fail silently.
    Bruce Golub
    Phosphor Media - "Your Success is our Business"

    Improve Your Customer Service | Get MORE Customers | Edit CSS/Javascript/HTML Easily | Make Your Site Faster | Get Indexed by Google | Free Modules | Follow Us on Facebook
    phosphormedia.com

    Comment


      #3
      I'm not really familiar with adding reCaptcha, however if you need that function to run prior to the submit, I would modify the add to cart function to be onSubmit of the form and add the execute call as an onClick of the button.
      Matt Zimmermann

      Miva Web Developer
      Alchemy Web Development
      https://www.alchemywebdev.com
      Site Development - Maintenance - Consultation

      Miva Certified Developer
      Miva Professional Developer

      https://www.dev4web.net | Twitter

      Comment


        #4
        Okay. Thanks guys.

        I would prefer it to run after everything is validated.

        That way the reCaptcha is only executed if all the required attributes are filled out or selected.

        Would that be a similar situation as you suggested Matt?
        Nick Harkins
        www.loveisarose.com

        Comment


          #5
          It should be.
          Matt Zimmermann

          Miva Web Developer
          Alchemy Web Development
          https://www.alchemywebdev.com
          Site Development - Maintenance - Consultation

          Miva Certified Developer
          Miva Professional Developer

          https://www.dev4web.net | Twitter

          Comment


            #6
            Okay.

            Do i also need to change the add to cart button html to onsubmit? or just the function in the Scripts.js file?

            Code:
                                <span onclick="document.forms.add.action = '&mvtj:urls:BASK:auto;'; document.forms.add.elements.Action.value = 'ADPR';">
                                    <input type="submit" value="Add to Cart" data-value="Add to Cart" id="js-add-to-cart" class="button button-block corners uppercase add-to-cart black bg-sky bold" />
                                </span>
            Nick Harkins
            www.loveisarose.com

            Comment


              #7
              I would append to the button's onClick call.
              Matt Zimmermann

              Miva Web Developer
              Alchemy Web Development
              https://www.alchemywebdev.com
              Site Development - Maintenance - Consultation

              Miva Certified Developer
              Miva Professional Developer

              https://www.dev4web.net | Twitter

              Comment


                #8
                I think this is a bit out of my skill set to get working properly.

                Even if I did get it to work, I just realized that if a user has javascript disabled, then they won't be able to add products to the cart. Can't seem to find a current noscript fallback.

                This makes me want to remove reCaptcha for our checkout as well.

                Somehow we still have lots of users with javascript disabled.

                Which is why we have noscript fallbacks all over our site.

                I guess we will have to deal with the bot spam.

                Or perhaps add a hidden field that won't allow forms to submit if it is filled out by a bot.

                Thanks for the help anyways though.

                Appreciate it.

                Nick Harkins
                www.loveisarose.com

                Comment

                Working...
                X