Skip to main content

Overview

3D Secure (3DS) adds an extra layer of authentication for card payments. When a card issuer requires 3DS, the Flowlix API returns a requires_action status with a redirect URL. Your integration must handle this status and redirect the customer to complete authentication. Flowlix supports 3DS 2.0 (the current standard mandated by PSD2/SCA in Europe). The decision on whether 3DS is needed is made by the card issuer — you don’t need to request it explicitly.

How it works

  1. Create a payment with return_url
  2. The response returns status: "pending" — the payment is being processed asynchronously
  3. Poll GET /v1/payments/{id} every 2–3 seconds
  4. If 3DS is required, the status changes to requires_action with a next_action.redirect_url (typically within 2–5 seconds)
  5. Redirect the customer to next_action.redirect_url
  6. Customer completes authentication (SMS code, biometric, etc.)
  7. Customer is redirected back to your return_url
  8. Continue polling — the status will become succeeded or failed
The requires_action status may not appear in the initial create response. The upstream processor needs a few seconds to determine whether 3DS is required. Always poll for status changes rather than relying on the create response alone.

Creating a payment with 3DS support

Include return_url in your payment request. All card and customer sub-fields are required by the API — see Direct API for the complete list.
curl -X POST https://api.flowlix.dev/v1/payments \
  -H "Authorization: Bearer fl_test_sk_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "amount": 5000,
    "currency": "eur",
    "return_url": "https://your-site.com/payment/complete",
    "card": {
      "number": "4635440000002223",
      "exp_month": 2,
      "exp_year": 2027,
      "cvc": "196",
      "holder_name": "Jenny Rosen"
    },
    "customer": {
      "email": "jenny.rosen@example.com",
      "first_name": "Jenny",
      "last_name": "Rosen",
      "country": "DE",
      "phone": "+491701234567",
      "address": "Kurfuerstendamm 21",
      "city": "Berlin",
      "zip": "10719"
    }
  }'

Handling the response

Initial response

The create response is always pending — the processor needs a few seconds to evaluate whether 3DS is required:
{
  "id": "pay_01h9z8xm2c7q4r8s9t0v1w2x3y",
  "object": "payment",
  "status": "pending",
  "amount": 5000,
  "currency": "eur",
  "next_action": null
}

Polling for status

Poll GET /v1/payments/{id} every 2–3 seconds. Two outcomes: No 3DS required — status moves directly to succeeded or failed. 3DS required — status changes to requires_action:
{
  "id": "pay_01h9z8xm2c7q4r8s9t0v1w2x3y",
  "status": "requires_action",
  "next_action": {
    "type": "redirect",
    "redirect_url": "https://acs.bank.com/3ds/..."
  }
}

Redirecting the customer

// Poll until status changes from pending
const poll = setInterval(async () => {
  const res = await fetch(`/v1/payments/${paymentId}`, { headers })
  const payment = await res.json()

  if (payment.status === 'requires_action') {
    clearInterval(poll)
    window.location.href = payment.next_action.redirect_url
  } else if (payment.status !== 'pending') {
    clearInterval(poll)
    showResult(payment.status) // succeeded or failed
  }
}, 3000)

After 3DS

The customer returns to your return_url. Resume polling — the status will become succeeded or failed:
curl https://api.flowlix.dev/v1/payments/pay_01h9z8xm2c7q4r8s9t0v1w2x3y \
  -H "Authorization: Bearer fl_test_sk_..."

Timeout

If the customer doesn’t complete 3DS in time, the payment status changes to expired. Create a new payment to retry.

3DS Decline Codes

When 3DS authentication fails, the payment status becomes failed with a specific decline_code:
CodeDescription
three_d_secure_failedCustomer failed authentication (wrong code, rejected)
three_d_secure_timeoutCustomer didn’t complete in time
three_d_secure_not_supportedCard doesn’t support 3DS
three_d_secure_error3DS infrastructure error (temporary)
See 3D Secure Declines for detailed guidance on handling each code.

Test cards

CardBehavior
4635 4400 0000 2223Triggers 3DS challenge (sandbox)
4111 1111 1111 1111Approves without 3DS (sandbox)