Koywe
En esta página

Guía de Integración para Pagar Proveedores

Esta guía te lleva a través de la implementación de pagos a proveedores en tu aplicación.

Prerequisitos

  • Credenciales API (key, secret, organizationId, merchantId)
  • Fondos en tu cuenta virtual
  • Información del proveedor lista
  • Comprensión de Cuentas Virtuales

Paso 1: Verificar Saldo de Cuenta Virtual

Siempre verifica el saldo antes de crear un payout:

async function getBalance(token, orgId, merchantId, currencySymbol) {
  const response = await axios.get(
    `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/accounts/balances`,
    {
      headers: { 'Authorization': `Bearer ${token}` }
    }
  );
  
  const balances = response.data;
  const balance = balances.find(b => b.currencySymbol === currencySymbol);
  
  return balance || { availableBalance: 0, currencySymbol };
}
 
// Uso
const balance = await getBalance(token, orgId, merchantId, 'COP');
console.log('Saldo disponible:', balance.availableBalance, 'COP');
 
// Verificar si es suficiente
if (balance.availableBalance < 500000) {
  console.error('Saldo insuficiente para payout');
  // Manejar: agregar fondos vía PAYIN o mostrar error
}

Crítico: Si el saldo es insuficiente, la orden PAYOUT fallará. Siempre verifica el saldo primero.


Paso 2: Crear Contacto de Proveedor

Almacena información del proveedor:

