# Endpoints

<span>Aquí te detallamos los </span>**endpoints**<span> (puntos de conexión específicos) diseñados para interactuar con la información de cobros de la inmobiliaria.</span>

En este apartado te explicaremos paso a paso cómo usar servicios clave, como por ejemplo:

- **Obtener cartera:**<span> Cómo consultarle a nuestro sistema cuáles son las deudas, facturas o saldos pendientes de los clientes.</span>
- **Registrar pagos:**<span> Cómo notificarle a nuby automáticamente en el instante en que un pago se ha procesado con éxito en tu plataforma.</span>

# Registrar Pago

Permite a una pasarela de pagos autenticada (por ejemplo, Palomma) **registrar el pago** de uno o varios ítems pagables en la inmobiliaria: una factura, un movimiento (concepto / interés) o una factura junto con sus intereses adjuntos. El endpoint es idempotente, devuelve un cuerpo en formato de arreglo y aplica automáticamente la configuración de la pasarela (forma de pago, envío DIAN).

<p class="callout info">**¿Para qué sirve este servicio?**  
Está pensado exclusivamente para integraciones con pasarelas de pago. La pasarela lo invoca cuando el deudor confirma el pago en línea de los ítems devueltos previamente por **GET /gateways/portfolio**. El sistema persiste el pago, genera el recibo de caja y, cuando la configuración lo requiera, dispara el envío del documento electrónico a la DIAN.</p>

<p class="callout danger">**Endpoint restringido a pasarelas registradas**  
Este endpoint **solo** puede ser consumido por clientes OAuth2 cuyo `client_name` esté registrado como una pasarela activa en la inmobiliaria. Cualquier otro cliente recibirá `403 Forbidden`.</p>

---

#### **1. El Endpoint (La dirección web)**

Apunta tu sistema a la siguiente dirección. Recuerda reemplazar <span style="color: rgb(241, 196, 15);">**{{instancia}}**</span> por la dirección web completa que utilizas para ingresar a tu plataforma.

```http
POST https://{{instancia}}/service/v2/public/gateways/payments
```

<p class="callout info">**¿Qué debes colocar en {{instancia}}?**  
Es muy sencillo: corresponde a la **dirección web principal** que utilizas a diario para ingresar a tu plataforma (incluyendo la terminación `.nuby.app` o `.arrendasoft.co`).  
Por ejemplo, si para entrar a tu sistema escribes `inmobiliaria.nuby.app` o `inmobiliaria.arrendasoft.co` en tu navegador, esa será exactamente tu instancia. Solo asegúrate de no incluir el "https://" ni barras diagonales ("/") al final.</p>

#### **2. La Petición (¿Qué debes enviarnos?)**

Debes enviar una petición **POST** con el detalle del pago en el cuerpo de la solicitud en formato JSON. La pasarela se identifica automáticamente a partir del `client_name` del token; **no debes enviar el nombre de la pasarela ni la forma de pago en el body**: ambos se derivan de la configuración registrada.

<table border="1" id="bkmrk-m%C3%A9todo-post-content-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 20%;"></col><col style="width: 80%;"></col></colgroup><tbody><tr><td>**Método**</td><td>POST</td></tr><tr><td>**Content-Type**</td><td>application/json</td></tr><tr><td>**Authorization**</td><td>**Bearer token**, Token obtenido al consumir el servicio **Login** con un cliente OAuth2 registrado como pasarela activa.</td></tr></tbody></table>

<p class="callout danger">**Autenticación requerida**  
Este servicio requiere un Token de autenticación válido. Debes incluir el encabezado `Authorization: Bearer TU_TOKEN` en cada petición. El token se obtiene consumiendo el servicio de **Login**. El cliente OAuth además debe estar registrado como pasarela activa; en caso contrario el endpoint responderá `403`.</p>

**Cuerpo de la petición (Body):**

<table border="1" id="bkmrk-campo-tipo-requerido" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 14%;"></col><col style="width: 50%;"></col></colgroup><thead><tr><th>Campo</th><th>Tipo</th><th>Requerido</th><th>Descripción</th></tr></thead><tbody><tr><td>**factura\_id**</td><td>integer</td><td>Condicional</td><td>Identificador de la factura a pagar. Debe existir en el sistema. Es **obligatorio** si no se envía `movimiento_id`.</td></tr><tr><td>**movimiento\_id**</td><td>integer | array</td><td>Condicional</td><td>Identificador (o lista de identificadores) de los movimientos a pagar. Es **obligatorio** si no se envía `factura_id`. Cuando se envía sin `factura_id`, por defecto solo se admite **un único** movimiento; sin embargo, si la pasarela tiene activo el filtro **«Agrupar conceptos del mismo periodo»**, se aceptan **varios movimientos del mismo tercero** y se procesan en **modo GROUP** (un solo recibo de caja + cuando aplique una factura agrupada por contrato).</td></tr><tr><td>**monto**</td><td>number</td><td>Sí</td><td>Valor pagado por el deudor. Debe ser un número mayor a cero. Si excede el saldo adeudado, el excedente se registra como **anticipo**.</td></tr><tr><td>**fecha\_pago**</td><td>string</td><td>Sí</td><td>Fecha y hora del pago. Formato estricto: `YYYY-MM-DD HH:MM:SS`.</td></tr><tr><td>**n\_comprobante**</td><td>string</td><td>No</td><td>Número de comprobante de la pasarela. Máximo **100** caracteres. Solo letras, números, guiones, guiones bajos, puntos y espacios. **Recomendado**: garantiza la idempotencia ante reintentos.</td></tr></tbody></table>

<p class="callout info">**Modos de operación**  
El endpoint clasifica el pago en uno de cuatro modos según los campos enviados y la configuración de la pasarela:  
— **Modo movimiento único**: `factura_id = null` y `movimiento_id = [n]`.  
— **Modo GROUP**: `factura_id = null` y `movimiento_id = [m1, m2, ...]` con varios movimientos del mismo tercero. Solo se admite cuando la pasarela tiene activo el filtro avanzado **«Agrupar conceptos del mismo periodo»**. Genera un único recibo de caja y, cuando aplique, una factura agrupada por contrato.  
— **Modo factura**: `factura_id = X` y `movimiento_id = []` o ausente.  
— **Modo factura + intereses adjuntos**: `factura_id = X` y `movimiento_id = [m1, m2, ...]`. Los movimientos que pertenezcan a la factura X se ignoran (ya están cubiertos); los demás se procesan como intereses sueltos en la misma transacción.</p>

**Ejemplo 1: Pago de una sola factura**

