Checkout API

Base URL: /v1/checkout
Auth:

  • POST /v1/checkout uses the business public key in the request body
  • other server-side checkout endpoints use Authorization: Bearer <secret_key>

Overview

Checkout creates a hosted payment session for one business order. The payer can use the payment methods enabled in the business preferences, while your application keeps one trx_id as the link between the order, redirects, status checks, and webhooks.

Recommended flow

  1. Optionally request a quote when the order is denominated in crypto.
  2. Create the checkout with a unique trx_id and explicit success and cancellation URLs.
  3. Store the returned checkout URL and identifiers before redirecting the customer.
  4. Let the hosted page create or refresh payment instructions as the customer changes method.
  5. Fulfill only after a signed webhook or server-side status check reports success.

Checkout creation is idempotent around trx_id and includes an additional short duplicate guard. Always use the transaction identifier returned by the API, especially when reused is true.

Quote checkout amount

GET /v1/checkout/quote

Preview the fiat/crypto conversion before creating a checkout session.

Query:

  • amount required
  • amount_type optional: fiat | crypto
  • asset optional, default solana:usdc
  • public_key optional but recommended when the quote should reflect the business fee rule

Example:

GET /v1/checkout/quote?amount=10&amount_type=crypto&asset=solana:usdc&public_key=pk_live_xxxxx

Response body:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Checkout quote",
  "data": {
    "amount_type": "crypto",
    "asset": "solana:usdc",
    "fiat_amount": 14319.04,
    "base_fiat_amount": 14097.58,
    "fee_amount": 221.46,
    "customer_pays_fee": true,
    "requested_crypto_amount": 10,
    "fiat_currency": "NGN",
    "crypto_amount": 10,
    "crypto_currency": "USDC",
    "network": "solana",
    "rate": 1409.758,
    "expires_at": "2026-05-11 10:30:00",
    "refresh_after_seconds": 60,
    "quote": {
      "rate": 1409.758
    }
  }
}

Notes:

  • quote previews are short-lived
  • refresh the quote again after refresh_after_seconds or once expires_at has passed
  • pass public_key when you want the quote to reflect the business who_pays_fee preference
  • omitting public_key falls back to Tsara's base checkout pricing
  • the endpoint returns 503 when crypto quote generation is temporarily unavailable

Checkout assets

GET /v1/checkout/assets

Return the supported checkout amount modes and crypto assets for the UI.

Notes:

  • this response reflects Tsara's currently supported checkout assets
  • updating the config changes both validation and the checkout/assets response
  • asset flags:
    • enabled: allowed in live checkout flows
    • test_enabled: allowed in test checkout flows
    • hold_supported: allowed when the checkout falls back to hold mode
  • asset UI metadata:
    • sort_order: controls returned display order
    • disabled_reason: optional machine-friendly reason
    • maintenance_message: optional user-facing message

Response body:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Checkout assets",
  "data": {
    "amount_types": ["fiat", "crypto"],
    "assets": [
      {
        "network_asset": "solana:usdc",
        "network": "solana",
        "asset": "usdc",
        "symbol": "USDC",
        "label": "Solana USDC",
        "sort_order": 10,
        "enabled": true,
        "test_enabled": true,
        "hold_supported": true,
        "disabled_reason": null,
        "maintenance_message": null
      },
      {
        "network_asset": "bsc:usdt",
        "network": "bsc",
        "asset": "usdt",
        "symbol": "USDT",
        "label": "BSC USDT",
        "sort_order": 50,
        "enabled": true,
        "test_enabled": true,
        "hold_supported": false,
        "disabled_reason": null,
        "maintenance_message": null
      }
    ]
  }
}

Create checkout session

POST /v1/checkout

Create a checkout transaction and return the hosted checkout URL.

Body:

{
  "public_key": "pk_live_xxxxx",
  "trx_id": "order_001",
  "email": "[email protected]",
  "name": "John Doe",
  "phone": "08000000000",
  "amount": 5000,
  "amount_type": "fiat",
  "currency": "NGN",
  "redirect_url": "https://example.com/checkout/return",
  "success_url": "https://example.com/checkout/success",
  "cancel_url": "https://example.com/checkout/cancelled",
  "timeout_url": "https://example.com/checkout/expired",
  "payment_method": "bank-transfer",
  "asset": "solana:usdc"
}

