Fill in Stripe Elements JS for SCA / 3D Secure 2 and Capybara

Chris Oliver

October 23, 2019

Stripe Elements Javascript now has support for Strong Customer Authentication (SCA) with 3D Secure 2 (3DS2). This makes handling these in system tests a bit harder. Filling out the credit card fields in the past has been pretty straightforward, but now we have an extra modal to handle with SCA authentication.

In test mode, Stripe Elements JS displays a modal iframe and then dynamically loads an inner iframe that displays the "Complete authentication" and "Fail authentication" buttons for testing.

Stripe Element's card element loads an iframe inside the div that it's mounted to. Similarly, Stripe handles SCA injecting an iframe on the body to create a modal. This iframe contains another iframe that holds the actual authentication buttons.

Here are 3 helper methods you can use with Capybara for interacting with Stripe's card element and SCA modal buttons.

require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :chrome, screen_size: [1400, 1400]

  # Fills out the Stripe card element with the provided card details in Capybara
  # You can also provide a custom selector to find the correct iframe
  # By default, we use the ID of "#card-element" which Stripe uses in their documentation
  def fill_stripe_elements(card: , expiry: '1234', cvc: '123', postal: '12345', selector: '#card-element > div > iframe')
    find_frame(selector) do
      card.to_s.chars.each do |piece|
        find_field('cardnumber').send_keys(piece)
      end

      find_field('exp-date').send_keys expiry
      find_field('cvc').send_keys cvc
      find_field('postal').send_keys postal
    end
  end

  # Completes SCA authentication successfully
  def complete_stripe_sca
    find_frame('body > div > iframe') do
      # This helps find the inner iframe in the SCA modal's challenge frame which doesn't load immediately
      sleep 1

      find_frame('#challengeFrame') do
        find_frame("iframe[name='acsFrame']") do 
          click_on "Complete authentication"
        end
      end
    end
  end

  # Fails SCA authentication
  def fail_stripe_sca
    find_frame('body > div > iframe') do
      # This helps find the inner iframe in the SCA modal's challenge frame which doesn't load immediately
      sleep 1

      find_frame('#challengeFrame') do
        find_frame("iframe[name='acsFrame']") do 
          click_on "Fail authentication"
        end
      end
    end
  end

  # Generic helper for finding an iframe 
  def find_frame(selector, &block)
    using_wait_time(15) do
      frame = find(selector)
      within_frame(frame) do
        block.call
      end
    end
  end
end

To fill out the Stripe Elements JS credit card field with the default #card-element > div > iframe selector, you can run:

fill_stripe_elements(card: '4242 4242 4242 4242')

To use a different selector if your element doesn't have the ID of card-element on it, you can pass in the selector option:

fill_stripe_elements(card: '4242 4242 4242 4242', selector: '#some-other-selector')

To complete the SCA modal and confirm authentication, you can run:

complete_stripe_sca

To fail the SCA modal and test when authentication fails, you can run:

fail_stripe_sca

I've been using this with Rails system tests for JumpstartRails.com for a while now and it works great.

👉 Plus, I've created the Payments with Ruby on Rails Master Class to walk through every single step of setting up Stripe payments with Strong Customer Authentication support for your Rails app. We cover everything from one-time payments, subscription billing, receipts, refunds, webhooks, strong customer authentication confirmation, and testing. It took me 3 months to learn, implement, and test Stripe with SCA in my apps, so I made this course to save you the trouble!

If you have any tweaks that improve this, leave a comment below!

P.S. You might enjoy following me on Twitter.


Comments