/stripe-guides

How to test 3D Secure payments in Stripe?

Learn how to test 3D Secure payments in Stripe, set up your test environment, use test cards, and handle authentication scenarios to ensure secure, compliant payments.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free consultation

How to test 3D Secure payments in Stripe?

How to Test 3D Secure Payments in Stripe

 

Introduction

 

Testing 3D Secure (3DS) payments in Stripe is essential for ensuring your integration works correctly before processing real transactions. 3D Secure adds an additional authentication layer for card payments, helping to reduce fraud and comply with regulations like Strong Customer Authentication (SCA) in Europe. This tutorial will guide you through the entire process of setting up and testing 3D Secure payments in Stripe's test environment.

 

Step 1: Set Up a Stripe Test Account

 

Before testing 3D Secure payments, you need a Stripe test account:

  1. Go to the Stripe website (https://stripe.com) and sign up for an account if you don't have one.
  2. Make sure you're in test mode, which is indicated by the "Test Mode" label in the dashboard.
  3. Locate your API keys in the Developers section of your Stripe dashboard.

// Example of accessing your API keys
// Dashboard → Developers → API keys
const stripe = require('stripe')('sk_test_YourTestSecretKey');

 

Step 2: Install and Configure the Stripe Library

 

Depending on your tech stack, install the appropriate Stripe library:

For Node.js:


npm install stripe

For PHP:


composer require stripe/stripe-php

For Python:


pip install stripe

 

Step 3: Implement Stripe Elements for Card Collection

 

Stripe Elements provides ready-made UI components for collecting card information securely:





  Stripe 3D Secure Test
  
  


  

 

Step 4: Create a Payment Intent with 3DS Support

 

Payment Intents are Stripe's API for handling complex payment flows, including 3D Secure:


// Server-side code (Node.js example)
const stripe = require('stripe')('sk_test_YourTestSecretKey');

async function createPaymentIntent() {
  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount: 1999, // Amount in cents
      currency: 'usd',
      payment_method_types: ['card'],
      // Request 3D Secure authentication
      setup_future_usage: 'off\_session',
      // Optional: Force 3D Secure for testing
      // automatic_payment_methods: {
      //   enabled: true,
      // }
    });
    
    return paymentIntent;
  } catch (error) {
    console.error('Error creating payment intent:', error);
    throw error;
  }
}

 

Step 5: Confirm the Payment and Handle 3D Secure Authentication

 

Add the confirmation logic to your client-side code:


// Client-side JavaScript
const form = document.getElementById('payment-form');

form.addEventListener('submit', async (event) => {
  event.preventDefault();
  
  // Disable the submit button to prevent repeated clicks
  const submitButton = form.querySelector('button');
  submitButton.disabled = true;
  
  // Get the PaymentIntent client secret from your server
  const response = await fetch('/create-payment-intent', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: 1999,
      currency: 'usd',
    }),
  });
  
  const data = await response.json();
  const { clientSecret } = data;
  
  // Confirm the payment with the card Element
  const result = await stripe.confirmCardPayment(clientSecret, {
    payment\_method: {
      card: card,
      billing\_details: {
        name: 'Test Customer',
      },
    },
  });
  
  if (result.error) {
    // Show error to your customer
    const errorElement = document.getElementById('card-errors');
    errorElement.textContent = result.error.message;
    submitButton.disabled = false;
  } else {
    // The payment is processed or requires 3D Secure authentication
    if (result.paymentIntent.status === 'succeeded') {
      // Payment succeeded
      window.location.href = '/success';
    } else if (result.paymentIntent.status === 'requires\_action') {
      // 3D Secure authentication is required
      const { error, paymentIntent } = await stripe.handleCardAction(
        result.paymentIntent.client\_secret
      );
      
      if (error) {
        // Authentication failed
        const errorElement = document.getElementById('card-errors');
        errorElement.textContent = error.message;
      } else if (paymentIntent.status === 'succeeded') {
        // Authentication succeeded
        window.location.href = '/success';
      }
    }
  }
  
  submitButton.disabled = false;
});

 

Step 6: Use Stripe Test Cards for 3D Secure Testing

 

Stripe provides specific test cards for 3D Secure testing:


// 3D Secure Authentication Required and Succeeds
4000000000003220

// 3D Secure Authentication Required and Fails
4000008400001629

// 3D Secure Required but Not Supported by Issuer
4000000000003063

// 3D Secure Not Required
4242424242424242

For each of these cards, you can use:

  • Any future expiry date (e.g., 12/34)
  • Any 3-digit CVC (e.g., 123)
  • Any billing postal code (e.g., 12345)

 