Notes:

  • amount_type is optional and defaults to fiat
  • supported values:
    • fiat: amount is treated as NGN
    • crypto: amount is treated as the requested crypto amount for the selected asset
  • asset is optional and defaults to solana:usdc
  • asset is used for crypto checkout generation, especially the non-hold crypto payment flow
  • payment_method is optional and defaults to bank-transfer
  • redirect_url is the general fallback return URL
  • success_url, cancel_url, and timeout_url are optional overrides for the hosted checkout redirect flow
  • redirect precedence:
    • success -> success_url ?? redirect_url
    • cancel -> cancel_url ?? redirect_url
    • timeout -> timeout_url ?? cancel_url ?? redirect_url
  • Tsara appends trx_id, reference, and status to the chosen redirect URL
  • data.expires_at is the overall checkout session expiry
  • account.crypto.expires_at is the current crypto deposit instruction expiry
  • for amount_type=crypto, the requested crypto amount is first priced before checkout instructions are created
  • when the business preference says the customer pays checkout fees, the payable fiat amount is uplifted before payment instructions are generated
  • when amount_type=crypto, account.crypto.requested_amount and account.crypto.requested_amount_type are returned
  • available_payment_methods reflects the business payment-method preference
  • default_payment_method is resolved from the business preference:
    • bank-transfer if available
    • otherwise crypto
  • provisioning behavior at create time:
    • bank-transfer only: generate fiat only
    • crypto only: generate crypto only
    • both: generate fiat only; crypto is created later when the user switches on the checkout UI
  • the same fee rule now applies consistently to quote, fiat-first checkout, and crypto-first checkout pricing
  • if crypto payment setup is temporarily unavailable:
    • crypto-only checkout creation fails
    • dual-mode checkout creation still succeeds with fiat account details only
  • crypto_available shows whether crypto payment details were created successfully for that checkout
  • crypto_error is returned when fiat checkout was created but crypto setup failed

Test-mode note:

  • when the provided public key is a test key, the controller uses the same amount_type pricing rules

Duplicate checkout protection

Each trx_id must be unique. As an additional safeguard against rapid duplicate submissions, Tsara may reuse a matching pending checkout created within the previous three minutes when the business, customer, amount, currency, amount type, asset, and environment are the same.

When this happens:

  • no new payment account or transaction is created
  • the response has reused: true
  • trx_id and data.id contain the original transaction ID
  • requested_trx_id contains the new transaction ID sent in the duplicate request
  • the returned checkout URL and payment instructions belong to the original transaction
  • subsequent status checks and webhook events use the original trx_id

Example reuse response:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Existing checkout reused",
  "data": {
    "id": "order_001",
    "status": "pending",
    "checkout_url": "https://checkout.tsara.ng/?trx_id=order_001",
    "reused": true,
    "requested_trx_id": "order_002"
  },
  "url": "https://checkout.tsara.ng/?trx_id=order_001",
  "trx_id": "order_001",
  "requested_trx_id": "order_002",
  "reused": true
}

Integrations must continue using the returned trx_id whenever reused is true. Completed, failed, cancelled, expired, or older transactions are not reused.

Crypto-first example:

{
  "public_key": "pk_live_xxxxx",
  "trx_id": "order_002",
  "email": "[email protected]",
  "name": "John Doe",
  "amount": 10,
  "amount_type": "crypto",
  "asset": "solana:usdc",
  "redirect_url": "https://example.com/checkout/return",
  "success_url": "https://example.com/checkout/success",
  "cancel_url": "https://example.com/checkout/cancelled"
}

