Pagar Proveedores - Guía de Integración

Integración completa paso a paso para payouts

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:

1async function getBalance(token, orgId, merchantId, currencySymbol) {
2 const response = await axios.get(
3 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/bank-accounts/balances`,
4 {
5 headers: { 'Authorization': `Bearer ${token}` }
6 }
7 );
8
9 const balances = response.data;
10 const balance = balances.find(b => b.currencySymbol === currencySymbol);
11
12 return balance || { availableBalance: 0, currencySymbol };
13}
14
15// Uso
16const balance = await getBalance(token, orgId, merchantId, 'COP');
17console.log('Saldo disponible:', balance.availableBalance, 'COP');
18
19// Verificar si es suficiente
20if (balance.availableBalance < 500000) {
21 console.error('Saldo insuficiente para payout');
22 // Manejar: agregar fondos vía PAYIN o mostrar error
23}

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:

1async function createProviderContact(token, orgId, merchantId, providerData) {
2 const response = await axios.post(
3 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts`,
4 {
5 email: providerData.email,
6 phone: providerData.phone,
7 fullName: providerData.fullName,
8 countrySymbol: providerData.country,
9 documentType: providerData.documentType,
10 documentNumber: providerData.documentNumber,
11 businessType: providerData.businessType // 'PERSON' o 'COMPANY'
12 },
13 {
14 headers: {
15 'Authorization': `Bearer ${token}`,
16 'Content-Type': 'application/json'
17 }
18 }
19 );
20
21 return response.data;
22}
23
24// Uso - Proveedor empresa
25const provider = await createProviderContact(token, orgId, merchantId, {
26 email: 'proveedor@ejemplo.com',
27 phone: '+573001234567',
28 fullName: 'Servicios ABC SAS',
29 country: 'CO',
30 documentType: 'NIT', // ID fiscal para empresas en Colombia
31 documentNumber: '900123456-1',
32 businessType: 'COMPANY'
33});
34
35console.log('Contacto de proveedor creado:', provider.id);

Paso 3: Agregar Cuenta Bancaria del Proveedor

Vincula los detalles de cuenta bancaria del proveedor:

1async function addProviderBankAccount(token, orgId, merchantId, contactId, bankData) {
2 const response = await axios.post(
3 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts/${contactId}/bank-accounts`,
4 {
5 countrySymbol: bankData.country,
6 currencySymbol: bankData.currency,
7 bankCode: bankData.bankCode,
8 accountNumber: bankData.accountNumber,
9 accountType: bankData.accountType, // 'checking' o 'savings'
10 holderName: bankData.holderName
11 },
12 {
13 headers: {
14 'Authorization': `Bearer ${token}`,
15 'Content-Type': 'application/json'
16 }
17 }
18 );
19
20 return response.data;
21}
22
23// Uso
24const bankAccount = await addProviderBankAccount(token, orgId, merchantId, provider.id, {
25 country: 'CO',
26 currency: 'COP',
27 bankCode: 'BANCOLOMBIA',
28 accountNumber: '1234567890',
29 accountType: 'checking',
30 holderName: 'Servicios ABC SAS'
31});
32
33console.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:

1async function createPayoutOrder(token, orgId, merchantId, payoutData) {
2 const response = await axios.post(
3 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
4 {
5 type: 'PAYOUT',
6 originCurrencySymbol: payoutData.currency,
7 destinationCurrencySymbol: payoutData.currency, // Misma moneda
8 amountIn: payoutData.amount,
9 contactId: payoutData.contactId,
10 destinationAccountId: payoutData.destinationAccountId, // ID de cuenta bancaria
11 description: payoutData.description,
12 externalId: payoutData.externalId // Tu referencia interna
13 },
14 {
15 headers: {
16 'Authorization': `Bearer ${token}`,
17 'Content-Type': 'application/json'
18 }
19 }
20 );
21
22 return response.data;
23}
24
25// Uso
26const payout = await createPayoutOrder(token, orgId, merchantId, {
27 currency: 'COP',
28 amount: 500000, // 500,000 COP
29 contactId: provider.id,
30 destinationAccountId: bankAccount.id,
31 description: 'Pago por Factura #INV-001',
32 externalId: `payout-inv-001-${Date.now()}`
33});
34
35console.log('Payout creado:', payout.id);
36console.log('Estado:', payout.status); // "PROCESSING"

Respuesta:

1{
2 "id": "ord_payout123",
3 "type": "PAYOUT",
4 "status": "PROCESSING",
5 "amountIn": 500000,
6 "originCurrencySymbol": "COP",
7 "destinationCurrencySymbol": "COP",
8 "externalId": "payout-inv-001-1699999999",
9 "description": "Pago por Factura #INV-001",
10 "createdAt": "2025-11-13T15:00:00Z"
11}

Paso 5: Monitorear Estado de Payout

Rastrea el payout vía webhooks o polling:

Vía Webhooks (Recomendado)

Node.js Express
1app.post('/webhooks/koywe', express.raw({type: 'application/json'}), async (req, res) => {
2 // Convertir body raw a string una vez
3 const rawBody = req.body.toString();
4
5 // Verificar firma con body string
6 const signature = req.headers['koywe-signature'];
7 if (!verifySignature(rawBody, signature, process.env.KOYWE_WEBHOOK_SECRET)) {
8 return res.status(401).send('Firma inválida');
9 }
10
11 // Parsear JSON desde string
12 const event = JSON.parse(rawBody);
13
14 switch (event.type) {
15 case 'order.processing':
16 console.log('Payout procesando:', event.data.orderId);
17 // Actualizar estado interno
18 await updatePayoutStatus(event.data.externalId, 'processing');
19 break;
20
21 case 'order.completed':
22 console.log('Payout completado:', event.data.orderId);
23 // Marcar como pagado en tu sistema
24 await markInvoiceAsPaid(event.data.externalId);
25 await notifyProvider(event.data.externalId);
26 break;
27
28 case 'order.failed':
29 console.log('Payout fallido:', event.data.orderId);
30 // Manejar fallo
31 await handlePayoutFailure(event.data.externalId, event.data.errorMessage);
32 break;
33 }
34
35 res.status(200).send('OK');
36});

Vía Polling (Alternativa)

Node.js
1async function waitForPayoutCompletion(token, orgId, merchantId, orderId, maxAttempts = 20) {
2 for (let i = 0; i < maxAttempts; i++) {
3 const order = await axios.get(
4 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders/${orderId}`,
5 { headers: { 'Authorization': `Bearer ${token}` } }
6 );
7
8 const status = order.data.status;
9 console.log(`Intento ${i + 1}: Estado = ${status}`);
10
11 if (status === 'COMPLETED') {
12 console.log('✓ Payout completado exitosamente');
13 return order.data;
14 }
15
16 if (status === 'FAILED') {
17 throw new Error(`Payout fallido: ${order.data.errorMessage}`);
18 }
19
20 // Esperar 5 segundos antes de verificar nuevamente
21 await new Promise(resolve => setTimeout(resolve, 5000));
22 }
23
24 throw new Error('Tiempo de espera agotado');
25}

