Saltar al contenido
ESENIr al Portal

Crear cobros

Cómo crear cobros uno por uno o en bulk, con vencimientos, recargo, estados, avisos al pagador y reintentos seguros.

En esta página podés consultar cómo crear, seguir y cancelar cobros, uno por uno o hasta 100 por llamada. Necesitás una cuenta de comercio conectada y su id_token en Authorization: Bearer: consultá Conectar comercios.

Cómo pasa un cobro de un estado a otro.

  • draft pasa a pending cuando le cargás el monto con PATCH.
  • Si ya entró plata y falta menos de $1, el cobro cuenta como total_payment; sin pagos sigue abierto.
  • pending pasa a total_payment con un pago total, a partial_payment con un pago parcial y a overdue si vence sin pagos.
  • partial_payment pasa a total_payment cuando se paga el saldo y a partial_overdue si vence antes.
  • draft y pending pasan a canceled cuando los cancelás, si no recibieron ningún pago.
  • Un cobro con pagos pasa a partially_refunded o refunded cuando se lo devuelve, en parte o por completo.

1Creá un cobro

Identificás cada cobro con tu referenceId, que vuelve en cada respuesta y evento.

En tu backend, creá el cobro en Sandbox:

cURL

curl -X POST https://integration-api.test.chytapay.com.ar/payment-request \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "referenceId": "factura-1001",
    "amount": 25000,
    "description": "Cuota de octubre",
    "dueDates": ["<FECHA_1>", "<FECHA_2>"],
    "surcharge": { "type": "percentage", "value": 10 },
    "conciliationType": "cuil",
    "sendWhatsappNotification": false,
    "sendEmailNotification": true,
    "customer": {
      "name": "Nombre Apellido",
      "email": "[email protected]",
      "taxDocument": "20000000001"
    },
    "metadata": { "orden": "1001" }
  }'

Node

const BASE = 'https://integration-api.test.chytapay.com.ar';

const res = await fetch(`${BASE}/payment-request`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${idToken}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    referenceId: 'factura-1001',
    amount: 25000,
    description: 'Cuota de octubre',
    dueDates: ['<FECHA_1>', '<FECHA_2>'],
    surcharge: { type: 'percentage', value: 10 },
    conciliationType: 'cuil',
    sendWhatsappNotification: false,
    sendEmailNotification: true,
    customer: {
      name: 'Nombre Apellido',
      email: '[email protected]',
      taxDocument: '20000000001',
    },
    metadata: { orden: '1001' },
  }),
});
const cobro = await res.json(); // 201

Python

import requests

BASE = "https://integration-api.test.chytapay.com.ar"

res = requests.post(
    f"{BASE}/payment-request",
    headers={"Authorization": f"Bearer {id_token}"},
    json={
        "referenceId": "factura-1001",
        "amount": 25000,
        "description": "Cuota de octubre",
        "dueDates": ["<FECHA_1>", "<FECHA_2>"],
        "surcharge": {"type": "percentage", "value": 10},
        "conciliationType": "cuil",
        "sendWhatsappNotification": False,
        "sendEmailNotification": True,
        "customer": {
            "name": "Nombre Apellido",
            "email": "[email protected]",
            "taxDocument": "20000000001",
        },
        "metadata": {"orden": "1001"},
    },
)
cobro = res.json()  # 201

Respuesta

{
  "type": "standard",
  "paymentRequestId": "<uuid del cobro>",
  "referenceId": "factura-1001",
  "amount": 25000,
  "description": "Cuota de octubre",
  "dueDates": ["<FECHA_1>", "<FECHA_2>"],
  "state": "pending",
  "surcharge": { "type": "percentage", "value": 10 },
  "calculatedAmounts": [
    { "dueDate": "<FECHA_1>", "amount": 25000, "isOriginalAmount": true },
    { "dueDate": "<FECHA_2>", "amount": 27500, "surchargeApplied": 2500 }
  ],
  "sendWhatsappNotification": false,
  "sendEmailNotification": true,
  "metadata": { "orden": "1001" },
  "customer": {
    "name": "Nombre Apellido",
    "email": "[email protected]",
    "taxDocument": "20000000001"
  },
  "CVU": "<CVU de 22 dígitos>",
  "bankAccountInfo": {
    "accountHolderName": "<titular de la cuenta de comercio>",
    "accountHolderTaxId": "<CUIT del titular>",
    "bankName": "<banco>"
  }
}

