# Ventas

Lista, consulta y reembolsa las ventas que cobras con tus links, con la comisión y el neto de cada una.

> [!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 de links de pago en USD, sin SDK oficial**. Migración: <https://developers.vexorpay.com/docs/migracion-desde-el-sdk>

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

Cada venta (un pago único, el alta de una suscripción o una renovación) es una `order`. El
cobro se hace en tu cuenta de cobro: de cada venta se descuenta la comisión de vexorpay
(desde 2,7 % + USD 1, con el procesamiento incluido) y el resto se acredita en tu saldo (ver
[Saldo](/docs/api/balance)).

## El objeto Order

```json
{
  "id": "67f1c1e2c8a4b0d1f2e3a4b5",
  "paymentLinkId": "66f1c1e2c8a4b0d1f2e3a4b5",
  "subscriptionId": null,
  "checkoutId": "ch_4f8a2c9d1e7b",
  "reference": "ORD-1001",
  "metadata": { "utm_source": "instagram" },
  "status": "paid",
  "billingReason": "one_time",
  "currency": "usd",
  "subtotalCents": 2500,
  "taxCents": 0,
  "totalCents": 2500,
  "feeCents": 232,
  "feeBreakdown": {
    "vexorpay": { "amountCents": 168, "percent": 2.7, "fixedCents": 100 },
    "extras": [
      { "type": "cross_border", "amountCents": 38, "percent": 1.5 },
      { "type": "processing", "amountCents": 19, "percent": null },
      { "type": "fraud_prevention", "amountCents": 7, "percent": null }
    ]
  },
  "netCents": 2268,
  "refundedCents": 0,
  "refundableCents": 2500,
  "customerEmail": "cliente@ejemplo.com",
  "customerName": "Ana Pérez",
  "productName": "Curso de fotografía",
  "sandbox": false,
  "createdAt": "2026-10-24T07:55:33.971Z"
}
```

Todos los montos van en **centavos de USD**, aunque el comprador haya pagado en su moneda local.

| Campo | Qué es |
| --- | --- |
| `totalCents` | Lo que pagó el comprador (impuesto incluido, si se cobró). |
| `subtotalCents` | El precio antes de impuestos. |
| `taxCents` | El impuesto cobrado. Es `0` si te encargas de tus impuestos (la opción por defecto); con el impuesto automático activado, el impuesto calculado. Ver [Cómo funcionan los cobros](/docs/como-funcionan-los-cobros#impuestos). |
| `feeCents` | La comisión de vexorpay de la venta, con el procesamiento del pago incluido (desde 2,7 % + USD 1). `0` en una venta de USD 0; `null` mientras no se conozca. |
| `feeBreakdown` | El desglose de `feeCents` (`null` mientras no se conozca). `vexorpay` es la comisión general: `percent` % del total + `fixedCents` (p. ej. 2,7 % + USD 1, con los valores reales de la venta: el porcentaje cambia con el país de la tarjeta). `extras` son los adicionales de esa venta, cada uno con su `type`, `amountCents` y `percent` (`null` si es un monto fijo o no tiene un porcentaje publicado). Ver abajo. |
| `netCents` | Lo que te queda: el total menos el impuesto y la comisión. |
| `refundedCents` / `refundableCents` | Lo reembolsado y lo que todavía se puede reembolsar. |
| `status` | `paid`, `partially_refunded`, `refunded`, `pending` o `failed`. |
| `billingReason` | Motivo del cobro: `one_time` (pago único), `subscription_create` (alta de suscripción) o `subscription_cycle` (renovación). |
| `subscriptionId` | La [suscripción](/docs/api/subscriptions) a la que pertenece el cobro (`null` en un pago único). |
| `checkoutId` | La sesión de checkout que generó la venta (la que devuelve [`POST /checkout`](/docs/api/checkout)). |
| `reference` / `metadata` | **Tu** referencia de la venta y tu metadata. Ver abajo. |

> Al empezar una **prueba gratis** se registra una venta de USD 0 (`billingReason:
> "subscription_create"`, sin comisión). El primer cobro real llega como otra venta al terminar
> la prueba.

## Los adicionales de la comisión

Además de la comisión general (`feeBreakdown.vexorpay`), algunas ventas suman cargos según cómo se pagó. Cada uno llega en `feeBreakdown.extras` con uno de estos `type`:

| `type` | Cuándo aparece |
| --- | --- |
| `cross_border` | Tarjeta de fuera de la región del comprador (1,5 %). |
| `currency_conversion` | El comprador pagó en otra moneda (1 %). |
| `subscription` | Cobros de suscripciones (0,5 %). |
| `processing` | Procesamiento adicional del pago. |
| `fraud_prevention` | Prevención de fraude (unos centavos por pago). |
| `authentication` | Verificación 3D Secure de la tarjeta. |
| `other` | Otros cargos del procesamiento. |

La suma de `feeBreakdown.vexorpay.amountCents` y de los `extras` es `feeCents`. Pueden aparecer tipos nuevos: tratá un `type` desconocido como `other`.

## Identificar cada venta con tu referencia

**Toda venta trae una referencia única** en el campo `reference`, en sus [webhooks](/docs/api/webhooks) (`data.object.reference`) y en [`/events`](/docs/api/events):

- **Con el link público** (`https://www.vexorpay.com/p/a1b2c3d4e5`): cada visita genera una referencia nueva (`VXP-` + 12 caracteres, p. ej. `VXP-7KQ4M9TZ2HXA`). Puedes compartir el mismo link con toda tu audiencia: cada compra sale con la suya, nunca dos ventas con la misma.
- **Con tu propio id**: agrega `?reference=<tu-id>` al link (`/p/a1b2c3d4e5?reference=ORD-1001`) o manda `reference` en [`POST /api/v1/payment-links/{id}/checkout`](/docs/api/checkout), y se usa la tuya en vez de la generada. El checkout por API sin `reference` también genera una, y la respuesta la devuelve en `reference` (junto con `checkoutId`, que vuelve en la venta).
- El link también acepta `utm_source`, `utm_medium`, `utm_campaign`, `utm_content` y `utm_term`, que quedan en `metadata`.

En una suscripción, la referencia queda en la suscripción y cada renovación la hereda. Para buscar una venta por referencia: `GET /api/v1/orders?reference=VXP-7KQ4M9TZ2HXA`. La referencia admite hasta 200 caracteres.

## Listar ventas

`GET /api/v1/orders`

| Parámetro | Descripción |
| --- | --- |
| `status` | Filtra por estado. |
| `payment_link_id` | Ventas de un link. |
| `reference` | Ventas con esa referencia tuya (coincidencia exacta). |
| `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. **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 (hasta `refundableCents`). Si se omite, se reembolsa todo lo reembolsable. |

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

Devuelve la venta actualizada (`refundedCents`, `refundableCents` y `status`) y se dispara el webhook `payment.refunded`.

## Errores

| Error | HTTP | Causa |
| --- | --- | --- |
| `validation_error` | 400 | `status` inválido, campo desconocido, `amount` fuera de rango o la venta no está pagada. |
| `order_not_found` | 404 | No existe esa venta en tu cuenta (o es de otro modo). |
| `idempotency_conflict` | 409 | La `Idempotency-Key` ya se usó con un body distinto. |
| `upstream_error` | 502 | El procesador de pagos no procesó el reembolso. No se cambió nada: reintenta. |
