# Autorizaciones de cobro

Recoge el consentimiento firmado con el que tu usuario te permite cobrarle sin estar delante, para suscripciones y cobros programados, y consúltalo o retíralo.

Una autorización de cobro es el consentimiento firmado con el que tu usuario te permite cargar en su tarjeta sin que esté delante. Sin ella no hay cuota que se cobre sola ni cobro programado: un cargo sin autorización es un cargo no autorizado. UTILIA OS guarda la aceptación como prueba legal y la usan las [suscripciones](https://os.utilia.ai/docs/developers/sdk/subscriptions) con cobro automático, los [cobros programados](https://os.utilia.ai/docs/developers/sdk/invoices#programar-un-cobro) y `invoices.charge`. Funciona con la clave de tu aplicación externa, siempre desde tu servidor.

Guardar la tarjeta y autorizar el cobro son dos pasos distintos y hacen falta los dos, en ese orden: la [tarjeta guardada](https://os.utilia.ai/docs/developers/sdk/payment-methods) permite cobrar con el usuario delante; la autorización, cobrarle sin él.

## Antes de empezar

- El plan necesita **Finanzas** y **Pagos con tarjeta (Stripe)**, con la capacidad «Cobro automático de facturas».
- Scopes: `billing:payment-authorizations:read` para consultar, `:write` para registrar y `:revoke` para retirar. El de registrar es crítico: solo lo concede un Super Administrador, con motivo.
- El servicio se llamaba `mandates` hasta la versión 3.0.0. El nombre antiguo sigue funcionando como alias en desuso (devuelve el mismo servicio y avisa una vez por proceso).

| JavaScript | Python | Para qué |
| - | - | - |
| `paymentAuthorizations.getConsentText({ scope? })` | `payment_authorizations.get_consent_text(scope)` | Texto de consentimiento vigente |
| `paymentAuthorizations.create(datos)` | `payment_authorizations.create(datos)` | Registrar la aceptación |
| `paymentAuthorizations.list(filtros)` | `payment_authorizations.list(filtros)` | Listar |
| `paymentAuthorizations.get(id, { userId })` | `payment_authorizations.get(id, user_id=...)` | Ver una |
| `paymentAuthorizations.revoke(id, datos)` | `payment_authorizations.revoke(id, datos)` | Retirarla |

## Dos alcances, dos autorizaciones

**Qué cubre cada alcance**

### Cuotas de suscripción (RECURRING)

Cubre los cobros periódicos de una suscripción con cobro automático. No se gasta: vale hasta que se retire.

### Cobros programados (SCHEDULED_INVOICE)

Cubre los cobros programados en una fecha sobre facturas sueltas del mismo usuario.

_Una no sirve para lo otro: si tu aplicación hace las dos cosas, recoge dos autorizaciones del mismo usuario._

Si usas la de un alcance para lo otro, el servidor responde 409 `PAYMENT_AUTHORIZATION_NOT_ACTIVE` con un mensaje que dice qué alcance hace falta. Las autorizaciones de un solo uso las crea el propio motor de cobro; no se pueden pedir desde fuera.

## Recoger la autorización

Pide el texto justo antes de mostrarlo: no lo guardes de una sesión anterior ni lo compartas entre organizaciones. La tarjeta va como `setupIntentId` si el usuario la acaba de introducir, o como `paymentMethodId` si reutiliza una guardada; hace falta una de las dos.

**Recoger una autorización de cobro**

Participantes: Usuario, Tu servidor y UTILIA OS.

1. Tu servidor → UTILIA OS: `GET /api/external/v1/payment-authorizations/consent-text`. getConsentText con el alcance que necesitas.
2. UTILIA OS → Tu servidor (respuesta): Texto, versión y nombre fiscal de quien cobra
3. Tu servidor → Usuario: Muestra el texto tal cual. Sin reescribirlo.
4. Usuario → Tu servidor: Lo acepta. Tu servidor anota el momento, la IP y el navegador.
5. Tu servidor → UTILIA OS: `POST /api/external/v1/payment-authorizations`. create con el usuario, el texto, la versión, la aceptación, el alcance y la tarjeta.
6. UTILIA OS → Tu servidor (respuesta): La autorización registrada
7. UTILIA OS → Tu servidor: Aviso PAYMENT_AUTHORIZATION_GRANTED. Llega a tu servidor si tienes configurados los avisos.

_El texto, la versión, el momento, la IP y el navegador forman la prueba del consentimiento._

La IP y el navegador los mandas tú porque es tu servidor quien sabe desde dónde aceptó; UTILIA OS guarda la prueba sin posibilidad de cambio.

### JavaScript

```typescript
const consentimiento = await sdk.paymentAuthorizations.getConsentText({ scope: 'RECURRING' });
// Muestra consentimiento.text; consentimiento.companyName es quien cobrará

const autorizacion = await sdk.paymentAuthorizations.create({
  userId: 'user_01HXYZ',
  setupIntentId: 'seti_1OabcDEF',
  consentText: consentimiento.text,
  consentVersion: consentimiento.version,
  acceptedAt: new Date().toISOString(),
  acceptedIp: req.ip,
  acceptedUserAgent: req.headers['user-agent'],
  scope: 'RECURRING',
});
```

### Python

```python
from datetime import datetime, timezone

from utilia_sdk.models import CreateExternalPaymentAuthorizationInput

consentimiento = await sdk.payment_authorizations.get_consent_text(scope="RECURRING")
# Muestra consentimiento.text; consentimiento.company_name es quien cobrará

autorizacion = await sdk.payment_authorizations.create(
    CreateExternalPaymentAuthorizationInput(
        user_id="user_01HXYZ",
        setup_intent_id="seti_1OabcDEF",
        consent_text=consentimiento.text,
        consent_version=consentimiento.version,
        accepted_at=datetime.now(timezone.utc).isoformat(),
        accepted_ip=request.client.host,
        accepted_user_agent=request.headers.get("user-agent"),
        scope="RECURRING",
    )
)
```

Si el texto o la versión no son los vigentes, la llamada falla con un 422 `PAYMENT_AUTHORIZATION_CONSENT_MISMATCH` sin gastar la intención de guardado de la tarjeta: vuelve a pedir el texto y enséñaselo otra vez al usuario. `create` hace un único intento, sin reintentos automáticos.

## Consultar y retirar

`list` filtra por `userId` y por `active`. Ninguna respuesta incluye el número de la tarjeta ni el texto íntegro del consentimiento: solo la marca, los cuatro últimos dígitos y la caducidad.

### JavaScript

```typescript
const pagina = await sdk.paymentAuthorizations.list({ userId: 'user_01HXYZ', active: true });

await sdk.paymentAuthorizations.revoke(autorizacion.id, {
  userId: 'user_01HXYZ',
  reason: 'El usuario la retiró desde su perfil.',
});
```

### Python

```python
from utilia_sdk.models import (
    ListExternalPaymentAuthorizationsFilters,
    RevokeExternalPaymentAuthorizationInput,
)

pagina = await sdk.payment_authorizations.list(
    ListExternalPaymentAuthorizationsFilters(user_id="user_01HXYZ", active=True)
)

await sdk.payment_authorizations.revoke(
    autorizacion.id,
    RevokeExternalPaymentAuthorizationInput(
        user_id="user_01HXYZ", reason="El usuario la retiró desde su perfil."
    ),
)
```

> **Atención: Retirar una autorización no se deshace**
>
> La retirada es inmediata: cancela los cobros programados pendientes que dependían de ella y pasa a cobro manual las suscripciones que la usaban. Antes de ofrecer el botón en tu interfaz, explica al usuario qué deja de cobrarse.

## Errores y avisos

| `errorCode` | HTTP | Qué hacer |
| - | - | - |
| `PAYMENT_AUTHORIZATION_CONSENT_MISMATCH` | 422 | Vuelve a pedir el texto y muéstraselo al usuario. |
| `PAYMENT_AUTHORIZATION_NOT_FOUND` | 404 | No existe o no es de ese usuario. |
| `PAYMENT_METHOD_NOT_FOUND` | 404 | La tarjeta no existe o no es de ese usuario. |
| `STRIPE_ACCOUNT_NOT_READY` | 409 | La cuenta de cobro de la organización no está lista. |
| `EXTERNAL_USER_NOT_FOUND` | 404 | [Identifica](https://os.utilia.ai/docs/developers/sdk/users) antes al usuario. |

Los bloqueos de plan (`MODULE_NOT_ENABLED`, `CAPABILITY_NOT_ENABLED`) y la falta de scope (`INSUFFICIENT_SCOPE`) llegan como 403 y no se reintentan.

Avisos que recibe tu servidor: `PAYMENT_AUTHORIZATION_GRANTED` (el usuario ha autorizado), `PAYMENT_AUTHORIZATION_CONSUMED` (una de un solo uso se gastó al cobrar), `PAYMENT_AUTHORIZATION_REVOKED` (retirada por el usuario o por el equipo) y `PAYMENT_METHOD_EXPIRING` (la tarjeta caduca pronto: pide otra antes de que falle la renovación).

## Siguiente paso

- [Suscripciones](https://os.utilia.ai/docs/developers/sdk/subscriptions): Monta la cuota que se cobrará con esta autorización.
- [Endpoints de autorizaciones de cobro](https://os.utilia.ai/docs/developers/api/payment-authorizations): El contrato REST completo, campo a campo.