Response body:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Checkout Created",
  "data": {
    "id": "order_001",
    "status": "pending",
    "checkout_url": "https://checkout.tsara.ng/?trx_id=order_001",
    "expires_at": "2026-05-07 14:00:00",
    "available_payment_methods": ["bank-transfer", "crypto"],
    "default_payment_method": "bank-transfer",
    "crypto_available": true,
    "crypto_error": null,
    "account": {
      "fiat": {
        "bank_code": "090286",
        "bank_name": "Example MFB",
        "account_name": "TSARA CHECKOUT",
        "account_number": "1234567890"
      },
      "crypto": {
        "is_type": "offramp",
        "reference": "6c01338d-67e5-4d0c-a394-9d3e367f46a5",
        "publicKey": "7fYw4wLbW7F5L6LwY6u3pM7cQ2T8K1m3A9zP6n2sQ4rX",
        "address": "7fYw4wLbW7F5L6LwY6u3pM7cQ2T8K1m3A9zP6n2sQ4rX",
        "network": "solana",
        "asset": "usdc",
        "network_asset": "solana:usdc",
        "deposit_address": "7fYw4wLbW7F5L6LwY6u3pM7cQ2T8K1m3A9zP6n2sQ4rX",
        "deposit_asset": "solana:usdc",
        "fiat_amount": 14097.58,
        "fiat_currency": "NGN",
        "expires_at": "2026-05-07 13:50:00",
        "status": "AWAITING_DEPOSIT",
        "type": "OFFRAMP",
        "rate": {
          "USDC": 1409.758
        },
        "amount": 10
      }
    }
  },
  "url": "https://checkout.tsara.ng/?trx_id=order_001",
  "trx_id": "order_001",
  "available_payment_methods": ["bank-transfer", "crypto"],
  "default_payment_method": "bank-transfer",
  "crypto_available": true,
  "crypto_error": null,
  "account": {
    "fiat": {
      "bank_code": "090286",
      "bank_name": "Example MFB",
      "account_name": "TSARA CHECKOUT",
      "account_number": "1234567890"
    },
    "crypto": {
      "is_type": "offramp",
      "reference": "6c01338d-67e5-4d0c-a394-9d3e367f46a5",
      "publicKey": "7fYw4wLbW7F5L6LwY6u3pM7cQ2T8K1m3A9zP6n2sQ4rX",
      "address": "7fYw4wLbW7F5L6LwY6u3pM7cQ2T8K1m3A9zP6n2sQ4rX",
      "network": "solana",
      "asset": "usdc",
      "network_asset": "solana:usdc",
      "deposit_address": "7fYw4wLbW7F5L6LwY6u3pM7cQ2T8K1m3A9zP6n2sQ4rX",
      "deposit_asset": "solana:usdc",
      "fiat_amount": 14097.58,
      "fiat_currency": "NGN",
      "expires_at": "2026-05-07 13:50:00",
      "status": "AWAITING_DEPOSIT",
      "type": "OFFRAMP",
      "rate": {
        "USDC": 1409.758
      },
      "amount": 10
    }
  }
}

Fallback example when crypto setup fails:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Checkout Created",
  "data": {
    "id": "order_001",
    "status": "pending",
    "checkout_url": "https://checkout.tsara.ng/?trx_id=order_001",
    "expires_at": "2026-05-07 14:00:00",
    "available_payment_methods": ["bank-transfer", "crypto"],
    "default_payment_method": "bank-transfer",
    "crypto_available": false,
    "crypto_error": "Crypto payment is currently unavailable for this checkout.",
    "account": {
      "fiat": {
        "bank_code": "090286",
        "bank_name": "Example MFB",
        "account_name": "TSARA CHECKOUT",
        "account_number": "1234567890"
      }
    }
  }
}

Crypto-only example:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Checkout Created",
  "data": {
    "id": "order_001",
    "status": "pending",
    "checkout_url": "https://checkout.tsara.ng/?trx_id=order_001",
    "expires_at": "2026-05-07 14:00:00",
    "available_payment_methods": ["crypto"],
    "default_payment_method": "crypto",
    "crypto_available": true,
    "crypto_error": null,
    "account": {
      "crypto": {
        "network_asset": "solana:usdc",
        "asset": "usdc"
      }
    }
  }
}

Fetch checkout account

GET /v1/checkout/account

Return the current payment account details for a checkout transaction.

Query:

  • trx_id (required)
  • method (optional): fiat | crypto

Notes:

  • when method=crypto, only the crypto block is returned
  • when method=fiat, only the fiat block is returned
  • when method is omitted, both blocks are returned when available
  • this endpoint also returns available_payment_methods and default_payment_method so the checkout UI can recover the business payment-mode state after refresh

Example:

GET /v1/checkout/account?trx_id=order_001&method=crypto
Authorization: Bearer sk_live_xxxxx

Response body:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Account updated!",
  "data": {
    "available_payment_methods": ["bank-transfer", "crypto"],
    "default_payment_method": "bank-transfer",
    "crypto": {
      "is_type": "offramp",
      "reference": "6c01338d-67e5-4d0c-a394-9d3e367f46a5",
      "publicKey": "0xcd257a5867B42556fa14c35de714154B277f70fD",
      "address": "0xcd257a5867B42556fa14c35de714154B277f70fD",
      "deposit_address": "0xcd257a5867B42556fa14c35de714154B277f70fD",
      "deposit_asset": "solana:usdc",
      "fiat_amount": 14097.58,
      "fiat_currency": "NGN",
      "expires_at": "2026-05-07 13:50:00",
      "status": "AWAITING_DEPOSIT",
      "type": "OFFRAMP",
      "asset": "usdc",
      "network_asset": "solana:usdc",
      "network": "solana",
      "rate": {
        "USDC": 1409.758
      },
      "amount": 10
    }
  }
}

