Skip to main content

Overview

An on-ramp converts fiat currency (COP, MXN, USD) into cryptocurrency (USDC, USDT). Users pay with local payment methods and receive crypto in their wallet.

Complete On-Ramp Flow

1

Create User & Wallet

Set up user profile and destination wallet
2

Get Quotation

Request price quote for conversion
3

Create Ramp

Execute ramp with quote ID
4

User Pays

User completes payment via PSE/SPEI/etc
5

Conversion

KillB converts fiat to crypto
6

Crypto Delivered

USDC/USDT sent to user’s wallet

Implementation Example

const executeOnRamp = async (userId, walletAddress, amount) => {
  try {
    // 1. Create wallet account if needed
    let wallet = await findWalletAccount(userId, 'POLYGON', 'USDC');
    
    if (!wallet) {
      wallet = await createWalletAccount(userId, walletAddress, 'USDC', 'POLYGON');
    }
    
    // 2. Get quotation
    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(`You will receive: ${quote.toAmount} USDC`);
    
    // 3. Create 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. Redirect to payment
    return {
      rampId: ramp.id,
      paymentUrl: ramp.paymentInfo[0].url,
      expectedAmount: quote.toAmount
    };
    
  } catch (error) {
    console.error('On-ramp failed:', error);
    throw error;
  }
};

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

Payment Methods

User redirected to PSE checkout:
// Ramp response includes payment URL
const paymentUrl = ramp.paymentInfo[0].url;

// Redirect user
window.location.href = paymentUrl;

// User completes payment on PSE
// Webhook notifies when payment confirmed
Flow:
  1. Redirect to PSE URL
  2. User selects bank
  3. User authenticates
  4. User confirms payment
  5. Webhook received

Monitoring Progress

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('Status:', ramp.status);
    
    if (ramp.status === 'COMPLETED') {
      console.log('Crypto delivered!');
      console.log('Transaction:', ramp.transferProof);
      clearInterval(pollInterval);
    } else if (['FAILED', 'CANCELED'].includes(ramp.status)) {
      console.error('Ramp failed:', ramp.details);
      clearInterval(pollInterval);
    }
  }, 10000); // Check every 10 seconds
};
Use webhooks as primary notification method, with polling as backup.

Handling User Flow

Build a smooth user experience:
// Step 1: Quote page
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>You send: {quote.fromAmount} COP</p>
          <p>You receive: {quote.toAmount} USDC</p>
          <p>Rate: {quote.rate}</p>
          <button onClick={() => createAndRedirect(quote)}>
            Continue
          </button>
        </div>
      )}
    </div>
  );
};

// Step 2: Redirect to payment
const createAndRedirect = async (quote) => {
  const ramp = await createRamp(quote.id, userId, walletId);
  
  // Save ramp ID for status checking
  localStorage.setItem('currentRampId', ramp.id);
  
  // Redirect to PSE
  window.location.href = ramp.paymentInfo[0].url;
};

// Step 3: Return page - check status
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: {status}</div>;
};

Next Steps