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!