Update checkout crypto asset

POST /v1/checkout/crypto

Rebuild the crypto payment details for an existing checkout transaction using a selected network:asset.

Body:

{
  "trx_id": "order_001",
  "asset": "base:usdc"
}

Notes:

  • requires Authorization: Bearer <secret_key>
  • updates the stored account->crypto payload for the transaction
  • returns the refreshed crypto details for the checkout UI
  • also returns available_payment_methods and default_payment_method
  • hold mode currently supports solana:usdc only
  • if the existing crypto payment instruction for the requested network:asset is still valid, it is reused instead of creating a new one
  • non-hold mode returns 503 when crypto payment refresh is temporarily unavailable

Response body:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Crypto payment account updated!",
  "data": {
    "available_payment_methods": ["bank-transfer", "crypto"],
    "default_payment_method": "bank-transfer",
    "crypto": {
      "is_type": "offramp",
      "reference": "6c01338d-67e5-4d0c-a394-9d3e367f46a5",
      "publicKey": "0xcd257a5867B42556fa14c35de714154B277f70fD",
      "address": "0xcd257a5867B42556fa14c35de714154B277f70fD",
      "deposit_address": "0xcd257a5867B42556fa14c35de714154B277f70fD",
      "deposit_asset": "base:usdc",
      "fiat_amount": 14097.58,
      "fiat_currency": "NGN",
      "expires_at": "2026-05-07 13:50:00",
      "status": "AWAITING_DEPOSIT",
      "type": "OFFRAMP",
      "asset": "usdc",
      "network_asset": "base:usdc",
      "network": "base",
      "rate": {
        "USDC": 1409.758
      },
      "amount": 10
    }
  }
}

Checkout status

GET /v1/checkout/status

Query:

  • reference or trx_id

Notes:

  • returns available_payment_methods and default_payment_method for UI state recovery

Example:

GET /v1/checkout/status?trx_id=order_001
Authorization: Bearer sk_live_xxxxx

Response body:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Transaction Status",
  "data": {
    "status": "pending",
    "trx_id": "order_001",
    "available_payment_methods": ["bank-transfer", "crypto"],
    "default_payment_method": "bank-transfer"
  }
}

Checkout index

GET /v1/checkout

Fetch checkout transactions for the authenticated business.

Query:

  • trx_id (optional)
  • reference (optional)
  • status (optional, list mode)
  • customer_email (optional, list mode)
  • date_from (optional, list mode)
  • date_to (optional, list mode)
  • page (optional, list mode, default 1)
  • limit (optional, list mode, default 20, max 100)

Notes:

  • when fetching a single checkout by trx_id or reference, the response also includes available_payment_methods and default_payment_method
  • the account field is returned in the same normalized checkout shape used by create/account/crypto responses
  • when no filter is passed, the endpoint returns a list of checkout transactions ordered by latest first
  • list mode returns pagination metadata in meta
  • date-only filters like 2026-05-16 are expanded to full-day boundaries:
    • date_from => 2026-05-16 00:00:00
    • date_to => 2026-05-16 23:59:59

Single checkout response:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Transactions",
  "data": {
    "id": 1,
    "trx_id": "order_001",
    "reference": "ts-12345678",
    "status": "pending",
    "account": {
      "fiat": {
        "bank_code": "090286",
        "bank_name": "Example MFB",
        "account_name": "TSARA CHECKOUT",
        "account_number": "1234567890"
      }
    },
    "available_payment_methods": ["bank-transfer", "crypto"],
    "default_payment_method": "bank-transfer"
  }
}

List response:

{
  "success": true,
  "status": "success",
  "status_code": 200,
  "message": "Transactions",
  "data": [
    {
      "id": 2,
      "trx_id": "order_002",
      "reference": "ts-87654321",
      "status": "pending",
      "account": {
        "crypto": {
          "network_asset": "solana:usdc",
          "asset": "usdc"
        }
      },
      "available_payment_methods": ["crypto"],
      "default_payment_method": "crypto"
    },
    {
      "id": 1,
      "trx_id": "order_001",
      "reference": "ts-12345678",
      "status": "pending",
      "account": {
        "fiat": {
          "bank_code": "090286",
          "bank_name": "Example MFB",
          "account_name": "TSARA CHECKOUT",
          "account_number": "1234567890"
        }
      },
      "available_payment_methods": ["bank-transfer", "crypto"],
      "default_payment_method": "bank-transfer"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 2,
    "total_pages": 1
  }
}