# 3D Secure (/expanded/3d-secure)

Add 3D Secure card authentication.



With PayPal's JavaScript SDK v6, you can support Strong Customer Authentication (SCA) and 3D Secure (3DS) in two payment flows:

* **One-time payments** process a single transaction with 3DS when required. The SDK collects card details, creates an order, and captures payment upon approval.
* **Save payment methods (vaulting)** store cards for future purchases. The server creates setup tokens, and the client submits card details to complete vaulting with optional 3DS.

> **Info:** The code examples on this page use USD and US-based scenarios. PayPal supports multiple currencies and countries. See [currency codes](/api/codes/currency) and [country codes](/api/codes/country-region) for the full list, and [payment methods](/payment-methods) for country-specific options.

## One-time payment [#one-time-payment]

On your server, create an order and define SCA and 3DS settings. Use `payment_source.card.attributes.verification.method` and supply return and cancel URLs in `experience_context`.

```javascript
// POST /paypal-api/checkout/orders
// Body (example — adapt amounts, currency, and URLs)
{
  "intent": "CAPTURE",
  "purchase_units": [
    { "amount": { "currency_code": "USD", "value": "100.00" } }
  ],
  "payment_source": {
    "card": {
      "attributes": {
        "verification": { "method": "SCA_ALWAYS" } // or SCA_WHEN_REQUIRED
      },
      "experience_context": {
        "return_url": "https://example.com/returnUrl",
        "cancel_url": "https://example.com/cancelUrl"
      }
    }
  }
}
```

### Verification methods [#verification-methods]

* Use `SCA_ALWAYS` to attempt 3DS for eligible cards and regions.
* Use `SCA_WHEN_REQUIRED` to rely on network or regulatory mandates.

### Submit card details [#submit-card-details]

On the client side, initialize the SDK and card fields. Submit the order and handle each possible outcome.

```javascript
const clientToken = await getClientToken();
const sdk = await window.paypal.createInstance({
  clientToken,
  components: ["card-fields"],
});
const session = sdk.createCardFieldsOneTimePaymentSession();

// Render fields
const numberField = session.createCardFieldsComponent({
  type: "number",
  placeholder: "Card number",
});
const cvvField = session.createCardFieldsComponent({
  type: "cvv",
  placeholder: "CVV",
});
const expiryField = session.createCardFieldsComponent({
  type: "expiry",
  placeholder: "MM/YY",
});

[
  "#paypal-card-fields-number",
  "#paypal-card-fields-cvv",
  "#paypal-card-fields-expiry",
].map((sel, i) =>
  document
    .querySelector(sel)
    .appendChild([numberField, cvvField, expiryField][i]),
);

// Pay click
document.querySelector("#pay-button").addEventListener("click", async () => {
  try {
    const orderId = await createOrder({
      enable3DS: true,
      scaMethod: getScaMethodFromUI(),
    });
    const { state, data } = await session.submit(orderId, {
      billingAddress: { postalCode: "95131" },
    });

    switch (state) {
      case "succeeded": {
        const { orderId, liabilityShift } = data;
        // 3DS may or may not have occurred; Use liabilityShift
        // to determine if the payment should be captured
        const result = await captureOrder(data.orderId);
        // success UI
        break;
      }
      case "canceled": {
        // buyer closed 3DS modal
        break;
      }
      case "failed": {
        // inspect data.message
        break;
      }
    }
  } catch (e) {
    console.error(e);
  }
});
```

### Example order endpoint [#example-order-endpoint]

Use this Express handler to create a PayPal order and optionally include 3DS parameters based on your request body.

```javascript
app.post("/paypal-api/checkout/orders", async (req, res) => {
  const { enable3DS, scaMethod = "SCA_WHEN_REQUIRED" } = req.body || {};
  const body = {
    intent: "CAPTURE",
    purchase_units: [{ amount: { currency_code: "USD", value: "100.00" } }],
  };
  if (enable3DS) {
    body.payment_source = {
      card: {
        attributes: { verification: { method: scaMethod } },
        experience_context: {
          return_url: "https://example.com/returnUrl",
          cancel_url: "https://example.com/cancelUrl",
        },
      },
    };
  }
  // call PayPal Orders API with body and return { id }
});
```

## Save payment method [#save-payment-method]

On the server, create a vault setup token to save a payment method and require 3DS. Specify a verification method in the request.

```javascript
// POST /paypal-api/vault/setup-token
// Body (example)
{
  "payment_source": {
    "card": {
      "experience_context": {
        "return_url": "https://example.com/returnUrl",
        "cancel_url": "https://example.com/cancelUrl"
      },
      "verification_method": "SCA_ALWAYS" // or SCA_WHEN_REQUIRED
    }
  }
}
```

### Submit card with setup token [#submit-card-with-setup-token]

On the client side, initialize the SDK and card fields. Submit the order and handle each possible outcome.

```javascript
const clientToken = await getClientToken();
const sdk = await window.paypal.createInstance({
  clientToken,
  components: ["card-fields"],
});
const session = sdk.createCardFieldsSavePaymentSession();

// Render 3 fields as shown above...

const { setupToken } = await createVaultSetupToken({
  enable3DS: true,
  scaMethod: getScaMethodFromUI(),
});
const { state, data } = await session.submit(setupToken);

switch (state) {
  case "succeeded": {
    // Persist data.vaultSetupToken on your server (maps to a vaulted instrument)
    break;
  }
  case "canceled": {
    // user dismissed 3DS
    break;
  }
  case "failed": {
    // show retry message
    break;
  }
}
```

## Best practices [#best-practices]

* Include both the `return_url` and `cancel_url` in all 3DS requests.
* Handle every submit state and add `break;` in each switch case.
* Track the liability shift in `data` to manage risk.
