概述
使用 KillB JavaScript SDK 的常见集成模式的实际示例。完整入金示例
完整实现入金流程:复制
import { KillB } from '@killb/sdk';
async function completeOnRampFlow() {
// 初始化 SDK
const killb = new KillB({
email: process.env.KILLB_EMAIL!,
password: process.env.KILLB_PASSWORD!,
environment: 'sandbox'
});
await killb.initialize();
// 来自您应用程序的用户数据
const userData = {
firstName: 'María',
lastName: 'López',
email: '[email protected]',
phone: '+525512345678',
walletAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
};
try {
// 步骤 1: 创建或获取用户
let user;
try {
user = await killb.users.getByExternalId(`user-${userData.email}`);
console.log('✅ 找到现有用户');
} catch (error) {
// 用户不存在,创建新用户
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.id);
}
// 步骤 2: 创建或获取钱包账户
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.id);
} else {
console.log('✅ 找到现有钱包');
}
// 步骤 3: 获取报价
const quote = await killb.quotations.create({
fromCurrency: 'MXN',
toCurrency: 'USDC',
amount: 1000,
amountIsToCurrency: false,
cashInMethod: 'SPEI',
cashOutMethod: 'POLYGON'
});
console.log('✅ 报价已创建');
console.log(' 发送:', quote.fromAmount, 'MXN');
console.log(' 接收:', quote.toAmount, 'USDC');
console.log(' 汇率:', quote.rate);
console.log(' 过期时间:', Math.floor((quote.expiresAt - Date.now()) / 1000), '秒');
// 步骤 4: 创建出入金
const ramp = await killb.ramps.create({
quotationId: quote.id,
userId: user.id,
accountId: wallet.id,
externalId: `onramp-${Date.now()}`
});
console.log('✅ 出入金已创建:', ramp.id);
console.log(' 状态:', ramp.status);
// SPEI 支付说明
const paymentInfo = ramp.paymentInfo[0];
console.log('\n📋 支付说明:');
console.log(' CLABE:', paymentInfo.CLABE);
console.log(' 银行:', paymentInfo.Bank);
console.log(' 受益人:', paymentInfo.Beneficiary);
console.log(' 参考号:', paymentInfo.concepto);
// 步骤 5: 在沙盒环境中,模拟支付
if (killb.environment === 'sandbox') {
console.log('\n⚡ 模拟支付...');
await killb.faker.cashIn(ramp.id);
}
// 步骤 6: 等待完成
console.log('\n⏳ 等待出入金完成...');
const completedRamp = await ramp.waitForCompletion({
pollInterval: 5000,
timeout: 300000,
onStatusChange: (status) => {
console.log(' 状态已更新:', status);
}
});
console.log('✅ 出入金已完成!');
console.log(' 转账证明:', completedRamp.transferProof);
return completedRamp;
} catch (error) {
console.error('❌ 错误:', error);
throw error;
}
}
// 运行示例
completeOnRampFlow()
.then(() => console.log('\n🎉 全部完成!'))
.catch(console.error);
Express.js 集成
使用 KillB SDK 的完整 Express.js 服务器:复制
import express from 'express';
import { KillB, WebhookHandler } from '@killb/sdk';
const app = express();
app.use(express.json());
// 初始化 KillB SDK
const killb = new KillB({
email: process.env.KILLB_EMAIL!,
password: process.env.KILLB_PASSWORD!,
environment: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox'
});
// 在服务器启动时初始化
killb.initialize().then(() => {
console.log('✅ KillB SDK 已初始化');
});
// 创建入金端点
app.post('/api/onramp/create', async (req, res) => {
try {
const { userId, walletAddress, amount } = req.body;
// 获取或创建钱包
let wallet = await killb.accounts.findOne({
userId,
type: 'WALLET',
'data.address': walletAddress
});
if (!wallet) {
wallet = await killb.accounts.create({
type: 'WALLET',
userId,
data: { /* 钱包数据 */ }
});
}
// 创建报价
const quote = await killb.quotations.create({
fromCurrency: 'COP',
toCurrency: 'USDC',
amount,
amountIsToCurrency: false,
cashInMethod: 'PSE',
cashOutMethod: 'POLYGON'
});
// 创建出入金
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('入金创建错误:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
// 获取出入金状态端点
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: '未找到出入金' });
}
});
// Webhook 端点
const webhookHandler = new WebhookHandler(process.env.WEBHOOK_SECRET!);
webhookHandler.on('ramp.completed', async (data) => {
console.log('出入金已完成:', data.id);
// 更新您的数据库
await db.ramps.update(data.id, { status: 'COMPLETED' });
// 通知用户
await sendEmail(data.userId, '交易已完成');
});
webhookHandler.on('ramp.failed', async (data) => {
console.log('出入金失败:', 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: '无效签名' });
}
});
app.listen(3000, () => {
console.log('服务器运行在端口 3000');
});
React 集成
用于 KillB 操作的 React hooks:复制
import { KillB } from '@killb/sdk';
import { useState, useEffect } from 'react';
// 初始化 SDK(在应用级别执行一次)
export const killb = new KillB({
email: process.env.REACT_APP_KILLB_EMAIL!,
password: process.env.REACT_APP_KILLB_PASSWORD!,
environment: 'sandbox'
});
// 用于入金的自定义 hook
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 {
// 获取钱包或创建
const wallet = await killb.accounts.create({
type: 'WALLET',
userId,
data: {
/* 钱包数据 */
address: walletAddress,
network: 'POLYGON',
currency: 'USDC'
}
});
// 获取报价
const quote = await killb.quotations.create({
fromCurrency: 'COP',
toCurrency: 'USDC',
amount,
amountIsToCurrency: false,
cashInMethod: 'PSE',
cashOutMethod: 'POLYGON'
});
// 创建出入金
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 };
}
// 组件使用
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('创建出入金失败:', error);
}
};
if (paymentUrl) {
return (
<div>
<h3>完成支付</h3>
<a href={paymentUrl} target="_blank" rel="noopener">
使用 PSE 支付
</a>
</div>
);
}
return (
<form onSubmit={handleSubmit}>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="COP 金额"
/>
<button type="submit" disabled={loading}>
{loading ? '创建中...' : '获取报价并支付'}
</button>
{error && <p>错误: {error.message}</p>}
</form>
);
}
Next.js API 路由
复制
// 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: '不允许的方法' });
}
try {
const { userId, accountId, amount, fromCurrency, toCurrency } = req.body;
// 验证输入
if (!userId || !accountId || !amount) {
return res.status(400).json({ error: '缺少必需字段' });
}
// 创建报价
const quote = await killb.quotations.create({
fromCurrency,
toCurrency,
amount,
amountIsToCurrency: false,
cashInMethod: fromCurrency === 'COP' ? 'PSE' : 'SPEI',
cashOutMethod: 'POLYGON'
});
// 创建出入金
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('出入金创建失败:', error);
res.status(500).json({
success: false,
error: error.message,
code: error.code
});
}
}
批量操作
高效处理多个操作:复制
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(`✅ 已创建: ${successful.length}`);
console.log(`❌ 失败: ${failed.length}`);
return {
successful: successful.map(r => (r as any).value),
failed: failed.map(r => (r as any).reason)
};
}
// 获取多个账户的余额
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
};
}
错误恢复
实现健壮的错误处理:复制
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('报价已过期,获取新报价...');
// 获取新报价并重试
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('余额不足,无法重试');
throw error;
}
if (attempt >= maxRetries) {
throw error;
}
// 指数退避
const delay = Math.pow(2, attempt) * 1000;
console.log(`重试 ${attempt}/${maxRetries},${delay}ms 后...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
监控和日志
添加全面的日志记录:复制
import { KillB } from '@killb/sdk';
import winston from 'winston';
// 设置日志记录器
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'killb.log' })
]
});
// 使用日志记录初始化 SDK
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)
}
});
// 使用指标包装 SDK 调用
async function createRampWithMetrics(data: any) {
const startTime = Date.now();
try {
const ramp = await killb.ramps.create(data);
const duration = Date.now() - startTime;
logger.info('出入金已创建', {
rampId: ramp.id,
duration,
userId: data.userId
});
return ramp;
} catch (error: any) {
const duration = Date.now() - startTime;
logger.error('出入金创建失败', {
error: error.message,
code: error.code,
duration,
userId: data.userId
});
throw error;
}
}
测试
使用 SDK 进行单元测试:复制
import { KillB } from '@killb/sdk';
import { describe, it, expect, beforeAll } from '@jest/globals';
describe('KillB SDK 集成', () => {
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('应该创建用户', async () => {
const user = await killb.users.create({
type: 'PERSON',
externalId: `test-${Date.now()}`,
data: {
firstName: 'Test',
lastName: 'User',
email: '[email protected]',
// ... 其他必需字段
}
});
expect(user.id).toBeDefined();
expect(user.status).toBe('ACTIVE');
testUser = user;
});
it('应该创建报价', 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('应该完成完整的入金流程', async () => {
// ... 完整流程测试
});
});