# Monederos estáticos

> Direcciones de depósito permanentes vinculadas a un pedido o usuario específico, ideales para pagos recurrentes y a largo plazo.

Los monederos estáticos son direcciones permanentes para recibir pagos en criptomonedas. Están vinculadas a un `order_id` específico y son únicas por la combinación de `project_id + order_id + currency + network`.

Usa monederos estáticos para:

- Depósitos recurrentes del mismo usuario
- Direcciones de pago a largo plazo mostradas en el perfil del usuario
- Flujos de depósitos de alto volumen donde quieres una dirección estable por usuario

## Crear monedero estático

`POST /v1/static-wallet`

### Parámetros de la solicitud

| Campo | Tipo | Requerido | Descripción |
|-------|------|-----------|-------------|
| `currency` | string | sí | Criptomoneda (USDT, BTC, ETH, etc.) |
| `network` | string | sí | Código de red |
| `order_id` | string | sí | Tu ID de pedido/usuario (hasta 255 caracteres) |
| `label` | string | no | Etiqueta del monedero (hasta 255 caracteres) |
| `url_callback` | string | sí | URL para las notificaciones de webhook |
| `invite_code` | string | no | Código de referido |

### Ejemplo de solicitud

```json
{
  "currency": "USDT",
  "network": "TRX-TRC20",
  "order_id": "USER-123",
  "label": "User deposit #123",
  "url_callback": "https://your-site.com/webhook/static"
}
```

### Ejemplo de respuesta

```json
{
  "state": 0,
  "result": {
    "uuid": "019b2265-34d8-7001-a230-8f97de90d481",
    "address": "TXYZabc123...",
    "currency": "USDT",
    "network": "TRX-TRC20",
    "label": "User deposit #123",
    "order_id": "USER-123",
    "status": "active",
    "url": "https://go.2328.io/static/019b2265-34d8-7001-a230-8f97de90d481",
    "created_at": "2026-01-20T12:00:00Z",
    "qr": "data:image/png;base64,iVBORw0..."
  }
}
```

## Información del monedero

Obtén la información de un monedero estático mediante `uuid` o `address`.

`POST /v1/static-wallet/info`

### Parámetros de la solicitud

| Campo | Tipo | Requerido | Descripción |
|-------|------|-----------|-------------|
| `uuid` | string | sí* | UUID del monedero estático |
| `address` | string | sí* | Dirección del monedero blockchain |

> **INFO:** Se requiere al menos uno de `uuid` o `address`.

### Ejemplo de respuesta

```json
{
  "state": 0,
  "result": {
    "uuid": "019b2265-34d8-7001-a230-8f97de90d481",
    "address": "TXYZabc123...",
    "currency": "USDT",
    "network": "TRX-TRC20",
    "status": "active",
    "total_received": "1250.50",
    "transactions_count": 3,
    "created_at": "2026-01-20T12:00:00Z",
    "qr": "data:image/png;base64,iVBORw0..."
  }
}
```

- `total_received` — suma de todos los depósitos recibidos por este monedero, en `currency`.
- `transactions_count` — número de depósitos recibidos hasta ahora.
- `qr` — data URI con código QR codificado en Base64 de la dirección de depósito (siempre presente para los monederos estáticos, la dirección se asigna al crearlos).

## Lista de monederos

`POST /v1/static-wallet/list`

### Parámetros de la solicitud

| Campo | Tipo | Requerido | Descripción |
|-------|------|-----------|-------------|
| `status` | string | no | Filtrar por estado (`active`, `inactive`) |
| `currency` | string | no | Filtrar por divisa |
| `network` | string | no | Filtrar por red |
| `order_id` | string | no | Filtrar por order_id |
| `page` | int | no | Número de página (por defecto: 1) |
| `per_page` | int | no | Elementos por página (por defecto: 20, máx.: 100) |

### Ejemplo de respuesta

```json
{
  "state": 0,
  "result": {
    "items": [
      {
        "uuid": "019b2265-...",
        "address": "TXYZabc123...",
        "currency": "USDT",
        "network": "TRX-TRC20",
        "status": "active",
        "total_received": "1250.50",
        "transactions_count": 3
      }
    ],
    "paginate": {
      "count": 1,
      "current_page": 1,
      "per_page": 20,
      "total": 1,
      "total_pages": 1,
      "has_more": false
    }
  }
}
```

## Activar / desactivar monedero

Alterna si un monedero estático acepta nuevos pagos.

`POST /v1/static-wallet/disable`

`POST /v1/static-wallet/enable`

### Solicitud

