Stripe workflow with 3d secure

Can anyone enlighten me on the work flow for accepting cards that require 3DS additional security?

The stripe element collects the card details and gets a token which is used by the FileMaker hook script. This attempts to create a charge but returns the error code “authentication_required”

You’ll need to add an additional callback - take a look at which has a JavaScript snippet showing how to do that.

Thanks. All sorted now with “Payment Intents”

Hi Paul, have you successfully implemented payment intents including 3DS?

I’m almost done with it, but I have a small issue: In curl I specify the url that Stripe redirects to after the 3DS authentication as “-d return_url=”

The issue is, it looks like Stripe ignores “#/paymentResult” in the url when adding the parameters. That is, the url parameters are added after "“”, instead of after “/paymentResult”

So after 3DS authentication, the return url looks like this (test fictional data)

But it should look like this:

Still I can parse the parameters in the FM script on load, so it’s not the end of the world … but it doesn’t look very clean, is anybody having the same issue?

I thought it might be useful to show how we solved the 3d secure implementation.

Stripe Payment Process
include <script src=“”></script> in dom header (Site, Environment, DOM Header Insertions)

Add the following html element to form (uses tailwind classes for styling but could be customised). We use a modal specifically for payments so the paymentIntent can be generated and passed into the modal when it is loaded.

<div id=“card-element” class=” bg-gray-100 border rounded-lg px-2 py-3 sm:p-6 sm:h-16" w-full>

Run the following javascript on form load (for page/modal containing abover html)

window.stripe = Stripe(app.stripeKey {locale:‘en’});
var elements = window.stripe.elements();
var card = elements.create(‘card’);
window.card = card
card.addEventListener(‘change’, function(event) {
    _.set(model, ‘payment.cardComplete’, event.complete);
    if (event.error) {
        _.set(model, ‘payment.errorMsg’, event.error.message);
    }else {
       _.set(model, ‘payment.errorMsg’, ‘’);

Generate a payment intent on fms and send back to bf (either on form load or when items to be purchased are confirmed)
Have a payment button which runs the following

    .confirmCardPayment(model.paymentIntent.body.client_secret, {
        payment_method: {
            card: window.card,
    .then(function(result) {
        if (result.error) {
            model.payment.stripeResult = result.error.message;
            window.EventBus.$emit(‘processNamedAction’, {
                “action”: “namedAction”,
                “name”: “paymentError”,
                “options”: {}
        if (result.paymentIntent) {
            model.paid = true;
            model.payment.stripeResult = result;
            window.EventBus.$emit(‘processNamedAction’, {
                “action”: “namedAction”,
                “name”: “paymentSuccess”,
                “options”: {}

This will emit one of two named actions: paymentSuccess or paymentError
We use keys such as model.paying and model.paid to keep track of how far through the process the client has got, e.g. so buttons can be disabled to prevent double activation.

1 Like

Thanks for sharing,

with Code bases after >0.10.4you can simply use:
BF.namedAction('paymentError',{} )
in place of

            window.EventBus.$emit(‘processNamedAction’, {
                “action”: “namedAction”,
                “name”: “paymentError”,
                “options”: {}

Does the exact same thing but will be easy to remember in the future.
See docs for more details

1 Like

Thank you very much for sharing.