跳转到主要内容

概览

On-ramp 将法币(COP, MXN, USD)转换为加密货币(USDC, USDT)。用户使用本地支付方式支付,并在钱包中接收加密货币。

完整的 On-Ramp 流程

1

创建用户和钱包

设置用户资料和目标钱包
2

获取报价

请求转换的报价
3

创建 Ramp

使用报价 ID 执行 ramp
4

用户支付

用户通过 PSE/SPEI/等完成支付
5

转换

KillB 将法币转换为加密货币
6

加密货币交付

USDC/USDT 发送到用户的钱包

实现示例

const executeOnRamp = async (userId, walletAddress, amount) => {
  try {
    // 1. 如果需要,创建钱包账户
    let wallet = await findWalletAccount(userId, 'POLYGON', 'USDC');
    
    if (!wallet) {
      wallet = await createWalletAccount(userId, walletAddress, 'USDC', 'POLYGON');
    }
    
    // 2. 获取报价
    const quote = await fetch('/api/v2/quotations', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        fromCurrency: 'COP',
        toCurrency: 'USDC',
        amount: amount,
        amountIsToCurrency: false,
        cashInMethod: 'PSE',
        cashOutMethod: 'POLYGON'
      })
    }).then(r => r.json());
    
    console.log(`您将收到: ${quote.toAmount} USDC`);
    
    // 3. 创建 ramp
    const ramp = await fetch('/api/v2/ramps', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        quotationId: quote.id,
        userId: userId,
        accountId: wallet.id,
        externalId: `onramp-${Date.now()}`
      })
    }).then(r => r.json());
    
    // 4. 重定向到支付
    return {
      rampId: ramp.id,
      paymentUrl: ramp.paymentInfo[0].url,
      expectedAmount: quote.toAmount
    };
    
  } catch (error) {
    console.error('On-ramp 失败:', error);
    throw error;
  }
};

// 使用
const result = await executeOnRamp('user-id', '0x742d35...', 100000);
window.location.href = result.paymentUrl;

支付方式

用户重定向到 PSE 结账:
// Ramp 响应包含支付 URL
const paymentUrl = ramp.paymentInfo[0].url;

// 重定向用户
window.location.href = paymentUrl;

// 用户在 PSE 上完成支付
// Webhook 在支付确认时通知
流程:
  1. 重定向到 PSE URL
  2. 用户选择银行
  3. 用户验证身份
  4. 用户确认支付
  5. 收到 Webhook

监控进度

const monitorRamp = async (rampId) => {
  const pollInterval = setInterval(async () => {
    const ramp = await fetch(`/api/v2/ramps/${rampId}`, {
      headers: { 'Authorization': `Bearer ${token}` }
    }).then(r => r.json());
    
    console.log('状态:', ramp.status);
    
    if (ramp.status === 'COMPLETED') {
      console.log('加密货币已交付!');
      console.log('交易:', ramp.transferProof);
      clearInterval(pollInterval);
    } else if (['FAILED', 'CANCELED'].includes(ramp.status)) {
      console.error('Ramp 失败:', ramp.details);
      clearInterval(pollInterval);
    }
  }, 10000); // 每 10 秒检查一次
};
使用 webhooks 作为主要通知方式,轮询作为备用。

处理用户流程

构建流畅的用户体验:
// 步骤 1:报价页面
const QuotePage = () => {
  const [quote, setQuote] = useState(null);
  
  const getQuote = async (amount) => {
    const q = await createQuotation(amount, 'COP', 'USDC');
    setQuote(q);
  };
  
  return (
    <div>
      <input onChange={(e) => getQuote(e.target.value)} />
      {quote && (
        <div>
          <p>您发送: {quote.fromAmount} COP</p>
          <p>您收到: {quote.toAmount} USDC</p>
          <p>汇率: {quote.rate}</p>
          <button onClick={() => createAndRedirect(quote)}>
            继续
          </button>
        </div>
      )}
    </div>
  );
};

// 步骤 2:重定向到支付
const createAndRedirect = async (quote) => {
  const ramp = await createRamp(quote.id, userId, walletId);
  
  // 保存 ramp ID 用于状态检查
  localStorage.setItem('currentRampId', ramp.id);
  
  // 重定向到 PSE
  window.location.href = ramp.paymentInfo[0].url;
};

// 步骤 3:返回页面 - 检查状态
const ReturnPage = () => {
  const rampId = localStorage.getItem('currentRampId');
  const [status, setStatus] = useState(null);
  
  useEffect(() => {
    const checkStatus = async () => {
      const ramp = await getRamp(rampId);
      setStatus(ramp.status);
      
      if (ramp.status === 'COMPLETED') {
        showSuccess(ramp);
      }
    };
    
    checkStatus();
    const interval = setInterval(checkStatus, 5000);
    return () => clearInterval(interval);
  }, [rampId]);
  
  return <div>状态: {status}</div>;
};

下一步