```json
{
    "factura_id": 4521,
    "monto": 1750000.00,
    "fecha_pago": "2026-04-25 14:30:00",
    "n_comprobante": "PALM-2026-04-25-998877"
}
```

**Ejemplo 2: Pago de un movimiento (concepto suelto)**

```json
{
    "movimiento_id": 98515,
    "monto": 280000.00,
    "fecha_pago": "2026-04-25 14:30:00",
    "n_comprobante": "PALM-2026-04-25-998878"
}
```

**Ejemplo 3: Pago de una factura junto con sus intereses adjuntos**

```json
{
    "factura_id": 4521,
    "movimiento_id": [98410, 98411],
    "monto": 1820000.00,
    "fecha_pago": "2026-04-25 14:30:00",
    "n_comprobante": "PALM-2026-04-25-998879"
}
```

#### **3. La Respuesta (¿Qué te entregaremos?)**

El sistema responde con HTTP `200` cuando el pago se registra correctamente. La clave `body` es **siempre un arreglo**, incluso cuando el pago resulta en un solo registro. Cada elemento del arreglo describe un documento generado por la operación: la factura pagada, una factura nueva creada por intereses facturables, o un anticipo si hubo excedente.

```json
{
    "success": true,
    "status": 200,
    "message": "El pago fue registrado exitosamente.",
    "body": [
        {
            "factura_id": 4521,
            "movimiento_id": null,
            "pago_id": 87123,
            "recibo_id": 56412,
            "documento_contable_id": 102345,
            "monto_pagado": 1750000.00,
            "fecha_pago": "2026-04-25 14:30:00",
            "forma_pago_id": 7,
            "forma_pago": "Pasarela Palomma",
            "n_comprobante": "PALM-2026-04-25-998877",
            "saldo_anterior": 1750000.00,
            "saldo_actual": 0.00,
            "estado": "pagada",
            "mensaje": "El pago cubrió el total de la factura.",
            "estado_dian": "pendiente",
            "gateway": "palomma",
            "client_name": "palomma_inmobiliaria_xyz"
        }
    ],
    "alertas": [],
    "data": {
        "factura_id": 4521,
        "pago_id": 87123,
        "recibo_id": 56412,
        "documento_contable_id": 102345,
        "confirm_pay_id": 9921,
        "gateway": "palomma",
        "client_name": "palomma_inmobiliaria_xyz",
        "documentos_generados": [
            { "tipo": "recibo", "id": 56412, "numero": "REC-56412" },
            { "tipo": "factura", "id": 4521, "numero": "FAC-4521" }
        ]
    }
}
```

**Ejemplo de respuesta con excedente (anticipo):**

```json
{
    "success": true,
    "status": 200,
    "message": "El pago fue registrado exitosamente.",
    "body": [
        {
            "factura_id": 4521,
            "movimiento_id": null,
            "pago_id": 87124,
            "recibo_id": 56413,
            "documento_contable_id": 102346,
            "monto_pagado": 1750000.00,
            "fecha_pago": "2026-04-25 14:30:00",
            "forma_pago_id": 7,
            "forma_pago": "Pasarela Palomma",
            "n_comprobante": "PALM-2026-04-25-998880",
            "saldo_anterior": 1750000.00,
            "saldo_actual": 0.00,
            "estado": "pagada",
            "mensaje": "El pago cubrió el total de la factura.",
            "estado_dian": "pendiente",
            "gateway": "palomma",
            "client_name": "palomma_inmobiliaria_xyz"
        },
        {
            "factura_id": null,
            "movimiento_id": null,
            "pago_id": null,
            "recibo_id": null,
            "documento_contable_id": 102347,
            "monto_pagado": 50000.00,
            "fecha_pago": "2026-04-25 14:30:00",
            "forma_pago_id": 7,
            "forma_pago": "Pasarela Palomma",
            "n_comprobante": "PALM-2026-04-25-998880",
            "saldo_anterior": null,
            "saldo_actual": null,
            "estado": "anticipo",
            "mensaje": "Se generó un anticipo por valor de 50000.00 con el excedente del pago.",
            "estado_dian": null,
            "gateway": "palomma",
            "client_name": "palomma_inmobiliaria_xyz"
        }
    ],
    "alertas": [],
    "data": {
        "factura_id": 4521,
        "pago_id": 87124,
        "recibo_id": 56413,
        "documento_contable_id": 102346,
        "anticipo_documento_contable_ids": [102347],
        "confirm_pay_id": 9922,
        "gateway": "palomma",
        "client_name": "palomma_inmobiliaria_xyz"
    }
}
```

##### **Campos de la respuesta**

<table border="1" id="bkmrk-clave-descripci%C3%B3n-su" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>**Clave**</th><th>**Descripción**</th></tr></thead><tbody><tr><td>**success**</td><td>Indica si la operación fue exitosa (`true`) o no (`false`).</td></tr><tr><td>**status**</td><td>Código HTTP devuelto.</td></tr><tr><td>**message**</td><td>Mensaje descriptivo del resultado.</td></tr><tr><td>**body**</td><td>Arreglo con uno o más registros generados por la operación (ver tabla detallada abajo).</td></tr><tr><td>**alertas**</td><td>Arreglo de alertas no bloqueantes generadas durante el proceso (por ejemplo, fallas en el envío DIAN).</td></tr><tr><td>**data**</td><td>Objeto con la información consolidada del pago (IDs principales, identificador de confirmación, gateway, cliente). Adicionalmente puede incluir `documentos_generados`: un arreglo de objetos `{tipo, id, numero}` que lista todos los documentos creados u afectados por el pago (recibo de caja y factura(s)). El campo `tipo` puede ser `"recibo"` o `"factura"`; `numero` es el consecutivo legible para el usuario final.</td></tr><tr><td>**error\_code**</td><td>Solo presente en respuestas de error. Códigos posibles: `"DUPLICATE_PAYMENT"`, `"VALIDATION_ERROR"`, `"GATEWAY_NOT_FOUND"`, `"GATEWAY_NOT_ACTIVE"`, `"GATEWAY_PAYMENT_METHOD_REQUIRED"`, `"INTERNAL_ERROR"`.</td></tr><tr><td>**duplicate**</td><td>Solo presente en respuestas `409 Conflict`. Contiene el snapshot del pago previamente registrado.</td></tr></tbody></table>

##### **Estructura de cada elemento de `body[]`**

