# Migrating from Checkout Request to Checkout Session

Checkout Requests are deprecated. This guide shows how to move an existing Checkout Request integration to [Checkout Sessions](/docs/checkout/using-checkout-session) without changing what your customers see.

## Why migrate

- **A session cannot be reused indefinitely.** A signed Checkout Request has no expiration. It stays valid for as long as your secret key does. A Checkout Session has a status and expires.
- **No signing code to maintain.** You create a session with a standard API call authenticated with your secret key, instead of building and signing an HMAC payload yourself.


## What changes

|  | Checkout Request | Checkout Session |
|  --- | --- | --- |
| Where it is created | Built and signed offline on your server, no call to Shift4 | Created on your server with a call to [`POST /checkout-sessions`](/docs/api/shift4-gateway/checkout/createcheckoutsession) |
| What the browser receives | The whole signed payload: signature and JSON, BASE64-encoded | Only the session's `clientSecret` |
| How Checkout is opened | `checkoutRequest` | `clientSecret` (`data-client-secret` on the script tag) |
| 3D Secure | `threeDSecure` options on each request | Account defaults and 3DS Fraud Rules (see [3D Secure](#3d-secure)) |


## Mapping the attributes

| Checkout Request | Checkout Session |
|  --- | --- |
| `charge.amount`, `charge.currency` | `lineItems[].product.amount`, `lineItems[].product.currency` |
| `charge.capture` | `capture` |
| `charge.metadata` | `metadata` |
| `subscription.planId` | `lineItems[].product.plan` |
| `customerId` | `customer` |
| `customFieldsTitle`, `customFields` | `customFieldsTitle`, `customFields` |
| `locale` | `locale` |
| `charge.amount: 0` with `capture: false` (card verification) | `action: card_verification` with `currency` |
| `termsAndConditionsUrl` | Not available, configure it in Settings → Checkout & Payment Links |
| `threeDSecure` | Not available, see [3D Secure](#3d-secure) |


## Examples

In every example, the browser side changes the same way: instead of the signed Checkout Request, pass the session's `clientSecret` to Checkout.

```html
<!-- Before -->
<script src="https://dev.shift4.com/checkout.js" class="shift4-button"
  data-key="pk_test_xxxxxxxxxxxxxxxxxxxxxxxx"
  data-checkout-request="NjUwMWRhNTg0Nzll...">
</script>

<!-- After -->
<script src="https://dev.shift4.com/checkout.js" class="shift4-button"
  data-key="pk_test_xxxxxxxxxxxxxxxxxxxxxxxx"
  data-client-secret="chcs_xxxxxxxxxxxxxxxxxxxxxxxx">
</script>
```

### Charge

#### Before

```json
{
  "charge": {
    "amount": 4999,
    "currency": "EUR"
  }
}
```

#### After

```bash
curl https://api.shift4.com/checkout-sessions \
  -u pr_test_tXHm9qV9qV9bjIRHcQr9PLPa: \
  -H "Content-Type: application/json" \
  -d '{
    "lineItems": [
      {
        "product": { "name": "Order 12345", "amount": 4999, "currency": "EUR" },
        "quantity": 1
      }
    ]
  }'
```

### Charge with metadata

#### Before

```json
{
  "charge": {
    "amount": 4999,
    "currency": "EUR",
    "metadata": {
      "orderId": 12345
    }
  }
}
```

#### After

```bash
curl https://api.shift4.com/checkout-sessions \
  -u pr_test_tXHm9qV9qV9bjIRHcQr9PLPa: \
  -H "Content-Type: application/json" \
  -d '{
    "lineItems": [
      {
        "product": { "name": "Order 12345", "amount": 4999, "currency": "EUR" },
        "quantity": 1
      }
    ],
    "metadata": {
      "orderId": "12345"
    }
  }'
```

`metadata` moves from `charge` to the top level of the session and is attached to the created charge. Checkout Session `metadata` values must be strings, so send numbers as strings and update any code that reads them back, for example from webhooks.

### Charge with 3D Secure

#### Before

```json
{
  "charge": {
    "amount": 15000,
    "currency": "USD"
  },
  "threeDSecure": {
    "enable": true,
    "requireEnrolledCard": false,
    "requireSuccessfulLiabilityShiftForEnrolledCard": true
  }
}
```

#### After

```bash
curl https://api.shift4.com/checkout-sessions \
  -u pr_test_tXHm9qV9qV9bjIRHcQr9PLPa: \
  -H "Content-Type: application/json" \
  -d '{
    "lineItems": [
      {
        "product": { "name": "Order A-1001", "amount": 15000, "currency": "USD" },
        "quantity": 1
      }
    ]
  }'
```

The session has no `threeDSecure` options. To keep 3D Secure required, create a 3DS Fraud Rule once for your account. See [3D Secure](#3d-secure).

### Subscription for an existing customer

#### Before

```json
{
  "customerId": "cust_xxxxxxxxxxxxxxxxxxxxxxxx",
  "subscription": {
    "planId": "plan_xxxxxxxxxxxxxxxxxxxxxxxx",
    "metadata": {
      "userId": "42"
    }
  }
}
```

#### After

```bash
curl https://api.shift4.com/checkout-sessions \
  -u pr_test_tXHm9qV9qV9bjIRHcQr9PLPa: \
  -H "Content-Type: application/json" \
  -d '{
    "customer": "cust_xxxxxxxxxxxxxxxxxxxxxxxx",
    "lineItems": [
      {
        "product": { "plan": "plan_xxxxxxxxxxxxxxxxxxxxxxxx" },
        "quantity": 1
      }
    ],
    "metadata": {
      "userId": "42"
    }
  }'
```

The session's `metadata` is attached to the created subscription, the same way `subscription.metadata` was.

## 3D Secure

A Checkout Session has no `threeDSecure` options. Instead, you control when 3D Secure is triggered with [3DS Fraud Rules](/docs/fraud-prevention/rules), which apply to every charge on your account.

| Checkout Request option | Checkout Session |
|  --- | --- |
| `enable` | Set by 3DS Fraud Rules. 3D Secure is required only when a rule that triggers 3D Secure matches the charge. To require it for every charge, use a rule with `:is_any_request`. |
| `requireEnrolledCard` | Always `false`. Cards that do not support 3D Secure can still be charged. |
| `requireSuccessfulLiabilityShiftForEnrolledCard` | Always `true`. The charge fails when the card supports 3D Secure but the verification is not successful. |


The most common Checkout Request setting, `enable: true` with the other two options at their defaults, therefore maps to a single 3DS Fraud Rule that triggers 3D Secure for every charge. The system rule *Trigger 3DS Fraud Rule for all incoming charges* does exactly that and is enabled by default.

Rules that bypass 3D Secure are checked before rules that trigger it. If a bypass rule matches a charge, for example one of the [system rules](/docs/fraud-prevention/rules#system-fraud-rules) for charges outside the scope of PSD2 or low-value charges, the charge is processed without 3D Secure even when a rule with `:is_any_request` is enabled. Disable those rules if every charge must go through 3D Secure.

## Opening Checkout with a Checkout Session

To see how to open Checkout with a Checkout Session, go to the examples page.

Instead of opening Checkout on your page, you can also redirect the customer to the payment page: every Checkout Session has a `url` that you can send the customer to.