Ambos endpoints aceptan un único parámetro:

```json
{
  "uuid": "019b2265-34d8-7001-a230-8f97de90d481"
}
```

### Ejemplo de respuesta

```json
{
  "state": 0,
  "result": {
    "uuid": "019b2265-34d8-7001-a230-8f97de90d481",
    "status": "inactive",
    "message": "Static wallet disabled successfully"
  }
}
```

Para `enable`, `status` es `"active"` y `message` muestra `"Static wallet enabled successfully"`.

## Transacciones del monedero

Obtén una lista de todos los depósitos recibidos por un monedero estático.

`POST /v1/static-wallet/transactions`

### Parámetros de la solicitud

| Campo | Tipo | Requerido | Descripción |
|-------|------|-----------|-------------|
| `uuid` | string | sí | UUID del monedero estático |
| `date_from` | date | no | Fecha de inicio (YYYY-MM-DD) |
| `date_to` | date | no | Fecha de fin (YYYY-MM-DD) |
| `page` | int | no | Número de página (por defecto: 1) |
| `per_page` | int | no | Elementos por página (por defecto: 15, máx.: 5000) |

### Ejemplo de respuesta

```json
{
  "state": 0,
  "result": {
    "items": [
      {
        "uuid": "abc123-def456-...",
        "order_id": "USER-123",
        "amount": "100.00",
        "currency": "USDT",
        "payment_status": "paid",
        "txid": "0xabc123def456...",
        "fee_amount": "3.00",
        "net_amount": "97.00",
        "created_at": "2026-01-20T15:30:00Z"
      }
    ],
    "paginate": {
      "count": 1,
      "hasPages": true,
      "perPage": 15,
      "page": 1
    }
  }
}
```

- `fee_amount` — comisión de la plataforma deducida de este depósito, en `currency`.
- `net_amount` — monto acreditado al saldo del comerciante después de la comisión.

## Webhooks de monedero estático

Cuando se recibe un pago en un monedero estático, el sistema envía un webhook a `url_callback`.

> **WARNING:** El formato del webhook para monederos estáticos difiere del de los webhooks de pago habituales. En particular, los webhooks de monedero estático incluyen un campo `merchant_amount` que deberías usar para acreditar.

### Payload del webhook

```json
{
  "uuid": "a28b293f-5c76-4053-8062-ae9ca4ab784b",
  "order_id": "USER-7666308594",
  "amount": "10.00000000",
  "currency": "USDT",
  "amount_usd": "10.00000000",
  "exchange_rate": "1.00000000",
  "payer_currency": "USDT",
  "payer_amount": "10.00000000",
  "network": "TRX-TRC20",
  "address": "TMU9Tgpchvgbywkbj5SdC8KJS73t5m3M7G",
  "payment_status": "paid",
  "txid": "8369ede26a0da05b1bae154b4bb4072eb2453db30ba86b21831902670929454f",
  "tx_explorer_url": "https://tronscan.org/#/transaction/8369ede26a0da05b1bae154b4bb4072eb2453db30ba86b21831902670929454f",
  "payment_amount": "10.00000000",
  "merchant_amount": "9.920000000000000000",
  "created_at": "2026-05-09T16:13:04+03:00",
  "sign": "dd958d1405febce670a9a196e9141784b9f2a5f39cd6d1832d6f3f68d0de1e10"
}
```

> **INFO:** Los webhooks de monedero estático **no** incluyen `url` ni `expires_at` (dado que la dirección es permanente, no una sesión). **Sí** incluyen `exchange_rate` y `created_at`.

### Referencia de campos

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `uuid` | string | UUID de la transacción (factura) de este depósito |
| `order_id` | string | El `order_id` de tu monedero estático |
| `amount` | decimal (8 dp) | Monto cripto recibido |
| `currency` | string | Cripto recibida (coincide con la `currency` del monedero) |
| `amount_usd` | decimal (8 dp) | Valor en USD al momento de la recepción |
| `exchange_rate` | decimal | Tipo de cambio cripto / USD utilizado |
| `payer_currency` | string | Igual que `currency` para los monederos estáticos |
| `payer_amount` | decimal (8 dp) | Igual que `amount` para los monederos estáticos |
| `network` | string | Red blockchain |
| `address` | string | Dirección del monedero estático |
| `payment_status` | string | Estado actual del dep?sito; normalmente `paid`, pero AML puede producir `aml_lock`, que no debe acreditarse autom?ticamente |
| `txid` | string | Hash de la transacción en la blockchain |
| `tx_explorer_url` | string \| null | URL de la transacción en el explorador de blockchain. Es `null` si no hay `txid` o si la transferencia es P2P interna. |
| `payment_amount` | decimal (8 dp) | Igual que `amount` |
| `merchant_amount` | decimal (18 dp) | **Monto después de la deducción de comisión** — úsalo para acreditar |
| `created_at` | string (ISO 8601) | Cuándo se recibió el depósito |
| `sign` | string (hex) | Firma HMAC-SHA256 del payload |

