Overview
Real-world examples demonstrating common integration patterns with the KillB JavaScript SDK.Complete On-Ramp Example
Full implementation of an on-ramp flow:Copy
import { KillB } from '@killb/sdk';
async function completeOnRampFlow() {
// Initialize SDK
const killb = new KillB({
email: process.env.KILLB_EMAIL!,
password: process.env.KILLB_PASSWORD!,
environment: 'sandbox'
});
await killb.initialize();
// User data from your application
const userData = {
firstName: 'María',
lastName: 'López',
email: '[email protected]',
phone: '+525512345678',
walletAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
};
try {
// Step 1: Create or get user
let user;
try {
user = await killb.users.getByExternalId(`user-${userData.email}`);
console.log('✅ Existing user found');
} catch (error) {
// User doesn't exist, create new
user = await killb.users.create({
type: 'PERSON',
externalId: `user-${userData.email}`,
data: {
firstName: userData.firstName,
lastName: userData.lastName,
email: userData.email,
phone: userData.phone,
dateOfBirth: '1990-01-01',
address: {
street1: 'Calle Principal 123',
city: 'Ciudad de México',
state: 'CDMX',
zipCode: '01000',
countryCode: 'MX'
},
document: {
type: 'RFC',
number: 'LOMM900310ABC',
issuedCountryCode: 'MX'
}
}
});
console.log('✅ User created:', user.id);
}
// Step 2: Create or get wallet account
const walletExternalId = `wallet-${user.id}-polygon`;
let wallet;
const existingAccounts = await killb.accounts.getByUserId(user.id);
wallet = existingAccounts.find(a => a.externalId === walletExternalId);
if (!wallet) {
wallet = await killb.accounts.create({
type: 'WALLET',
userId: user.id,
externalId: walletExternalId,
data: {
firstName: userData.firstName,
lastName: userData.lastName,
email: userData.email,
phone: userData.phone,
currency: 'USDC',
network: 'POLYGON',
address: userData.walletAddress,
countryCode: 'MX',
document: {
type: 'RFC',
number: 'LOMM900310ABC',
issuedCountryCode: 'MX'
}
}
});
console.log('✅ Wallet created:', wallet.id);
} else {
console.log('✅ Existing wallet found');
}
// Step 3: Get quotation
const quote = await killb.quotations.create({
fromCurrency: 'MXN',
toCurrency: 'USDC',
amount: 1000,
amountIsToCurrency: false,
cashInMethod: 'SPEI',
cashOutMethod: 'POLYGON'
});
console.log('✅ Quote created');
console.log(' Send:', quote.fromAmount, 'MXN');
console.log(' Receive:', quote.toAmount, 'USDC');
console.log(' Rate:', quote.rate);
console.log(' Expires in:', Math.floor((quote.expiresAt - Date.now()) / 1000), 'seconds');
// Step 4: Create ramp
const ramp = await killb.ramps.create({
quotationId: quote.id,
userId: user.id,
accountId: wallet.id,
externalId: `onramp-${Date.now()}`
});
console.log('✅ Ramp created:', ramp.id);
console.log(' Status:', ramp.status);
// SPEI payment instructions
const paymentInfo = ramp.paymentInfo[0];
console.log('\n📋 Payment Instructions:');
console.log(' CLABE:', paymentInfo.CLABE);
console.log(' Bank:', paymentInfo.Bank);
console.log(' Beneficiary:', paymentInfo.Beneficiary);
console.log(' Reference:', paymentInfo.concepto);
// Step 5: In sandbox, simulate payment
if (killb.environment === 'sandbox') {
console.log('\n⚡ Simulating payment...');
await killb.faker.cashIn(ramp.id);
}
// Step 6: Wait for completion
console.log('\n⏳ Waiting for ramp to complete...');
const completedRamp = await ramp.waitForCompletion({
pollInterval: 5000,
timeout: 300000,
onStatusChange: (status) => {
console.log(' Status updated:', status);
}
});
console.log('✅ Ramp completed!');
console.log(' Transfer proof:', completedRamp.transferProof);
return completedRamp;
} catch (error) {
console.error('❌ Error:', error);
throw error;
}
}
// Run the example
completeOnRampFlow()
.then(() => console.log('\n🎉 All done!'))
.catch(console.error);
Express.js Integration
Complete Express.js server with KillB SDK:Copy
import express from 'express';
import { KillB, WebhookHandler } from '@killb/sdk';
const app = express();
app.use(express.json());
// Initialize KillB SDK
const killb = new KillB({
email: process.env.KILLB_EMAIL!,
password: process.env.KILLB_PASSWORD!,
environment: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox'
});
// Initialize on server start
killb.initialize().then(() => {
console.log('✅ KillB SDK initialized');
});
// Create on-ramp endpoint
app.post('/api/onramp/create', async (req, res) => {
try {
const { userId, walletAddress, amount } = req.body;
// Get or create wallet
let wallet = await killb.accounts.findOne({
userId,
type: 'WALLET',
'data.address': walletAddress
});
if (!wallet) {
wallet = await killb.accounts.create({
type: 'WALLET',
userId,
data: { /* wallet data */ }
});
}
// Create quotation
const quote = await killb.quotations.create({
fromCurrency: 'COP',
toCurrency: 'USDC',
amount,
amountIsToCurrency: false,
cashInMethod: 'PSE',
cashOutMethod: 'POLYGON'
});
// Create ramp
const ramp = await killb.ramps.create({
quotationId: quote.id,
userId,
accountId: wallet.id,
externalId: `order-${req.body.orderId}`
});
res.json({
success: true,
rampId: ramp.id,
paymentUrl: ramp.paymentInfo[0].url,
expectedAmount: quote.toAmount
});
} catch (error) {
console.error('On-ramp creation error:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get ramp status endpoint
app.get('/api/ramp/:id/status', async (req, res) => {
try {
const ramp = await killb.ramps.get(req.params.id);
res.json({
id: ramp.id,
status: ramp.status,
fromAmount: ramp.fromAmount,
toAmount: ramp.toAmount,
transferProof: ramp.transferProof
});
} catch (error) {
res.status(404).json({ error: 'Ramp not found' });
}
});
// Webhook endpoint
const webhookHandler = new WebhookHandler(process.env.WEBHOOK_SECRET!);
webhookHandler.on('ramp.completed', async (data) => {
console.log('Ramp completed:', data.id);
// Update your database
await db.ramps.update(data.id, { status: 'COMPLETED' });
// Notify user
await sendEmail(data.userId, 'Transaction completed');
});
webhookHandler.on('ramp.failed', async (data) => {
console.log('Ramp failed:', data.id);
await handleRampFailure(data);
});
app.post('/webhooks/killb', async (req, res) => {
try {
await webhookHandler.handle(
req.body,
req.headers['x-signature-sha256'] as string
);
res.status(200).json({ received: true });
} catch (error) {
res.status(401).json({ error: 'Invalid signature' });
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
React Integration
React hooks for KillB operations:Copy
import { KillB } from '@killb/sdk';
import { useState, useEffect } from 'react';
// Initialize SDK (do this once at app level)
export const killb = new KillB({
email: process.env.REACT_APP_KILLB_EMAIL!,
password: process.env.REACT_APP_KILLB_PASSWORD!,
environment: 'sandbox'
});
// Custom hook for on-ramp
function useOnRamp() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const createOnRamp = async (
userId: string,
walletAddress: string,
amount: number
) => {
setLoading(true);
setError(null);
try {
// Get wallet or create
const wallet = await killb.accounts.create({
type: 'WALLET',
userId,
data: {
/* wallet data */
address: walletAddress,
network: 'POLYGON',
currency: 'USDC'
}
});
// Get quote
const quote = await killb.quotations.create({
fromCurrency: 'COP',
toCurrency: 'USDC',
amount,
amountIsToCurrency: false,
cashInMethod: 'PSE',
cashOutMethod: 'POLYGON'
});
// Create ramp
const ramp = await killb.ramps.create({
quotationId: quote.id,
userId,
accountId: wallet.id
});
setLoading(false);
return ramp;
} catch (err) {
setError(err as Error);
setLoading(false);
throw err;
}
};
return { createOnRamp, loading, error };
}
// Component usage
function OnRampWidget() {
const { createOnRamp, loading, error } = useOnRamp();
const [amount, setAmount] = useState('');
const [paymentUrl, setPaymentUrl] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const ramp = await createOnRamp(
currentUser.killbUserId,
currentUser.walletAddress,
parseFloat(amount)
);
setPaymentUrl(ramp.paymentInfo[0].url);
} catch (error) {
console.error('Failed to create ramp:', error);
}
};
if (paymentUrl) {
return (
<div>
<h3>Complete Payment</h3>
<a href={paymentUrl} target="_blank" rel="noopener">
Pay with PSE
</a>
</div>
);
}
return (
<form onSubmit={handleSubmit}>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount in COP"
/>
<button type="submit" disabled={loading}>
{loading ? 'Creating...' : 'Get Quote & Pay'}
</button>
{error && <p>Error: {error.message}</p>}
</form>
);
}
Next.js API Route
Copy
// pages/api/ramps/create.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { killb } from '@/lib/killb';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const { userId, accountId, amount, fromCurrency, toCurrency } = req.body;
// Validate inputs
if (!userId || !accountId || !amount) {
return res.status(400).json({ error: 'Missing required fields' });
}
// Create quotation
const quote = await killb.quotations.create({
fromCurrency,
toCurrency,
amount,
amountIsToCurrency: false,
cashInMethod: fromCurrency === 'COP' ? 'PSE' : 'SPEI',
cashOutMethod: 'POLYGON'
});
// Create ramp
const ramp = await killb.ramps.create({
quotationId: quote.id,
userId,
accountId,
externalId: `nextjs-${Date.now()}`
});
res.status(201).json({
success: true,
rampId: ramp.id,
paymentUrl: ramp.paymentInfo[0].url,
rate: quote.rate,
expectedAmount: quote.toAmount
});
} catch (error: any) {
console.error('Ramp creation failed:', error);
res.status(500).json({
success: false,
error: error.message,
code: error.code
});
}
}
Batch Operations
Process multiple operations efficiently:Copy
import { KillB } from '@killb/sdk';
async function batchCreateUsers(usersData: any[]) {
const killb = new KillB(config);
await killb.initialize();
const results = await Promise.allSettled(
usersData.map(data => killb.users.create({
type: 'PERSON',
externalId: data.id,
data: data
}))
);
const successful = results.filter(r => r.status === 'fulfilled');
const failed = results.filter(r => r.status === 'rejected');
console.log(`✅ Created: ${successful.length}`);
console.log(`❌ Failed: ${failed.length}`);
return {
successful: successful.map(r => (r as any).value),
failed: failed.map(r => (r as any).reason)
};
}
// Get balances for multiple accounts
async function getAllBalances(accountIds: string[]) {
const balances = await Promise.all(
accountIds.map(id => killb.savings.getBalance(id))
);
const total = balances.reduce((sum, b) => sum + parseFloat(b.amount), 0);
return {
accounts: balances,
totalUSD: total
};
}
Error Recovery
Implement robust error handling:Copy
import {
KillB,
QuotationExpiredError,
InsufficientBalanceError,
UserNotFoundError
} from '@killb/sdk';
async function createRampWithRetry(
quote: Quotation,
userId: string,
accountId: string,
maxRetries = 3
) {
let attempt = 0;
while (attempt < maxRetries) {
try {
return await killb.ramps.create({
quotationId: quote.id,
userId,
accountId
});
} catch (error) {
attempt++;
if (error instanceof QuotationExpiredError) {
console.log('Quote expired, getting new quote...');
// Get new quote and retry
const newQuote = await killb.quotations.create({
fromCurrency: quote.fromCurrency,
toCurrency: quote.toCurrency,
amount: quote.fromAmount,
amountIsToCurrency: false,
cashInMethod: quote.cashInMethod,
cashOutMethod: quote.cashOutMethod
});
quote = newQuote;
continue;
}
if (error instanceof InsufficientBalanceError) {
console.error('Insufficient balance, cannot retry');
throw error;
}
if (attempt >= maxRetries) {
throw error;
}
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
Monitoring & Logging
Add comprehensive logging:Copy
import { KillB } from '@killb/sdk';
import winston from 'winston';
// Setup logger
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'killb.log' })
]
});
// Initialize SDK with logging
const killb = new KillB({
email: process.env.KILLB_EMAIL!,
password: process.env.KILLB_PASSWORD!,
debug: true,
logger: {
log: (message: string) => logger.info(message),
error: (message: string) => logger.error(message)
}
});
// Wrap SDK calls with metrics
async function createRampWithMetrics(data: any) {
const startTime = Date.now();
try {
const ramp = await killb.ramps.create(data);
const duration = Date.now() - startTime;
logger.info('Ramp created', {
rampId: ramp.id,
duration,
userId: data.userId
});
return ramp;
} catch (error: any) {
const duration = Date.now() - startTime;
logger.error('Ramp creation failed', {
error: error.message,
code: error.code,
duration,
userId: data.userId
});
throw error;
}
}
Testing
Unit tests with SDK:Copy
import { KillB } from '@killb/sdk';
import { describe, it, expect, beforeAll } from '@jest/globals';
describe('KillB SDK Integration', () => {
let killb: KillB;
let testUser: any;
beforeAll(async () => {
killb = new KillB({
email: process.env.KILLB_TEST_EMAIL!,
password: process.env.KILLB_TEST_PASSWORD!,
environment: 'sandbox'
});
await killb.initialize();
});
it('should create a user', async () => {
const user = await killb.users.create({
type: 'PERSON',
externalId: `test-${Date.now()}`,
data: {
firstName: 'Test',
lastName: 'User',
email: '[email protected]',
// ... other required fields
}
});
expect(user.id).toBeDefined();
expect(user.status).toBe('ACTIVE');
testUser = user;
});
it('should create a quotation', async () => {
const quote = await killb.quotations.create({
fromCurrency: 'COP',
toCurrency: 'USDC',
amount: 100000,
amountIsToCurrency: false,
cashInMethod: 'PSE',
cashOutMethod: 'POLYGON'
});
expect(quote.id).toBeDefined();
expect(quote.rate).toBeGreaterThan(0);
expect(quote.toAmount).toBeGreaterThan(0);
});
it('should complete full on-ramp flow', async () => {
// ... full flow test
});
});