Reemplazá <FECHA_1> y <FECHA_2> por fechas futuras dentro de 35 días (por ejemplo, hoy +7 y +14 días). El ejemplo concilia por CUIL, el modo recomendado: antes, validá el documento.

Qué devuelve

201 con paymentRequestId, state (pending, o draft sin monto), CVU y bankAccountInfo (adónde transfiere el pagador) y, con monto y 2 fechas, calculatedAmounts (cada vencimiento con recargo). No devuelve conciliationType ni un surcharge de tipo none; en modo CVU omite customer.taxDocument.

2Consultá un cobro

Pedilo con GET /payment-request/{referenceId}:

cURL
curl https://integration-api.test.chytapay.com.ar/payment-request/factura-1001 \
  -H "Authorization: Bearer $ID_TOKEN"

Devuelve el cobro con state actualizado; omite customer.taxDocument, conciliationType y las fechas de aviso. Si no existe para la cuenta de comercio, da 404. GET y PATCH devuelven dueDates y calculatedAmounts[].dueDate como fecha UTC, sin la hora: conservá el vencimiento enviado.

Qué incluye el listado

GET /payment-request?startDate=AAAA-MM-DD&endDate=AAAA-MM-DD lista cobros abiertos, con una excepción: también devuelve los checkouts que vencieron o se cancelaron sin pagador. Ambas fechas son opcionales. Los demás cobros pagados, vencidos o cancelados dejan de aparecer: seguí su estado con el GET por referenceId o los webhooks. Cada ítem trae status, amount (0 en borradores), dueDate (primer vencimiento en UTC), paidAmount, createdAt y customer sin taxDocument, con phoneNumber como string normalizado. status usa los estados públicos en inglés: draft, pending, partial_payment, total_payment, overdue, partial_overdue, canceled, refunded y partially_refunded.

3Duplicados y reintentos

No hay idempotency key: el referenceId es único por integración y cuenta de comercio, incluidos los cancelados. Repetirlo da 409 y no devuelve el cobro existente:

JSON
{
  "error": "ReferenceIdAlreadyExists",
  "message": "The provided reference ID already exists for this client",
  "referenceId": "factura-1001"
}

Usá el id de tu entidad (factura, pedido, cuota), no uno por intento. Si un create da timeout:

  1. Hacé GET /payment-request/{referenceId}. Si da 200, el cobro se creó: usá esa respuesta.
  2. Si da 404, reintentá el POST con el mismo cuerpo y referenceId.
  3. Si el POST da 409, otro intento lo creó: volvé al paso 1.

Si el cobro del GET no coincide con lo que querías crear, reusaste por error un referenceId viejo.

4Cancelá un cobro

Usá POST /payment-request/{referenceId}/cancel, sin cuerpo:

cURL

curl -X POST https://integration-api.test.chytapay.com.ar/payment-request/factura-1001/cancel \
  -H "Authorization: Bearer $ID_TOKEN"

Respuesta

{ "referenceId": "factura-1001", "state": "canceled" }

Solo se cancelan cobros en draft o pending sin pagos: si no, 409 PaymentRequestCannotBeCanceled. No genera evento: la respuesta 200 es la confirmación. Si después entra una transferencia, llega payment.unmatched con reason: "canceled", que es opt-in.

Campos

Los campos de un cobro estándar. Los opcionales que no usás se omiten, no van en null. Los campos desconocidos se ignoran; los prohibidos por nombre dan 400.

Campo Obligatorio Reglas
referenceId Sí 8 a 100 caracteres: letras, números, - y _. Ver duplicados.
description Sí La ve el pagador. 3 a 500 caracteres.
dueDates Sí 1 o 2 fechas: ver vencimientos.
customer Sí El pagador (tabla de abajo).
amount No Pesos, mayor a 0, mandá hasta 2 decimales, tope 200.000.000. Sin él, nace borrador.
surcharge Con monto y 2 fechas { "type", "value" } del segundo vencimiento. Prohibido sin amount.
sendWhatsappNotification No Avisos por WhatsApp. Si lo omitís con monto, rige lo que elegiste en el Portal (WhatsApp). Sin monto, solo false.
sendEmailNotification Con monto Avisos por email. Sin monto, solo false.
sendWhatsappPaymentConfirmation No Ver confirmación de pago.
reminderNotificationDate No YYYY-MM-DD. Reemplaza el recordatorio previo. Solo con monto.
deferredInitialNotificationDate No YYYY-MM-DD. Difiere el aviso inicial. Solo con monto.
conciliationType No cvu (default) o cuil, el recomendado.
webhookUrl No HTTPS, hasta 2048 caracteres. Reemplaza la URL de webhook para este cobro.
metadata No Strings: hasta 50 claves de 40 caracteres, valores de 500, 8 KB. Vuelve en el GET y los eventos.
Campo de customer Obligatorio Reglas
name Sí 2 a 150 caracteres.
taxDocument Con conciliationType: "cuil" CUIL de 11 dígitos o DNI de 7 u 8. En modo CVU se ignora.
email Con sendEmailNotification: true Válido, hasta 320 caracteres.
phoneNumber Con sendWhatsappNotification: true { "countryCode": "+54", "number": "..." }, 6 a 20 caracteres: dígitos, espacios y + - ( ).

