> ## 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.

# Framework Examples

> Embed Caibo checkout SDK in React, Angular, and Vue 3 applications with copy-ready code snippets.

# 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](/ipg/iframe-checkout/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:

```js theme={null}
// 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 });
});
```

<Warning>
  Never expose `process.env.CAIBO_API_KEY` to the browser. The whole purpose of the token is to keep the `apiKey` server-side.
</Warning>

## 2) Frontend: open the iframe with the token

<Tabs>
  <Tab title="React">
    ```jsx theme={null}
    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>;
    }
    ```
  </Tab>

  <Tab title="Angular">
    ```ts theme={null}
    async pay(): Promise<void> {
      // Your backend creates the PR and mints the token in one call.
      const { requestId, token } = await firstValueFrom(
        this.http.post<{ requestId: string; token: string }>(
          '/api/checkout/session',
          { amount: this.amount, currency: this.currency }
        )
      );

      await this.loadSdk();
      (window as any).CaiboCheckout.init({
        mode: 'modal',
        paymentUrl: `https://pay.caibo.digital/main?requestId=${requestId}&token=${token}`,
        onSuccess: (data: any) => this.handleSuccess(data),
        onFailure: (data: any) => this.handleFailure(data),
        onCancel:  ()          => this.handleCancel()
      });
    }

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

  <Tab title="Vue 3">
    ```js theme={null}
    async function pay() {
      // 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('https://pay.caibo.digital/assets/js/checkout-sdk.js');
      window.CaiboCheckout.init({
        mode: 'modal',
        paymentUrl: `https://pay.caibo.digital/main?requestId=${requestId}&token=${token}`,
        onSuccess: (data) => emit('success', data),
        onFailure: (data) => emit('failure', data)
      });
    }

    function loadSdk(src) {
      return new Promise((resolve) => {
        if (window.CaiboCheckout) { resolve(); return; }
        const s = document.createElement('script');
        s.src = src;
        s.onload = resolve;
        document.head.appendChild(s);
      });
    }
    ```
  </Tab>
</Tabs>

## Common Patterns

<CardGroup cols={2}>
  <Card title="Mint server-side, on demand" icon="server">
    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.
  </Card>

  <Card title="Lazy-load the SDK" icon="bolt">
    Inject the script tag only when the user clicks **Pay**, so first-page-load performance is not affected.
  </Card>

  <Card title="Re-mint on token expiry" icon="rotate">
    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.
  </Card>

  <Card title="Reuse the SDK global" icon="repeat">
    The script exposes `window.CaiboCheckout` once. Guard against duplicate `<script>` injection by checking for it before loading.
  </Card>
</CardGroup>

## Next Steps

* [Mint a Checkout Session Token](/ipg/iframe-checkout/checkout-session-token) — endpoint reference and scope rules.
* [SDK API Reference](/ipg/iframe-checkout/sdk-api-reference) — every option and event payload.
* [Browser Support & Notes](/ipg/iframe-checkout/browser-support) — compatibility matrix and security notes.
