跳转到主要内容

概述

使用 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 () => {
    // ... 完整流程测试
  });
});

下一步