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:
1 async 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 16 const balance = await getBalance(token, orgId, merchantId, 'COP'); 17 console.log('Saldo disponible:', balance.availableBalance, 'COP'); 18 19 // Verificar si es suficiente 20 if (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:
1 async 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 25 const 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 35 console.log('Contacto de proveedor creado:', provider.id);
Paso 3: Agregar Cuenta Bancaria del Proveedor
Vincula los detalles de cuenta bancaria del proveedor:
1 async 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 24 const 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 33 console.log('Cuenta bancaria agregada:', bankAccount.id);
Códigos Bancarios por País
| País | Bancos Ejemplo | Formato de Código |
|---|---|---|
| Colombia | BANCOLOMBIA, DAVIVIENDA, BOGOTA | Nombre de banco |
| Brasil | BANCO_DO_BRASIL, BRADESCO, ITAU | Código de banco |
| México | BBVA_MEXICO, SANTANDER_MEXICO | Identificador de banco |
| Chile | BANCO_CHILE, BCI, SANTANDER_CHILE | Nombre de banco |
Referencia completa de códigos bancarios →
Paso 4: Crear Orden PAYOUT
Crear la orden de payout:
1 async 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 26 const 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 35 console.log('Payout creado:', payout.id); 36 console.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
1 app.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
1 async 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
1 async 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 70 await completePayoutFlow();