Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.caibo.digital/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The embedded checkout SDK is framework-agnostic. The snippets below show idiomatic integrations for the three most common frontend frameworks. The pattern is the same in each: your backend creates the payment request and mints a checkout session token, the frontend asks your backend for { requestId, token }, then lazy-loads the SDK and calls CaiboCheckout.init() with a paymentUrl that carries the token. Your merchant apiKey never reaches the browser.

1) Backend: mint the token

The frontend frameworks below assume an endpoint on your own server that creates the payment request, mints the token, and returns both. A minimal Node.js sketch:
// POST /api/checkout/session  (your own auth -- session cookie, JWT, ...)
app.post('/api/checkout/session', requireAuth, async (req, res) => {
  // 1) Create the payment request
  const pr = await fetch('https://apay.caibo.digital/payment-requests', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.CAIBO_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ amount: req.body.amount, unit: req.body.currency, /* ... */ })
  }).then(r => r.json());

  // 2) Mint a checkout session token bound to that request
  const session = await fetch(
    `https://apay.caibo.digital/payment-requests/${pr.id}/checkout-session`,
    {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.CAIBO_API_KEY,
        'Content-Type': 'application/json'
      },
      body: '{}'
    }
  ).then(r => r.json());

  // 3) Return ONLY the values the browser actually needs
  res.json({ requestId: pr.id, token: session.token, expiresAt: session.expiresAt });
});
Never expose process.env.CAIBO_API_KEY to the browser. The whole purpose of the token is to keep the apiKey server-side.

2) Frontend: open the iframe with the token

import { useState } from 'react';

function CheckoutButton({ amount, currency, onSuccess, onFailure }) {
  const [busy, setBusy] = useState(false);

  function loadSdk() {
    return new Promise((resolve) => {
      if (window.CaiboCheckout) { resolve(); return; }
      const s = document.createElement('script');
      s.src = 'https://pay.caibo.digital/assets/js/checkout-sdk.js';
      s.onload = resolve;
      document.head.appendChild(s);
    });
  }

  async function pay() {
    setBusy(true);
    try {
      // Your backend creates the PR and mints the token in one call.
      const { requestId, token } = await fetch('/api/checkout/session', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ amount, currency })
      }).then(r => r.json());

      await loadSdk();
      window.CaiboCheckout.init({
        mode: 'modal',
        paymentUrl: `https://pay.caibo.digital/main?requestId=${requestId}&token=${token}`,
        onSuccess,
        onFailure
      });
    } finally {
      setBusy(false);
    }
  }

  return <button onClick={pay} disabled={busy}>Pay Now</button>;
}

Common Patterns

Mint server-side, on demand

Mint the token the moment the user clicks Pay, not on page load. The token is valid for 15 minutes; minting it lazily avoids wasted tokens for users who never check out.

Lazy-load the SDK

Inject the script tag only when the user clicks Pay, so first-page-load performance is not affected.

Re-mint on token expiry

If the customer leaves the iframe open past 15 minutes, mint a fresh token server-side and re-open the iframe. The SDK does not auto-refresh.

Reuse the SDK global

The script exposes window.CaiboCheckout once. Guard against duplicate <script> injection by checking for it before loading.

Next Steps