Fuera de estas reglas, 400 con el detalle: consultá los errores.

Vencimientos y recargo

dueDates lleva 1 o 2 fechas, posteriores a hoy y a 35 días o menos:

Formato Ejemplo Vence
YYYY-MM-DD "<FECHA_1>" Al terminar ese día, hora Argentina.
YYYY-MM-DDTHH:MM[:SS] "<FECHA_1>T18:30" En ese minuto. Sin zona es -03:00; acepta Z o un offset. Al menos 3 minutos en el futuro.

En borradores con hora, mandá siempre el offset explícito (por ejemplo, <FECHA_1>T18:30-03:00). Entre las 21 y las 24 de Argentina, la validación toma el día UTC: usá pasado mañana para una fecha sin hora.

Con hora, el cobro es de corta duración. Con 2 fechas, el segundo elemento de dueDates lleva surcharge, obligatorio con monto (Chytapay no las reordena). Se debe amount hasta el primer vencimiento y amount más el recargo desde el día siguiente.

type es percentage (0 a 100, sobre el monto), fixed (pesos, hasta 200.000.000) o none (value: 0). Con amount: 25000 y { "type": "percentage", "value": 10 }, el segundo vencimiento es de 27.500, en calculatedAmounts.

Cobros de corta duración

Para reservas, entradas o cualquier cobro que tiene que cerrarse en un horario, mandá el vencimiento con hora: "<FECHA_1>T18:30". Cambia varias cosas respecto de una fecha sola:

Fecha sola Con hora
Vence Al terminar ese día, hora Argentina En ese minuto. Sin zona es -03:00
Vencimientos 1 o 2, con recargo en el segundo Uno solo, sin recargo
Evento al vencer Ninguno payment_request.expired
Avisos al pagador Recordatorio un día antes Con menos de 24 horas no hay recordatorio previo: quedan el inicial y el del día
En bulk Sí No, da 400

Si el cobro se paga en el widget, el cobro de checkout vence entre 3 y 60 minutos.

Borrador sin monto

Sin amount, el cobro nace en draft, con un vencimiento, sin recargo ni avisos (flags solo en false, fechas de aviso prohibidas). Su CVU no recibe pagos para ese borrador: una transferencia queda sin imputar (no_pending). Completalo con PATCH /payment-request/{referenceId}:

cURL

curl -X PATCH https://integration-api.test.chytapay.com.ar/payment-request/factura-1002 \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 18000 }'

Respuesta

{
  "referenceId": "factura-1002",
  "previousAmount": null,
  "amount": 18000,
  "description": "Inscripción",
  "state": "pending",
  "dueDates": ["<FECHA_1>"]
}
  • Solo en draft sin pagos; si no, 409.
  • Pasa a pending. Un borrador no envía avisos, ni después del PATCH.
  • Si no lo completás, Chytapay lo descarta a partir de una hora sin modificaciones.

Estados

Create, GET, cancel, PATCH y bulk devuelven state, y los eventos payment_request.*, stateType. No existe PAID: es total_payment.

Estado Significa
draft Borrador sin monto.
pending Esperando el pago.
partial_payment Pago parcial, queda saldo.
total_payment Pagado.
overdue Venció sin pagos.
partial_overdue Venció con un pago parcial.
canceled Cancelado.
refunded Devuelto por completo. Ver devoluciones.
partially_refunded Devuelto en parte.

Con 2 fechas, el cobro sigue abierto hasta el segundo vencimiento. Una fecha por día vence a las 00:00 hora Argentina, sin evento; una con hora vence en ese minuto con payment_request.expired. Consultá los eventos en Webhooks.

