# Conciliación diaria

Trae una vez al día todos los pagos que Makatea registró y cuádralos contra tu sistema y tu banco.

Los webhooks avisan al momento, pero una entrega puede fallar. Esta pasada diaria es la red de seguridad. Lee el libro de pagos con `GET /v1/payments` desde el último punto que guardaste.

## El libro de pagos

Cada movimiento de dinero es un renglón que no se edita. Hay dos tipos.

- `kind: "payment"` es un pago o abono.
- `kind: "reversal"` es la anulación de un pago. Lleva el monto en negativo y `voids_payment_id` apunta al pago anulado.

Cada renglón trae dos fechas.

- `paid_at` es cuándo entró el dinero. Puede ser de días atrás.
- `recorded_at` es cuándo se anotó en Makatea. Es la que usa la sincronización.

Un pago con fecha de la semana pasada que alguien registró hoy aparece en tu corrida de hoy. Por eso el punto de control es `recorded_at` y no `paid_at`.

## La corrida

1. Lee tu último punto de control. La primera vez usa la fecha de arranque.
2. Pide `GET /v1/payments?updated_since=<punto>&limit=500`. Con ese filtro los renglones vienen del más viejo al más nuevo.
3. Procesa la página. Mientras `pagination.has_more` sea `true`, pide la siguiente con `cursor=<next_cursor>` y los mismos filtros.
4. Al terminar guarda el `recorded_at` del último renglón como tu nuevo punto.

El filtro incluye el instante exacto del punto, así que el último renglón de ayer vuelve a salir hoy. Por eso cada renglón se aplica una sola vez por su `id`.

```sh
curl -s "$MAKATEA_API/v1/payments?updated_since=2026-09-26T06:00:00Z&limit=500" \
  -H "Authorization: Bearer $MAKATEA_KEY"
```

```json
{
  "data": [
    {
      "id": "b7e2…",
      "billing_order_id": "91d0…",
      "member_id": "6f1c…",
      "amount": 1850,
      "kind": "payment",
      "method": "transfer",
      "reference": "SPEI-4471",
      "voided": false,
      "paid_at": "2026-09-25T18:00:00Z",
      "recorded_at": "2026-09-26T15:12:40Z",
      "allocation": { "capital": 1400, "interest": 450, "late_fee": 0, "other": 0, "unassigned": 0 }
    }
  ],
  "pagination": { "limit": 500, "has_more": false, "next_cursor": null }
}
```

Un ejemplo completo en Node.

```js
const API = process.env.MAKATEA_API;
const KEY = process.env.MAKATEA_KEY;

export async function conciliar(desde) {
  let cursor = null;
  let punto = desde;
  for (;;) {
    const q = new URLSearchParams({ updated_since: desde, limit: "500" });
    if (cursor) q.set("cursor", cursor);
    const r = await fetch(`${API}/v1/payments?${q}`, { headers: { Authorization: `Bearer ${KEY}` } });
    if (r.status === 429) {
      await new Promise((ok) => setTimeout(ok, Number(r.headers.get("Retry-After") ?? 5) * 1000));
      continue;
    }
    if (!r.ok) throw new Error(`Makatea ${r.status}: ${await r.text()}`);
    const { data, pagination } = await r.json();
    for (const renglon of data) {
      await aplicarEnMiSistema(renglon); // idempotente por renglon.id
      punto = renglon.recorded_at;
    }
    if (!pagination.has_more) return punto; // guárdalo para la siguiente corrida
    cursor = pagination.next_cursor;
  }
}
```

## Cómo aplicar cada renglón

- **Usa `id` como llave.** Si el renglón ya existe en tu sistema, sáltalo. La corrida se puede repetir sin miedo.
- **Una anulación resta.** Aplica el `reversal` con su monto negativo y marca el pago original como anulado.
- **`allocation` dice cómo se repartió.** Capital, interés, moratorio, otros cargos y lo que queda por aplicar. Es `null` cuando la cuota no tiene desglose.
- **Cruza contra el banco con `reference`.** Es la referencia que se capturó al registrar el pago, por ejemplo la clave de rastreo del SPEI.

## Cuadre del día

Al final de la corrida compara tres números.

| Número | De dónde sale |
|---|---|
| Suma de `amount` de los renglones con `paid_at` de ayer | `GET /v1/payments` |
| Depósitos de ayer en tu estado de cuenta | Tu banco |
| Cobrado de ayer | `GET /v1/stats?from=<ayer>&to=<ayer>`, campo `metrics.collected` |

`metrics.collected` excluye pagos anulados y aplicaciones de saldo a favor. Si los tres números no coinciden, la diferencia está en un pago sin registrar o en un depósito sin referencia.

## Los otros objetos

El mismo patrón sirve para cuotas y clientes. `GET /v1/billing-orders?updated_since=…` y `GET /v1/members?updated_since=…` devuelven lo que cambió, ordenado por `updated_at`, con el mismo cursor. Ahí el punto de control es el `updated_at` del último renglón.