<table border="1" id="bkmrk-clave-descripci%C3%B3n-fa" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>**Clave**</th><th>**Descripción**</th></tr></thead><tbody><tr><td>**factura\_id**</td><td>Identificador de la factura pagada o generada. `null` para registros de anticipo.</td></tr><tr><td>**movimiento\_id**</td><td>Identificador (o arreglo de IDs) de los movimientos asociados al registro. Puede ser `null`.</td></tr><tr><td>**pago\_id**</td><td>Identificador del pago creado en el sistema. `null` para anticipos.</td></tr><tr><td>**recibo\_id**</td><td>Identificador del recibo de caja generado.</td></tr><tr><td>**documento\_contable\_id**</td><td>Identificador del documento contable asociado (recibo, anticipo, etc.).</td></tr><tr><td>**monto\_pagado**</td><td>Valor efectivamente aplicado a la factura/movimiento. `null` para anticipos sin asignación específica.</td></tr><tr><td>**fecha\_pago**</td><td>Fecha y hora del pago. Formato: `YYYY-MM-DD HH:MM:SS`.</td></tr><tr><td>**forma\_pago\_id**</td><td>Identificador interno de la forma de pago contable (derivada de la configuración de la pasarela).</td></tr><tr><td>**forma\_pago**</td><td>Nombre legible de la forma de pago.</td></tr><tr><td>**n\_comprobante**</td><td>Número de comprobante enviado por la pasarela.</td></tr><tr><td>**saldo\_anterior**</td><td>Saldo de la factura/movimiento antes de aplicar el pago.</td></tr><tr><td>**saldo\_actual**</td><td>Saldo restante después de aplicar el pago.</td></tr><tr><td>**estado**</td><td>Estado del registro tras la operación. Valores posibles: - `"pagada"`: el monto cubrió el saldo total de la factura (`saldo_actual = 0`).
- `"pendiente"`: el monto fue inferior al saldo y la factura sigue con saldo pendiente. El campo `mensaje` describe el saldo restante.
- `"anticipo"`: la fila representa un excedente registrado como anticipo (sin factura asociada).

</td></tr><tr><td>**mensaje**</td><td>Mensaje legible que describe el resultado del pago a nivel de cada fila. Valores típicos: - `"El pago cubrió el total de la factura."` cuando `estado = "pagada"`.
- `"El pago fue registrado parcialmente. La factura aún tiene un saldo pendiente de {monto}."` cuando `estado = "pendiente"`.
- `"Se generó un anticipo por valor de {monto} con el excedente del pago."` cuando `estado = "anticipo"`.

</td></tr><tr><td>**estado\_dian**</td><td>Estado del envío DIAN. Valores posibles: `"pendiente"` (envío en cola o fallido) o `null` (no aplica). El detalle del fallo se reporta en `alertas`.</td></tr><tr><td>**gateway**</td><td>Slug de la pasarela autenticada (por ejemplo, `"palomma"`).</td></tr><tr><td>**client\_name**</td><td>Nombre del cliente OAuth2 autenticado.</td></tr></tbody></table>

##### **Escenarios especiales del cuerpo**

<table border="1" id="bkmrk-escenario-comportami" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 30%;"></col><col style="width: 70%;"></col></colgroup><thead><tr><th>Escenario</th><th>Comportamiento del `body`</th></tr></thead><tbody><tr><td>**Pago simple**</td><td>Un único elemento que describe el recibo generado.</td></tr><tr><td>**Intereses facturables**</td><td>Al menos dos elementos: uno por la factura original pagada y uno por la factura nueva creada para los intereses facturables.</td></tr><tr><td>**Excedente / anticipo**</td><td>Cuando el monto pagado supera el saldo de los ítems enviados, el sistema genera un **anticipo** con el sobrante. La respuesta incluirá una fila adicional con `estado = "anticipo"`, `documento_contable_id` poblado, `monto_pagado` con el valor del anticipo, y los campos `fecha_pago`, `forma_pago_id`, `forma_pago` y `n_comprobante` heredados del pago original. Los campos `factura_id`, `movimiento_id`, `pago_id`, `recibo_id`, `saldo_anterior` y `saldo_actual` serán `null`. **Importante:** el sistema garantiza que primero se cubre el saldo de cada item enviado (factura y/o movimientos) y solo el sobrante real se transforma en anticipo; nunca se desvía a anticipo el monto destinado a un movimiento explícitamente declarado.</td></tr><tr><td>**Falla en envío DIAN**</td><td>El pago queda persistido (`estado = "pagada"`), `estado_dian = "pendiente"`, y `alertas` contiene una entrada `{ "level": "warning", "code": "DIAN_SEND_FAILED", "message": "...", "factura_id": ... }`.</td></tr></tbody></table>

#### **4. Idempotencia**

El endpoint detecta automáticamente reintentos de un mismo pago y responde `409 Conflict` sin volver a registrarlo. Las reglas de idempotencia son:

<table border="1" id="bkmrk-disparador-comportam" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 30%;"></col><col style="width: 70%;"></col></colgroup><thead><tr><th>Disparador</th><th>Comportamiento</th></tr></thead><tbody><tr><td>**Mismo `n_comprobante`**</td><td>Cuando el `n_comprobante` ya fue registrado para la misma pasarela, se devuelve `409` con el registro previo.</td></tr><tr><td>**Sin `n_comprobante`, mismos datos esenciales**</td><td>Si los datos `(factura_id, movimiento_ids ordenados, monto, fecha_pago, client_name)` coinciden con un pago previo, se devuelve `409`.</td></tr><tr><td>**`n_comprobante` distinto, mismos datos esenciales**</td><td>Se aplica un hash de respaldo sobre los datos esenciales y se devuelve `409` si coincide con un pago previo.</td></tr></tbody></table>

<p class="callout info">**Cómo interpretar el 409**  
Una respuesta `409 Conflict` de este endpoint **no es un fallo**: significa que el pago **ya existe** en nuby. La pasarela debe tratarla como confirmación idempotente y **no reintentar**. El cuerpo incluye `error_code = "DUPLICATE_PAYMENT"`, `is_business_error = true` y, en `data`, los identificadores del pago original (`confirm_pay_id`, `confirm_pay_reference_code`, `factura_id`, `pago_id`, `recibo_id`, `documento_contable_id`).</p>

**Ejemplo de respuesta 409:**

```json
{
    "success": false,
    "status": 409,
    "message": "El pago ya fue registrado previamente para la pasarela palomma_inmobiliaria_xyz.",
    "body": [ /* snapshot del body original */ ],
    "error_code": "DUPLICATE_PAYMENT",
    "data": {
        "factura_id": 4521,
        "pago_id": 87123,
        "recibo_id": 56412,
        "documento_contable_id": 102345,
        "confirm_pay_id": 9921,
        "confirm_pay_reference_code": "PALM-2026-04-25-998877",
        "gateway": "palomma",
        "client_name": "palomma_inmobiliaria_xyz"
    },
    "duplicate": {
        "payload": { /* payload original recibido en el primer intento */ }
    }
}
```

