Stripe 3D Secure card authentication with a Saved Card

Suraj Batuwana
6 min readNov 8, 2023

What is 3D Secure Card Authentication?

3-D Secure card authentication, often abbreviated as 3DS or 3-D Secure, is a security protocol and technology designed to enhance the safety of online credit and debit card transactions. It was originally developed by Visa as “Verified by Visa” and has since been adopted by various card networks, including MasterCard (“MasterCard SecureCode”) and American Express (“American Express SafeKey”). 3DS is an essential tool in the fight against card-not-present (CNP) fraud, where a cardholder’s physical card is not present during the transaction, as is common in online shopping.

The 3DS authentication process typically involves the following key elements:

1. Merchant Website Integration: When a cardholder initiates an online transaction at a 3DS-enabled merchant website, the merchant’s payment gateway communicates with the cardholder’s issuing bank to check whether the transaction requires 3DS authentication.

2. Authentication Request: If the transaction triggers 3DS, the cardholder is redirected to a secure authentication page provided by their issuing bank. Here, they are prompted to provide additional information to verify their identity. This may include a one-time password (OTP), a PIN, or other authentication factors.

3. Issuing Bank Verification: The cardholder’s issuing bank validates the provided information and checks it against their records. This step helps ensure that the person making the transaction is the legitimate cardholder.

4. Authentication Response: Once the cardholder’s identity is verified, the issuing bank sends an authentication response back to the merchant’s payment gateway. This response can confirm the authentication and authorize the transaction, or it may decline it if the authentication fails.

3-D Secure authentication offers several benefits and plays a crucial role in securing online card transactions.

1. Reduced Fraud Risk: 3DS adds an extra layer of security by verifying the cardholder’s identity. This significantly reduces the risk of fraudulent transactions, as it becomes more challenging for cybercriminals to use stolen card details.

2. Liability Shift: In many cases, if a 3DS-authenticated transaction is later disputed as fraudulent, the liability for the chargeback shifts from the merchant to the issuing bank. This incentivizes merchants to use 3DS for added protection.

3. Consumer Confidence: Shoppers gain confidence knowing that their online transactions are secure and that their card details are less likely to be misused.

4. Regulatory Compliance:Some regions and industries have regulatory requirements that mandate the use of strong authentication methods like 3DS to protect sensitive payment data.

5. Global Acceptance: 3DS is widely adopted and supported by major card networks and financial institutions, making it a global standard for online card transactions.

6. Customization Options: Merchants can often customize the 3DS process to balance security with a smooth customer experience, tailoring it to their specific needs.

While 3D Secure card authentication greatly enhances the security of online payments, it’s essential to consider the user experience. Some customers may find the additional steps of 3DS slightly cumbersome, and merchants should aim to strike a balance between security and convenience to ensure a seamless shopping experience for their customers. Additionally, the implementation of 3DS should be in line with the latest standards and best practices to keep up with evolving security threats and technologies.

How easy is it to integrate 3-D Secure with Stripe?

As of my last knowledge update in January 2022, integrating 3-D Secure (3DS) with Stripe is relatively straightforward, thanks to Stripe’s developer-friendly tools and documentation. Stripe offers a seamless and well-documented process for adding 3DS authentication to your online payment flow. However, please note that technology and APIs can evolve, so I recommend checking the latest Stripe documentation for any updates or changes in the integration process.

Here are the general steps to integrate 3D Secure with Stripe:

1. Set up a Stripe account:
If you don’t have one already, you’ll need to sign up for a Stripe account. You can do this on the Stripe website.

2. Install the Stripe API Library:
You will need to install the Stripe API library for your preferred programming language. Stripe provides libraries for various programming languages, making it easier to integrate with your application. Common languages include Python, Ruby, JavaScript, and more.

3. Add Stripe Elements:
Stripe Elements is a pre-built UI component that simplifies the process of securely collecting payment information from your customers. It includes card details, and it also supports 3-D Secure authentication. You’ll need to integrate Stripe elements into your checkout page or payment form.

4. Configure Your Stripe Account:
In your Stripe dashboard, you can configure your account settings to enable 3DS. You can choose to enable 3DS for all transactions or specific ones, depending on your business needs and compliance requirements.

5. Handle 3D secure authentication:
When a payment is initiated, you’ll need to check whether 3DS authentication is required. Stripe will typically send you a ‘client_secret’ when the payment is created. You can then use this client secret to initiate the 3DS authentication process.

