2

I'm trying to validate my form before the stripe payment modal appears, but the modal appears before the form can be validated. I'm using jquery validation plugin for my validation. I've tried adding the Stripe integration code in the submitHandler function, but still no luck. Below is what I currently have for my code:

<!-- google & apple pay btn -->
<div id="payment-request-button"
     class="btn__link btn__link--fullwidth-mob btn__link--paypal-flex btn__link--marginLeft-0">
    <%--A Stripe Element will be inserted here--%>
</div>

<!--  google & apple validation btn -->
<input id="paymentRequestButtonValidation" 
       class="btn__link btn__link--fullwidth-mob btn__link--secondary btn__submit" 
       type="submit" value="G Pay Validation">
var btnSubmitId = '';

//-- get the id of the btn submit
$('.btn__submit').click(function (event) {
    btnSubmitId = event.target.id;
});

/* Stripe Integration */
let searchParams = new URLSearchParams(window.location.search);
var urlAmount = searchParams.get('Amount');
var urlAmountNum = parseFloat(urlAmount);
var donationAmount = urlAmountNum * 100;
var clientSecret = "<%= clientSecret %>";
var stripe = Stripe(stripeCredentials);
var paymentRequest = stripe.paymentRequest({
    country: 'GB',
    currency: 'gbp',
    total: {
        label: 'Test payment',
        amount: donationAmount
    },
    requestPayerName: true,
    requestPayerEmail: true,
});
var elements = stripe.elements();
var prButton = elements.create('paymentRequestButton', {
    paymentRequest: paymentRequest,
});

// Check the availability of the Payment Request API first.
paymentRequest.canMakePayment().then(function (result) {
    if (result) {
        // if available mount/create the button
        prButton.mount('#payment-request-button');
    } else {
        // if NOT available hide the button and console log it
        document.getElementById('payment-request-button').style.display = 'none';
        console.log('ERROR - btn not available & can\'t be mounted');
    }
});

paymentRequest.on('paymentmethod', function (ev) {
    // Confirm the PaymentIntent without handling potential next actions (yet).
    stripe.confirmCardPayment(
        clientSecret, { payment_method: ev.paymentMethod.id}, 
                      { handleActions: false }

    ).then(function (confirmResult) {
        if (confirmResult.error) {
            // Report to the browser that the payment failed, prompting it to
            // re-show the payment interface, or show an error message and close
            // the payment interface.
            ev.complete('fail');
        } else {
            // Report to the browser that the confirmation was successful, prompting
            // it to close the browser payment method collection interface.
            ev.complete('success');
            // Check if the PaymentIntent requires any actions and if so let Stripe.js
            // handle the flow. If using an API version older than "2019-02-11"
            // instead check for: `paymentIntent.status === "requires_source_action"`.
            if (confirmResult.paymentIntent.status === "requires_action") {
                // Let Stripe.js handle the rest of the payment flow.
                stripe.confirmCardPayment(clientSecret).then(function (result) {
                    if (result.error) {
                        // The payment failed -- ask your customer for a new payment method.
                    } else {
                        // The payment has succeeded.
                    }
                });
            } else {
                // The payment has succeeded.
            }
        }
    });

    prButton.on('click', function (ev) {
        // get the current amount from the #donationValue inout field
        paymentRequest.update({
            total: {
                label: 'Test payment',
                amount: $("#donationValue").val() * 100,
            },
        });
    })
});

// -- single form validation
$('#singleForm').validate({
    rules: {
        donationValue: {
            required: true,
            number: true,
            twoDecimal: true
        },
        donationtype: {
            required: true
        },
        firstname: {
            required: true
        },
        lastname: {
            required: true
        },
        email: {
            required: true
        },
        addressSearch: {
            required: true
        },
        address1: {
            required: true
        },
        postcode: {
            required: function (e) {
                return $(e).closest('form').find('#country').val() == 'United Kingdom';
            }
        },
        town: {
            required: true
        },
        country: {
            required: true
        },
        mobile: {
            required: '#receiveSMS:checked'
        }
    },
    messages: {
        donationValue: {
            required: 'Please enter your donation amount.',
            number: 'Please enter a valid donation amount.'
        },
        donationtype: 'Please select one of the options for Gift Aid.',
        firstname: 'Please enter a valid first name.',
        lastname: 'Please enter a valid last name.',
        email: 'Please enter a valid email address.',
        addressSearch: 'Please enter a valid postcode, street name or address.',
        address1: 'Please enter a valid address.',
        postcode: 'Please enter a valid postcode.',
        town: 'Please enter a valid town/city.',
        country: 'Please select a valid country.',
        mobile: 'Please enter your mobile phone number above'
    },
    highlight: function (element) {
        $(element).parent().find('span').addClass('error__text--icon');
        $(element).parent().find('input').addClass('form__input--error');
        $(element).parent().find('select').addClass('form__input--error');

        if (element.id == 'dtOwnDonation') {
            $(element).parent().find('span').removeClass('error__text--icon');
        }
    },
    unhighlight: function (element) {
        $(element).parent().find('span').removeClass('error__text--icon');
        $(element).parent().find('input').removeClass('form__input--error');
        $(element).parent().find('select').removeClass('form__input--error');
    },
    submitHandler: function () {
        if (btnSubmitId == 'singleBtnValidation') {
            $('#singleBtn').click();
            console.log('debit/credit card form - validation successful');

        } else if (btnSubmitId == 'paymentRequestButtonValidation') {
            console.log('paymentRequestButtonValidation - validation successful');
        }
    }
});

Thanks in advance!

George Sun
  • 85
  • 6
  • 1
    Stripe elements are in an iframe that you can't access. You would be violating PCI compliance if you could access those elements. – Barmar Dec 16 '21 at 16:52
  • Thanks for the information. Is there another way for me to run validation on the form, before the payment popup? I won't be validating the stripe fields elements, just users details. Like email, phone number, address etc. – user3546749 Dec 16 '21 at 17:19
  • Ah, we don't use the Stripe popup. We embed the stripe.js elements in our regular form. I'm not sure how to do it with Stripe Checkout. – Barmar Dec 16 '21 at 17:21
  • Ah ok, Thanks for taking a look anyways :) – user3546749 Dec 16 '21 at 17:23

1 Answers1

2

So you're using PaymentRequest Button, which by default does not give your webpage an event when the browser modal (e.g. Apple Pay or Google Pay) is shown (when PaymentRequest Button is pressed by the customer).

What you can instead do is manually mount your own button (styled as an Apple Pay or Google Pay button depending on the browser) and then manually show the PaymentRequest Button sheet using paymentRequest.show(): https://stripe.com/docs/js/payment_request/show

That way, you can do any form validation on the button press. The Stripe docs mention this approach for using your own button here: https://stripe.com/docs/stripe-js/elements/payment-request-button?html-or-react=html#html-js-own-button

hmunoz
  • 2,791
  • 5
  • 11