#### **5. Seguridad y Posibles Errores**

El sistema realiza validaciones de autenticación, autorización por pasarela, configuración y consistencia del payload. Si alguna falla, devolverá un error con su respectivo código HTTP. La estructura siempre incluye `success: false`, `status`, `message` y, cuando aplica, `error_code`.

<table border="1" id="bkmrk-c%C3%B3digo-http-descripc" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 12%;"></col><col style="width: 88%;"></col></colgroup><thead><tr><th>Código HTTP</th><th>Descripción</th></tr></thead><tbody><tr><td>**400**</td><td>**Token faltante o inválido.** Posibles causas:  
— No se envió el encabezado `Authorization`.  
— El encabezado no tiene el formato `Bearer {token}`.  
— El token no fue encontrado en el sistema.</td></tr><tr><td>**401**</td><td>El token ha expirado. Debes generar uno nuevo consumiendo el servicio de **Login**.</td></tr><tr><td>**403**</td><td>El cliente OAuth autenticado no está registrado como pasarela activa en la inmobiliaria.</td></tr><tr><td>**409**</td><td>**Pago duplicado.** El pago ya fue registrado previamente. `error_code = "DUPLICATE_PAYMENT"`. La pasarela debe tratar la respuesta como confirmación idempotente y **no reintentar**. Ver sección de Idempotencia.</td></tr><tr><td>**422**</td><td>**Error de validación.** `error_code = "VALIDATION_ERROR"`. Posibles causas:  
— No se envió ni `factura_id` ni `movimiento_id`.  
— `factura_id` no es un entero positivo o no existe.  
— Algún `movimiento_id` no es un entero positivo o no existe.  
— Sin `factura_id` se envió más de un `movimiento_id`.  
— `monto` no es numérico o es menor o igual a cero.  
— `fecha_pago` no cumple el formato `YYYY-MM-DD HH:MM:SS`.  
— `n_comprobante` supera los 100 caracteres o contiene caracteres no permitidos.  
— La `factura_id` y los `movimiento_id` pertenecen a terceros distintos.  
— La pasarela no tiene una forma de pago configurada (`error_code = "GATEWAY_PAYMENT_METHOD_REQUIRED"`).  
— El cliente no corresponde a una pasarela registrada (`error_code = "GATEWAY_NOT_FOUND"`) o la pasarela está inactiva (`error_code = "GATEWAY_NOT_ACTIVE"`).  
— Errores de negocio retornados por el motor de ingresos (por ejemplo, factura ya pagada).</td></tr><tr><td>**500**</td><td>Error interno al registrar el pago. `error_code = "INTERNAL_ERROR"`. El detalle se registra en el canal de log `PASARELAS`.</td></tr></tbody></table>

---

#### **6. Ejemplos de integración**

Aquí tienes ejemplos de código listos para que tus desarrolladores los adapten:

<details id="bkmrk-curl-%23-pago-de-una-f"><summary>cURL</summary>

```bash
# Pago de una factura
curl -X POST "https://{{instancia}}/service/v2/public/gateways/payments" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TU_TOKEN_AQUI" \
-d '{
  "factura_id": 4521,
  "monto": 1750000.00,
  "fecha_pago": "2026-04-25 14:30:00",
  "n_comprobante": "PALM-2026-04-25-998877"
}'

# Pago de una factura junto con sus intereses adjuntos
curl -X POST "https://{{instancia}}/service/v2/public/gateways/payments" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TU_TOKEN_AQUI" \
-d '{
  "factura_id": 4521,
  "movimiento_id": [98410, 98411],
  "monto": 1820000.00,
  "fecha_pago": "2026-04-25 14:30:00",
  "n_comprobante": "PALM-2026-04-25-998879"
}'
```

</details><details id="bkmrk-php-%3C%3Fphp-%24instance-"><summary>PHP</summary>

```php
<?php

$instance = 'tu_instancia';
$token = 'TU_TOKEN_AQUI'; // Token de un cliente OAuth registrado como pasarela

$url = "https://{$instance}/service/v2/public/gateways/payments";

$payload = [
    'factura_id'     => 4521,
    'monto'          => 1750000.00,
    'fecha_pago'     => '2026-04-25 14:30:00',
    'n_comprobante'  => 'PALM-2026-04-25-998877',
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    "Authorization: Bearer {$token}",
]);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "Código HTTP: {$http_code}\n";
$data = json_decode($response, true);

if ($http_code === 200) {
    echo "Pago registrado. Recibo: {$data['data']['recibo_id']}\n";
} elseif ($http_code === 409) {
    echo "Pago ya existía previamente. confirm_pay_id: {$data['data']['confirm_pay_id']}\n";
    // No reintentar: el pago ya está registrado en nuby.
} else {
    echo "Error registrando el pago:\n{$response}\n";
}

?>
```

</details><details id="bkmrk-python-import-reques"><summary>Python</summary>

```python
import requests

# 1. Configura tus datos de acceso
instancia = 'mi-inmobiliaria.nuby.app'
token = 'TU_TOKEN_AQUI'  # Token de un cliente OAuth registrado como pasarela

# 2. Prepara el body del pago
url = f"https://{instancia}/service/v2/public/gateways/payments"

payload = {
    "factura_id": 4521,
    "monto": 1750000.00,
    "fecha_pago": "2026-04-25 14:30:00",
    "n_comprobante": "PALM-2026-04-25-998877",
}

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {token}",
}

# 3. Envía la petición POST y procesa la respuesta
try:
    response = requests.post(url, json=payload, headers=headers)
    print(f"Código HTTP: {response.status_code}")
    data = response.json()

    if response.status_code == 200:
        print(f"Pago registrado. Recibo: {data['data']['recibo_id']}")
    elif response.status_code == 409:
        print(f"Pago ya existía previamente. confirm_pay_id: {data['data']['confirm_pay_id']}")
        # No reintentar: el pago ya está registrado en nuby.
    else:
        print("Error registrando el pago.")
        print(response.text)
except Exception as e:
    print(f"Error de conexión: {e}")

```

</details><details id="bkmrk-javascript-%2F%2F-1.-con"><summary>JavaScript</summary>