## Buenas prácticas

- **`order_id` único** — usa un `order_id` único para cada usuario o pedido
- **Idempotencia** — verifica `txid` antes de procesar para evitar acreditaciones duplicadas
- **Verifica firmas** — verifica SIEMPRE la firma `sign` antes de acreditar fondos
- **Usa `merchant_amount`** — acredita a los usuarios basándote en `merchant_amount`, no en `payment_amount`

## Ciclo de vida y idempotency

Un static wallet es una identidad de depósito reutilizable, no un invoice. No tiene cantidad esperada ni fecha de caducidad. Una sola dirección puede generar cualquier número de transacciones de depósito a lo largo de su vida útil.

La creación se idempotent para el mismo proyecto merchant, `order_id`, `currency` y `network`: se devuelve la cartera existente. Mantén esa tupla estable y persiste la cartera devuelta `uuid`; No uses un `order_id` nuevo cada vez que el mismo cliente abra la pantalla de depósito.

El idempotency de depósito es diferente del idempotency de cartera:

- `order_id` identifica el mapeo de cartera reutilizable/cliente;
- la `uuid` de cartera identifica el registro permanente de la cartera;
- webhook `uuid` identifica una transacción de depósito detectada;
- `txid` identifica la transferencia on-chain y es la clave principal de deduplicación para la acreditación.

Utiliza una restricción de unicidad de base de datos para la identidad procesada de la cadena/red/txid y reclama la propiedad en la misma transacción que acredita el saldo interno del cliente.

## Activar y desactivar la semántica

Desactivar una cartera impide que la aplicación la procese como destino de depósito activo; No borra la dirección ni su historial y no puede detener una transferencia de blockchain ya enviada por un usuario.

> **DANGER:** Nunca digas a los usuarios que los fondos enviados a una dirección inactiva se devuelven automáticamente. Las transferencias en blockchain son irreversibles. Desactiva solo después de eliminar la dirección de tu interfaz de usuario y mantén un procedimiento operativo de recuperación para depósitos atrasados.

Reactivar preserva la misma identidad y dirección de la cartera. No crees un reemplazo solo para cambiar la etiqueta; Las etiquetas no son identificadores settlement.

## Static wallet casos límite

| Situación | Manejo correcto |
|-----------|------------------|
| Solicitud de creación de duplicados | Aceptar la cartera existente devuelta y verificar su tupla persistida en lugar de esperar una nueva dirección. |
| Múltiples depósitos a una dirección | Crear una fila de depósito local separada para cada transacción `uuid`/`txid`; nunca marcar la propia cartera como “pagada.” |
| Duplicado webhook | Devolver HTTP 200 después de encontrar el txid ya comprometido; nunca acreditar nuevamente. |
| Retraso de confirmación o reobservación de la cadena | Continuar procesando idempotent y conciliar desde `/v1/static-wallet/transactions`. |
| Depósito por debajo de un mínimo auto-convert | Esperar crédito en la moneda de origen sin un bloque `convert` completado. |
| Auto-convert tiene éxito | Almacenar los valores de pago de origen y el resultado `convert` de destino por separado. |
| Token incorrecto o red incorrecta | No fabricar un crédito. Registrar evidencia y escalar al soporte/recuperación porque la recuperabilidad es específica de la cadena. |
| Cadena basada en memo/tag | Mostrar y validar cada campo de destino devuelto por la plataforma; solo una dirección puede ser insuficiente cuando se requiere un memo. |
| Bloqueo AML | No acreditar al usuario final hasta que el estado autorizado sea liberado a través del proceso de cumplimiento. |
| Cartera deshabilitada después de mostrar dirección | Eliminarla de la interfaz inmediatamente, pero continuar monitoreando alertas operativas para transferencias tardías. |

## Modelo Reconciliation

Ejecutar un trabajo periódico que recorra `/v1/static-wallet/transactions`, actualice o inserte depósitos por txid, y compare su `merchant_amount`, estado y resultado de conversión opcional con su libro mayor interno. La entrega Webhook debería hacer que reconciliation sea rápido, pero reconciliation debe hacerlo completo.