Ejemplo Completo de Extremo a Extremo

Flujo Completo de Payout
1async function completePayoutFlow() {
2 try {
3 // 1. Autenticar
4 const token = await authenticate(apiKey, secret);
5 console.log('✓ Autenticado');
6
7 // 2. Verificar saldo
8 const balance = await getBalance(token, orgId, merchantId, 'COP');
9 if (balance.availableBalance < 500000) {
10 throw new Error('Saldo insuficiente');
11 }
12 console.log(`✓ Saldo suficiente: ${balance.availableBalance} COP`);
13
14 // 3. Crear/obtener contacto de proveedor
15 const provider = await createProviderContact(token, orgId, merchantId, {
16 email: 'proveedor@ejemplo.com',
17 fullName: 'Servicios ABC SAS',
18 country: 'CO',
19 documentType: 'NIT',
20 documentNumber: '900123456-1',
21 businessType: 'COMPANY'
22 });
23 console.log('✓ Proveedor creado:', provider.id);
24
25 // 4. Agregar cuenta bancaria
26 const bankAccount = await addProviderBankAccount(
27 token, orgId, merchantId, provider.id,
28 {
29 country: 'CO',
30 currency: 'COP',
31 bankCode: 'BANCOLOMBIA',
32 accountNumber: '1234567890',
33 accountType: 'checking',
34 holderName: 'Servicios ABC SAS'
35 }
36 );
37 console.log('✓ Cuenta bancaria agregada:', bankAccount.id);
38
39 // 5. Crear payout
40 const payout = await createPayoutOrder(token, orgId, merchantId, {
41 currency: 'COP',
42 amount: 500000,
43 contactId: provider.id,
44 destinationAccountId: bankAccount.id,
45 description: 'Pago por Factura #INV-001',
46 externalId: `payout-${Date.now()}`
47 });
48 console.log('✓ Payout creado:', payout.id);
49 console.log(' Estado:', payout.status);
50
51 // 6. Esperar confirmación (o confiar en webhooks)
52 console.log('Esperando completación...');
53 const completed = await waitForPayoutCompletion(
54 token, orgId, merchantId, payout.id
55 );
56
57 console.log('✓ Payout completado exitosamente!');
58 console.log(' ID de Orden:', completed.id);
59 console.log(' Monto:', completed.amountIn, completed.originCurrencySymbol);
60
61 return completed;
62
63 } catch (error) {
64 console.error('Error en flujo de payout:', error.message);
65 throw error;
66 }
67}
68
69// Ejecutar
70await completePayoutFlow();

Próximos Pasos