# Disputas

Consulta las disputas (chargebacks) de tus ventas de Polar y acéptalas.

> [!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 sobre Polar (Merchant of Record), sin SDK oficial**. Migración: <https://developers.vexorpay.com/docs/migracion-desde-el-sdk>

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

Las disputas (`dispute`, o *chargeback*) son reclamos que abre el titular de una tarjeta sobre una venta. Polar es el Merchant of Record y **las defiende por ti**: no recibe evidencia del creador, y lo único que puedes hacer desde la API es listarlas, verlas y aceptarlas. Polar no avisa las disputas por webhook: vexorpay las sincroniza cada 10 minutos.

## Listar disputas

`GET /api/v1/disputes`

### Parámetros

| Parámetro | Descripción |
| --- | --- |
| `status` | `prevented`, `early_warning`, `needs_response`, `under_review`, `won` o `lost`. |
| `order_id` | Filtrar por la venta disputada (el `id` de [`/orders`](/docs/api/orders)). |
| `limit` / `starting_after` / `ending_before` | Paginación por cursor (default 10, máximo 100). |

### Ejemplo

**curl**

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

**Node.js**

```js
const res = await fetch(
  'https://api.vexorpay.com/api/v1/disputes?status=needs_response',
  {
    headers: { Authorization: 'Bearer vxp_live_tu_clave' },
  },
)

const data = await res.json()
```

**PHP**

```php
<?php

$client = curl_init(
  'https://api.vexorpay.com/api/v1/disputes?status=needs_response',
);
curl_setopt_array($client, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer vxp_live_tu_clave'],
]);

$data = json_decode(curl_exec($client), true);
```

**Python**

```python

response = requests.get(
    'https://api.vexorpay.com/api/v1/disputes',
    params={'status': 'needs_response'},
    headers={'Authorization': 'Bearer vxp_live_tu_clave'},
)
data = response.json()
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://api.vexorpay.com/api/v1/disputes?status=needs_response", nil)
req.Header.Set("Authorization", "Bearer vxp_live_tu_clave")

res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
```

### Respuesta exitosa

`200 OK`

```json
{
  "data": [
    {
      "id": "68f1c1e2c8a4b0d1f2e3a4b5",
      "provider": "polar",
      "orderId": "67f1c1e2c8a4b0d1f2e3a4b5",
      "paymentLinkId": "66f1c1e2c8a4b0d1f2e3a4b5",
      "reference": null,
      "customerEmail": "cliente@mail.com",
      "polarDisputeId": "2b1f0c9e-...",
      "status": "needs_response",
      "statusLabel": "Requiere respuesta",
      "reason": "fraudulent",
      "reasonLabel": "fraudulent",
      "amountCents": 1900,
      "currency": "usd",
      "feeCents": 1500,
      "evidenceDeadline": "2026-10-15T12:00:00.000Z",
      "daysLeft": 7,
      "isOpen": true,
      "canAccept": true,
      "outcome": null,
      "sandbox": false,
      "paymentLinkUrl": "https://www.vexorpay.com/p/a1b2c3d4e5",
      "createdAt": "2026-10-08T12:03:00.000Z",
      "resolvedAt": null
    }
  ],
  "has_more": false,
  "total_count": 1,
  "url": "/v1/disputes?limit=10"
}
```

Campos clave:

- `status`: estado de la disputa en Polar. Abiertas: `early_warning`, `needs_response`, `under_review`. Cerradas: `prevented`, `won`, `lost`.
- `orderId`: la venta disputada ([`GET /api/v1/orders/{id}`](/docs/api/orders)). `polarDisputeId` es el id de la disputa en Polar.
- `amountCents`: el monto disputado, con impuesto.
- `canAccept`: si se puede aceptar (dar por perdida). Es `true` mientras la disputa está abierta en `needs_response` o `early_warning`.
- `feeCents`: la fee de la disputa (USD 15), que Polar descuenta de tu saldo.
- `evidenceDeadline` / `daysLeft`: plazo de respuesta que informa Polar (`null` cuando no aplica).

## Obtener una disputa

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

El id es el de vexorpay (el campo `id` de la lista, no el `polarDisputeId`). El entorno lo define la API key: con una clave `vxp_test_...` operas sobre disputas de prueba. Devuelve el mismo objeto de la lista o `404 dispute_not_found`.

## Aceptar una disputa

`POST /api/v1/disputes/{id}/accept`

La das por perdida: el monto disputado no vuelve. **Es irreversible.** Es la única acción posible sobre una disputa. Devuelve la disputa actualizada. Con un token OAuth exige el scope `disputes:write`.

```bash
curl -X POST https://api.vexorpay.com/api/v1/disputes/68f1c1e2c8a4b0d1f2e3a4b5/accept \
  -H "Authorization: Bearer vxp_live_tu_clave"
```

## Errores

| Error | HTTP | Causa |
| --- | --- | --- |
| `validation_error` | 400 | `status` no es un estado válido o el cursor de paginación no existe. |
| `dispute_not_found` | 404 | No existe esa disputa en tu cuenta (o es de otro modo). |
| `dispute_not_respondable` | 400 | La disputa ya está cerrada o no se puede aceptar. |
| `polar_reconnect_required` | 409 | Aceptar una disputa exige reconectar Polar desde el dashboard (la conexión es anterior a este permiso). |
| `polar_update_failed` | 502 | Polar no aceptó la disputa. Reintenta. |