```javascript
// 1. Configura tus datos de acceso
const instancia = 'mi-inmobiliaria.nuby.app';
const token = 'TU_TOKEN_AQUI'; // Token de un cliente OAuth registrado como pasarela

// 2. Prepara el body del pago
const url = `https://${instancia}/service/v2/public/gateways/payments`;

const payload = {
    factura_id: 4521,
    monto: 1750000.00,
    fecha_pago: '2026-04-25 14:30:00',
    n_comprobante: 'PALM-2026-04-25-998877',
};

// 3. Función para registrar el pago
async function registrarPago() {
    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`,
            },
            body: JSON.stringify(payload),
        });

        console.log(`Código HTTP: ${response.status}`);
        const data = await response.json();

        if (response.status === 200) {
            console.log(`Pago registrado. Recibo: ${data.data.recibo_id}`);
        } else if (response.status === 409) {
            console.log(`Pago ya existía previamente. confirm_pay_id: ${data.data.confirm_pay_id}`);
            // No reintentar: el pago ya está registrado en nuby.
        } else {
            console.log('Error registrando el pago.');
            console.log(data);
        }
    } catch (error) {
        console.error(`Error de conexión: ${error}`);
    }
}

registrarPago();

```

</details>

# Obtener Cartera

Permite a una pasarela de pagos autenticada (por ejemplo, Palomma) consultar la **cartera pagable** de un tercero: facturas pendientes, conceptos facturables, conceptos no facturables e intereses de mora adjuntos a facturas de canon. El sistema aplica automáticamente la configuración de filtros avanzados registrada para la pasarela autenticada.

<p class="callout info">**¿Para qué sirve este servicio?**  
Está pensado exclusivamente para integraciones con pasarelas de pago. La pasarela lo consume para mostrarle al deudor los ítems que puede pagar en línea (facturas, conceptos e intereses), respetando la configuración comercial de la inmobiliaria (qué incluir, qué excluir, qué resoluciones se admiten, etc.).</p>

<p class="callout danger">**Endpoint restringido a pasarelas registradas**  
Este endpoint **solo** puede ser consumido por clientes OAuth2 cuyo `client_name` esté registrado como una pasarela activa en la inmobiliaria. Cualquier otro cliente recibirá `403 Forbidden`.</p>

---

#### **1. El Endpoint (La dirección web)**

Apunta tu sistema a la siguiente dirección. Recuerda reemplazar <span style="color: rgb(241, 196, 15);">**{{instancia}}**</span> por la dirección web completa que utilizas para ingresar a tu plataforma.

```http
GET https://{{instancia}}/service/v2/public/gateways/portfolio
```

<p class="callout info">**¿Qué debes colocar en {{instancia}}?**  
Es muy sencillo: corresponde a la **dirección web principal** que utilizas a diario para ingresar a tu plataforma (incluyendo la terminación `.nuby.app` o `.arrendasoft.co`).  
Por ejemplo, si para entrar a tu sistema escribes `inmobiliaria.nuby.app` o `inmobiliaria.arrendasoft.co` en tu navegador, esa será exactamente tu instancia. Solo asegúrate de no incluir el "https://" ni barras diagonales ("/") al final.</p>

#### **2. La Petición (¿Qué debes enviarnos?)**

Este servicio requiere autenticación mediante un Token JWT obtenido por OAuth2. La pasarela se identifica automáticamente a partir del `client_name` del token; **no debes enviarlo en el body ni como query string**.

<table border="1" id="bkmrk-m%C3%A9todo-get-content-t" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 20%;"></col><col style="width: 80%;"></col></colgroup><tbody><tr><td>**Método**</td><td>GET</td></tr><tr><td>**Content-Type**</td><td>application/json</td></tr><tr><td>**Authorization**</td><td>**Bearer token**, Token obtenido al consumir el servicio **Login** con un cliente OAuth2 registrado como pasarela activa.</td></tr></tbody></table>

<p class="callout danger">**Autenticación requerida**  
Este servicio requiere un Token de autenticación válido. Debes incluir el encabezado `Authorization: Bearer TU_TOKEN` en cada petición. El token se obtiene consumiendo el servicio de **Login**. El cliente OAuth además debe estar registrado como pasarela activa; en caso contrario el endpoint responderá `403`.</p>

**Parámetros de consulta (query string):**

<table border="1" id="bkmrk-par%C3%A1metro-tipo-reque" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 24%;"></col><col style="width: 10%;"></col><col style="width: 10%;"></col><col style="width: 12%;"></col><col style="width: 44%;"></col></colgroup><thead><tr><th>Parámetro</th><th>Tipo</th><th>Requerido</th><th>Por defecto</th><th>Descripción</th></tr></thead><tbody><tr><td>**documento**</td><td>string</td><td>No</td><td>—</td><td>Número de documento del tercero (deudor) cuya cartera se desea consultar. Si se omite, se devolverá la cartera de todos los terceros que cumplan los demás filtros.</td></tr><tr><td>**page**</td><td>integer</td><td>No</td><td>1</td><td>Número de página de resultados que se desea recuperar.</td></tr><tr><td>**page\_size**</td><td>integer</td><td>No</td><td>10</td><td>Número máximo de ítems por página. Valor máximo permitido: **1000**.</td></tr><tr><td>**pendientes**</td><td>boolean</td><td>No</td><td>true</td><td>Si es `true`, se devuelven únicamente ítems con `estado = "pendiente"` y `saldo > 0`.</td></tr><tr><td>**incluir\_borrador**</td><td>boolean</td><td>No</td><td>false</td><td>Si es `true`, incluye facturas en estado **Borrador** en el resultado.</td></tr><tr><td>**incluir\_conceptos\_no\_factura**</td><td>boolean</td><td>No</td><td>false</td><td>Si es `true`, incluye conceptos pagables que aún no tienen factura emitida.</td></tr><tr><td>**incluir\_conceptos\_futuros**</td><td>boolean</td><td>No</td><td>false</td><td>Si es `true`, incluye conceptos cuyo periodo de aplicación es posterior a la fecha actual (cobros anticipados).</td></tr><tr><td>**rango\_conceptos\_futuros\_meses**</td><td>integer</td><td>No</td><td>null</td><td>Cantidad de meses hacia adelante en los que se admite incluir conceptos futuros. Solo aplica si `incluir_conceptos_futuros = true`. Use `null` para no acotar.</td></tr><tr><td>**incluir\_intereses\_mora**</td><td>boolean</td><td>No</td><td>false</td><td>Si es `true`, los intereses de mora de una factura se devuelven adjuntos como ítems internos de su factura padre.</td></tr><tr><td>**resolucion\_ids**</td><td>array | string</td><td>No</td><td>—</td><td>Lista de IDs de resoluciones de facturación admitidas. Acepta un arreglo o una cadena separada por comas (por ejemplo: `"3,7,12"`). Solo se devuelven facturas emitidas con esas resoluciones.</td></tr><tr><td>**agrupar\_conceptos\_periodo**</td><td>boolean</td><td>No</td><td>false</td><td>Si es `true`, consolida los conceptos del mismo contrato, tercero y periodo (mismas fechas de inicio y fin) en un único ítem pagable. Los conceptos negativos del periodo solo se descuentan si existe un concepto positivo con saldo suficiente. **Requiere** que al menos uno de `incluir_conceptos_no_factura` o `incluir_conceptos_futuros` esté activo; en caso contrario el filtro se ignora.</td></tr></tbody></table>

<p class="callout info">**Filtros avanzados de la pasarela**  
Los filtros enviados por el caller se combinan con la **configuración de filtros avanzados** registrada para la pasarela autenticada en la inmobiliaria. Si la pasarela no tiene configuración propia, se aplica el comportamiento por defecto (solo facturas con estado pendiente, sin borradores, sin conceptos sueltos ni intereses adjuntos). Los filtros realmente aplicados se devuelven en la respuesta dentro de `filters_applied`.</p>

<p class="callout warning">**Exclusión de facturas a propietario**  
El endpoint **excluye automáticamente** las facturas cuyos detalles estén asociados a movimientos de tipo `FACTURA_PROPIETARIO` (por ejemplo, comisiones del contrato facturadas al propietario). Estas facturas siguen una lógica contable distinta —generan `EGRESO_PROPIETARIO` en lugar de `INGRESO_INQUILINO`— y no son pagables vía pasarela en esta versión. No se incluyen en la respuesta ni en el conteo total.</p>

**Ejemplos de peticiones:**

```http
GET https://{{instancia}}/service/v2/public/gateways/portfolio
```

```http
GET https://{{instancia}}/service/v2/public/gateways/portfolio?documento=1020304050
```

```http
GET https://{{instancia}}/service/v2/public/gateways/portfolio?documento=1020304050&pendientes=true&page=1&page_size=20
```

```http
GET https://{{instancia}}/service/v2/public/gateways/portfolio?documento=1020304050&incluir_intereses_mora=true&incluir_conceptos_futuros=true&rango_conceptos_futuros_meses=3
```

```http
GET https://{{instancia}}/service/v2/public/gateways/portfolio?documento=1020304050&resolucion_ids=3,7
```

#### **3. La Respuesta (¿Qué te entregaremos?)**

El sistema devolverá una respuesta JSON con los ítems pagables del tercero, los filtros realmente aplicados y la información de paginación. Cada ítem se entrega con la **misma estructura canónica**, sin importar si representa una factura de contrato, una factura de venta, un concepto facturable, un concepto no facturable o una factura con intereses adjuntos.

```json
{
    "success": true,
    "status": 200,
    "message": "La cartera de la pasarela fue consultada correctamente.",
    "body": [
        {
            "factura_id": 4521,
            "factura_numero": "291",
            "contrato_id": 187,
            "contrato_numero": "C-000187",
            "tercero_id": 272,
            "tercero_documento": "1020304050",
            "tercero_nombre": "ANA MARÍA PÉREZ RODRÍGUEZ",
            "fecha_vencimiento": "2026-04-30",
            "observaciones": "Factura del Contrato C-000187",
            "valor_total": 1750000.00,
            "saldo": 1750000.00,
            "estado": "pendiente",
            "items": [
                {
                    "movimiento_id": 98231,
                    "descripcion": "Canon de arrendamiento - Canon abril 2026",
                    "valor_unitario": 1600000.00,
                    "cantidad": 1,
                    "valor_iva": 0.00,
                    "valor_retencion": 0.00,
                    "valor_reteiva": 0.00,
                    "valor_reteica": 0.00,
                    "valor_descuento": 0.00
                },
                {
                    "movimiento_id": 98232,
                    "descripcion": "Administración - Cuota administración abril 2026",
                    "valor_unitario": 150000.00,
                    "cantidad": 1,
                    "valor_iva": 0.00,
                    "valor_retencion": 0.00,
                    "valor_reteiva": 0.00,
                    "valor_reteica": 0.00,
                    "valor_descuento": 0.00
                },
                {
                    "movimiento_id": 98410,
                    "descripcion": "Intereses de mora - Mora canon marzo 2026",
                    "valor_unitario": 35000.00,
                    "cantidad": 1,
                    "valor_iva": 0.00,
                    "valor_retencion": 0.00,
                    "valor_reteiva": 0.00,
                    "valor_reteica": 0.00,
                    "valor_descuento": 0.00
                }
            ]
        },
        {
            "factura_id": null,
            "factura_numero": null,
            "contrato_id": 187,
            "contrato_numero": "C-000187",
            "tercero_id": 272,
            "tercero_documento": "1020304050",
            "tercero_nombre": "ANA MARÍA PÉREZ RODRÍGUEZ",
            "fecha_vencimiento": "2026-05-15",
            "observaciones": "Costo del Contrato C-000187",
            "valor_total": 280000.00,
            "saldo": 280000.00,
            "estado": "pendiente",
            "items": [
                {
                    "movimiento_id": 98515,
                    "descripcion": "Reparación locativa - Reparación locativa baño principal",
                    "valor_unitario": 280000.00,
                    "cantidad": 1,
                    "valor_iva": 0.00,
                    "valor_retencion": 0.00,
                    "valor_reteiva": 0.00,
                    "valor_reteica": 0.00,
                    "valor_descuento": 0.00
                }
            ]
        }
    ],
    "filters_applied": {
        "documento": "1020304050",
        "pendientes": true,
        "incluir_borrador": false,
        "incluir_conceptos_no_factura": true,
        "incluir_conceptos_futuros": false,
        "incluir_intereses_mora": true
    },
    "pagination": {
        "total_records": 2,
        "total_pages": 1,
        "current_page": 1,
        "page_size": 10,
        "current_page_records": 2,
        "has_next_page": false,
        "has_previous_page": false
    }
}
```

##### **Campos de la respuesta**

<table border="1" id="bkmrk-clave-descripci%C3%B3n-su" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>**Clave**</th><th>**Descripción**</th></tr></thead><tbody><tr><td>**success**</td><td>Indica si la operación fue exitosa (`true`) o no (`false`).</td></tr><tr><td>**status**</td><td>Código HTTP devuelto (debe coincidir con el código real de la respuesta).</td></tr><tr><td>**message**</td><td>Mensaje descriptivo del resultado de la operación.</td></tr><tr><td>**body**</td><td>Arreglo de ítems pagables (ver tabla detallada abajo). Puede ser vacío si no hay cartera.</td></tr><tr><td>**filters\_applied**</td><td>Objeto con los filtros realmente aplicados (combinación de los filtros del caller + la configuración de la pasarela).</td></tr><tr><td>**pagination**</td><td>Objeto con la información de paginación (ver tabla detallada abajo).</td></tr></tbody></table>

##### **Estructura de cada ítem (`body[]`)**

<table border="1" id="bkmrk-clave-descripci%C3%B3n-fa" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>**Clave**</th><th>**Descripción**</th></tr></thead><tbody><tr><td>**factura\_id**</td><td>Identificador interno de la factura (PK en base de datos). `null` cuando el ítem corresponde a un concepto sin factura emitida (concepto facturable o no facturable). **No es el número visible al usuario**; ver `factura_numero`.</td></tr><tr><td>**factura\_numero**</td><td>Número consecutivo visible de la factura (mismo valor que se muestra como "Factura N°" en la interfaz de nuby). `null` cuando el ítem es un concepto sin factura emitida.</td></tr><tr><td>**contrato\_id**</td><td>Identificador del contrato asociado. `null` para facturas de venta sin contrato.</td></tr><tr><td>**contrato\_numero**</td><td>Número/código visible del contrato. `null` si no aplica.</td></tr><tr><td>**tercero\_id**</td><td>Identificador del tercero (deudor) al que pertenece el ítem.</td></tr><tr><td>**tercero\_documento**</td><td>Número de documento de identificación del tercero.</td></tr><tr><td>**tercero\_nombre**</td><td>Nombre completo (o razón social) del tercero.</td></tr><tr><td>**fecha\_vencimiento**</td><td>Fecha de vencimiento del ítem. Formato: `YYYY-MM-DD`.</td></tr><tr><td>**observaciones**</td><td>Mensaje genérico que identifica la naturaleza del ítem. Valores posibles: - `"Factura del Contrato {numero}"` — cuando el ítem es una factura asociada a un contrato (reemplaza `{numero}` por el consecutivo del contrato).
- `"Factura de Venta"` — cuando el ítem es una factura de venta sin contrato asociado.
- `"Costo del Contrato {numero}"` — cuando el ítem corresponde a un concepto/movimiento de un contrato sin factura emitida.

El detalle por concepto/producto se expone en el campo `descripcion` de cada renglón de `items[]`.</td></tr><tr><td>**valor\_total**</td><td>Valor total original del ítem (suma de los renglones internos).</td></tr><tr><td>**saldo**</td><td>Saldo pendiente por pagar a la fecha de la consulta.</td></tr><tr><td>**estado**</td><td>Estado lógico del ítem. Valores posibles: `"pendiente"`, `"pagada"`, `"anulada"`. Las facturas con estados internos fuera de alcance se omiten del resultado.</td></tr><tr><td>**items**</td><td>Arreglo de renglones internos del ítem (ver tabla detallada abajo). Para facturas, contiene los movimientos que la componen y, si aplica, los intereses de mora adjuntos.</td></tr></tbody></table>

##### **Mapeo de estados de factura**

<table border="1" id="bkmrk-estado-interno-estad" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 28%;"></col><col style="width: 22%;"></col><col style="width: 50%;"></col></colgroup><thead><tr><th>Estado interno</th><th>Estado expuesto</th><th>Descripción</th></tr></thead><tbody><tr><td>**1 (Facturada)**</td><td>`"pendiente"`</td><td>Factura emitida y aún sin pagar.</td></tr><tr><td>**5 (Borrador)**</td><td>`"pendiente"`</td><td>Factura en estado borrador. Solo aparece si `incluir_borrador = true`.</td></tr><tr><td>**2 (Anulada)**</td><td>`"anulada"`</td><td>Factura anulada manualmente.</td></tr><tr><td>**6 (Anulada por NC)**</td><td>`"anulada"`</td><td>Factura anulada por nota crédito.</td></tr><tr><td>**3 (Pagada)**</td><td>`"pagada"`</td><td>Factura ya cancelada en su totalidad.</td></tr><tr><td>**4, 7**</td><td>—</td><td>Estados fuera de alcance. La factura se excluye del resultado.</td></tr></tbody></table>

##### **Estructura de cada renglón interno (`body[].items[]`)**

<table border="1" id="bkmrk-clave-descripci%C3%B3n-mo" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>**Clave**</th><th>**Descripción**</th></tr></thead><tbody><tr><td>**movimiento\_id**</td><td>Identificador del movimiento de contrato asociado al renglón. Puede ser `null` o `0` para renglones de facturas de venta sin movimiento.</td></tr><tr><td>**descripcion**</td><td>Texto descriptivo del renglón con el formato `"{producto} - {descripcion}"`, donde `{producto}` es el nombre del producto/concepto y `{descripcion}` es la descripción puntual de ese ítem (mes facturado, observación específica, etc.). Si alguno de los componentes no existe se incluye vacío respetando el separador.</td></tr><tr><td>**valor\_unitario**</td><td>Valor unitario del renglón (base, sin IVA ni retenciones).</td></tr><tr><td>**cantidad**</td><td>Cantidad facturada.</td></tr><tr><td>**valor\_iva**</td><td>Valor del IVA aplicado al renglón.</td></tr><tr><td>**valor\_retencion**</td><td>Valor de la retención en la fuente aplicada.</td></tr><tr><td>**valor\_reteiva**</td><td>Valor de la retención de IVA aplicada.</td></tr><tr><td>**valor\_reteica**</td><td>Valor de la retención de ICA aplicada.</td></tr><tr><td>**valor\_descuento**</td><td>Valor del descuento aplicado al renglón.</td></tr></tbody></table>

##### **Información de paginación (`pagination`)**

<table border="1" id="bkmrk-clave-descripci%C3%B3n-to" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 28%;"></col><col style="width: 72%;"></col></colgroup><thead><tr><th>**Clave**</th><th>**Descripción**</th></tr></thead><tbody><tr><td>**total\_records**</td><td>Cantidad total de ítems pagables que cumplen los filtros (sin paginar).</td></tr><tr><td>**total\_pages**</td><td>Cantidad total de páginas calculadas con el `page_size` solicitado.</td></tr><tr><td>**current\_page**</td><td>Página actualmente devuelta.</td></tr><tr><td>**page\_size**</td><td>Tamaño de página efectivamente aplicado.</td></tr><tr><td>**current\_page\_records**</td><td>Cantidad de ítems devueltos en la página actual.</td></tr><tr><td>**has\_next\_page**</td><td>`true` si existe una página siguiente.</td></tr><tr><td>**has\_previous\_page**</td><td>`true` si existe una página anterior.</td></tr></tbody></table>

#### **4. Seguridad y Posibles Errores**

El sistema realiza validaciones de autenticación, autorización por pasarela y parámetros. Si alguna falla, devolverá un error con su respectivo código HTTP. La estructura del cuerpo de error siempre incluye `success: false`, `status`, `message` y `body: []`.

<table border="1" id="bkmrk-c%C3%B3digo-http-descripc" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 12%;"></col><col style="width: 88%;"></col></colgroup><thead><tr><th>Código HTTP</th><th>Descripción</th></tr></thead><tbody><tr><td>**400**</td><td>**Token faltante o inválido.** Posibles causas:  
— No se envió el encabezado `Authorization`.  
— El encabezado no tiene el formato `Bearer {token}`.  
— El token no fue encontrado en el sistema.  
  
**Parámetros inválidos.** Posibles causas:  
— `page` no es un entero positivo.  
— `page_size` no es un entero positivo.</td></tr><tr><td>**401**</td><td>El token ha expirado. Debes generar uno nuevo consumiendo el servicio de **Login**.</td></tr><tr><td>**403**</td><td>El cliente OAuth autenticado no está registrado como pasarela activa en la inmobiliaria. Mensaje: `"El cliente autenticado no esta autorizado para consumir este endpoint."`</td></tr><tr><td>**500**</td><td>Error interno al resolver la cartera. El detalle se registra en el canal de log `PASARELAS` y al caller solo se le devuelve un mensaje genérico.</td></tr></tbody></table>

---

#### **5. Ejemplos de integración**

Aquí tienes ejemplos de código listos para que tus desarrolladores los adapten:

<details id="bkmrk-curl-%23-consultar-la-"><summary>cURL</summary>

```bash
# Consultar la cartera del tercero con documento 1020304050
curl -X GET "https://{{instancia}}/service/v2/public/gateways/portfolio?documento=1020304050&pendientes=true&page=1&page_size=20" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TU_TOKEN_AQUI"

# Cartera incluyendo intereses de mora y conceptos futuros (3 meses)
curl -X GET "https://{{instancia}}/service/v2/public/gateways/portfolio?documento=1020304050&incluir_intereses_mora=true&incluir_conceptos_futuros=true&rango_conceptos_futuros_meses=3" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TU_TOKEN_AQUI"
```

</details><details id="bkmrk-php-%3C%3Fphp-%24instance-"><summary>PHP</summary>

```php
<?php

$instance = 'tu_instancia'; // Reemplaza con tu instancia real
$token = 'TU_TOKEN_AQUI'; // Token obtenido del servicio Login (cliente OAuth registrado como pasarela)

$queryParams = http_build_query([
    'documento' => '1020304050',
    'pendientes' => 'true',
    'incluir_intereses_mora' => 'true',
    'page' => 1,
    'page_size' => 20,
]);

$url = "https://{$instance}/service/v2/public/gateways/portfolio?{$queryParams}";

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    "Authorization: Bearer {$token}",
]);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "Código HTTP: {$http_code}\n";

if ($http_code === 200) {
    $data = json_decode($response, true);
    echo "Total ítems: " . ($data['pagination']['total_records'] ?? 0) . "\n";
    foreach ($data['body'] as $item) {
        $tipo = $item['factura_id'] ? "Factura #{$item['factura_id']}" : "Concepto";
        echo "  [{$tipo}] {$item['tercero_nombre']} - Saldo: {$item['saldo']} - Vence: {$item['fecha_vencimiento']}\n";
    }
} else {
    echo "Error consultando la cartera:\n{$response}\n";
}

?>
```

</details><details id="bkmrk-python-import-reques"><summary>Python</summary>

```python
import requests

# 1. Configura tus datos de acceso
instancia = 'mi-inmobiliaria.nuby.app'
token = 'TU_TOKEN_AQUI'  # Token de un cliente OAuth registrado como pasarela

# 2. Prepara la dirección y los parámetros
url = f"https://{instancia}/service/v2/public/gateways/portfolio"

params = {
    "documento": "1020304050",
    "pendientes": "true",
    "incluir_intereses_mora": "true",
    "page": 1,
    "page_size": 20,
}

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {token}",
}

# 3. Envía la petición GET y procesa la respuesta
try:
    response = requests.get(url, params=params, headers=headers)
    print(f"Código HTTP: {response.status_code}")

    if response.status_code == 200:
        data = response.json()
        print(f"Total ítems: {data['pagination']['total_records']}")
        for item in data['body']:
            tipo = f"Factura #{item['factura_id']}" if item['factura_id'] else "Concepto"
            print(f"  [{tipo}] {item['tercero_nombre']} - Saldo: {item['saldo']} - Vence: {item['fecha_vencimiento']}")
    else:
        print("Error consultando la cartera.")
        print(response.text)
except Exception as e:
    print(f"Error de conexión: {e}")

```

</details><details id="bkmrk-javascript-%2F%2F-1.-con"><summary>JavaScript</summary>

```javascript
// 1. Configura tus datos de acceso
const instancia = 'mi-inmobiliaria.nuby.app';
const token = 'TU_TOKEN_AQUI'; // Token de un cliente OAuth registrado como pasarela

// 2. Prepara la dirección con parámetros
const params = new URLSearchParams({
    documento: '1020304050',
    pendientes: 'true',
    incluir_intereses_mora: 'true',
    page: 1,
    page_size: 20,
});

const url = `https://${instancia}/service/v2/public/gateways/portfolio?${params}`;

// 3. Función para consultar la cartera
async function consultarCartera() {
    try {
        const response = await fetch(url, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`,
            },
        });

        console.log(`Código HTTP: ${response.status}`);

        if (response.ok) {
            const data = await response.json();
            console.log(`Total ítems: ${data.pagination.total_records}`);
            data.body.forEach(item => {
                const tipo = item.factura_id ? `Factura #${item.factura_id}` : 'Concepto';
                console.log(`  [${tipo}] ${item.tercero_nombre} - Saldo: ${item.saldo} - Vence: ${item.fecha_vencimiento}`);
            });
        } else {
            const errorData = await response.text();
            console.log('Error consultando la cartera.');
            console.log(errorData);
        }
    } catch (error) {
        console.error(`Error de conexión: ${error}`);
    }
}

consultarCartera();

```

</details>