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

# Direct Integration

> Build a fully custom checkout UI using the Settlx API

# Direct Integration

Build your own checkout UI using the Settlx API directly. This gives you full control over the customer experience while Settlx handles address generation, payment detection, and settlement.

## Overview

In a direct integration:

* **Your frontend** renders the token/chain selector and payment screen
* **Your backend** creates the payment and receives webhooks
* **Settlx** generates addresses, monitors the blockchain, and handles settlement

## Step 1 — Load available tokens

When your checkout page loads, fetch the list of supported tokens. This endpoint is public — no API key required.

```javascript theme={null}
const { data: tokens } = await fetch('https://api.settlx.io/api/v1/tokens').then(r => r.json());

// Render your token + chain selector from this list
tokens.forEach(token => {
  renderTokenOption({
    symbol: token.symbol,
    name: token.name,
    icon: token.iconUrl,
    chains: token.chains,
  });
});
```

## Step 2 — Create the payment

Once the customer selects their chain and token, call `POST /api/v1/payments` from your backend. This single call creates the payment record and returns the deposit address in one response.

```javascript theme={null}
// Your backend endpoint — called by your frontend after token selection
app.post('/checkout/payment', async (req, res) => {
  const { chain, token, orderId, amount } = req.body;

  const response = await fetch('https://api.settlx.io/api/v1/payments', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SETTLX_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: amount.toString(),
      currency: 'USD',
      chain,
      token,
      expiresInMinutes: 15,
      webhookUrl: `${process.env.BASE_URL}/webhooks/settlx`,
      metadata: { orderId },
    }),
  });

  const { data } = await response.json();

  // Return only what the frontend needs — never forward your API key
  res.json({
    paymentId: data.paymentId,
    address: data.address,
    qrCode: data.qrCode,
    cryptoAmount: data.cryptoAmount,
    cryptoCurrency: data.cryptoCurrency,
    chain: data.chain,
    expiresAt: data.expiresAt,
  });
});
```

<Tip>
  Always pass `metadata.orderId`. It is included in every webhook payload, allowing you to match the confirmed payment to your internal order without a database lookup.
</Tip>

## Step 3 — Display the payment screen

Your frontend displays the address and exact amount returned in the previous step.

```javascript theme={null}
// After receiving the response from your backend
function renderPaymentScreen(payment) {
  showAddress(payment.address);
  showQRCode(payment.qrCode);
  showAmount(`Send exactly ${payment.cryptoAmount} ${payment.cryptoCurrency}`);
  startCountdown(payment.expiresAt);

  // Poll for payment status
  pollPaymentStatus(payment.paymentId);
}
```

## Step 4 — Poll for status updates

Poll the public status endpoint every 5–10 seconds to track payment progress. No API key required.

```javascript theme={null}
async function pollPaymentStatus(invoiceId) {
  const response = await fetch(`https://api.settlx.io/api/v1/invoices/${invoiceId}/status`);
  const { data } = await response.json();

  if (data.status === 'settled') {
    redirectToSuccess();
  } else if (data.status === 'expired') {
    showExpiredUI();
  } else {
    // Payment still pending or awaiting confirmations — check again shortly
    setTimeout(() => pollPaymentStatus(invoiceId), 5000);
  }
}
```

## Step 5 — Handle webhooks on your backend

Settlx sends a `POST` request to your `webhookUrl` as the payment progresses. Always verify the signature before processing.

```javascript theme={null}
const TOLERANCE_SECONDS = 5 * 60;

function verifySettlxWebhook(rawBody, signatureHeader, secret) {
  if (!signatureHeader) return false;
  let timestamp = null;
  const v1Sigs = [];
  for (const part of signatureHeader.split(',')) {
    const [k, v] = part.split('=', 2);
    if (k === 't') timestamp = parseInt(v, 10);
    if (k === 'v1') v1Sigs.push(v);
  }
  if (!timestamp || v1Sigs.length === 0) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > TOLERANCE_SECONDS) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');
  const expectedBuf = Buffer.from(expected, 'hex');

  return v1Sigs.some((c) => {
    const cBuf = Buffer.from(c, 'hex');
    return cBuf.length === expectedBuf.length && crypto.timingSafeEqual(cBuf, expectedBuf);
  });
}

app.post('/webhooks/settlx', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySettlxWebhook(
    req.body,
    req.headers['x-webhook-signature'],
    process.env.SETTLX_WEBHOOK_SECRET,
  )) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const event = JSON.parse(req.body);

  switch (event.event) {
    case 'invoice.settled': {
      // Funds are in your wallet — safe to fulfill
      const { orderId } = event.data.invoice.metadata;
      fulfillOrder(orderId);
      break;
    }
    case 'invoice.expired': {
      const { orderId } = event.data.invoice.metadata;
      markOrderExpired(orderId);
      break;
    }
  }

  res.status(200).json({ received: true });
});
```

<Note>
  The signature format is `t=<unix_seconds>,v1=<hex>` (Stripe-style). See [Webhook Integration](/webhooks/integration) for verification examples in Python, PHP, and Go.
</Note>

## Full React example

```jsx theme={null}
function CryptoCheckout({ amount, orderId }) {
  const [tokens, setTokens] = useState([]);
  const [payment, setPayment] = useState(null);
  const [status, setStatus] = useState('selecting');

  useEffect(() => {
    fetch('https://api.settlx.io/api/v1/tokens')
      .then(r => r.json())
      .then(({ data }) => setTokens(data));
  }, []);

  const handleSelect = async (chain, token) => {
    const res = await fetch('/checkout/payment', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ chain, token, orderId, amount }),
    });
    const data = await res.json();
    setPayment(data);
    setStatus('awaiting_payment');
  };

  if (status === 'selecting') {
    return <TokenSelector tokens={tokens} onSelect={handleSelect} />;
  }

  return (
    <PaymentScreen
      address={payment.address}
      qrCode={payment.qrCode}
      amount={payment.cryptoAmount}
      currency={payment.cryptoCurrency}
      chain={payment.chain}
      expiresAt={payment.expiresAt}
    />
  );
}
```

## Integration checklist

<Steps>
  <Step title="Fetch token list on page load">
    Call `GET /api/v1/tokens` (public) to populate your chain and token selector. Cache the response for up to 5 minutes.
  </Step>

  <Step title="Create payment from your backend">
    After the customer selects their token, call `POST /api/v1/payments` from your server. Never make this call from the browser — it requires your API key.
  </Step>

  <Step title="Display address and QR code">
    Show the returned `address`, `qrCode`, `cryptoAmount`, and `cryptoCurrency` to the customer.
  </Step>

  <Step title="Poll for status">
    Poll `GET /api/v1/invoices/{invoiceId}/status` every 5–10 seconds and update your UI as confirmations arrive.
  </Step>

  <Step title="Fulfill the order on invoice.settled">
    Handle `invoice.settled` on your backend to fulfill the order. Verify the `X-Webhook-Signature` header on every delivery.
  </Step>
</Steps>