6. Authenticate with 3-D Secure:
Redirect the customer to a secure authentication page provided by their issuing bank. The customer will need to provide the required authentication information. Stripe will help you manage this process.

7. Handle Authentication Results:
Once the 3DS authentication is completed, Stripe will provide you with the results. You can use these results to determine whether the payment is authorized or not.

8. Handle payment confirmation:
After successful 3DS authentication and payment authorization, you can confirm the payment using the ‘client_secret’ you received earlier.

9. Test and debug:
Before deploying your integration, it’s crucial to test it thoroughly using Stripe’s test environment. You can use test card numbers to simulate various scenarios, including 3-D Secure authentication.

10. Go Live:
Once you’re satisfied with the testing, you can switch to live mode and begin processing actual payments with 3DS authentication.

Stripe’s documentation and support are excellent resources throughout this process. They provide detailed guides, code examples, and developer tools to help you implement 3D Secure smoothly. Keep in mind that Stripe’s tools and features may evolve, so it’s essential to refer to their official documentation for the most up-to-date information on integrating 3D Secure with Stripe.

3D Secure card Authentication with Stripe and Java

Steps

  1. Write a Java API to create a Stripe Payment intent with a saved card for a customer.

public class CreatePaymentResponse {
private String clientSecret;
public CreatePaymentResponse(String clientSecret) {
this.clientSecret = clientSecret;
}
}

....

// Create a Tax Calculation for the items being sold
Calculation taxCalculation = .......

PaymentIntentCreateParams params =
PaymentIntentCreateParams.builder()
.setAmount(calculateOrderAmount(Arrays.asList(postBody.getItems()), taxCalculation))
.setCurrency("aud")
.setCustomer("cus_xxxxxxxxxx") // stripe customer id
.setPaymentMethod("card_xxxxxxxxxxxxxx") // stripe saved card id
.putMetadata("tax_calculation", taxCalculation.getId())
.build();

// Create a PaymentIntent with the order amount and currency
PaymentIntent paymentIntent = PaymentIntent.create(params);

CreatePaymentResponse paymentResponse = new CreatePaymentResponse(paymentIntent.getClientSecret());

// Your API can return a json string of CreatePaymentResponse like gson.toJson(paymentResponse)

2. Write a java script to call the API (checkout.js)

// This is your test publishable API key.
const stripe = Stripe("pk_test_xxxxxx");

// The items the customer wants to buy
const items = [{ id: "d5c6c590-7e26-11ee-b962-0242ac120002", amount: 2234 }];

let clientSecret;

initialize();
checkStatus();

document
.querySelector("#payment-form")
.addEventListener("submit", handleSubmit);

// Fetches a payment intent and captures the client secret
async function initialize() {
const response = await fetch("http://localhost:8080/api/payment/create-payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items }),
});
var secret = await response.json();

clientSecret = secret.clientSecret;

elements = stripe.elements();

}

async function handleSubmit(e) {
e.preventDefault();
setLoading(true);

const { error } = await stripe.confirmPayment({
clientSecret,
confirmParams: {
// Make sure to change this to your payment completion page
return_url: "http://localhost:8080/success.html",
receipt_email: emailAddress,
},
});

if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message);
} else {
showMessage("An unexpected error occurred.");
}

setLoading(false);
}

// Fetches the payment intent status after payment submission
async function checkStatus() {
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);

if (!clientSecret) {
return;
}

const { paymentIntent } = await stripe.retrievePaymentIntent(clientSecret);

switch (paymentIntent.status) {
case "succeeded":
showMessage("Payment succeeded!");
break;
case "processing":
showMessage("Your payment is processing.");
break;
case "requires_payment_method":
showMessage("Your payment was not successful, please try again.");
break;
default:
showMessage("Something went wrong.");
break;
}
}

3. Write the HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Accept a payment</title>
<script src="https://js.stripe.com/v3/"></script>
<script src="checkout.js" defer></script>
</head>
<body>
<!-- Display a payment form.
<form id="payment-form">
<button id="submit">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text">Pay now</span>
</button>
<div id="payment-message" class="hidden"></div>
</form>
</body>
</html>

Upon loading the HTML, an automated process is initiated, triggering a REST call to generate a payment intent. Subsequently, when the “Pay now” button is selected, you will encounter the 3-D Secure (3DS) confirmation step.

--

--

Suraj Batuwana

Technology Evangelist, Technical Blogger with multidisciplinary skills with experience in full spectrum of design, architecture and development