Skip to Content
Aceptar PagosGuía de Integración

Guía de Integración para Recibir Pagos

Esta guía completa te lleva a través de la integración de aceptación de pagos en tu aplicación para uso en producción.

Requisitos Previos

Antes de comenzar:

  • Credenciales API (key, secret, organizationId, merchantId)
  • Endpoint webhook configurado (recomendado)
  • Acceso a entorno de pruebas
  • Comprensión de Conceptos Clave
  • Familiaridad con Tipos de Orden

Resumen de Integración


Paso 1: Autenticar

Obtén un token de acceso para todas las solicitudes posteriores:

curl -X POST 'https://api-sandbox.koywe.com/api/v1/auth/sign-in' \ -H 'Content-Type: application/json' \ -d '{ "apiKey": "tu_api_key", "secret": "tu_secret" }'
const axios = require('axios'); async function authenticate() { const response = await axios.post( 'https://api-sandbox.koywe.com/api/v1/auth/sign-in', { apiKey: process.env.KOYWE_API_KEY, secret: process.env.KOYWE_SECRET } ); return response.data.token; } // Uso const token = await authenticate();
import requests import os def authenticate(): response = requests.post( 'https://api-sandbox.koywe.com/api/v1/auth/sign-in', json={ 'apiKey': os.environ['KOYWE_API_KEY'], 'secret': os.environ['KOYWE_SECRET'] } ) response.raise_for_status() return response.json()['token'] # Uso token = authenticate()

Respuesta:

{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }

Gestión de Tokens: Los tokens expiran después de 1 hora. Implementa lógica de caché y actualización de tokens en producción.

Gestión de Tokens en Producción

