> ## Documentation Index
> Fetch the complete documentation index at: https://docs.choosefloat.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Accepting payments

> The complete custom-integration walkthrough: checkout, redirect, callback, reconciliation.

This guide covers everything a production custom integration needs. If you haven't made your first API call yet, start with the [Quickstart](/quickstart).

## 1. Show Float at checkout

Before offering Float as a payment option, make sure the cart total is inside your configured range. Fetch your config once and cache it (it rarely changes — and the endpoint is rate-limited to 5 requests/hour):

```bash theme={null}
curl https://uat-secure.choosefloat.com/api/config \
  -H "Authorization: Bearer $FLOAT_CLIENT_SECRET"
```

Use `rules` to decide when to show the Float option and to render instalment messaging (e.g. cart total ÷ max term).

## 2. Create the checkout

When the shopper picks Float, create a checkout **server-side**:

```json POST /api/checkouts theme={null}
{
  "checkout": {
    "amount": 249900,
    "currency": "GBP",
    "client_reference_id": "order-1001",
    "notify_url": "https://example.com/float/notify",
    "success_url": "https://example.com/orders/1001/success",
    "cancel_url": "https://example.com/orders/1001/cancel",
    "customer": {
      "first_name": "Eric",
      "last_name": "Cartman",
      "email": "eric.cartman@example.com",
      "phone_number": "0101234567",
      "billing_address": "28201 E. Bonanza St, South Park"
    },
    "line_items": [
      { "name": "Clyde Frog", "amount": 249900, "quantity": 1 }
    ]
  }
}
```

Field-by-field details are in the [API reference](/api-reference/create-checkout). Practical notes:

* **`client_reference_id`** is your reconciliation key — use your order ID or cart session ID. It's echoed back verbatim in the callback.
* **`notify_url`** must be a publicly reachable HTTPS endpoint. It receives the payment result server-to-server.
* **`success_url` / `cancel_url`** are browser redirect targets only — treat them as UX, not truth.
* **`customer.email`** is required; pre-filling the rest reduces friction on Float's page.
* **`line_items`** appear on the shopper's payment summary — include them for a better checkout experience.
* [Sign the request](/authentication#request-signing) with `X-Signature`.

Persist the returned checkout `id` on your order before redirecting.

## 3. Redirect the shopper

Send the browser to the returned `payment_url` with a plain HTTP redirect. Don't iframe it unless Float has explicitly enabled iframe integration for your account (this requires domain allowlisting on Float's side).

## 4. Handle the callback

Float POSTs the result to your `notify_url` the moment the payment reaches a terminal state. This is the authoritative signal — implement it per the [callbacks guide](/guides/callbacks):

1. Verify the `X-Signature` header.
2. Look up the order by `client_reference_id`.
3. If `successful`: mark paid, fulfil. If `cancelled`: release the order.
4. Respond `2xx` — optionally with `{ "redirect_url": "..." }` to send the shopper to an order-specific confirmation page.

## 5. Handle the browser return

The shopper lands on your `success_url` or `cancel_url` (or the `redirect_url` you returned from the callback). Because the callback usually arrives first, the order state should already be settled — render it. If the callback hasn't arrived yet (rare, but possible), poll your own order state briefly or call `GET /api/checkouts/:id` rather than showing an error.

## 6. Reconcile

For robustness, run a periodic reconciliation for orders stuck in a pending state older than \~1 hour:

```bash theme={null}
curl https://uat-secure.choosefloat.com/api/checkouts/9b2f61c4-6c1e-4b7a-9a1d-3f2a8c0de111 \
  -H "Authorization: Bearer $FLOAT_CLIENT_SECRET"
```

```json theme={null}
{
  "data": {
    "id": "9b2f61c4-6c1e-4b7a-9a1d-3f2a8c0de111",
    "type": "checkouts",
    "attributes": {
      "amount": 249900,
      "currency": "GBP",
      "status": "successful",
      "created_at": "2026-07-17T10:30:00Z"
    }
  }
}
```

A checkout still `received` after its \~30-minute payment session has lapsed will never complete — treat it as abandoned and create a new checkout if the shopper returns.

## Edge cases worth handling

<AccordionGroup>
  <Accordion title="Shopper closes the browser after paying">
    The callback still fires — your `notify_url` is server-to-server. This is exactly why fulfilment must key off the callback, not the `success_url`.
  </Accordion>

  <Accordion title="Duplicate callbacks">
    Callbacks can be retried and duplicated. Make your handler idempotent: if the order is already marked paid, acknowledge with a `2xx` and do nothing.
  </Accordion>

  <Accordion title="Amount changed after checkout creation">
    A Float checkout is fixed-amount. If the cart changes, abandon the old checkout and create a new one — never reuse a `payment_url` for a different total.
  </Accordion>

  <Accordion title="Shopper clicks back and pays twice">
    Guard on your side: if an order is already paid, don't create another checkout for it. Use one `client_reference_id` per payable order state.
  </Accordion>
</AccordionGroup>
