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.
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.
// 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,
});
});
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.
Step 3 — Display the payment screen
Your frontend displays the address and exact amount returned in the previous step.
// 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.
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.
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 });
});
The signature format is t=<unix_seconds>,v1=<hex> (Stripe-style). See Webhook Integration for verification examples in Python, PHP, and Go.
Full React example
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
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.
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.
Display address and QR code
Show the returned address, qrCode, cryptoAmount, and cryptoCurrency to the customer.
Poll for status
Poll GET /api/v1/invoices/{invoiceId}/status every 5–10 seconds and update your UI as confirmations arrive.
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.