Cobra en pesos colombianos
Cobra a compradores en Colombia con Bre-B, Nequi o Daviplata. Los pagos se liquidan en pesos colombianos en un saldo COP propio — nunca se convierten a bolívares ni a USD.
Cómo funciona COP
- Crea un pago con
POST /v1/cop/paymentseligiendo unchannel:breb,nequiodaviplata. Los montos son pesos enteros (amountCop), mínimo COP 20.000 por pago; un monto menor responde 400amount_below_minimumconminimumCop. - La respuesta indica qué hace el comprador (
next.type). El pago se completa de forma asíncrona: escuchapayment.completed/payment.failedo consúltalo conGET /v1/cop/payments/:id. - Los pagos completados se acreditan a tu saldo COP (
GET /v1/cop/balance) descontando la comisión COP. COP se mantiene separado de tus saldos en VES y stablecoins. - Los pesos nuevos quedan retenidos 24 horas:
GET /v1/cop/balancereportaavailableCop,heldCopynextReleaseAt. Ver Saldos, retenciones y payouts. - Los retiros en COP se envían por transferencia bancaria en Colombia; solicítalos a VEX Pay. Recibes
payout.completedconcurrency: "COP"cuando se paga.
Los SDKs de Node (vexpay.cop), Python (vexpay.cop), PHP ($vexpay->cop) y Laravel (VexPay::cop()) cubren todos los endpoints de COP: payments.create, retrieve, submitOtp, cancel, refund y balance.retrieve.
Canales
| channel | El comprador | Datos del comprador | Vence |
|---|---|---|---|
breb | Escanea el QR o ingresa la llave en cualquier app bancaria colombiana | Opcionales | ~15 min |
nequi | Aprueba la notificación push en la app Nequi | buyer obligatorio (email, phone, documentType, documentNumber) | ~5 min |
daviplata | Te dicta el código que recibe por SMS y tú lo envías | buyer obligatorio; documentType CC, CE o TI | ~5 min |
buyer.phone es un celular colombiano (10 dígitos que empiezan por 3; +57 opcional). documentType es uno de CC, CE, TI, NIT, PP.
Precios
| Movimiento | Comisión |
|---|---|
| Pago COP completado | 4,5 % del monto + COP 1.500, en pesos (nunca más que el monto) |
| Reembolso total | La comisión se devuelve con el reembolso |
Ejemplo: un comprador paga 100.000 COP → comisión 6.000 (4.500 + 1.500) → se acreditan 94.000 COP. El pago mínimo es de 20.000 COP.
Checkout alojado
Los links de pago y las sesiones de checkout pueden cobrar en COP sin que construyas ninguna pantalla. La página alojada deja al comprador elegir la moneda (bolívares, cripto o pesos colombianos), muestra el total en cada una y ejecuta el paso de Bre-B, Nequi o Daviplata. Agrega cop a methods al crear la sesión, o deja methods vacío: COP se ofrece siempre que esté habilitado en tu cuenta.
curl -X POST "${API_URL}/v1/checkout/sessions" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{ "amountUsd": 25.00, "methods": ["c2p", "usdt", "cop"], "reference": "order-1042" }'- La sesión tiene precio en USD. El monto en pesos es el monto en USD × la tasa USDT/COP de VEX Pay más un margen cambiario de 2,5 %, redondeado hacia arriba a pesos enteros, y queda fijo cuando el comprador inicia el pago.
- Si no hay una tasa vigente, la página oculta la opción en pesos; bolívares y cripto siguen funcionando.
- Los links de vendedores de marketplace (merchants) también ofrecen COP: el pago se acredita al saldo COP de ese vendedor, descontando la comisión COP, cuando termina la más tardía entre tu retención de vendedores y la retención de 24 h del saldo.
- COP se oculta cuando el total en pesos es menor al mínimo de COP 20.000 (en un link donde el comprador elige la cantidad, se muestra deshabilitado hasta que el total lo alcance).
- Aplica la comisión COP como en cualquier pago COP. Los webhooks
payment.*de pagos de checkout incluyen ademásamountUsdycopRate, y el bloque habitualcheckoutSession(id,reference,metadata).
Paso 01
Bre-B (QR o llave)
Muestra la imagen QR (next.qrPngBase64, PNG en base64) y la llave (next.transferKey) para copiar. Solo se devuelven en esta respuesta.
curl -X POST "${API_URL}/v1/cop/payments" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--header "Idempotency-Key: order-1042" \
--data '{ "amountCop": 100000, "channel": "breb", "reference": "order-1042" }'const payment = await vexpay.cop.payments.create(
{ amountCop: 100000, channel: 'breb', reference: 'order-1042' },
{ idempotencyKey: 'order-1042' },
);
// payment.next.qrPngBase64, payment.next.transferKeypayment = vexpay.cop.payments.create(
{"amountCop": 100000, "channel": "breb", "reference": "order-1042"},
idempotency_key="order-1042",
)$payment = $vexpay->cop->payments->create(
['amountCop' => 100000, 'channel' => 'breb', 'reference' => 'order-1042'],
['idempotency_key' => 'order-1042'],
);{
"id": "6f1c…",
"status": "pending",
"method": "COP",
"channel": "breb",
"amountCop": 100000,
"reference": "order-1042",
"metadata": {},
"expiresAt": "2026-10-07T20:15:00.000Z",
"createdAt": "2026-10-07T20:00:00.000Z",
"next": { "type": "show_qr", "qrPngBase64": "iVBORw0KGgo…", "transferKey": "BREB-…" }
}Muestra el QR con <img src="data:image/png;base64,{qrPngBase64}">.
Paso 02
Nequi (push)
curl -X POST "${API_URL}/v1/cop/payments" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{
"amountCop": 50000,
"channel": "nequi",
"buyer": { "email": "juan@example.com", "phone": "3001234567", "documentType": "CC", "documentNumber": "1234567890" }
}'await vexpay.cop.payments.create({
amountCop: 50000,
channel: 'nequi',
buyer: { email: 'juan@example.com', phone: '3001234567', documentType: 'CC', documentNumber: '1234567890' },
});vexpay.cop.payments.create({
"amountCop": 50000,
"channel": "nequi",
"buyer": {"email": "juan@example.com", "phone": "3001234567", "documentType": "CC", "documentNumber": "1234567890"},
})$vexpay->cop->payments->create([
'amountCop' => 50000,
'channel' => 'nequi',
'buyer' => ['email' => 'juan@example.com', 'phone' => '3001234567', 'documentType' => 'CC', 'documentNumber' => '1234567890'],
]);La respuesta trae next: { "type": "approve_in_app", "phoneMasked": "****567" }. Pídele al comprador que abra Nequi y apruebe.
Paso 03
Daviplata (código SMS)
Crea el pago con channel: "daviplata" y un buyer. El comprador recibe un código por SMS; envíalo para completar el pago. Un código incorrecto responde 400 invalid_otp y el pago sigue pendiente hasta vencer.
curl -X POST "${API_URL}/v1/cop/payments/{id}/otp" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{ "otp": "123456" }'await vexpay.cop.payments.submitOtp(payment.id, { otp: '123456' });vexpay.cop.payments.submit_otp(payment.id, {"otp": "123456"})$vexpay->cop->payments->submitOtp($payment->id, ['otp' => '123456']);Estados, cancelación y reembolsos
| status | Significado |
|---|---|
pending | Esperando al comprador. |
completed | Pagado y acreditado a tu saldo COP (feeCop presente). |
failed | failureCode: "expired" (no pagó a tiempo) o "rejected" (el comprador lo rechazó). |
canceled | Lo cancelaste con POST /v1/cop/payments/:id/cancel antes de que se pagara. |
refunded | Reembolsado por completo con POST /v1/cop/payments/:id/refund. |
Los reembolsos son solo totales y dentro de las 96 horas siguientes al pago (después: 409 refund_window_expired). Lo acreditado se descuenta de tu saldo COP (una venta que sigue en la retención de 24 h se toma de lo retenido) y la comisión se devuelve; si los pesos disponibles más los retenidos no alcanzan, la respuesta es 409 insufficient_balance. Poco después del pago un reembolso también puede responder 503 processor_unavailable (no disponible temporalmente): no se descuenta nada; reintenta más tarde.
Webhooks
Los pagos COP envían payment.completed, payment.failed, payment.canceled y payment.reversed (reembolso). El payload incluye method: "COP", channel, amountCop, feeCop, reference y metadata.
{
"event": "payment.completed",
"data": {
"paymentId": "6f1c…",
"status": "completed",
"method": "COP",
"currency": "COP",
"channel": "nequi",
"amountCop": 100000,
"feeCop": 6000,
"reference": "order-1042",
"metadata": {},
"livemode": true
}
}Modo de prueba
Las llaves de prueba (y los tenants en modo de prueba) envían los pagos COP al sandbox del procesador. Usa estos valores:
| Canal | Datos del comprador | Qué pasa |
|---|---|---|
breb | Ninguno | El sandbox devuelve un QR y una llave. Confirma consultando GET /v1/cop/payments/:id o por webhook. |
nequi | Cualquier dato válido, p. ej. teléfono 3001234567, CC 1234567890, correo juan@example.com | Se completa solo, alrededor de un minuto después de crearlo. |
daviplata | Igual que Nequi; documentType solo CC, CE o TI | El OTP 123456, 000000 o 111111 lo completa; cualquier otro código responde 400 invalid_otp. |
En el sandbox los documentos y teléfonos solo se validan por formato: phone son 10 dígitos que empiezan por 3 (+57 opcional), documentNumber de 3 a 20 letras, dígitos o guiones.