<!-- Source: https://docs.infraio.xyz/es/concepts/orders -->
<!-- Last updated: 2026-10-04 -->

# Órdenes

Si [CheckoutSession](https://docs.infraio.xyz/es/concepts/sessions) es lo que ve el comprador,
**Order** es lo que *te* importa a ti. Es el registro permanente de:

- Qué se estaba comprando (line items)
- Cuánto se debía y cuánto se ha pagado efectivamente
- Reembolsos pendientes y aplicados
- Tu referencia externa (`external_ref`): típicamente tu propio
  order ID, almacenado en la Order y devuelto en
  `GET /b2b/v1/orders/{id}` (no se replica en los payloads de
  webhook: ver abajo)

Un pedido no necesita líneas de artículos: envía `amount` en lugar de `items` para cobrar un importe fijo (una factura, un depósito, un enlace de pago con importe libre). Envía uno u otro, nunca ambos.

Una CheckoutSession muere después de que uno de sus PaymentIntents
liquide o el TTL expire. La Order vive para siempre.

## Cuándo se crea una Order

Cuando llamas a `POST /b2b/v1/checkout-sessions/quick`, InfraIO Pay
crea **tanto** una nueva Order *como* una nueva CheckoutSession en
una transacción. Si ya tienes una Order y quieres reintentar el
checkout (p. ej., tras el abandono del comprador), usa
`POST /b2b/v1/checkout-sessions` en su lugar para adjuntar una
sesión nueva a la orden existente, preservando el rastro de
auditoría.

## Ciclo de vida

```mermaid
stateDiagram-v2
    [*] --> PENDING: order created
    PENDING --> PAID:              full payment settled
    PENDING --> PARTIAL_PAID:      underpayment settled
    PENDING --> CANCELED:          session expired or canceled
    PARTIAL_PAID --> PAID:         merchant resolves OR remainder paid
    PAID --> PARTIALLY_REFUNDED:   partial refund executed
    PAID --> REFUNDED:             full refund executed
    PARTIALLY_REFUNDED --> REFUNDED: subsequent refund covers the rest
    PAID --> [*]
    REFUNDED --> [*]
    CANCELED --> [*]
```

| Estado | Significa |
| --- | --- |
| `PENDING` | Una CheckoutSession está activa y sin resolver. |
| `PAID` | Importe total liquidado. **El webhook `payment.settled` se dispara aquí.** Seguro para fulfillment. |
| `PARTIAL_PAID` | El dinero llegó pero por menos del total. Ver "Pago insuficiente" abajo. |
| `CANCELED` | Sesión expirada o cancelada por el comerciante. `metadata.canceled_reason` explica el motivo (`payment_timeout`, `merchant_canceled`, …). |
| `REFUNDED` | Todo el importe pagado ha sido reembolsado. |
| `PARTIALLY_REFUNDED` | Se ejecutó algún reembolso pero queda saldo pagado. |

> **Note:**
>
> **El estado sobre el que ramificar tu lógica de fulfillment es `PAID`**,
> no el `COMPLETED` de la CheckoutSession. El webhook `payment.settled`
> es la señal canónica.

## El campo `external_ref`

Al crear una sesión puedes incluir `external_ref` (cualquier cadena
de hasta 255 caracteres: típicamente tu propio order ID). Se enhebra
a través de todo el pipeline:

- Almacenado en la Order
- Visible en el dashboard del comerciante para búsquedas de soporte
- Devuelto en `GET /b2b/v1/orders/{id}` para que un manejador de
  webhook pueda obtenerlo tras recibir `payment.settled`

> **Warning:**
>
> Los payloads de webhook **no** incluyen `external_ref`. Para mapear
> un webhook `payment.*` de vuelta a tu propio registro, toma
> `order_id` del payload y obtén la orden.

## Pago insuficiente

Si la transferencia on-chain del comprador se confirma por menos del
total de la orden, la Order va a `PARTIAL_PAID`. Tienes tres
opciones:

1. **Aceptar y resolver.** Cambia la orden a `PAID` y dispara
   `order.resolved`. Esto no está disponible a través de la API REST,
   así que para un flujo self-serve usa la opción 2 (cobrar el
   remanente).
2. **Esperar el remanente.** Crea una nueva CheckoutSession contra
   la misma Order con `amount_due` = residual. El comprador paga
   la diferencia; cuando eso liquide, la Order se mueve a `PAID`.
3. **Cancelar y reembolsar.** Reembolsa el importe parcial y cancela
   la Order. El comprador es responsable de cualquier tarifa de
   cadena.

## Reembolsos

Los reembolsos son una superficie de API separada y una página de
concepto separada. Consulta
[Conceptos → Reembolsos](https://docs.infraio.xyz/es/concepts/refunds).

## Endpoints de la API

| Método | Ruta | Notas |
| --- | --- | --- |
| `POST` | `/b2b/v1/orders` | Crea una Order sin una sesión (raro) |
| `GET` | `/b2b/v1/orders/:order_id` | Lee una orden completa con line items + historial de pagos |
| `PATCH` | `/b2b/v1/orders/:order_id/cancel` | Cancela una orden no pagada |
| `PATCH` | `/b2b/v1/orders/:order_id/reopen` | Reabre una orden autocancelada (`payment_timeout`) |

## Qué sigue

- [Conceptos → Sesiones](https://docs.infraio.xyz/es/concepts/sessions): la cáscara de cara
  al comprador que envuelve una Order.
- [Conceptos → Reembolsos](https://docs.infraio.xyz/es/concepts/refunds): estados de
  reembolso y el paso manual de envío on-chain.
- [Webhooks → Resumen](https://docs.infraio.xyz/es/webhooks/overview): cada evento disparado
  durante el ciclo de vida de una Order.
