This is engineering guidance, not legal advice. Whether SCA applies depends on where your acquirer and your customer's issuer sit, and your acquirer is the one to confirm which exemptions you can request.
What is 3D Secure? (And Why Europeans Made It Mandatory)
3D Secure (3DS) authentication is the default expectation under EU Strong Customer Authentication (SCA) rules in PSD2 (with documented exemptions for low-value sub-€30 transactions, MITs, TRA, trusted beneficiaries, and recurring fixed amounts). 3DS also gives you liability shift: when authentication succeeds, responsibility for fraud chargebacks moves from you to the card issuer. Without 3DS, you carry 100% of fraud losses, which average 0.5-2% of transaction volume. EMV 3DS (3DS 2.x) cuts customer friction with risk-based authentication: only 10-20% of transactions get a customer challenge when you send rich context data (purchase history, device fingerprinting, shipping details). That's the fraud protection without the 15% checkout abandonment of legacy 3DS 1.0.
3D Secure is the credit card industry's version of two-factor authentication. When someone tries to buy something, their bank might pop up and say "prove you actually own this card" via SMS, app notification, or biometric check.
Europeans made it mandatory under Strong Customer Authentication, and honestly, it's not terrible. Yes, it adds friction. But it also shifts fraud liability from you to the bank. That's a good trade.
EMV 3DS (3DS 2.x): The Version That Doesn't Totally Suck
The original 3D Secure (1.0) was a UX nightmare - popup windows, confusing redirects, and a 15% abandonment rate. EMV 3DS (also called 3DS 2.x, with versions 2.2 and 2.3 currently active in the market) is much smarter. It uses risk-based authentication to decide which transactions actually need extra verification, so most customers never see a challenge - this is the frictionless flow. Only higher-risk transactions get pushed into the challenge flow.
Send rich context data (customer history, device info, shipping details) and issuers can make better risk decisions, which means fewer challenges for your legitimate customers.
SCA exemptions worth knowing under PSD2:
- Low-value transactions - under €30 (with cumulative caps per card).
- Merchant-initiated transactions (MITs) - subscriptions and stored-credential charges with prior consent.
- Transaction Risk Analysis (TRA) - acquirers below specified fraud thresholds can request exemption for lower-risk transactions, up to value-based limits.
- Trusted beneficiaries - the customer has added you to their bank's whitelist.
- Recurring transactions of fixed amount - same merchant, same amount, after an initial authenticated transaction.
- Corporate cards processed via secure B2B protocols.
The exemption is requested by the acquirer but granted (or refused) by the issuer, so don't assume an exemption flag guarantees no challenge.
def create_3ds_payment(amount, card_details, customer_info):
"""
Create payment with 3D Secure authentication.
3DS 2.0 is much smarter than the old version - it uses machine learning
to decide which transactions actually need extra authentication. Many
transactions go through without any customer friction.
The key is providing rich context data so the bank can make an
intelligent risk assessment.
"""
payment_intent = {
'amount': amount,
'currency': 'EUR', # SCA applies under PSD2; sub-€30 transactions may qualify for the low-value exemption
'payment_method_data': {
'type': 'card',
'card': card_details
},
# This metadata is crucial for 3DS 2.0 risk assessment
# The more context you provide, the less likely customers
# will be challenged for authentication
'metadata': {
'customer_email': customer_info['email'], # Helps identify returning customers
'customer_phone': customer_info['phone'], # Additional identity verification
'shipping_address': customer_info['address'], # Flags if different from billing
'billing_address': customer_info['billing_address'], # Primary verification point
'customer_age_days': customer_info.get('account_age_days'), # Established customers = lower risk
'previous_purchases': customer_info.get('purchase_count', 0), # Purchase history helps
'delivery_timeframe': 'same_day', # Delivery speed affects risk score
'purchase_category': 'digital_goods' # Product type influences risk
},
# Request 3DS explicitly for EU compliance
'payment_method_options':
}
}
return payment_processor.create_payment(payment_intent)
def handle_3ds_authentication(payment_intent):
"""
Handle the 3D Secure authentication flow.
This is where customers might get redirected to their bank's website
to prove they own the card. The flow varies by bank - some use SMS,
others use apps, some use biometrics.
Your job is to handle the redirect gracefully and process the result.
"""
if payment_intent.status == 'requires_action':
# Customer needs to complete 3DS authentication
# This happens when the bank decides extra verification is needed
# Get the authentication URL from the payment processor
auth_url = payment_intent.next_action.redirect_to_url.url
return_url = payment_intent.next_action.redirect_to_url.return_url
return
elif payment_intent.status == 'succeeded':
# Payment went through without challenge, or customer successfully authenticated
# You now have liability shift protection from the card network
return
elif payment_intent.status == 'requires_payment_method':
# 3DS authentication failed - customer couldn't prove card ownership
# This is different from a regular decline - the card might be valid
# but the customer failed authentication
return
else:
# Other failure reasons - expired card, insufficient funds, etc.
return
The exemptions as code, with the TRA fraud-rate thresholds that decide eligibility. From chapter 33.
SCA Exemption Engine
from dataclasses import dataclass
from decimal import Decimal
from enum import Enum
from typing import Optional
class SCAExemption(Enum):
LOW_VALUE = "low_value" # < €30
TRUSTED_BENEFICIARY = "trusted" # Whitelisted recipient
RECURRING = "recurring" # Subsequent recurring payment
LOW_RISK = "low_risk" # TRA score < threshold
CORPORATE = "corporate" # Secure corporate payment
@dataclass
class SCADecision:
sca_required: bool
exemption: Optional[SCAExemption] = None
risk_score: Optional[float] = None
reason: Optional[str] = None
class SCAExemptionEngine:
"""Determine SCA exemptions per PSD2 RTS."""
def __init__(self, tra_model, trusted_beneficiary_store):
self.tra_model = tra_model
self.trusted_store = trusted_beneficiary_store
# Fraud rate thresholds for TRA exemption
self.tra_thresholds =
def evaluate(
self,
customer_id: str,
amount: Decimal,
creditor_iban: str,
is_recurring: bool = False,
is_corporate: bool = False
) -> SCADecision:
"""Evaluate transaction for SCA exemption eligibility."""
# 1. Low value exemption (< €30)
if amount < 30:
# Check cumulative: 5 consecutive or €100 total triggers SCA
if not self._exceeds_low_value_limits(customer_id, amount):
return SCADecision(
sca_required=False,
exemption=SCAExemption.LOW_VALUE,
reason=f"Amount €{amount} below €30 threshold"
)
# 2. Trusted beneficiary exemption
if self.trusted_store.is_trusted(customer_id, creditor_iban):
return SCADecision(
sca_required=False,
exemption=SCAExemption.TRUSTED_BENEFICIARY,
reason="Creditor on trusted beneficiary list"
)
# 3. Recurring payment exemption (after initial SCA)
if is_recurring and self._has_initial_sca(customer_id, creditor_iban):
return SCADecision(
sca_required=False,
exemption=SCAExemption.RECURRING,
reason="Recurring payment with prior SCA"
)
# 4. Corporate exemption (dedicated secure protocols)
if is_corporate:
return SCADecision(
sca_required=False,
exemption=SCAExemption.CORPORATE,
reason="Corporate payment with secure protocol"
)
# 5. Transaction Risk Analysis (TRA) exemption
risk_score = self.tra_model.score_transaction(
customer_id=customer_id,
amount=float(amount),
creditor_iban=creditor_iban
)
tra_eligible = self._check_tra_eligibility(amount, risk_score)
if tra_eligible:
return SCADecision(
sca_required=False,
exemption=SCAExemption.LOW_RISK,
risk_score=risk_score,
reason=f"TRA score {risk_score:.2f} below threshold for €{amount}"
)
# No exemption - SCA required
return SCADecision(
sca_required=True,
risk_score=risk_score,
reason="No applicable exemption"
)
def _check_tra_eligibility(self, amount: Decimal, risk_score: float) -> bool:
"""Check if transaction qualifies for TRA exemption."""
# Must have fraud rate below threshold for amount tier
our_fraud_rate = self._get_fraud_rate()
for threshold_amount, max_fraud_rate in self.tra_thresholds.items():
if amount <= threshold_amount and our_fraud_rate < max_fraud_rate:
# Also check risk score - typically need < 30 risk points
if risk_score < 0.30:
return True
return False
def _exceeds_low_value_limits(self, customer_id: str, amount: Decimal) -> bool:
"""Check if low-value cumulative limits exceeded."""
# Get recent low-value transactions without SCA
recent = self._get_recent_low_value_transactions(customer_id)
consecutive_count = len(recent)
cumulative_amount = sum(t.amount for t in recent) + amount
# PSD2 limits: 5 consecutive OR €100 cumulative
return consecutive_count >= 5 or cumulative_amount >= 100
SCA exemptions aren't automatic. You have to request them, and the issuing bank can still override you and require SCA anyway. If your TRA fraud rate goes over the threshold (0.13% for transactions under €500), you lose the exemption entirely for 6+ months. Monitor your fraud rate continuously: lose TRA eligibility and every transaction needs full two-factor authentication, which drops conversion 20-30%.
What the challenge looks like on Stripe. From chapter 9.
3D Secure Handling
3D Secure adds authentication for card payments (required in Europe under PSD2). Handle the requires_action state:
if intent.status == 'requires_action':
# Customer needs to authenticate - redirect to 3DS flow
return
elif intent.status == 'succeeded':
fulfill_order(intent.metadata['order_id'])
Where the rules are heading, from chapter 26.
Strong Customer Authentication (SCA) under PSD2 RTS (Regulation (EU) 2018/389) remains the baseline for EEA card-not-present, account access, and remote payment initiation, with the usual two-factor + dynamic linking requirements and exemptions (TRA, low-value, trusted beneficiaries, MIT). PSD3 and the Payment Services Regulation (PSR) were politically agreed in November 2025, finalized in April 2026, and apply 21 months after publication in the Official Journal. Expect tighter fraud liability, harmonized SCA exemptions, stronger open banking access (PSD2 successor), and explicit treatment of IBAN-name checks. Treat the timetable as moving, and design SCA/3DS flows so authentication policy is configurable.