# `Worldpay.ThreeDS`
[🔗](https://github.com/iamkanishka/worldpay/blob/v1.0.0/lib/worldpay/access/three_ds.ex#L1)

Worldpay **3DS API** — Strong Customer Authentication.

Handles the full 3DS authentication flow:

1. Device data collection
2. Authentication request (sends order + device data to card schemes)
3. Challenge display (if outcome is `"challenged"`)
4. Apply `eci` + `authenticationValue` to the card payment

## Web flow

    # Step 1 — device data collection
    {:ok, ddc} = Worldpay.ThreeDS.device_data(session_href, config)
    # → render ddc["_links"]["3ds:deviceDataCollection"]["href"] in an iFrame

    # Step 2 — authenticate
    {:ok, auth} = Worldpay.ThreeDS.authenticate(auth_body, config)

    # auth["outcome"] is "authenticated" | "challenged" | "unAuthenticated" | "authenticationOutage"

    # Step 3 (if challenged) — render iFrame from auth["_links"]["3ds:challenge"]["href"]
    # Customer completes challenge, then re-poll or receive webhook

    # Step 4 — attach result to payment
    three_ds = Worldpay.ThreeDS.build_auth_object(auth)

    {:ok, payment} =
      Worldpay.CardPayments.authorize(
        Map.put(instruction, "threeDS", three_ds),
        config
      )

## Outcomes

| Outcome | Action |
|---|---|
| `"authenticated"` | Apply `eci` + `authenticationValue` to payment |
| `"challenged"` | Render challenge iFrame; await completion |
| `"unAuthenticated"` | Do not proceed; SCA failed |
| `"authenticationOutage"` | Issuer outage; exemption may auto-apply |

# `auth_result`

```elixir
@type auth_result() :: %{
  eci: String.t() | nil,
  authentication_value: String.t() | nil,
  transaction_id: String.t() | nil,
  version: String.t() | nil
}
```

# `authenticate`

```elixir
@spec authenticate(map(), Worldpay.Config.t()) ::
  {:ok, map()} | {:error, Worldpay.Error.t()}
```

Authenticate the customer (step 2).

### Required body fields

- `sessionHref` — from Checkout SDK
- `deviceData` — from device data collection response
- `merchant.entity`
- `instruction.value`
- `instruction.paymentInstrument`

### Optional fields

- `challengePreference` — `"noChallengeRequested"` | `"challengeRequested"` | `"challengeMandated"` | `"noChallengeRequestedTRAPerformed"`
- `customerData` — South Korea / Toss Pay domestic payments
- `bypassOn` / `continueOn` — 3DS bypass controls (Jun 2026)

# `authenticate_and_extract`

```elixir
@spec authenticate_and_extract(%{required(String.t()) =&gt; term()}, Worldpay.Config.t()) ::
  {:ok, auth_result()} | {:error, Worldpay.Error.t()}
```

Authenticate and extract the fields needed for a card payment.

Returns `{:ok, %{eci: ..., authentication_value: ..., transaction_id: ..., version: ...}}`
on success, `{:error, Error.t()}` on challenge required or failure.

# `build_auth_object`

```elixir
@spec build_auth_object(%{required(String.t()) =&gt; term()}) :: %{
  required(String.t()) =&gt; String.t()
}
@spec build_auth_object(map()) :: map()
```

Build the `threeDS` object to embed in a Card Payments authorize request
from a successful authentication response map.

# `device_data`

```elixir
@spec device_data(String.t(), map(), Worldpay.Config.t()) ::
  {:ok, map()} | {:error, Worldpay.Error.t()}
```

Initiate device data collection (step 1 of web 3DS flow).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
