# Crear cobros a nombre de un comercio

> Cómo crea una plataforma un cobro a nombre de un comercio conectado en Lane: la cabecera Lane-On-Behalf-Of, los productos, los productos extra, los medios de pago, la página que ve el comprador y los errores.

Usas tu propia llave y dices a nombre de quién actúas. El cobro es del comercio; queda marcado como hecho por tu plataforma.

La plata llega a la cuenta del comercio y el cobro aparece en su panel. La marca de que lo creó tu plataforma es lo que hace que recibas sus avisos y que cuente para tu parte.

### POST /v1/checkout/sessions

Crea el cobro y devuelve la dirección de la página de pago. Responde `201`.

**Cabeceras**

| Campo | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `Authorization` | texto | sí | `Bearer` y la llave secreta de **tu** empresa. Con `sk_test_` el cobro es de prueba; con `sk_live_`, real. |
| `Lane-On-Behalf-Of` | UUID | sí | El `id` del comercio conectado. |
| `Idempotency-Key` | texto | no | Un valor propio de cada pedido. Si no te llegó la respuesta y reintentas con la misma clave, no se crea otro cobro: recibes el mismo. Úsala siempre. |

**Cuerpo**

| Campo | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `amount` | entero | sí | En centavos. `12000000` son $ 120.000 COP. Ver [Montos](https://lane.money/docs/montos). |
| `description` | texto | no | Lo que ve el comprador. |
| `line_items` | lista | no | Los productos del pedido, para que el comprador reconozca lo que pidió: `name`, `quantity`, `unit_amount` (centavos, por unidad) e `image_url`. Son para mostrar: lo que se cobra es `amount`. |
| `extra_items` | lista | no | Hasta 3 productos que el comprador puede sumar antes de pagar: `id` (el tuyo, sin repetir), `name`, `unit_amount`, y si quieres `compare_at_amount` (el precio anterior, para mostrarlo tachado; tiene que ser mayor que `unit_amount`) e `image_url`. |
| `available_payment_methods` | lista | no | Con qué se puede pagar: `card`, `pse`, `nequi`, `daviplata`, `breb`. Omitido, todos. |
| `payment_method` | texto | no | Uno solo de esos valores, cuando el comprador ya eligió. Equivale a mandar la lista con un medio. |
| `customer` | objeto | no | `name` y `email` del comprador, para empezar el formulario ya lleno. |
| `success_url` | URL | no | A dónde vuelve el comprador cuando paga. Admite `{PAYMENT_ID}` y `{CHECKOUT_SESSION_ID}`. |
| `cancel_url` | URL | no | A dónde vuelve si no paga. |
| `expires_in` | entero | no | Segundos de vida, entre `60` y `31536000` (un año). Por defecto `43200` (12 horas). |
| `metadata` | objeto | no | Lo que quieras guardar con el cobro. Vuelve al consultar el pago, no en el aviso. |

**Crear el cobro de un pedido**

_cURL_

```bash
curl -X POST https://api.lane.money/v1/checkout/sessions \
  -H "Authorization: Bearer sk_test_…" \
  -H "Lane-On-Behalf-Of: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "Idempotency-Key: pedido-4821" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 12000000,
    "description": "Pedido 4821",
    "line_items": [
      { "name": "Camiseta negra", "quantity": 2, "unit_amount": 5000000 },
      { "name": "Envío", "quantity": 1, "unit_amount": 2000000 }
    ],
    "extra_items": [
      { "id": "sku-medias", "name": "Medias", "unit_amount": 1500000, "compare_at_amount": 2000000 }
    ],
    "customer": { "name": "Ana Pérez", "email": "ana@ejemplo.com" }
  }'
```

_Node_

```js
// En tu servidor, cuando tu bot cierra un pedido.
async function crearCobro(pedido) {
  const respuesta = await fetch("https://api.lane.money/v1/checkout/sessions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LANE_SECRET_KEY}`,
      "Lane-On-Behalf-Of": pedido.comercioLaneId,
      // La misma clave en cada reintento del mismo pedido.
      "Idempotency-Key": `pedido-${pedido.id}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      amount: pedido.totalCentavos,
      description: `Pedido ${pedido.numero}`,
      line_items: pedido.productos,
    }),
  });

  const cobro = await respuesta.json();
  if (!respuesta.ok) throw new Error(`${cobro.code}: ${cobro.error}`);

  // Con este id reconoces el pedido cuando llegue el aviso.
  await pedido.guardarSesion(cobro.id);
  return cobro.checkout_url;
}
```

**Respuesta**
```json
{
  "object": "checkout.session",
  "id": "c1f4a2de-…",
  "environment": "test",
  "amount": 12000000,
  "currency": "COP",
  "status": "created",
  "checkout_url": "https://lane.money/pay/c1f4a2de-…",
  "payment": null
}
```

Guarda `id`: es con lo que reconoces el pedido cuando llegue el aviso (`checkout_session_id`). `payment` llega en `null` porque el pago se crea cuando el comprador elige cómo pagar.

## Los medios de pago

La página ofrece un medio cuando se cumplen tres cosas:

1. **Lo pediste,** o no pediste ninguno (y entonces entran todos).
2. **El comercio lo tiene habilitado** en el modo de tu llave.
3. **El medio admite el monto.** Los topes de cada red están en [Medios instantáneos](https://lane.money/docs/guias/medios-instantaneos).

| Si… | Pasa esto |
| --- | --- |
| Algún medio no cumple | Ese medio no se ofrece y el cobro se crea con los demás. |
| El comercio no tiene ninguno de los que pediste | `422` con `code: payment_method_unavailable`. `details` dice cuáles tiene. |
| Ninguno admite el monto | `422`, con el tope en el mensaje. |
| Mandas un valor que no existe | `400`. Las marcas de tarjeta (`visa`, `mastercard`…) no son valores: el medio es `card`. |

Los medios instantáneos (PSE, Nequi, Daviplata y Bre-B) no admiten reembolso ni anulación. La tarjeta sí, y la hace el comercio.

## Los productos extra

El comprador ve cada extra en el resumen del pedido, con su precio y, si mandaste `compare_at_amount`, el anterior tachado. Lo suma o lo quita antes de pagar. El precio es el que quedó en el cobro: la página no puede cambiarlo.

> **Cuidado: El total puede ser mayor que el que mandaste.** Si ofreces `extra_items`, el comprador puede sumarlos y se cobra `amount` más esos extras. Lee el total y los extras del aviso del pago: no asumas el monto que enviaste. Si con un extra el total ya no cabe en un medio, ese medio deja de ofrecerse.

## Lo que ve el comprador

- **El pedido,** con sus productos y el total en pesos, y los extras para sumar.
- **Los medios que el cobro ofrece,** para elegir uno. Con un solo medio, la página va directo a él.
- **Con tarjeta,** escribe los datos de la tarjeta y después su código de seguridad para confirmar.
- **Con PSE,** va al portal de su banco y vuelve. **Con Nequi,** aprueba en su app. **Con Daviplata,** escribe el código que le llega por SMS. **Con Bre-B,** escanea un código o usa su llave.
- **Si un intento falla,** puede intentar con otro medio en la misma página.
- **Cuando paga,** ve la confirmación y, si mandaste `success_url`, vuelve ahí.

La página lleva el logo y los colores que el comercio configuró en su panel, en **Tu checkout**. Si no configuró nada, sale con la marca de Lane.

## Errores

**Forma de un error**
```json
{
  "error": "Este comercio todavía no puede cobrar con ese medio de pago",
  "code": "payment_method_unavailable",
  "details": "Los que tiene habilitados son: card, pse.",
  "request_id": "b7d1c6f0-…"
}
```

| Respuesta | `code` | Qué pasó |
| --- | --- | --- |
| `403` | `not_a_platform` | Tu empresa no es una plataforma, o está en pausa. |
| `404` | `merchant_not_connected` | Ese comercio no está conectado contigo, o no existe. La respuesta es la misma en los dos casos. |
| `409` | `conflict` | El comercio todavía no puede cobrar en el modo de tu llave. |
| `422` | `payment_method_unavailable` | El comercio no tiene habilitado ninguno de los medios que pediste. |
| `422` |  | Ningún medio de los que pediste admite ese monto. |
| `400` |  | Un medio de pago que no existe, un `id` repetido en `extra_items` o un `compare_at_amount` que no es mayor que `unit_amount`. |
| `422` | `idempotency_key_reused` | Esa `Idempotency-Key` ya se usó con otra petición. |
| `409` | `idempotency_key_in_use` | La primera petición con esa clave todavía se está procesando. Reintenta en unos segundos. |
| `403` | `on_behalf_not_supported` | Esa ruta no admite actuar a nombre de un comercio. |
| `429` | `rate_limited` | Más de 300 peticiones en un minuto con la misma llave. |

## Consultar un pago

### GET /v1/payments/{id}

El pago de un cobro que tú creaste, con la cabecera `Lane-On-Behalf-Of`.

Solo encuentra los pagos de cobros que creó tu plataforma; uno que el comercio hizo por su cuenta responde `404`. Responde sin los datos del comprador y sin los identificadores de su tarjeta. El `id` del pago lo trae el aviso.

## Lo que se ignora en estos cobros

En los cobros que crea una plataforma se ignoran `multi_use`, `max_uses`, `error_url`, `style_config` y `checkout_options`: la página es la de Lane, con la marca del comercio.
