> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.crisscross.money/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.crisscross.money/_mcp/server.

# Create Checkout Session

POST https://api.crisscross.money/v1/checkout/session
Content-Type: application/json

Creates a checkout session. For hosted integrations (`integrationType: hosted`) the
response includes a signed hosted-checkout `paymentLink`; direct integrations receive
identifiers only — no `paymentLink` — and initiate the payment against the returned
`sessionId` via `POST /payment`.


Reference: https://docs.crisscross.money/api-reference/collect/payments/payment-initiation/create-checkout-session

## Request

### Body (application/json)

This endpoint expects a CreateCheckoutSessionRequest.

- `merchantId` (string, required) — The merchant identifier.
- `merchantReference` (string, required) — Your reference for the checkout session. **Unique per merchant** — reusing a reference is rejected with `409 DUPLICATE_REFERENCE` carrying the existing session's id. Use a unique value per logical order; retrying a request with the same reference is a safe probe for whether the original creation succeeded. Always supply a non-empty value: an empty string is accepted, but it is exempt from the uniqueness check and the session cannot be found via Search Payments. See the [Payment Status & Recovery guide](/collect/payment-recovery).
- `amount` (integer, required) — Amount in minor units, denominated in `currency` (the initiation currency).
- `currency` (enum, required) — Currency of the session (ISO 4217). Without a `rateLockId` this is the collection currency the payer pays in. With a `rateLockId` it must equal the lock's base currency (the currency you price in, e.g. `USD`); the payer is then charged the equivalent in the lock's quote currency at the lock's all-in rate.
  - Allowed values: `ZAR`, `NGN`, `UGX`, `ZMW`, `GHS`, `KES`, `TZS`, `ETB`, `USD`, `XAF`, `XOF`, `SLE`
- `integrationType` (enum, required) — Type of integration.
  - Allowed values: `hosted`, `direct`
- `payerDetails` (CreateCheckoutSessionPayerDetails, required)
- `collectionCurrency` (enum, optional) — Optional. The **collection currency** — the currency the payer is actually charged in. Defaults to the primary currency for `payerDetails.location` when omitted. When `collectionCurrency` differs from `currency`, [Adaptive Currency Conversion](/adaptive-currency-conversion) applies: priced by the supplied `rateLockId`, or by its default per-session quote minted at session creation.
  - Allowed values: `ZAR`, `NGN`, `UGX`, `ZMW`, `GHS`, `KES`, `TZS`, `ETB`, `USD`, `XAF`, `XOF`, `SLE`
- `redirectUrl` (string, optional) — Optional URL to redirect after payment.
- `rateLockId` (string, optional) — Optional rate lock to apply to this session. When set, the session `currency` must equal the lock's base currency, the session's collection currency must equal the lock's quote currency, and the payer is charged the equivalent in the lock's quote currency at the lock's all-in rate. The lock is verified at session creation and again when a payment is initiated; a lock that is unknown, expired, or cancelled is rejected with `422`. If omitted and `collectionCurrency` differs from `currency`, the conversion falls back to [Adaptive Currency Conversion](/adaptive-currency-conversion)'s default per-session rate.
- `paymentAttributes` (map from string to string, optional) — Optional session attributes to pass through the payment lifecycle. All values must be strings. Three keys are meaningful to the hosted checkout — `showChargedAs`, `showConversionRate` and `showConversionFee` — each defaulting to shown and accepting `"false"` to hide that line from the payer.
- `expiresInSeconds` (integer, optional) — Optional session lifetime in seconds (30 seconds to 7 days). The payment confirmation window expires this long after creation; defaults to the standard session lifetime when omitted.

## Response

### 201

