# Cobro con tarjeta guardada

> Guarda una tarjeta con una sesión de inscripción y cóbrala después sin el cliente presente: el flujo CIT/MIT para mensualidades y recompras en Lane.

El cliente autoriza su tarjeta una vez. Después cobras desde tu servidor, sin él.

Las redes distinguen dos situaciones: el cliente está frente a la pantalla, o no está. Los nombres del contrato son `initiator: "customer"` y `initiator: "merchant"`, y cada uno tiene reglas distintas. Confundirlos es la causa más común de rechazo en un cobro recurrente.

|  | Cliente presente | Cliente ausente |
| --- | --- | --- |
| `initiator` | `customer` | `merchant` |
| CVV | Obligatorio | Prohibido — la API lo rechaza |
| `order_type` | `purchase` y los de serie | Cualquiera menos `purchase` |
| Enlace al primer cobro | No aplica | Obligatorio en cobros de serie |

## 1. Guarda la tarjeta

Una sesión de inscripción abre una pantalla donde el cliente entrega su tarjeta. No cobra nada: solo la deja autorizada para después.

### POST /v1/enrollment/sessions

Crea la pantalla donde el cliente guarda su tarjeta.

**Crear la inscripción**

```bash
curl -X POST https://gxxazsplhgyezwxghrnn.supabase.co/functions/v1/blink-api/v1/enrollment/sessions \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": { "email": "cliente@ejemplo.com" },
    "success_url": "https://mitienda.com/tarjeta-guardada",
    "expires_in": 3600
  }'
```

_Respuesta 201_

```json
{
  "id": "7d2b…",
  "object": "enrollment.session",
  "environment": "test",
  "status": "pending",
  "enrollment_url": "https://…"
}
```

`expires_in` va entre 60 y 86400 segundos. Cuando el cliente termina, llega el evento `instrument.enrolled` y la tarjeta aparece en el listado de instrumentos.

## 2. Encuentra el instrumento

**Listar tarjetas guardadas**

```bash
curl "https://gxxazsplhgyezwxghrnn.supabase.co/functions/v1/blink-api/v1/instruments" \
  -H "Authorization: Bearer sk_test_…"
```

_Respuesta 200_

```json
{
  "object": "list",
  "data": [
    {
      "object": "payment.instrument",
      "id": "c1a9f3e0-…",
      "status": "active",
      "customer": { "email": "cliente@ejemplo.com" },
      "bin": "518617",
      "last4": "4242",
      "brand": "MASTERCARD",
      "product": "CREDIT",
      "supports_installments": true,
      "enrolled_at": "2026-09-06T18:20:11.000Z"
    }
  ]
}
```

## 3. El primer cobro, con el cliente

El primero de la serie lo inicia el cliente: está mirando la pantalla y escribe su CVV. Ese cobro es el que la red va a usar como referencia de todos los siguientes, así que **guarda su identificador**.

**Primer cobro (CIT)**

```bash
curl -X POST https://gxxazsplhgyezwxghrnn.supabase.co/functions/v1/blink-api/v1/payments \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 8900000,
    "currency": "COP",
    "instrument_id": "c1a9f3e0-…",
    "initiator": "customer",
    "cvv": "123",
    "order_type": "recurring"
  }'
```

## 4. Los siguientes, sin él

Un cobro de serie sin el cliente presente tiene que apuntar al primero. Manda `parent_transaction_data` con el `first_payment_id` que guardaste, o con los datos de red que devolvió aquel cobro. Sin eso la API responde `400`, antes de intentar nada.

**Cobro recurrente (MIT)**

_cURL_

```bash
curl -X POST https://gxxazsplhgyezwxghrnn.supabase.co/functions/v1/blink-api/v1/payments \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 8900000,
    "currency": "COP",
    "instrument_id": "c1a9f3e0-…",
    "initiator": "merchant",
    "order_type": "recurring",
    "parent_transaction_data": { "first_payment_id": "9b41d0c7-…" }
  }'
```

_Node_

```js
// El día 1 de cada mes, para cada suscripción activa.
async function cobrarMensualidad(sub) {
  const res = await fetch("https://gxxazsplhgyezwxghrnn.supabase.co/functions/v1/blink-api/v1/payments", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LANE_SECRET_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      amount: sub.montoCentavos,
      currency: "COP",
      instrument_id: sub.instrumentId,
      initiator: "merchant",              // el cliente no está
      order_type: "recurring",
      parent_transaction_data: {
        first_payment_id: sub.primerCobroId,
      },
    }),
  });

  const pago = await res.json();
  if (!res.ok) throw new Error(pago.error);
  return pago;   // status: succeeded | failed | processing
}
```

## Los tipos de orden

| `order_type` | Para qué | Quién lo inicia |
| --- | --- | --- |
| `purchase` | Una compra suelta. | Solo el cliente |
| `recurring` | Mensualidad de monto fijo. | Ambos |
| `installment` | Una compra dividida en pagos. | Ambos |
| `standing_order` | Cobro periódico de monto variable. | Ambos |
| `unscheduled` | Recarga automática, sin fecha fija. | Ambos |
| `deferred` · `delayed` | Cobro que se hace después de la compra. | Ambos |
| `no_show` · `resubmission` | Penalidad por no presentarse, reintento de un rechazo. | Solo el comercio |

Los cobros de serie —`recurring`, `installment`, `standing_order`, `deferred`, `delayed`— son los que exigen el enlace al primero. `unscheduled`, `no_show` y `resubmission` no.

## Verificar que la tarjeta sigue viva

Antes de un ciclo de cobros puedes preguntar si una tarjeta responde, sin cobrar nada: `intent: "account-status"` con `amount: 0`. Al tarjetahabiente no le aparece nada.

**Verificación sin cobro**
```json
{
  "amount": 0,
  "instrument_id": "c1a9f3e0-…",
  "intent": "account-status",
  "initiator": "customer",
  "cvv": "123"
}
```

> **Cuidado: Una tarjeta reemitida deja de servir.** Si al cliente le cambian la tarjeta por vencimiento o robo, el instrumento guardado queda muerto y no se actualiza solo. Cuando un cobro recurrente falle por eso, la salida es pedirle al cliente que vuelva a inscribir su tarjeta.
