3D Secure Flow
3DS Authentication Flow
Customer Store Payment Gateway Issuer
│ │ │ │
│ 1. Checkout │ │ │
├────────────────►│ │ │
│ │ 2. Create 3DS │ │
│ ├───────────────────►│ │
│ │ 3. Return auth │ │
│ │◄───────────────────┤ │
│ 4. Redirect │ │ │
│◄────────────────┤ │ │
│ 5. 3DS Page │ │ │
│─────────────────┼────────────────────┼───────────────►│
│ 6. Challenge │ │ │
│◄────────────────┼────────────────────┼───────────────┤
│ 7. Result │ │ │
│─────────────────┼───────────────────►│ │
│ │ 8. Verify │ │
│ ├───────────────────►│ │
│ │ 9. Auth result │ │
│ │◄───────────────────┤ │
3DS Service
namespace Vendor\Payment\Service\ThreeDS;
class ThreeDsService
{
private ThreeDsGatewayInterface $gateway;
public function initiate(
OrderInterface $order,
CardDataInterface $card
): ThreeDsResult {
$response = $this->gateway->createAuthentication(new ThreeDsRequest([
'amount' => $order->getGrandTotal(),
'currency' => $order->getCurrencyCode(),
'card_number' => $card->getNumber(),
'card_expiry' => $card->getExpiry(),
'cardholder_name' => $card->getHolderName(),
'order_id' => $order->getIncrementId(),
'return_url' => $this->getReturnUrl(),
]));
return new ThreeDsResult([
'status' => $response->getStatus(),
'acs_url' => $response->getAcsUrl(),
'pareq' => $response->getPaReq(),
'md' => $response->getMd(),
'requires_challenge' => $response->requiresChallenge(),
]);
}
public function validate(string $pareSponse, string $md): ThreeDsValidationResult
{
$response = $this->gateway->validateAuthentication(new ValidateRequest([
'pareq_response' => $pareSponse,
'md' => $md,
]));
return new ThreeDsValidationResult([
'authenticated' => $response->isAuthenticated(),
'eci' => $response->getEci(),
'cavv' => $response->getCavv(),
'xid' => $response->getXid(),
]);
}
}
Card Tokenization
Tokenization Architecture
Frontend (Client) Payment Gateway Vault (Encrypted)
│ │ │
1. Card data │ │
├─────────────────► │ │
│ 2. Token │ │
│◄────────────────┤ │
│ 3. Store token │ │
│ ├──────────────────►│
│ │ │
Token Vault
namespace Vendor\Payment\Model\Vault;
class TokenVault
{
private TokenRepositoryInterface $tokenRepo;
private EncryptorInterface $encryptor;
public function store(
CustomerInterface $customer,
TokenDataInterface $tokenData
): TokenInterface {
// Tokenize card via gateway
$gatewayToken = $this->gateway->tokenize(new TokenizeRequest([
'card_number' => $tokenData->getCardNumber(),
'card_expiry' => $tokenData->getExpiry(),
'cardholder_name' => $tokenData->getHolderName(),
]));
// Store only non-sensitive data
$token = new TokenData([
'customer_id' => $customer->getId(),
'gateway_token' => $gatewayToken->getToken(),
'card_type' => $gatewayToken->getCardType(),
'card_last_four' => $gatewayToken->getLastFour(),
'card_expiry' => $gatewayToken->getExpiry(),
'is_active' => true,
]);
return $this->tokenRepo->save($token);
}
public function charge(
TokenInterface $token,
float $amount,
string $currency
): PaymentResult {
return $this->gateway->charge(new ChargeRequest([
'token' => $token->getGatewayToken(),
'amount' => $amount,
'currency' => $currency,
]));
}
}
PCI Compliance
namespace Vendor\Payment\Security\Pci;
class PciComplianceManager
{
public function sanitizeCardData(array $data): array
{
// Never store raw card numbers
unset($data['card_number']);
unset($data['cvv']);
// Mask card number for display
if (isset($data['card_last_four'])) {
$data['card_display'] = '**** **** **** ' . $data['card_last_four'];
}
return $data;
}
public function validateStorage(array $storageConfig): ComplianceResult
{
$errors = [];
if ($storageConfig['encrypt_card_data'] !== true) {
$errors[] = 'Card data must be encrypted at rest';
}
if ($storageConfig['tokenize'] !== true) {
$errors[] = 'Card data must be tokenized';
}
if ($storageConfig['log_card_data'] === true) {
$errors[] = 'Raw card data must never be logged';
}
return new ComplianceResult(
empty($errors),
$errors
);
}
}
3DS Frictionless Flow
Frictionless Authentication
namespace Vendor\Payment\Service\ThreeDS;
class FrictionlessHandler
{
public function handle(ThreeDsResult $result): bool
{
// Check if issuer supported frictionless flow
if ($result->isFrictionless()) {
// No challenge required - proceed with payment
$this->logger->info('3DS frictionless authentication', [
'eci' => $result->getEci(),
'score' => $result->getAuthenticationScore(),
]);
return true;
}
// Challenge required - redirect to ACS
return false;
}
}
3DS Result Handling
namespace Vendor\Payment\Service\ThreeDS;
class ThreeDsResultHandler
{
public function processResult(
ThreeDsValidationResult $result,
OrderInterface $order
): void {
$payment = $order->getPayment();
if ($result->isAuthenticated()) {
// Store 3DS data for liability shift
$payment->setAdditionalInformation('3ds_eci', $result->getEci());
$payment->setAdditionalInformation('3ds_cavv', $result->getCavv());
$payment->setAdditionalInformation('3ds_xid', $result->getXid());
$payment->setAdditionalInformation('3ds_authenticated', true);
// Proceed with authorization
$this->authService->authorize($order, $payment);
} else {
// 3DS failed - decline or retry
$order->setState(Order::STATE_CANCELED);
$order->addCommentToStatusHistory(
'3D Secure authentication failed'
);
}
}
}
Token Management
Token Lifecycle
namespace Vendor\Payment\Model\Vault;
class TokenLifecycle
{
public function onCardExpiry(TokenInterface $token): void
{
// Notify customer
$this->notificationService->send(
$token->getCustomerId(),
'Your saved card ending in ' . $token->getLastFour() . ' has expired'
);
// Deactivate token
$token->setIsActive(false);
$this->tokenRepo->save($token);
}
public function onDelete(TokenInterface $token): void
{
// Remove from gateway
$this->gateway->deleteToken($token->getGatewayToken());
// Remove from vault
$this->tokenRepo->delete($token);
}
}
Token Security
namespace Vendor\Payment\Security\Token;
class TokenSecurity
{
public function validateTokenAccess(
TokenInterface $token,
CustomerInterface $customer
): bool {
// Only token owner can use it
if ($token->getCustomerId() !== $customer->getId()) {
return false;
}
// Check token is active
if (!$token->isActive()) {
return false;
}
// Check expiry
if ($this->isExpired($token)) {
return false;
}
return true;
}
}
Quiz
1. What is 3D Secure?
2. What is card tokenization?
3. What does PCI compliance require?
Flashcards
Question
What is 3D Secure?
Click to reveal answer
Answer
Cardholder authentication for fraud prevention
Question
What is tokenization?
Click to reveal answer
Answer
Replacing card data with a non-sensitive token
Question
What is ECI in 3DS?
Click to reveal answer
Answer
Electronic Commerce Indicator - authentication level
Question
What does liability shift mean?
Click to reveal answer
Answer
Fraud liability moves from merchant to issuer after 3DS
Revision Notes
Key Takeaways
- 1. 3D Secure adds authentication layer for card payments
- 2. Frictionless flow completes without customer challenge
- 3. Tokenization replaces card data with reusable tokens
- 4. PCI compliance requires never storing raw card numbers
- 5. 3DS provides liability shift from merchant to issuer
Interview Tips
- • Explain the 3DS authentication flow steps
- • Discuss frictionless vs challenge authentication
- • Describe tokenization security benefits
- • Talk about PCI compliance requirements
Cheat Sheet
3D Secure:
Frictionless → auto-approved (low risk)
Challenge → customer authenticates (high risk)
ECI → authentication level (05=full, 06=attempted)
CAVV → cardholder authentication value
Tokenization:
Store token (not card)
Token → gateway → charge
No raw card data in DB
PCI Compliance:
Never store raw card numbers
Never log CVV
Encrypt sensitive data
Use tokenization