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
}def get_balance(token, org_id, merchant_id, currency_symbol):
response = requests.get(
f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/accounts/balances',
headers={'Authorization': f'Bearer {token}'}
)
response.raise_for_status()
balances = response.json()
balance = next((b for b in balances if b['currencySymbol'] == currency_symbol), None)
return balance or {'availableBalance': 0, 'currencySymbol': currency_symbol}
# Uso
balance = get_balance(token, org_id, merchant_id, 'COP')
print(f"Disponible: {balance['availableBalance']} COP")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`,
{
email: providerData.email,
phone: providerData.phone,
fullName: providerData.fullName,
countrySymbol: providerData.country,
documentType: providerData.documentType,
documentNumber: providerData.documentNumber,
businessType: providerData.businessType // 'PERSON' o 'COMPANY'
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
// Uso - Proveedor empresa
const provider = await createProviderContact(token, orgId, merchantId, {
email: 'proveedor@ejemplo.com',
phone: '+573001234567',
fullName: 'Servicios ABC SAS',
country: 'CO',
documentType: 'NIT', // ID fiscal para empresas en Colombia
documentNumber: '900123456-1',
businessType: 'COMPANY'
});
console.log('Contacto de proveedor creado:', provider.id);def create_provider_contact(token, org_id, merchant_id, provider_data):
response = requests.post(
f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/contacts',
json={
'email': provider_data['email'],
'phone': provider_data.get('phone'),
'fullName': provider_data['full_name'],
'countrySymbol': provider_data['country'],
'documentType': provider_data['document_type'],
'documentNumber': provider_data['document_number'],
'businessType': provider_data['business_type']
},
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return response.json()
# Uso
provider = create_provider_contact(token, org_id, merchant_id, {
'email': 'proveedor@ejemplo.com',
'phone': '+573001234567',
'full_name': 'Servicios ABC SAS',
'country': 'CO',
'document_type': 'NIT',
'document_number': '900123456-1',
'business_type': 'COMPANY'
})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`,
{
countrySymbol: bankData.country,
currencySymbol: bankData.currency,
bankCode: bankData.bankCode,
accountNumber: bankData.accountNumber,
accountType: bankData.accountType, // 'checking' o 'savings'
holderName: bankData.holderName
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
// Uso
const bankAccount = await addProviderBankAccount(token, orgId, merchantId, provider.id, {
country: 'CO',
currency: 'COP',
bankCode: 'BANCOLOMBIA',
accountNumber: '1234567890',
accountType: 'checking',
holderName: 'Servicios ABC SAS'
});
console.log('Cuenta bancaria agregada:', bankAccount.id);def add_provider_bank_account(token, org_id, merchant_id, contact_id, bank_data):
response = requests.post(
f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/contacts/{contact_id}/accounts',
json={
'countrySymbol': bank_data['country'],
'currencySymbol': bank_data['currency'],
'bankCode': bank_data['bank_code'],
'accountNumber': bank_data['account_number'],
'accountType': bank_data['account_type'],
'holderName': bank_data['holder_name']
},
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return response.json()
# Uso
bank_account = add_provider_bank_account(token, org_id, merchant_id, provider['id'], {
'country': 'CO',
'currency': 'COP',
'bank_code': 'BANCOLOMBIA',
'account_number': '1234567890',
'account_type': 'checking',
'holder_name': 'Servicios ABC SAS'
})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:
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"def create_payout_order(token, org_id, merchant_id, payout_data):
import time
response = requests.post(
f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/orders',
json={
'type': 'PAYOUT',
'originCurrencySymbol': payout_data['currency'],
'destinationCurrencySymbol': payout_data['currency'],
'amountIn': payout_data['amount'],
'contactId': payout_data['contact_id'],
'destinationAccountId': payout_data['destination_account_id'],
'description': payout_data['description'],
'externalId': payout_data['external_id']
},
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return response.json()
# Uso
payout = create_payout_order(token, org_id, merchant_id, {
'currency': 'COP',
'amount': 500000,
'contact_id': provider['id'],
'destination_account_id': bank_account['id'],
'description': 'Pago por Factura #INV-001',
'external_id': f'payout-inv-001-{int(time.time())}'
})
print(f"Payout creado: {payout['id']}")curl -X POST 'https://api-sandbox.koywe.com/api/v1/organizations/TU_ORG_ID/merchants/TU_MERCHANT_ID/orders' \
-H 'Authorization: Bearer TU_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"type": "PAYOUT",
"originCurrencySymbol": "COP",
"destinationCurrencySymbol": "COP",
"amountIn": 500000,
"contactId": "cnt_provider123",
"destinationAccountId": "ba_xyz789",
"description": "Pago por Factura #INV-001",
"externalId": "payout-inv-001-1699999999"
}'Respuesta:
{
"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)
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)
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
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, {
email: 'proveedor@ejemplo.com',
fullName: 'Servicios ABC SAS',
country: 'CO',
documentType: 'NIT',
documentNumber: '900123456-1',
businessType: 'COMPANY'
});
console.log('✓ Proveedor creado:', provider.id);
// 4. Agregar cuenta bancaria
const bankAccount = await addProviderBankAccount(
token, orgId, merchantId, provider.id,
{
country: 'CO',
currency: 'COP',
bankCode: 'BANCOLOMBIA',
accountNumber: '1234567890',
accountType: 'checking',
holderName: 'Servicios ABC SAS'
}
);
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
Last updated on