# Ventas de Polar

Lista, consulta y reembolsa las ventas que cobra Polar (Merchant of Record) con tus links.

> [!WARNING]
> El SDK `vexor` de npm (Stripe, MercadoPago, PayPal, Square, Talo) está **deprecado**: ya no se mantiene y no debe usarse. vexorpay es hoy una **API REST sobre Stripe, siempre en USD, sin SDK oficial**. Migración: <https://developers.vexorpay.com/docs/migracion-desde-el-sdk>

Fuente: <https://developers.vexorpay.com/docs/api/orders>

Si cobrás con **Polar**, cada venta (un pago único o la renovación de una suscripción) es una `order`. Polar es el Merchant of Record: cobra al comprador, calcula y liquida el impuesto y te acredita el neto en tu saldo de Polar (ver [Saldo](/docs/api/balance)). Las ventas que cobraste antes con Stripe siguen en [Cobros](/docs/api/charges).

## El objeto Order

```json
{
  "id": "66f1c1e2c8a4b0d1f2e3a4d1",
  "provider": "polar",
  "polarOrderId": "5ca7caa3-d059-49aa-85c0-a0dfbee9844e",
  "paymentLinkId": "66f1c1e2c8a4b0d1f2e3a4b5",
  "polarSubscriptionId": null,
  "status": "paid",
  "billingReason": "purchase",
  "currency": "usd",
  "subtotalCents": 2500,
  "taxCents": 0,
  "totalCents": 2500,
  "netCents": 2337,
  "refundedCents": 0,
  "refundableCents": 2500,
  "customerEmail": "cliente@ejemplo.com",
  "customerName": "Ana Pérez",
  "productName": "Curso de fotografía",
  "seats": null,
  "sandbox": false,
  "createdAt": "2026-10-24T07:55:33.971Z"
}
```

- `totalCents` es lo que pagó el comprador y `taxCents`, el impuesto que liquida Polar. `netCents` es el monto sin impuesto (después de descuentos): es lo que Polar acredita en tu saldo, y de ahí descuenta su comisión. Lo que te queda por cada venta lo ves en Movimientos del dashboard.
- `subtotalCents` es el precio antes de descuentos; si el precio tenía el impuesto incluido (compradores de la UE), el impuesto está adentro.
- `refundableCents` es lo que todavía se puede reembolsar (sin impuesto).
- `status`: `paid`, `partially_refunded`, `refunded`, `pending` o `void`.
- `billingReason`: `purchase` (pago único), `subscription_create` o `subscription_cycle` (renovación).

## Listar ventas

`GET /api/v1/orders`

| Parámetro | Descripción |
| --- | --- |
| `status` | Filtra por estado. |
| `payment_link_id` | Ventas de un link. |
| `limit` / `starting_after` / `ending_before` | Paginación por cursor (default 10, máximo 100). |

```bash
curl "https://api.vexorpay.com/api/v1/orders?status=paid" \
  -H "Authorization: Bearer vxp_live_tu_clave"
```

## Ver una venta

`GET /api/v1/orders/{id}`

Devuelve la venta o `404 order_not_found`.

## Reembolsar una venta

`POST /api/v1/orders/{id}/refund`

Reembolsa una venta `paid` o `partially_refunded`, total o parcialmente. El dinero sale de tu saldo de Polar y el impuesto se devuelve en proporción. **Es irreversible.** Soporta `Idempotency-Key`: un replay devuelve la venta sin volver a reembolsar.

| Campo | Tipo | Requerido | Descripción |
| --- | --- | --- | --- |
| `amount` | number | no | Dólares a reembolsar, sin impuesto. Si se omite, se reembolsa todo `refundableCents`. |
| `reason` | string | no | `customer_request` (default), `duplicate`, `fraudulent`, `service_disruption`, `satisfaction_guarantee` u `other`. |
| `comment` | string | no | Nota interna (hasta 500 caracteres). |

```bash
curl -X POST https://api.vexorpay.com/api/v1/orders/66f1c1e2c8a4b0d1f2e3a4d1/refund \
  -H "Authorization: Bearer vxp_live_tu_clave" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: refund-ORD-1001" \
  -d '{ "amount": 10, "reason": "customer_request" }'
```

Devuelve la venta actualizada (`refundedCents`, `refundableCents` y `status`).

## Errores

| Error | HTTP | Causa |
| --- | --- | --- |
| `validation_error` | 400 | `status` inválido, campo desconocido, `amount` fuera de rango, `reason` inválido o la venta no está pagada. |
| `order_not_found` | 404 | No existe esa venta en tu cuenta (o es de otro modo). |
| `polar_reconnect_required` | 409 | Tu conexión con Polar es anterior a los reembolsos: reconecta Polar desde el dashboard. |
| `idempotency_conflict` | 409 | La `Idempotency-Key` ya se usó con un body distinto. |
| `polar_update_failed` | 502 | Polar no procesó el reembolso. No se cambió nada: reintenta. |
