3D Secure
Handle the 3DS challenge redirect and resume the flow after the customer returns
Overview
3D Secure (3DS2 / EMV 3DS) is required for most European card payments under PSD2 Strong Customer Authentication rules. SettleFlow handles the orchestration with the acquirer — your integration only needs to handle the browser redirect and the status check after the customer comes back.
When does 3DS trigger?
- When the card's issuer requires it (the common case for European cards).
- When you force it explicitly by sending
"3DS": "yes"on the payment request. - When your account is configured as 3DS-only — in that case payments without
3DS=yesfail with error code15.
Some combinations are mandatory:
| Scenario | Behaviour |
|---|---|
| Maestro card | Always requires 3DS. Missing 3DS=yes returns code 22. |
| Account not allowed to use 3DS | 3DS=yes rejected with code 8. |
3DS=yes sent without ReturnUrl | Rejected with code 106. |
Flow
┌────────────┐ 1. POST /v1/payment/direct ┌─────────────┐
│ Merchant │ ─────────────────────────────▶ │ SettleFlow │
│ server │ │ │
│ │ ◀───── { 3DSecureUrl } ──────── │ │
└────────────┘ └─────────────┘
│
│ 2. Return 3DSecureUrl to the browser
▼
┌────────────┐ 3. Browser redirected ┌─────────────┐
│ Customer │ ─────────────────────────────▶ │ Card issuer │
│ browser │ (3DS challenge) │ ACS page │
│ │ ◀───────────────────────────── │ │
└────────────┘ └─────────────┘
│
│ 4. Customer redirected back
▼
┌────────────┐ 5. POST /v1/status/direct ┌─────────────┐
│ Merchant │ ─────────────────────────────▶ │ SettleFlow │
│ server │ ◀────── { Status: ... } ────── │ │
└────────────┘ └─────────────┘
│
│ 6. Settleflow also fires a webhook
▼ asynchronously to CallbackUrlStep-by-step
1. Request the payment with 3DS enabled
curl -X POST https://api.settleflow.io/v1/payment/direct \
-H "epro-api-key: sk_test_..." \
-d '{
"Amount": "4999",
"Uid": "customer-42",
"Tid": "order-2026-001",
"Email": "jane@example.com",
"CardNumber": "4000000000003220",
"CardMonth": "12",
"CardYear": "2028",
"CardCVV": "123",
"3DS": "yes",
"ReturnUrl": "https://your-shop.com/payment/return",
"CallbackUrl": "https://your-shop.com/webhooks/settleflow"
}'2. Inspect the response
{
"Code": 0,
"Result": {
"OperationType": "payment",
"Status": "pending",
"Tid": "order-2026-001",
"Reference": "pr_abc123",
"3DSecure": "yes",
"3DSecureUrl": "https://3ds-acs.example.com/challenge?token=..."
}
}When 3DSecure=yes, the 3DSecureUrl field is present and Status is pending.
3. Redirect the customer
Return an HTTP 302 from your server, or open 3DSecureUrl in the customer's browser. The customer will see the issuer's challenge page.
res.redirect(302, result["3DSecureUrl"]);4. Handle the return to ReturnUrl
After the challenge, the customer is redirected back to the ReturnUrl you supplied. SettleFlow may append query parameters identifying the transaction — at minimum, you always have your own Tid and the Reference from step 2.
5. Confirm the final status
Query POST /v1/status/direct to resolve the payment:
curl -X POST https://api.settleflow.io/v1/status/direct \
-H "epro-api-key: sk_test_..." \
-d '{ "Reference": "pr_abc123" }'Possible outcomes:
Status | What to show the customer |
|---|---|
captured | Success — order confirmed. |
authorized | Success — capture pending. |
pending | Still processing. Retry the status call after 1–2 s, or display a processing screen. |
failed | Challenge failed or declined — surface a recoverable error. |
cancelled | Customer abandoned the challenge. |
See the polling pattern on the status page for a defensive retry loop.
6. Trust the webhook for authoritative state
Even if the customer never returns to your ReturnUrl (closed tab, mobile browser lost focus…), SettleFlow will still deliver a webhook to your CallbackUrl. Use that as the source of truth for order fulfillment — see Webhooks.
Testing
Use the dedicated 3DS test card in sandbox:
| Card number | Behaviour |
|---|---|
4000000000003220 | Forces a 3DS challenge and returns a 3DSecureUrl in sandbox. |
See Sandbox & test cards for the full list.