class KoyweClient { constructor(apiKey, secret, baseUrl) { this.apiKey = apiKey; this.secret = secret; this.baseUrl = baseUrl; this.token = null; this.tokenExpiry = null; } async getToken() { // Devolver token en caché si aún es válido if (this.token && this.tokenExpiry > Date.now()) { return this.token; } // Obtener nuevo token const response = await axios.post(`${this.baseUrl}/auth/sign-in`, { apiKey: this.apiKey, secret: this.secret }); this.token = response.data.token; // Establecer vencimiento a 55 minutos (buffer de 5 min) this.tokenExpiry = Date.now() + (55 * 60 * 1000); return this.token; } }

Paso 2: Obtener Métodos de Pago Disponibles

Consulta qué métodos de pago están disponibles para tu país y moneda objetivo:

curl -X GET 'https://api-sandbox.koywe.com/api/v1/payment-method?countrySymbol=CO&currencySymbol=COP' \ -H 'Authorization: Bearer TU_TOKEN'
async function getPaymentMethods(token, countrySymbol, currencySymbol) { const response = await axios.get( 'https://api-sandbox.koywe.com/api/v1/payment-method', { params: { countrySymbol: countrySymbol, // ej., 'CO' para Colombia currencySymbol: currencySymbol // ej., 'COP' }, headers: { 'Authorization': `Bearer ${token}` } } ); return response.data; } // Uso - Obtener métodos de pago colombianos const methods = await getPaymentMethods(token, 'CO', 'COP'); console.log('Métodos disponibles:', methods); // Respuesta ejemplo // [ // { // "method": "PSE", // "name": "PSE - Pagos Seguros en Línea", // "supportedCountries": ["CO"], // "supportedCurrencies": ["COP"], // "extra": { // "banks": ["BANCOLOMBIA", "DAVIVIENDA", "BOGOTA"] // } // }, // { // "method": "NEQUI", // "name": "Nequi", // "supportedCountries": ["CO"], // "supportedCurrencies": ["COP"] // } // ]
def get_payment_methods(token, country_symbol, currency_symbol): response = requests.get( 'https://api-sandbox.koywe.com/api/v1/payment-method', params={ 'countrySymbol': country_symbol, 'currencySymbol': currency_symbol }, headers={'Authorization': f'Bearer {token}'} ) response.raise_for_status() return response.json() # Uso methods = get_payment_methods(token, 'CO', 'COP') for method in methods: print(f"Método: {method['method']} - {method['name']}")

Caché: Almacena métodos de pago en caché por país/moneda para reducir llamadas API. Los métodos no cambian frecuentemente.


Paso 3: Crear Contacto (Opcional pero Recomendado)

Crea un contacto para el cliente para rastrear el historial de pagos y cumplir con requisitos de cumplimiento:

async function createContact(token, orgId, merchantId, contactData) { const response = await axios.post( `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts`, { email: contactData.email, // Opcional pero recomendado phone: contactData.phone, // Opcional fullName: contactData.fullName, // Requerido countrySymbol: contactData.countrySymbol, // Requerido (ej., 'CO') documentType: contactData.documentType, // Requerido (ej., 'CC') documentNumber: contactData.documentNumber, // Requerido businessType: 'PERSON' // 'PERSON' o 'COMPANY' }, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } } ); return response.data; } // Uso const contact = await createContact(token, orgId, merchantId, { email: 'cliente@ejemplo.com', phone: '+573001234567', fullName: 'Juan Pérez', countrySymbol: 'CO', documentType: 'CC', // ID colombiano documentNumber: '1234567890' }); console.log('Contacto creado:', contact.id);
def create_contact(token, org_id, merchant_id, contact_data): response = requests.post( f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/contacts', json={ 'email': contact_data.get('email'), 'phone': contact_data.get('phone'), 'fullName': contact_data['full_name'], 'countrySymbol': contact_data['country_symbol'], 'documentType': contact_data['document_type'], 'documentNumber': contact_data['document_number'], 'businessType': 'PERSON' }, headers={ 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } ) response.raise_for_status() return response.json() # Uso contact = create_contact(token, org_id, merchant_id, { 'email': 'cliente@ejemplo.com', 'phone': '+573001234567', 'full_name': 'Juan Pérez', 'country_symbol': 'CO', 'document_type': 'CC', 'document_number': '1234567890' })
curl -X POST 'https://api-sandbox.koywe.com/api/v1/organizations/TU_ORG_ID/merchants/TU_MERCHANT_ID/contacts' \ -H 'Authorization: Bearer TU_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "email": "cliente@ejemplo.com", "phone": "+573001234567", "fullName": "Juan Pérez", "countrySymbol": "CO", "documentType": "CC", "documentNumber": "1234567890", "businessType": "PERSON" }'

Tipos de Documento por País

PaísCódigoTipo de DocumentoEjemplo
ColombiaCCCédula de Ciudadanía1234567890
BrasilCPFCadastro de Pessoas Físicas12345678900
MéxicoRFCRegistro Federal de ContribuyentesXAXX010101000
ChileRUTRol Único Tributario11111111-1

Referencia completa de tipos de documento →


Paso 4: Crear Orden PAYIN

Crea la orden de pago con todos los detalles requeridos:

async function createPayinOrder(token, orgId, merchantId, orderData) { const response = await axios.post( `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`, { type: 'PAYIN', // Tipo de orden originCurrencySymbol: orderData.currency, // ej., 'COP' destinationCurrencySymbol: orderData.currency, // Misma que origen para PAYIN amountIn: orderData.amount, // Monto a cobrar description: orderData.description, // Descripción visible para cliente externalId: orderData.externalId, // Tu referencia interna (para idempotencia) contactId: orderData.contactId, // Contacto del Paso 3 (opcional) paymentMethods: [ // Al menos un método de pago requerido { method: orderData.paymentMethod, // ej., 'PSE', 'PIX', 'SPEI' extra: orderData.extra // Datos adicionales (ej., banco para PSE) } ], successUrl: orderData.successUrl, // Redirección después de éxito failedUrl: orderData.failedUrl // Redirección después de fallo }, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } } ); return response.data; } // Uso const order = await createPayinOrder(token, orgId, merchantId, { currency: 'COP', amount: 50000, // 50,000 COP description: 'Pago por Orden #12345', externalId: `order-12345-${Date.now()}`, // Único por intento contactId: contact.id, paymentMethod: 'PSE', extra: { bankAccount: { name: 'BANCOLOMBIA' } }, // Selección de banco para PSE (extras por método) successUrl: 'https://tusitio.com/pago/exitoso', failedUrl: 'https://tusitio.com/pago/fallido' }); console.log('ID de Orden:', order.id); console.log('URL de Pago:', order.paymentUrl); // Redirige al cliente a order.paymentUrl
def create_payin_order(token, org_id, merchant_id, order_data): response = requests.post( f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/orders', json={ 'type': 'PAYIN', 'originCurrencySymbol': order_data['currency'], 'destinationCurrencySymbol': order_data['currency'], 'amountIn': order_data['amount'], 'description': order_data['description'], 'externalId': order_data['external_id'], 'contactId': order_data.get('contact_id'), 'paymentMethods': [ { 'method': order_data['payment_method'], 'extra': order_data.get('extra') } ], 'successUrl': order_data['success_url'], 'failedUrl': order_data['failed_url'] }, headers={ 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } ) response.raise_for_status() return response.json() # Uso import time order = create_payin_order(token, org_id, merchant_id, { 'currency': 'COP', 'amount': 50000, 'description': 'Pago por Orden #12345', 'external_id': f'order-12345-{int(time.time())}', 'contact_id': contact['id'], 'payment_method': 'PSE', 'extra': 'BANCOLOMBIA', 'success_url': 'https://tusitio.com/pago/exitoso', 'failed_url': 'https://tusitio.com/pago/fallido' }) print(f"ID de Orden: {order['id']}") print(f"URL de Pago: {order['paymentUrl']}")
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": "PAYIN", "originCurrencySymbol": "COP", "destinationCurrencySymbol": "COP", "amountIn": 50000, "description": "Pago por Orden #12345", "externalId": "order-12345-1699999999", "contactId": "cnt_abc123", "paymentMethods": [ { "method": "PSE", "extra": { "bankAccount": { "name": "BANCOLOMBIA" } } } ], "successUrl": "https://tusitio.com/pago/exitoso", "failedUrl": "https://tusitio.com/pago/fallido" }'

Respuesta:

{ "id": "ord_abc123xyz", "type": "PAYIN", "status": "PENDING", "amountIn": 50000, "originCurrencySymbol": "COP", "destinationCurrencySymbol": "COP", "paymentUrl": "https://checkout.koywe.com/pay/ord_abc123xyz", "externalId": "order-12345-1699999999", "description": "Pago por Orden #12345", "createdAt": "2025-11-13T10:00:00Z" }

URL de Pago Generada: La paymentUrl es donde debes redirigir a tu cliente para completar el pago.


Paso 5: Redirigir Cliente al Pago

Envía al cliente a la URL de pago:

// Redirección directa window.location.href = order.paymentUrl; // O abrir en nueva ventana window.open(order.paymentUrl, '_blank'); // O devolver URL al frontend res.json({ orderId: order.id, paymentUrl: order.paymentUrl });
function CheckoutButton() { const handleCheckout = async () => { try { // Llamar a tu backend para crear orden const response = await fetch('/api/create-payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: 50000, description: 'Orden #12345' }) }); const { paymentUrl } = await response.json(); // Redirigir al pago window.location.href = paymentUrl; } catch (error) { console.error('Error de pago:', error); } }; return ( <button onClick={handleCheckout}> Pagar Ahora </button> ); }

Paso 6: Manejar Webhooks

Escucha eventos webhook para rastrear el estado del pago:

const express = require('express'); const crypto = require('crypto'); app.post('/webhooks/koywe', express.raw({type: 'application/json'}), async (req, res) => { // Convertir body raw a string una vez const rawBody = req.body.toString(); // 1. Verificar firma de webhook const signature = req.headers['koywe-signature']; const secret = process.env.KOYWE_WEBHOOK_SECRET; const expectedSignature = crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex'); if (signature !== expectedSignature) { console.error('Firma de webhook inválida'); return res.status(401).send('Firma inválida'); } // 2. Parsear evento desde string const event = JSON.parse(rawBody); const eventId = event.id; // 3. Verificar duplicado (idempotencia) if (await isEventProcessed(eventId)) { return res.status(200).send('Ya procesado'); } // 4. Manejar evento basado en tipo try { switch (event.type) { case 'order.created': console.log('Orden creada:', event.data.orderId); break; case 'order.pending': console.log('Orden pendiente:', event.data.orderId); await updateOrderStatus(event.data.externalId, 'pending'); break; case 'order.processing': console.log('Pago en proceso:', event.data.orderId); await updateOrderStatus(event.data.externalId, 'processing'); break; case 'order.paid': console.log('Pago confirmado:', event.data.orderId); await updateOrderStatus(event.data.externalId, 'paid'); await sendConfirmationEmail(event.data.externalId); break; case 'order.completed': console.log('Fondos acreditados:', event.data.orderId); await updateOrderStatus(event.data.externalId, 'completed'); // CUMPLIR LA ORDEN AQUÍ await fulfillOrder(event.data.externalId); await sendShippingNotification(event.data.externalId); break; case 'order.failed': console.log('Pago falló:', event.data.orderId); await updateOrderStatus(event.data.externalId, 'failed'); await sendFailureNotification(event.data.externalId); break; case 'order.expired': console.log('Orden expiró:', event.data.orderId); await updateOrderStatus(event.data.externalId, 'expired'); break; case 'order.cancelled': console.log('Orden cancelada:', event.data.orderId); await updateOrderStatus(event.data.externalId, 'cancelled'); break; } // 5. Marcar evento como procesado await markEventProcessed(eventId); // 6. Responder rápidamente (< 5 segundos) res.status(200).send('OK'); } catch (error) { console.error('Error procesando webhook:', error); // Aún devolver 200 para prevenir reintentos por errores de procesamiento res.status(200).send('OK'); // Registrar error para revisión manual await logWebhookError(event, error); } });
from flask import Flask, request import hmac import hashlib import json app = Flask(__name__) @app.route('/webhooks/koywe', methods=['POST']) def webhook_handler(): # 1. Verificar firma signature = request.headers.get('Koywe-Signature') secret = os.environ['KOYWE_WEBHOOK_SECRET'] expected_signature = hmac.new( secret.encode(), request.data, hashlib.sha256 ).hexdigest() if signature != expected_signature: return 'Firma inválida', 401 # 2. Parsear evento event = json.loads(request.data) event_id = event['id'] # 3. Verificar duplicado if is_event_processed(event_id): return 'Ya procesado', 200 # 4. Manejar evento try: event_type = event['type'] order_data = event['data'] if event_type == 'order.paid': update_order_status(order_data['externalId'], 'paid') send_confirmation_email(order_data['externalId']) elif event_type == 'order.completed': update_order_status(order_data['externalId'], 'completed') # CUMPLIR LA ORDEN AQUÍ fulfill_order(order_data['externalId']) elif event_type == 'order.failed': update_order_status(order_data['externalId'], 'failed') send_failure_notification(order_data['externalId']) # Marcar como procesado mark_event_processed(event_id) return 'OK', 200 except Exception as error: print(f'Error webhook: {error}') log_webhook_error(event, error) return 'OK', 200 # Devolver 200 para prevenir reintentos

Crítico: Siempre verifica las firmas de webhook para asegurar que el webhook proviene de Koywe.

Guía completa de webhooks →


Próximos Pasos

Last updated on