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

# Checkout

> Accept M-PESA, Airtel Money and bank transfer on one Tanda-hosted payment page.

Checkout, the unified payment SDK, is the quickest way to accept payments. Your server creates a session, you redirect the customer to the link Tanda returns, and the customer pays by M-PESA, Airtel Money or bank transfer on a Tanda-hosted page. Tanda handles the phone prompt and bank details for you.

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant M as Your server
    participant T as Tanda
    M->>T: POST /api/b2b/fiat/payment-sdk
    T-->>M: transactionId + paymentSdkLink
    M->>C: Redirect to paymentSdkLink
    C->>T: Chooses a method and pays
    T-->>C: Redirect to your success, failure or cancel URL
    T->>M: transaction_updated webhook
    M->>T: GET /api/b2b/transactions/{transactionId} (confirm)
    M->>C: Fulfil the order
```

## 1. Create a session

Call [Create checkout session](https://api.tandapay.io/api-reference/endpoint/create-checkout-session.md) from your server.

```bash cURL theme={null}
curl -X POST https://api.tandapay.io/api/b2b/fiat/payment-sdk \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1500,
    "chargeCurrency": "KES",
    "walletCurrency": "KES",
    "externalReference": "order_10045",
    "customerId": "customer_123",
    "prefillData": {
      "firstName": "Brian",
      "lastName": "Otieno",
      "email": "brian@example.com",
      "phoneNumber": "+254712345678"
    },
    "successRedirectUrl": "https://merchant.co.ke/checkout/success",
    "failureRedirectUrl": "https://merchant.co.ke/checkout/failed",
    "cancelRedirectUrl": "https://merchant.co.ke/checkout/cancelled"
  }'
```

```javascript Node.js (Express) theme={null}
app.post("/checkout", async (req, res) => {
  const order = await createOrder(req.body); // your own order record

  const session = await fetch("https://api.tandapay.io/api/b2b/fiat/payment-sdk", {
    method: "POST",
    headers: { Authorization: `Bearer ${await getToken()}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      amount: order.total,
      chargeCurrency: "KES",
      walletCurrency: "KES",
      externalReference: order.id,
      customerId: order.customerId,
      prefillData: { firstName: order.firstName, lastName: order.lastName, email: order.email },
      successRedirectUrl: `https://merchant.co.ke/orders/${order.id}/success`,
      failureRedirectUrl: `https://merchant.co.ke/orders/${order.id}/failed`,
      cancelRedirectUrl: `https://merchant.co.ke/orders/${order.id}`,
    }),
  }).then((r) => r.json());

  await saveTransactionId(order.id, session.data.transactionId);
  res.redirect(303, session.data.paymentSdkLink);
});
```

```python Python theme={null}
session = requests.post(
    "https://api.tandapay.io/api/b2b/fiat/payment-sdk",
    headers={"Authorization": f"Bearer {get_token()}"},
    json={
        "amount": order.total,
        "chargeCurrency": "KES",
        "walletCurrency": "KES",
        "externalReference": order.id,
        "successRedirectUrl": f"https://merchant.co.ke/orders/{order.id}/success",
        "failureRedirectUrl": f"https://merchant.co.ke/orders/{order.id}/failed",
        "cancelRedirectUrl": f"https://merchant.co.ke/orders/{order.id}",
    },
).json()

save_transaction_id(order.id, session["data"]["transactionId"])
return redirect(session["data"]["paymentSdkLink"])
```

```json Response theme={null}
{
  "success": true,
  "message": "Transaction created successfully",
  "data": {
    "transactionId": "aB3dE5fG7hJ9kL1mN2pQ",
    "paymentSdkLink": "https://gateway.tandapay.io/sdk/transaction/aB3dE5fG7hJ9kL1mN2pQ"
  }
}
```

### Options

**method** `string`

Show only one method: `momo` (M-PESA and Airtel Money) or `bank`. Leave it out to let the customer choose from everything enabled on your account.

**customerId** `string`

Your own ID for the customer. It comes back on the transaction, so you can match payments to customers.

**prefillData** `object`

`firstName`, `lastName`, `email`, `accountName` and `phoneNumber` (E.164, e.g. `+254712345678`) to fill in for the customer. Prefilled values can't be edited on the payment page, so only send details you're sure of.

**successRedirectUrl** `string`

Where Tanda sends the customer after a successful payment.

**failureRedirectUrl** `string`

Where Tanda sends the customer after a failed payment.

**cancelRedirectUrl** `string`

Where Tanda sends the customer if they cancel. All three redirect URLs are optional but recommended.

> [!TIP]
>
> Add your own reference to the redirect URLs, for example `https://merchant.co.ke/checkout/success?ref=order_10045`, so the page knows which order to look up.

## 2. Redirect the customer

Send the customer to `paymentSdkLink` exactly as returned. Don't build or modify the URL yourself. On the page they pick M-PESA, Airtel Money or bank transfer and complete the payment.

## 3. Handle the return

When the customer finishes, Tanda redirects them to the matching URL. Use this page to show a message such as "We're confirming your payment".

> [!WARNING]
>
> A redirect to your success URL is **not** proof of payment. Customers can open that URL directly. Always confirm the status from your server before fulfilling.

## 4. Confirm and fulfil

You'll receive `transaction_updated` [webhooks](https://api.tandapay.io/guides/webhooks/overview.md) as the payment progresses. Before you fulfil, confirm the outcome from your server with [Get transaction](https://api.tandapay.io/api-reference/endpoint/get-transaction.md), using the `transactionId` or your `externalReference`.

```bash theme={null}
curl https://api.tandapay.io/api/b2b/transactions/aB3dE5fG7hJ9kL1mN2pQ \
  -H "Authorization: Bearer $TOKEN"
```

```json Response theme={null}
{
  "success": true,
  "data": {
    "transactionId": "aB3dE5fG7hJ9kL1mN2pQ",
    "externalReference": "order_10045",
    "customerId": "customer_123",
    "type": "deposit",
    "method": "momo",
    "amount": 1500,
    "chargeCurrency": "KES",
    "walletCurrency": "KES",
    "chargeStatus": "successful",
    "status": "SUCCESSFUL",
    "fullTimestamp": "2026-10-28T09:14:22.000Z"
  }
}
```

| `chargeStatus` | Meaning | What to do |
| :- | :- | :- |
| `pending` | The customer hasn't finished, or a step (phone prompt, bank transfer) is outstanding. | Wait. Show "confirming your payment". |
| `successful` | Money received. | Fulfil the order. |
| `failed` | The payment didn't go through. | Let the customer try again with a **new** session and a new `externalReference`. |

## Good practice

* Create a new session, with a new `externalReference`, for every attempt.
* Store `transactionId` against your order as soon as the session is created, so webhooks can find the order.
* Make your fulfilment idempotent: the same webhook can arrive more than once.
* Use [Get checkout methods](https://api.tandapay.io/api-reference/endpoint/get-checkout-methods.md) if you want to show method logos before the customer leaves your site.

## Test it

In sandbox, the value the customer enters on the hosted page decides the outcome. See [Testing](https://api.tandapay.io/guides/testing.md#checkout).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.