Avisos al pagador

Un aviso es un WhatsApp o email de Chytapay al pagador (no un evento a tu backend). Los canales se eligen en los campos, solo con monto. Con un pago parcial los recordatorios salen con el saldo; pagado, vencido o cancelado, no salen.

Cuándo sale cada aviso

Los recordatorios salen a las 10, hora Argentina.

Ver el calendario de avisos
Aviso Cuándo Monto
Inicial Al crear, o el día de deferredInitialNotificationDate Primer vencimiento, sin recargo
Recordatorio previo Un día antes del primer vencimiento, o el día de reminderNotificationDate Primer vencimiento, sin recargo
Día del vencimiento Ese día Primer vencimiento, sin recargo
Primer vencimiento pasado (2 fechas) El día siguiente Segundo vencimiento, con recargo
Día del segundo vencimiento (2 fechas) Ese día Segundo vencimiento, con recargo

Las dos fechas son posteriores a hoy y anteriores al primer vencimiento, y reminderNotificationDate además posterior a deferredInitialNotificationDate.

Confirmación de pago

El aviso de pago recibido por WhatsApp lo controla sendWhatsappPaymentConfirmation, no sendWhatsappNotification:

Valor Modo CVU Modo CUIL
Omitido Sale, en pagos totales y parciales No sale
true Sale, en pagos totales y parciales Solo en el pago total
false No sale No sale

En modo CUIL el pagador puede pedirla con "Recibir confirmación de pago", y false no lo apaga.

Creá cobros en bulk

POST /payment-request/bulk crea de 1 a 100 cobros, todos o ninguno. Cada ítem es un cobro individual, salvo que:

  • Siempre concilia por CUIL: customer.taxDocument es obligatorio.
  • El mismo documento dos veces en el lote da 422.
  • Una fecha con hora da 400.
  • No mandes reminderNotificationDate ni deferredInitialNotificationDate: no están soportadas en bulk.
  • conciliationType, type, expiresInMinutes y webhookUrl están prohibidos: los eventos van a la URL de la integración.

En tu backend, mandá el lote:

cURL

curl -X POST https://integration-api.test.chytapay.com.ar/payment-request/bulk \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentRequests": [
      {
        "referenceId": "cuota-2026-10-001",
        "amount": 12000,
        "description": "Cuota de octubre",
        "dueDates": ["<FECHA_1>"],
        "sendWhatsappNotification": false,
        "sendEmailNotification": false,
        "customer": { "name": "Nombre Apellido", "taxDocument": "20000000001" }
      },
      {
        "referenceId": "cuota-2026-10-002",
        "amount": 12000,
        "description": "Cuota de octubre",
        "dueDates": ["<FECHA_1>"],
        "sendWhatsappNotification": false,
        "sendEmailNotification": false,
        "customer": { "name": "Otro Pagador", "taxDocument": "30405060" }
      }
    ]
  }'

Respuesta

{
  "batchId": "<uuid del lote>",
  "total": 2,
  "created": 2,
  "sharedCvu": "<CVU de 22 dígitos>",
  "accountInfo": {
    "accountHolderName": "<titular de la cuenta de comercio>",
    "accountHolderTaxId": "<CUIT del titular>",
    "bankName": "<banco>"
  },
  "paymentRequests": [
    { "referenceId": "cuota-2026-10-001", "state": "pending" },
    { "referenceId": "cuota-2026-10-002", "state": "pending" }
  ]
}

paymentRequests sigue el orden del request; el detalle se consulta con GET.

Ver las respuestas del bulk
Código Cuándo
201 Se crearon todos: guardá batchId.
400 Array vacío, más de 100 ítems o un campo fuera de contrato.
422 errors: [{ index, referenceId, reason }] con todos los ítems que fallaron: corregilos y reenviá el lote.
429 El lote no entra entero en el tope de mensajes de WhatsApp: no se crea nada. Consultá los límites.

Con 400, 422 o 429 no se crea nada. Algunos reason, en inglés: Duplicate referenceId within the batch, The referenceId already exists for this integration, The taxDocument does not resolve to a valid CUIL y Duplicate payer within the batch: ....

sendWhatsappPaymentConfirmation vale para todo el lote y, si los ítems difieren, gana false. Los canales de aviso, al revés: si un ítem activa WhatsApp o email, queda activo para el lote.

Cobro de checkout

Un cobro type: "checkout" se paga desde el widget: consultá el cobro de checkout.

Seguí por acá