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 > ;
}
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 );
});
}
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 );
});
}
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