Cotizaciones y Tasas de Cambio
Las Cotizaciones proporcionan información de tasas de cambio y te permiten bloquear tasas por un período específico antes de crear órdenes.
¿Qué son las Cotizaciones?
Una Cotización es una solicitud de información de tasa de cambio que devuelve:
- Tasa de cambio actual entre monedas
- Comisiones (comisión Koywe, comisión de red, comisión de socio)
- Tiempo de expiración (cuánto tiempo es válida la tasa)
- Montos esperados (entrada y salida)
Tiempo de Vida (TTL) de Cotización
Validez Corta: Las cotizaciones son válidas por 10-15 segundos solamente. Después de la expiración, debes solicitar una nueva cotización. Siempre verifica el campo expiresAt o validFor.
¿Por qué tan corto?
- Los mercados de cripto y forex son volátiles
- Asegura precios precisos y en tiempo real
- Protege contra manipulación de precios
- Previene uso de tasas obsoletas
Cuándo Usar Cotizaciones
Usa cotizaciones cuando:
- Necesites mostrar a los clientes el monto exacto que recibirán
- Conviertas entre monedas (BALANCE_TRANSFER)
- Compres o vendas cripto (ONRAMP/OFFRAMP)
- Quieras transparencia en las tasas
Opcional pero Recomendado: Las cotizaciones son opcionales para la mayoría de tipos de orden pero muy recomendadas para transparencia y mejor experiencia de usuario.
Crear una Cotización
Solicitud Básica de Cotización
async function getQuote(token, orgId, merchantId, quoteData) {
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`,
{
type: quoteData.type, // Tipo de orden
originCurrencySymbol: quoteData.from, // Moneda origen
destinationCurrencySymbol: quoteData.to, // Moneda destino
amountIn: quoteData.amount // Monto a convertir
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
// Uso - Obtener cotización para BALANCE_TRANSFER
const quote = await getQuote(token, orgId, merchantId, {
type: 'BALANCE_TRANSFER',
from: 'COP',
to: 'USD',
amount: 1000000 // 1,000,000 COP
});
console.log('Tasa de cambio:', quote.exchangeRate);
console.log('Recibirás:', quote.amountOut, 'USD');
console.log('Válido por:', quote.validFor, 'segundos');def get_quote(token, org_id, merchant_id, quote_data):
response = requests.post(
f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/quotes',
json={
'type': quote_data['type'],
'originCurrencySymbol': quote_data['from'],
'destinationCurrencySymbol': quote_data['to'],
'amountIn': quote_data['amount']
},
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
)
return response.json()
# Uso
quote = get_quote(token, org_id, merchant_id, {
'type': 'BALANCE_TRANSFER',
'from': 'COP',
'to': 'USD',
'amount': 1000000
})
print(f"Tasa de cambio: {quote['exchangeRate']}")
print(f"Recibirás: {quote['amountOut']} USD")curl -X POST 'https://api-sandbox.koywe.com/api/v1/organizations/TU_ORG_ID/merchants/TU_MERCHANT_ID/quotes' \
-H 'Authorization: Bearer TU_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"type": "BALANCE_TRANSFER",
"originCurrencySymbol": "COP",
"destinationCurrencySymbol": "USD",
"amountIn": 1000000
}'Respuesta:
{
"id": "qte_abc123xyz",
"type": "BALANCE_TRANSFER",
"originCurrencySymbol": "COP",
"destinationCurrencySymbol": "USD",
"amountIn": 1000000,
"amountOut": 250,
"exchangeRate": 4000,
"koyweFee": 5000,
"networkFee": 0,
"partnerFee": 0,
"validFor": 300,
"validUntil": 1731513600,
"createdAt": "2025-11-13T15:00:00Z"
}Campos de Respuesta de Cotización
| Campo | Tipo | Descripción |
|---|---|---|
id | string | Identificador único de cotización (usar en órdenes) |
type | string | Tipo de orden (PAYIN, BALANCE_TRANSFER, etc.) |
originCurrencySymbol | string | Moneda origen |
destinationCurrencySymbol | string | Moneda destino |
amountIn | number | Monto de entrada |
amountOut | number | Monto de salida (lo que recibirás) |
exchangeRate | number | Tasa de cambio aplicada |
koyweFee | number | Comisión de Koywe (en moneda origen) |
networkFee | number | Comisión de red para cripto (en moneda origen) |
partnerFee | number | Comisión de socio si aplica |
validFor | number | Segundos hasta la expiración |
validUntil | number | Timestamp Unix de expiración |
Usar Cotizaciones en Órdenes
Bloqueo de Tasa
Cuando creas una orden con un quoteId, la tasa de cambio se bloquea:
async function createOrderWithQuote(token, orgId, merchantId) {
// 1. Obtener cotización
const quote = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`,
{
type: 'BALANCE_TRANSFER',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'USD',
amountIn: 1000000
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
console.log('Tasa bloqueada en:', quote.data.exchangeRate);
console.log('Válido por:', quote.data.validFor, 'segundos');
// 2. Crear orden con cotización (debe ser dentro del período validFor)
const order = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
{
type: 'BALANCE_TRANSFER',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'USD',
amountIn: 1000000,
quoteId: quote.data.id // Bloquear la tasa de la cotización
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
console.log('Orden creada con tasa bloqueada');
return order.data;
}Expiración de Cotización: Las cotizaciones son válidas por un tiempo limitado (típicamente 5 minutos). Si la cotización expira antes de crear la orden, necesitarás obtener una nueva cotización.
Tipos de Cotización por Tipo de Orden
Cotizaciones PAYIN
Para aceptar pagos (típicamente misma moneda, por lo que tasa = 1):
{
"type": "PAYIN",
"originCurrencySymbol": "COP",
"destinationCurrencySymbol": "COP",
"amountIn": 50000
}Respuesta:
{
"exchangeRate": 1,
"amountOut": 50000,
"koyweFee": 500,
"networkFee": 0
}Cotizaciones BALANCE_TRANSFER
Para conversión de moneda:
{
"type": "BALANCE_TRANSFER",
"originCurrencySymbol": "COP",
"destinationCurrencySymbol": "USD",
"amountIn": 1000000
}Respuesta:
{
"exchangeRate": 4000, // 4,000 COP = 1 USD
"amountOut": 250, // Recibirás 250 USD
"koyweFee": 5000 // 5,000 COP de comisión
}Cotizaciones ONRAMP
Para comprar cripto:
Red Requerida: Las cotizaciones ONRAMP requieren un parámetro network para especificar la red blockchain del activo cripto.
{
"type": "ONRAMP",
"originCurrencySymbol": "COP",
"destinationCurrencySymbol": "USDC",
"amountIn": 50000,
"network": "ETHEREUM" // Requerido: red blockchain
}Redes soportadas:
ETHEREUM(para USDC, USDT, ETH)POLYGON(para USDC, USDT, MATIC)BSC(para USDC, USDT, BNB)BITCOIN(para BTC)TRON(para USDT)
Respuesta:
{
"exchangeRate": 4050, // Incluye precio de cripto
"amountOut": 12.34, // Recibirás 12.34 USDC
"koyweFee": 500,
"networkFee": 2000, // Comisión de red blockchain
"network": "ETHEREUM"
}Cotizaciones OFFRAMP
Para vender cripto:
Red Requerida: Las cotizaciones OFFRAMP requieren un parámetro network para especificar la red blockchain del activo cripto.
{
"type": "OFFRAMP",
"originCurrencySymbol": "USDC",
"destinationCurrencySymbol": "COP",
"amountIn": 10, // 10 USDC
"network": "ETHEREUM" // Requerido: red blockchain
}Respuesta:
{
"exchangeRate": 3950, // Ligeramente menor que tasa de compra (spread)
"amountOut": 39500, // Recibirás 39,500 COP
"koyweFee": 500,
"network": "ETHEREUM"
}Comprender las Comisiones
Desglose de Comisiones
Cálculo ejemplo:
Monto Entrada: 1,000,000 COP
- Comisión Koywe: -5,000 COP
- Comisión Red: 0 COP
- Comisión Socio: 0 COP
= Monto Neto: 995,000 COP
÷ Tasa Cambio: ÷4,000 COP/USD
= Monto Salida: 248.75 USDTipos de Comisiones
| Tipo de Comisión | Cuándo se Aplica | Rango Típico |
|---|---|---|
| Comisión Koywe | Todas las transacciones | 0.5% - 2% |
| Comisión Red | Transacciones cripto (ONRAMP/OFFRAMP) | Variable (comisiones blockchain) |
| Comisión Socio | Si usas integraciones de socios | Varía por socio |
Mostrar Tasas a Usuarios
Visualización Amigable
async function displayQuoteToUser(token, orgId, merchantId, amount) {
const quote = await getQuote(token, orgId, merchantId, {
type: 'BALANCE_TRANSFER',
from: 'COP',
to: 'USD',
amount: amount
});
// Formatear para mostrar al usuario
const display = {
amountToSend: `${amount.toLocaleString()} COP`,
willReceive: `${quote.amountOut.toFixed(2)} USD`,
exchangeRate: `1 USD = ${quote.exchangeRate.toLocaleString()} COP`,
totalFees: `${quote.koyweFee.toLocaleString()} COP`,
expiresIn: `${Math.floor(quote.validFor / 60)} minutos`,
effectiveRate: (amount / quote.amountOut).toFixed(2) + ' COP/USD'
};
console.log('Envías:', display.amountToSend);
console.log('Recibes:', display.willReceive);
console.log('Tasa de cambio:', display.exchangeRate);
console.log('Comisiones totales:', display.totalFees);
console.log('Tasa expira en:', display.expiresIn);
console.log('Tasa efectiva:', display.effectiveRate);
return quote;
}
// Uso
await displayQuoteToUser(token, orgId, merchantId, 1000000);Salida:
Envías: 1,000,000 COP
Recibes: 248.75 USD
Tasa de cambio: 1 USD = 4,000 COP
Comisiones totales: 5,000 COP
Tasa expira en: 5 minutos
Tasa efectiva: 4020.00 COP/USDRecuperar una Cotización
Obtener detalles de una cotización existente:
async function getQuoteById(token, orgId, merchantId, quoteId) {
const response = await axios.get(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes/${quoteId}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
return response.data;
}
// Uso
const quote = await getQuoteById(token, orgId, merchantId, 'qte_abc123');
console.log('Estado de cotización:', quote);Mejores Prácticas
Cuándo Obtener una Cotización
Siempre obtén una cotización para:
- Conversiones de moneda (BALANCE_TRANSFER)
- Operaciones cripto (ONRAMP/OFFRAMP)
- Mostrar tasa a usuarios antes de que confirmen
Visualización de Tasa
Muestra a los usuarios:
- Tasa de cambio en términos familiares (1 USD = X COP)
- Comisiones totales claramente separadas
- Tasa efectiva (incluyendo comisiones)
- Tiempo de expiración
Manejo de Expiración
async function createOrderWithAutoRetry(token, orgId, merchantId, orderData) {
let quote = await getQuote(token, orgId, merchantId, {
type: orderData.type,
from: orderData.from,
to: orderData.to,
amount: orderData.amount
});
try {
// Intentar crear orden con cotización
return await createOrderWithQuote(token, orgId, merchantId, quote.id);
} catch (error) {
if (error.message.includes('expired') || error.message.includes('invalid quote')) {
// Cotización expirada, obtener nueva cotización y reintentar
console.log('Cotización expirada, obteniendo nueva cotización...');
quote = await getQuote(token, orgId, merchantId, {
type: orderData.type,
from: orderData.from,
to: orderData.to,
amount: orderData.amount
});
return await createOrderWithQuote(token, orgId, merchantId, quote.id);
}
throw error;
}
}Cotización vs Sin Cotización
Con Cotización
// 1. Obtener cotización
const quote = await getQuote(...);
// 2. Mostrar tasa al usuario
console.log('Tasa:', quote.exchangeRate);
// 3. Usuario confirma
// 4. Crear orden con tasa bloqueada
const order = await createOrder({
...orderData,
quoteId: quote.id
});Beneficios:
- Tasa bloqueada
- Usuario sabe exactamente qué recibirá
- Mejor transparencia
Sin Cotización
// Crear orden directamente (usa tasa actual)
const order = await createOrder({
type: 'BALANCE_TRANSFER',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'USD',
amountIn: 1000000
// Sin quoteId - usa tasa actual
});Consideraciones:
- Tasa determinada al momento de creación de orden
- Pequeña fluctuación de tasa posible
- Más rápido (una llamada API menos)
Escenarios Comunes
Escenario 1: Mostrar Tasa Antes de Pago
// Usuario ve página de checkout
const quote = await getQuote(token, orgId, merchantId, {
type: 'PAYIN',
from: 'COP',
to: 'COP',
amount: 50000
});
// Mostrar al usuario
console.log(`Pagar ${quote.amountIn} COP`);
console.log(`Comisión: ${quote.koyweFee} COP`);
console.log(`Total: ${quote.amountIn + quote.koyweFee} COP`);
// Usuario hace clic en "Pagar"
const order = await createOrder({
...orderData,
quoteId: quote.id
});Escenario 2: Herramienta Conversora de Moneda
// Convertidor de moneda en tiempo real
async function convertCurrency(amount, from, to) {
const quote = await getQuote(token, orgId, merchantId, {
type: 'BALANCE_TRANSFER',
from: from,
to: to,
amount: amount
});
return {
from: `${amount} ${from}`,
to: `${quote.amountOut} ${to}`,
rate: `1 ${to} = ${quote.exchangeRate} ${from}`,
fee: `${quote.koyweFee} ${from}`
};
}
// Uso
const result = await convertCurrency(1000000, 'COP', 'USD');
console.log(result);
// { from: "1000000 COP", to: "248.75 USD", rate: "1 USD = 4000 COP", fee: "5000 COP" }