Full 3D Secure payment flow with setup, device fingerprinting, challenge/frictionless handling, and capture
Full rework of the CyberSource 3D Secure payment flow for BAC (Banco Atlantida) approval. Previous implementation had multiple issues flagged by Gerson (BAC reviewer): non-secure ECI indicators, auth-only (no capture), and missing brand coverage.
- Setup —
POST /api/payments/setup calls CyberSource /risk/v1/authentication-setups to get a referenceId and device data collection URL
- Device Fingerprint — Hidden iframe loads CyberSource device data collection page (captures browser fingerprint)
- Payment —
POST /api/payments sends card details + referenceId + deviceInformation (browser data)
- 3DS Challenge — If
PENDING_AUTHENTICATION, user redirected to bank’s stepUpUrl for challenge
- 3DS Frictionless — If no
stepUpUrl, verify inline (low-risk transaction approved without user interaction)
- Verify —
POST /api/payments/verify validates 3DS result and captures payment
- Capture —
capture: true enabled (was previously auth-only)
| # |
Issue |
Fix |
| 1 |
Missing CyberSource credentials on server |
Added from migration-data/credentials.txt to OVH3 .env.local |
| 2 |
null.from() crash |
ordersRepo.ts imported browser-only supabase. Replaced with supabaseAdmin |
| 3 |
Payment amount mismatch |
Aligned fee calculation base (server vs client). Increased tolerance to 50 cents |
| 4 |
Exp year validation |
Accepted 2-digit years, auto-expand to 4-digit |
| 5 |
PENDING status not handled |
Combined actionList returns PENDING not PENDING_AUTHENTICATION |
| 6 |
Frictionless 3DS |
AMEX frictionless had no stepUpUrl. Added inline verify for frictionless flow |
| 7 |
AMEX INVALID_ACCOUNT |
BAC sandbox doesn’t support AMEX. Added error handling for 2xx with errorInformation |
| 8 |
3DS indicators missing |
Added deviceInformation (browser fingerprint) to payment request |
| 9 |
httpAcceptBrowserValue field name |
Was using wrong field name across all files |
| 10 |
Full 3DS setup flow |
Implemented setup → device data collection → payment with referenceId |
| 11 |
Capture not enabled |
Changed capture: false to capture: true |
| Brand |
veresEnrolled |
paresStatus |
ECI |
CAVV |
Indicator |
Status |
| Visa |
Y |
Y |
05 |
Present |
vbv |
Captured |
| Mastercard |
Y |
Y |
— |
ucafCollectionIndicator: 2 |
spa |
Captured |
| AMEX |
— |
— |
— |
— |
— |
INVALID_ACCOUNT (sandbox limitation) |
BAC requires all three brands (Visa, MC, AMEX). AMEX test cards return INVALID_ACCOUNT on the CyberSource sandbox — appears to be a sandbox/merchant config issue. The code handles AMEX correctly (detects card type 003, sends proper 3DS setup). Pending confirmation from Gerson/BAC whether AMEX needs to be enabled on the sandbox merchant account.
| File |
Purpose |
src/app/api/payments/setup/route.ts |
New — CyberSource authentication setup endpoint |
src/app/api/payments/route.ts |
supabaseAdmin, deviceInformation, capture, error handling |
src/app/api/payments/verify/route.ts |
supabaseAdmin fix |
src/app/cart/page.tsx |
3DS setup flow, device data collection, frictionless handling |
src/lib/orderTotalCalculator.ts |
supabaseAdmin, fee base alignment |
src/services/paymentApi.ts |
deviceInformation and referenceId types |