Step 7: Test the Complete 3D Secure Flow

 

Now, you'll want to test the complete flow:

  1. Enter one of the test card numbers from Step 6.
  2. Submit the payment form.
  3. For cards that require 3D Secure, a modal will appear.
  4. In the test environment, you can complete the authentication by clicking the "Complete" button.
  5. Verify that your success or failure handling code works as expected.

Here's server-side code to handle the webhook for the completed payment:


// Node.js with Express example
const express = require('express');
const app = express();
const stripe = require('stripe')('sk_test_YourTestSecretKey');

app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      'whsec\_YourWebhookSecret'
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle the event
  if (event.type === 'payment\_intent.succeeded') {
    const paymentIntent = event.data.object;
    // Payment was successful
    console.log('PaymentIntent was successful!');
  } else if (event.type === 'payment_intent.payment_failed') {
    const paymentIntent = event.data.object;
    console.log('Payment failed:', paymentIntent.last_payment_error?.message);
  }

  // Return a 200 response to acknowledge receipt of the event
  res.send();
});

app.listen(3000, () => console.log('Running on port 3000'));

 

Step 8: Test Different 3D Secure Scenarios

 

It's important to test various 3D Secure scenarios to ensure your application handles all cases correctly:


// Test function to try different 3D Secure scenarios
async function test3DSecureScenarios() {
  const testCases = [
    {
      description: "3D Secure Required & Succeeds",
      cardNumber: "4000000000003220",
      expectedOutcome: "Payment should complete successfully after 3DS authentication"
    },
    {
      description: "3D Secure Required & Fails",
      cardNumber: "4000008400001629",
      expectedOutcome: "Payment should fail after 3DS authentication"
    },
    {
      description: "3D Secure Required but Not Supported",
      cardNumber: "4000000000003063",
      expectedOutcome: "Payment should fail as 3DS is required but not supported"
    },
    {
      description: "3D Secure Not Required",
      cardNumber: "4242424242424242",
      expectedOutcome: "Payment should complete successfully without 3DS"
    }
  ];
  
  console.log("Starting 3D Secure test scenarios...");
  
  for (const testCase of testCases) {
    console.log(`\nTesting: ${testCase.description}`);
    console.log(`Card: ${testCase.cardNumber}`);
    console.log(`Expected outcome: ${testCase.expectedOutcome}`);
    
    // Here you would programmatically test each scenario
    // In a real implementation, you might use a testing framework
    // or simulate user input in a browser automation tool
  }
}

 

Step 9: Monitor and Debug 3D Secure Payments

 

Use Stripe's dashboard and logs to monitor and debug your 3D Secure test payments:


// Node.js example of retrieving and inspecting a PaymentIntent
async function checkPaymentStatus(paymentIntentId) {
  try {
    const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId);
    
    console.log('Payment Intent Status:', paymentIntent.status);
    console.log('Last Payment Error:', paymentIntent.last_payment_error);
    
    // Check if 3D Secure was used
    const charges = paymentIntent.charges.data;
    if (charges.length > 0) {
      console.log('3D Secure used:', charges[0].payment_method_details.card.three_d_secure !== null);
      console.log('3D Secure result:', charges[0].payment_method_details.card.three_d_secure?.result);
    }
    
    return paymentIntent;
  } catch (error) {
    console.error('Error retrieving payment intent:', error);
    throw error;
  }
}

 

Step 10: Implement Error Handling for 3D Secure Failures

 

Proper error handling is crucial for a good user experience:


// Client-side error handling for 3D Secure
function handle3DSecureError(error) {
  const errorMessages = {
    'authentication\_rejected': 'Your bank declined the authentication. Please try another payment method.',
    'not\_enrolled': 'Your card does not support 3D Secure. Please contact your bank or try another card.',
    'timeout': 'The authentication timed out. Please try again.',
    'processing\_error': 'An error occurred while processing the authentication. Please try again.'
  };
  
  const errorElement = document.getElementById('card-errors');
  const errorType = error.code || 'processing\_error';
  
  errorElement.textContent = errorMessages[errorType] || error.message || 'An unexpected error occurred.';
  
  // Log the error for debugging (in development only)
  console.error('3D Secure Error:', error);
  
  // Enable the payment button again
  document.querySelector('button[type="submit"]').disabled = false;
}

 

Conclusion

 

Testing 3D Secure payments in Stripe is a crucial step in ensuring your payment process is robust and compliant with security standards. By following this guide, you should be able to implement and test various 3D Secure scenarios before moving to a production environment. Remember to use Stripe's test cards and the test environment for all your testing needs, and only switch to the live environment when you're confident your integration works correctly.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022