async function createProviderContact(token, orgId, merchantId, providerData) {
  const response = await axios.post(
    `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts`,
    {
      firstName: providerData.firstName,
      countrySymbol: providerData.country,
      documentType: providerData.documentType,
      documentNumber: providerData.documentNumber,
      businessType: providerData.businessType,  // 'PERSON' o 'COMPANY'
      type: providerData.type,                  // 'PERSON', 'BUSINESS', etc.
      email: providerData.email,
      phone: providerData.phone
    },
    {
      headers: { 
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    }
  );
  
  return response.data;
}
 
// Uso - Proveedor empresa
const provider = await createProviderContact(token, orgId, merchantId, {
  firstName: 'Servicios ABC SAS',
  country: 'CO',
  documentType: 'NIT',  // ID fiscal para empresas en Colombia
  documentNumber: '9001234561',
  businessType: 'COMPANY',
  type: 'BUSINESS',
  email: 'proveedor@ejemplo.com',
  phone: '+573001234567'
});
 
console.log('Contacto de proveedor creado:', provider.id);

Paso 3: Agregar Cuenta Bancaria del Proveedor

Vincula los detalles de cuenta bancaria del proveedor:

async function addProviderBankAccount(token, orgId, merchantId, contactId, bankData) {
  const response = await axios.post(
    `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts/${contactId}/accounts`,
    {
      name: bankData.name,                // Nombre de la cuenta
      kind: 'BANK',                       // 'BANK' o 'CRYPTO'
      isDefault: bankData.isDefault,
      countrySymbol: bankData.country,
      currencySymbol: bankData.currency,
      entity: bankData.entity,            // Código banco (ej., 'BANCOLOMBIA')
      accountNumber: bankData.accountNumber,
      type: bankData.type                 // 'CHECKING', 'SAVINGS', o 'VIRTUAL'
    },
    {
      headers: { 
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    }
  );
  
  return response.data;
}
 
// Uso
const bankAccount = await addProviderBankAccount(token, orgId, merchantId, provider.id, {
  name: 'Bancolombia COP',
  country: 'CO',
  currency: 'COP',
  entity: 'BANCOLOMBIA',
  accountNumber: '1234567890',
  type: 'CHECKING',
  isDefault: true
});
 
console.log('Cuenta bancaria agregada:', bankAccount.id);

Códigos Bancarios por País

PaísBancos EjemploFormato de Código
ColombiaBANCOLOMBIA, DAVIVIENDA, BOGOTANombre de banco
BrasilBANCO_DO_BRASIL, BRADESCO, ITAUCódigo de banco
MéxicoBBVA_MEXICO, SANTANDER_MEXICOIdentificador de banco
ChileBANCO_CHILE, BCI, SANTANDER_CHILENombre de banco

Referencia completa de códigos bancarios →


Paso 4: Crear Orden PAYOUT

Crear la orden de payout:

async function createPayoutOrder(token, orgId, merchantId, payoutData) {
  const response = await axios.post(
    `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
    {
      type: 'PAYOUT',
      originCurrencySymbol: payoutData.currency,
      destinationCurrencySymbol: payoutData.currency,  // Misma moneda
      amountIn: payoutData.amount,
      contactId: payoutData.contactId,
      destinationAccountId: payoutData.destinationAccountId,  // ID de cuenta bancaria
      description: payoutData.description,
      externalId: payoutData.externalId  // Tu referencia interna
    },
    {
      headers: { 
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    }
  );
  
  return response.data;
}
 
// Uso
const payout = await createPayoutOrder(token, orgId, merchantId, {
  currency: 'COP',
  amount: 500000,  // 500,000 COP
  contactId: provider.id,
  destinationAccountId: bankAccount.id,
  description: 'Pago por Factura #INV-001',
  externalId: `payout-inv-001-${Date.now()}`
});
 
console.log('Payout creado:', payout.id);
console.log('Estado:', payout.status); // "PROCESSING"

Respuesta:

JSON
{
  "id": "ord_payout123",
  "type": "PAYOUT",
  "status": "PROCESSING",
  "amountIn": 500000,
  "originCurrencySymbol": "COP",
  "destinationCurrencySymbol": "COP",
  "externalId": "payout-inv-001-1699999999",
  "description": "Pago por Factura #INV-001",
  "createdAt": "2025-11-13T15:00:00Z"
}

Paso 5: Monitorear Estado de Payout

Rastrea el payout vía webhooks o polling:

Vía Webhooks (Recomendado)

JavaScript
app.post('/webhooks/koywe', express.raw({type: 'application/json'}), async (req, res) => {
  // Convertir body raw a string una vez
  const rawBody = req.body.toString();
  
  // Verificar firma con body string
  const signature = req.headers['koywe-signature'];
  if (!verifySignature(rawBody, signature, process.env.KOYWE_WEBHOOK_SECRET)) {
    return res.status(401).send('Firma inválida');
  }
  
  // Parsear JSON desde string
  const event = JSON.parse(rawBody);
  
  switch (event.type) {
    case 'order.processing':
      console.log('Payout procesando:', event.data.orderId);
      // Actualizar estado interno
      await updatePayoutStatus(event.data.externalId, 'processing');
      break;
      
    case 'order.completed':
      console.log('Payout completado:', event.data.orderId);
      // Marcar como pagado en tu sistema
      await markInvoiceAsPaid(event.data.externalId);
      await notifyProvider(event.data.externalId);
      break;
      
    case 'order.failed':
      console.log('Payout fallido:', event.data.orderId);
      // Manejar fallo
      await handlePayoutFailure(event.data.externalId, event.data.errorMessage);
      break;
  }
  
  res.status(200).send('OK');
});

Vía Polling (Alternativa)

JavaScript
async function waitForPayoutCompletion(token, orgId, merchantId, orderId, maxAttempts = 20) {
  for (let i = 0; i < maxAttempts; i++) {
    const order = await axios.get(
      `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders/${orderId}`,
      { headers: { 'Authorization': `Bearer ${token}` } }
    );
    
    const status = order.data.status;
    console.log(`Intento ${i + 1}: Estado = ${status}`);
    
    if (status === 'COMPLETED') {
      console.log('✓ Payout completado exitosamente');
      return order.data;
    }
    
    if (status === 'FAILED') {
      throw new Error(`Payout fallido: ${order.data.errorMessage}`);
    }
    
    // Esperar 5 segundos antes de verificar nuevamente
    await new Promise(resolve => setTimeout(resolve, 5000));
  }
  
  throw new Error('Tiempo de espera agotado');
}

Ejemplo Completo de Extremo a Extremo

JavaScript
async function completePayoutFlow() {
  try {
    // 1. Autenticar
    const token = await authenticate(apiKey, secret);
    console.log('✓ Autenticado');
    
    // 2. Verificar saldo
    const balance = await getBalance(token, orgId, merchantId, 'COP');
    if (balance.availableBalance < 500000) {
      throw new Error('Saldo insuficiente');
    }
    console.log(`✓ Saldo suficiente: ${balance.availableBalance} COP`);
    
    // 3. Crear/obtener contacto de proveedor
    const provider = await createProviderContact(token, orgId, merchantId, {
      firstName: 'Servicios ABC SAS',
      country: 'CO',
      documentType: 'NIT',
      documentNumber: '9001234561',
      businessType: 'COMPANY',
      type: 'BUSINESS',
      email: 'proveedor@ejemplo.com'
    });
    console.log('✓ Proveedor creado:', provider.id);
    
    // 4. Agregar cuenta bancaria
    const bankAccount = await addProviderBankAccount(
      token, orgId, merchantId, provider.id,
      {
        name: 'Bancolombia COP',
        kind: 'BANK',
        isDefault: true,
        country: 'CO',
        currency: 'COP',
        entity: 'BANCOLOMBIA',
        accountNumber: '1234567890',
        type: 'CHECKING'
      }
    );
    console.log('✓ Cuenta bancaria agregada:', bankAccount.id);
    
    // 5. Crear payout
    const payout = await createPayoutOrder(token, orgId, merchantId, {
      currency: 'COP',
      amount: 500000,
      contactId: provider.id,
      destinationAccountId: bankAccount.id,
      description: 'Pago por Factura #INV-001',
      externalId: `payout-${Date.now()}`
    });
    console.log('✓ Payout creado:', payout.id);
    console.log('  Estado:', payout.status);
    
    // 6. Esperar confirmación (o confiar en webhooks)
    console.log('Esperando completación...');
    const completed = await waitForPayoutCompletion(
      token, orgId, merchantId, payout.id
    );
    
    console.log('✓ Payout completado exitosamente!');
    console.log('  ID de Orden:', completed.id);
    console.log('  Monto:', completed.amountIn, completed.originCurrencySymbol);
    
    return completed;
    
  } catch (error) {
    console.error('Error en flujo de payout:', error.message);
    throw error;
  }
}
 
// Ejecutar
await completePayoutFlow();

Próximos Pasos