Session created successfully. When the session was priced by a [rate lock](/rate-locks), the response also carries `fxCommit` — the collection currency and amount the payer will be charged, the all-in rate, the markup, and the fee — so you can show the payer both amounts without a second call. A session priced by the default per-session rate returns identifiers only; read it back with [Get Checkout Session](#operation/getCheckoutSession) to see its `fxCommit`.

- `sessionId` (string, required) — Unique identifier for the session.
- `paymentLink` (string, optional) — The hosted checkout URL for this session, present when `integrationType` is `hosted`. Redirect the buyer to it exactly as returned — the `signature` query parameter authorises the session, so a URL rebuilt by hand will not authenticate.
- `payerId` (string, optional) — Persistent identifier for the payer (customer). CrissCross assigns one when the session is created, or returns the existing `payerId` if you supplied one in `payerDetails.payerId`. Store it against your customer record to recognise this customer on future sessions. See the [Payer ID guide](/payer-id).
- `signature` (string, optional) — HMAC that authorises this session on the hosted checkout. It is already appended to `paymentLink` as a `signature` query parameter, so for a hosted integration you do not need to handle it separately — redirect the buyer to `paymentLink` as returned. Treat it as a capability for this one session: anyone holding it can act on the session, so do not log it or expose it beyond the buyer's browser. Unrelated to the `svix-signature` header used to verify [webhooks](/core-concepts-webhooks).
- `fxCommit` (FxCommit, optional) — Present when the session was priced by a [rate lock](/rate-locks) (`rateLockId` supplied): the collection currency and amount the payer will be charged at the lock's `allInRate`, with `pricingModel: RATE_LOCK` and `quoteId` the `rlk_` lock id. Absent for same-currency sessions and for sessions priced by the default per-session rate.

## Errors

### 400 Bad Request Error

Invalid request parameters. Also returned when your account prices Adaptive Currency Conversion with rate locks and the session carries no `rateLockId` — every converted session must bring one.

- `message` (string, required) — Human-readable description of what went wrong.
- `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on.
- `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth.

### 401 Unauthorized Error

Unauthorized.

- `message` (string, required) — Human-readable description of what went wrong.
- `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on.
- `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth.

### 403 Forbidden Error

FX at checkout is not enabled on your account. Returned when the session would require a currency conversion — the session `currency` differs from the collection currency for the payer's location — and the feature has not been switched on for you. This check runs before any rate-lock handling, so while it is failing no rate-lock error can be reached, whatever `rateLockId` you send. Contact us to have FX at checkout enabled; see [Adaptive Currency Conversion](/adaptive-currency-conversion).

- `message` (string, required) — Human-readable description of what went wrong.
- `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on.
- `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth.

### 409 Conflict Error

Conflict — a session with this `merchantReference` already exists for this merchant. `existingSessionId` is the most recent session owning the reference; fetch its state with `GET /checkout/session?sessionId={existingSessionId}`. See the [Payment Status & Recovery guide](/collect/payment-recovery).

- `code` (enum, required) — Machine-readable conflict code.
  - Allowed values: `DUPLICATE_REFERENCE`
- `message` (string, required) — Error message describing what went wrong.
- `existingSessionId` (string, required) — Id of the most recent session owning this merchantReference.

### 422 Unprocessable Entity Error

Unprocessable Entity. When a `rateLockId` is supplied, the lock is verified at session creation: an unknown lock, one that has expired or been cancelled, a base currency not matching the session `currency`, or a quote currency not matching the session's collection currency is rejected here. The lock is verified again when a payment is initiated. All four rejections below share `422` — branch on `error`, not on the status. The lock is checked in order: it must exist, then its currencies must match the session, then it must still be usable. A currency mismatch is therefore reported before expiry is even evaluated.

- `message` (string, required) — Human-readable description of what went wrong.
- `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on.
- `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth.

### 502 Bad Gateway Error

The rate-lock service could not be reached. Transient — retry with backoff. This is an upstream failure, not a problem with your request or your lock.

- `message` (string, required) — Human-readable description of what went wrong.
- `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on.
- `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth.

## Types

### CreateCheckoutSessionPayerDetails

- `emailAddress` (string, required) — Email address of the payer, used for receipts and provider records.
- `location` (enum, required) — Country code of the payer (alpha-3).
  - Allowed values: `ZAF`, `NGA`, `UGA`, `ZMB`, `GHA`, `KEN`, `TZA`, `ETH`, `CMR`, `SEN`, `BEN`, `BFA`, `CIV`, `MLI`, `TGO`, `SLE`
- `fullName` (string, optional) — Full name of the payer.
- `phoneNumber` (string, optional) — Phone number of the payer.
- `payerId` (string, optional) — Optional. The `payerId` of an existing customer, used to associate this session with a payer you've seen before. Supply the `payerId` you stored from a previous session's response. Omit it for a new customer — CrissCross will assign one and return it. See the [Payer ID guide](/payer-id).

### FxCommit

Immutable FX conversion snapshot recorded when a session or transaction is priced in a currency other than the merchant's initiation currency. Written whenever [Adaptive Currency Conversion](/adaptive-currency-conversion) applies — priced per session by default, or by a [rate lock](/rate-locks).

- `quoteId` (string, required) — Identifier of the FX commitment that priced this conversion. For a session priced by a [rate lock](/rate-locks) this is the `rlk_` rate lock id — use it to read back which lock a session was priced against. For an ACC conversion it is the upstream quote identifier, preserved for audit.
- `collectionCurrency` (string, required) — ISO 4217 currency code the customer pays in.
- `collectionAmount` (string, required) — All-in amount the customer is asked to pay, in `collectionCurrency` major units (decimal string).
- `midRate` (string, required) — Spot mid-market rate before markup. `1 initiation-currency = midRate collection-currency` (decimal string, 8 decimal places).
- `markupBps` (integer, required) — Markup applied over the mid rate, in basis points (100 bps = 1%).
- `allInRate` (string, required) — The rate actually transacted at — `midRate * (1 + markupBps / 10000)` (decimal string, 8 decimal places).
- `fee` (string, required) — Markup component denominated in `collectionCurrency`, expressed as a major-unit decimal string.
- `expiresAt` (datetime, required) — RFC3339 timestamp. The commit is valid until this time; past-expiry payment submissions are rejected `422` (stale quote) and the hosted page refreshes the rate.
- `pricingModel` (enum, optional) — Which [Adaptive Currency Conversion](/adaptive-currency-conversion) flow priced the session — `SPOT_QUOTE` for the default [per-session rate](/api-reference/collect/per-session), `RATE_LOCK` for an attached [rate lock](/rate-locks). May be absent on sessions created before this field was introduced.
  - Allowed values: `SPOT_QUOTE`, `RATE_LOCK`

## Examples

### Hosted checkout

**Response**

```json
{
  "sessionId": "0d3f1d6c-1c49-4b89-9db6-1fdc06f24c8a",
  "paymentLink": "https://checkout.payos.money/session/0d3f1d6c-1c49-4b89-9db6-1fdc06f24c8a?signature=kRe8vQZ2mXpL7nT4wYbHs1JdA0uCgFiO6ExNq3ZrPyM",
  "payerId": "2a1c4f1a-0d44-4f16-8c8e-9a3b4c5d6e7f",
  "signature": "kRe8vQZ2mXpL7nT4wYbHs1JdA0uCgFiO6ExNq3ZrPyM"
}
```

**SDK Code**

```python Hosted checkout
import requests

url = "https://api.crisscross.money/v1/checkout/session"

response = requests.post(url)

print(response.json())
```

```javascript Hosted checkout
const url = 'https://api.crisscross.money/v1/checkout/session';
const options = {method: 'POST'};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Hosted checkout
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.crisscross.money/v1/checkout/session"

	req, _ := http.NewRequest("POST", url, nil)

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Hosted checkout
require 'uri'
require 'net/http'

url = URI("https://api.crisscross.money/v1/checkout/session")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
```

```java Hosted checkout
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.crisscross.money/v1/checkout/session")
  .asString();
```

```php Hosted checkout
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.crisscross.money/v1/checkout/session');

echo $response->getBody();
```

```csharp Hosted checkout
using RestSharp;

var client = new RestClient("https://api.crisscross.money/v1/checkout/session");
var request = new RestRequest(Method.POST);
IRestResponse response = client.Execute(request);
```

```swift Hosted checkout
import Foundation

let request = NSMutableURLRequest(url: NSURL(string: "https://api.crisscross.money/v1/checkout/session")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Direct integration — priced by a rate lock

**Response**

```json
{
  "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70",
  "payerId": "2a1c4f1a-0d44-4f16-8c8e-9a3b4c5d6e7f",
  "fxCommit": {
    "quoteId": "rlk_550e8400-e29b-41d4-a716-446655440000",
    "collectionCurrency": "KES",
    "collectionAmount": "13260.00",
    "midRate": "130.00",
    "markupBps": 200,
    "allInRate": "132.60",
    "fee": "260.00",
    "expiresAt": "2026-06-24T22:30:00Z",
    "pricingModel": "RATE_LOCK"
  }
}
```

**SDK Code**

```python Direct integration — priced by a rate lock
import requests

url = "https://api.crisscross.money/v1/checkout/session"

response = requests.post(url)

print(response.json())
```

```javascript Direct integration — priced by a rate lock
const url = 'https://api.crisscross.money/v1/checkout/session';
const options = {method: 'POST'};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Direct integration — priced by a rate lock
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.crisscross.money/v1/checkout/session"

	req, _ := http.NewRequest("POST", url, nil)

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Direct integration — priced by a rate lock
require 'uri'
require 'net/http'

url = URI("https://api.crisscross.money/v1/checkout/session")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
```

```java Direct integration — priced by a rate lock
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.crisscross.money/v1/checkout/session")
  .asString();
```

```php Direct integration — priced by a rate lock
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.crisscross.money/v1/checkout/session');

echo $response->getBody();
```

```csharp Direct integration — priced by a rate lock
using RestSharp;

var client = new RestClient("https://api.crisscross.money/v1/checkout/session");
var request = new RestRequest(Method.POST);
IRestResponse response = client.Execute(request);
```

```swift Direct integration — priced by a rate lock
import Foundation

let request = NSMutableURLRequest(url: NSURL(string: "https://api.crisscross.money/v1/checkout/session")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Payment Initiation_createCheckoutSession_example

**Request**

```json
{
  "merchantId": "1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
  "merchantReference": "ORDER12345",
  "amount": 15000,
  "currency": "NGN",
  "integrationType": "hosted",
  "payerDetails": {
    "emailAddress": "chinwe.okafor@example.com",
    "location": "NGA",
    "fullName": "Chinwe Okafor",
    "phoneNumber": "08012345678"
  },
  "redirectUrl": "https://merchant.com/redirect",
  "paymentAttributes": {
    "orderId": "ORDER12345"
  }
}
```

**Response**

```json
{
  "sessionId": "0d3f1d6c-1c49-4b89-9db6-1fdc06f24c8a",
  "paymentLink": "https://checkout.payos.money/session/0d3f1d6c-1c49-4b89-9db6-1fdc06f24c8a?signature=kRe8vQZ2mXpL7nT4wYbHs1JdA0uCgFiO6ExNq3ZrPyM",
  "payerId": "2a1c4f1a-0d44-4f16-8c8e-9a3b4c5d6e7f",
  "signature": "kRe8vQZ2mXpL7nT4wYbHs1JdA0uCgFiO6ExNq3ZrPyM"
}
```

**SDK Code**

```python Payment Initiation_createCheckoutSession_example
import requests

url = "https://api.crisscross.money/v1/checkout/session"

payload = {
    "merchantId": "1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "merchantReference": "ORDER12345",
    "amount": 15000,
    "currency": "NGN",
    "integrationType": "hosted",
    "payerDetails": {
        "emailAddress": "chinwe.okafor@example.com",
        "location": "NGA",
        "fullName": "Chinwe Okafor",
        "phoneNumber": "08012345678"
    },
    "redirectUrl": "https://merchant.com/redirect",
    "paymentAttributes": { "orderId": "ORDER12345" }
}
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Payment Initiation_createCheckoutSession_example
const url = 'https://api.crisscross.money/v1/checkout/session';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"merchantId":"1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d","merchantReference":"ORDER12345","amount":15000,"currency":"NGN","integrationType":"hosted","payerDetails":{"emailAddress":"chinwe.okafor@example.com","location":"NGA","fullName":"Chinwe Okafor","phoneNumber":"08012345678"},"redirectUrl":"https://merchant.com/redirect","paymentAttributes":{"orderId":"ORDER12345"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Payment Initiation_createCheckoutSession_example
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.crisscross.money/v1/checkout/session"

	payload := strings.NewReader("{\n  \"merchantId\": \"1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d\",\n  \"merchantReference\": \"ORDER12345\",\n  \"amount\": 15000,\n  \"currency\": \"NGN\",\n  \"integrationType\": \"hosted\",\n  \"payerDetails\": {\n    \"emailAddress\": \"chinwe.okafor@example.com\",\n    \"location\": \"NGA\",\n    \"fullName\": \"Chinwe Okafor\",\n    \"phoneNumber\": \"08012345678\"\n  },\n  \"redirectUrl\": \"https://merchant.com/redirect\",\n  \"paymentAttributes\": {\n    \"orderId\": \"ORDER12345\"\n  }\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Payment Initiation_createCheckoutSession_example
require 'uri'
require 'net/http'

url = URI("https://api.crisscross.money/v1/checkout/session")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"merchantId\": \"1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d\",\n  \"merchantReference\": \"ORDER12345\",\n  \"amount\": 15000,\n  \"currency\": \"NGN\",\n  \"integrationType\": \"hosted\",\n  \"payerDetails\": {\n    \"emailAddress\": \"chinwe.okafor@example.com\",\n    \"location\": \"NGA\",\n    \"fullName\": \"Chinwe Okafor\",\n    \"phoneNumber\": \"08012345678\"\n  },\n  \"redirectUrl\": \"https://merchant.com/redirect\",\n  \"paymentAttributes\": {\n    \"orderId\": \"ORDER12345\"\n  }\n}"

response = http.request(request)
puts response.read_body
```

```java Payment Initiation_createCheckoutSession_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.crisscross.money/v1/checkout/session")
  .header("Content-Type", "application/json")
  .body("{\n  \"merchantId\": \"1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d\",\n  \"merchantReference\": \"ORDER12345\",\n  \"amount\": 15000,\n  \"currency\": \"NGN\",\n  \"integrationType\": \"hosted\",\n  \"payerDetails\": {\n    \"emailAddress\": \"chinwe.okafor@example.com\",\n    \"location\": \"NGA\",\n    \"fullName\": \"Chinwe Okafor\",\n    \"phoneNumber\": \"08012345678\"\n  },\n  \"redirectUrl\": \"https://merchant.com/redirect\",\n  \"paymentAttributes\": {\n    \"orderId\": \"ORDER12345\"\n  }\n}")
  .asString();
```

```php Payment Initiation_createCheckoutSession_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.crisscross.money/v1/checkout/session', [
  'body' => '{
  "merchantId": "1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
  "merchantReference": "ORDER12345",
  "amount": 15000,
  "currency": "NGN",
  "integrationType": "hosted",
  "payerDetails": {
    "emailAddress": "chinwe.okafor@example.com",
    "location": "NGA",
    "fullName": "Chinwe Okafor",
    "phoneNumber": "08012345678"
  },
  "redirectUrl": "https://merchant.com/redirect",
  "paymentAttributes": {
    "orderId": "ORDER12345"
  }
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Payment Initiation_createCheckoutSession_example
using RestSharp;

var client = new RestClient("https://api.crisscross.money/v1/checkout/session");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"merchantId\": \"1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d\",\n  \"merchantReference\": \"ORDER12345\",\n  \"amount\": 15000,\n  \"currency\": \"NGN\",\n  \"integrationType\": \"hosted\",\n  \"payerDetails\": {\n    \"emailAddress\": \"chinwe.okafor@example.com\",\n    \"location\": \"NGA\",\n    \"fullName\": \"Chinwe Okafor\",\n    \"phoneNumber\": \"08012345678\"\n  },\n  \"redirectUrl\": \"https://merchant.com/redirect\",\n  \"paymentAttributes\": {\n    \"orderId\": \"ORDER12345\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Payment Initiation_createCheckoutSession_example
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "merchantId": "1b2a3d4c-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
  "merchantReference": "ORDER12345",
  "amount": 15000,
  "currency": "NGN",
  "integrationType": "hosted",
  "payerDetails": [
    "emailAddress": "chinwe.okafor@example.com",
    "location": "NGA",
    "fullName": "Chinwe Okafor",
    "phoneNumber": "08012345678"
  ],
  "redirectUrl": "https://merchant.com/redirect",
  "paymentAttributes": ["orderId": "ORDER12345"]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.crisscross.money/v1/checkout/session")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```