# Métodos de pago

Comprueba si la organización puede cobrar con tarjeta y guarda, lista, marca como predeterminada o retira la tarjeta de tus usuarios con Stripe Elements.

El servicio de métodos de pago gestiona las tarjetas que tus usuarios guardan para pagar sus facturas. La tarjeta se introduce en tu web con Stripe Elements, sobre la cuenta de Stripe de tu organización; UTILIA OS la registra y te deja listarla, elegir la predeterminada y retirarla. Cada clave solo ve las tarjetas de los usuarios de su propia aplicación.

> **Atención: Guardar la tarjeta no autoriza cobros futuros**
>
> Con una tarjeta guardada puedes cobrar **con el usuario delante**. Para cobrarle sin que esté (una cuota mensual, un cobro programado) hace falta además su [autorización de cobro](https://os.utilia.ai/docs/developers/sdk/payment-authorizations), un consentimiento firmado que sirve de prueba legal.

## Antes de empezar

| JavaScript | Python | Para qué | Scope |
| - | - | - | - |
| `paymentMethods.getReadiness()` | `payment_methods.get_readiness()` | Saber si se puede cobrar con tarjeta | `billing:payment-methods:read` |
| `paymentMethods.createSetupIntent(usuario)` | `payment_methods.create_setup_intent(usuario)` | Empezar a guardar una tarjeta | `billing:payment-methods:write` |
| `paymentMethods.list(userId)` | `payment_methods.list(user_id)` | Tarjetas activas del usuario | `billing:payment-methods:read` |
| `paymentMethods.setDefault(pmId, userId)` | `payment_methods.set_default(pm_id, user_id)` | Elegir la predeterminada | `billing:payment-methods:write` |
| `paymentMethods.remove(pmId, userId)` | `payment_methods.remove(pm_id, user_id)` | Retirar una tarjeta | `billing:payment-methods:write` |

El plan de la organización tiene que incluir **Finanzas** y **Pagos con tarjeta (Stripe)**. Si falta alguno, la respuesta es un [bloqueo de plan](https://os.utilia.ai/docs/developers/sdk/plan-errors) (`MODULE_NOT_ENABLED`): no se arregla conectando una cuenta ni dando permisos.

## Comprobar si se puede cobrar

Antes de montar el formulario de tarjeta, pregunta si la organización puede cobrar. Así no le pides la tarjeta a nadie para descubrir después que el cobro no es posible. Llámalo al abrir la pantalla de pago y al recibir un error de cobro, no en bucle: la situación cambia pocas veces.

La respuesta trae `organizationCanCharge` (el valor sobre el que decides), `platformAvailable` (si la pasarela está encendida en UTILIA OS), `livemode` (pagos reales o de prueba) y `reason`, con uno de estos motivos: `READY`, `PLATFORM_DISABLED`, `NO_ACCOUNT`, `ONBOARDING_INCOMPLETE`, `CHARGES_DISABLED` o `ACCOUNT_DEAUTHORIZED`.

### JavaScript

```typescript
const readiness = await sdk.paymentMethods.getReadiness();
if (!readiness.organizationCanCharge) {
  // No montes Elements: explica que el pago en línea no está disponible.
  console.log(readiness.reason);
}
```

### Python

```python
readiness = await sdk.payment_methods.get_readiness()
if not readiness.organization_can_charge:
    # No montes Elements: explica que el pago en línea no está disponible.
    print(readiness.reason)
```

## Guardar una tarjeta

Tu servidor pide a UTILIA OS el permiso de guardado con los datos del usuario (`externalId` y `email` obligatorios; `name`, `taxId`, `billingAddress` y `metadata` opcionales), y tu web monta con él el formulario de tarjeta de Stripe. `publishableKey` es la clave pública de la plataforma UTILIA OS y puede ir al navegador; lo que separa una organización de otra es `stripeAccountId`.

**Guardar una tarjeta**

Participantes: Tu servidor, Tu web, UTILIA OS y Stripe.

1. Tu servidor → UTILIA OS: `POST /api/external/v1/payment-methods/setup-intent`. createSetupIntent con los datos del usuario.
2. UTILIA OS → Tu servidor (respuesta): clientSecret, publishableKey y stripeAccountId
3. Tu servidor → Tu web: Los tres valores para Stripe Elements
4. Tu web → Stripe: confirmSetup con la tarjeta. El usuario la introduce en el formulario de Stripe.
5. Stripe → UTILIA OS: Aviso de tarjeta guardada. UTILIA OS la registra para ese usuario.
6. UTILIA OS → Tu servidor: Aviso PAYMENT_METHOD_ADDED. Si tienes configurados los avisos a tu servidor.

_La tarjeta se da por guardada cuando llega el aviso, no cuando termina el formulario._

### JavaScript

```typescript
// En tu servidor
const setup = await sdk.paymentMethods.createSetupIntent({
  externalId: 'user_01HXYZ',
  email: 'cliente@empresa.com',
});
// Devuelve a tu web setup.clientSecret, setup.publishableKey y setup.stripeAccountId
```

### Python

```python
from utilia_sdk.models import CreateInvoiceUser

# En tu servidor
setup = await sdk.payment_methods.create_setup_intent(
    CreateInvoiceUser(external_id="user_01HXYZ", email="cliente@empresa.com")
)
# Devuelve a tu web setup.client_secret, setup.publishable_key y setup.stripe_account_id
```

En tu web, con React y Stripe Elements (igual para los dos lenguajes del servidor):

```tsx
import { Elements, PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';

function SaveCard({ setup }: { setup: { clientSecret: string; publishableKey: string; stripeAccountId: string } }) {
  const stripePromise = loadStripe(setup.publishableKey, { stripeAccount: setup.stripeAccountId });
  return (
    <Elements stripe={stripePromise} options={{ clientSecret: setup.clientSecret }}>
      <SaveForm />
    </Elements>
  );
}

function SaveForm() {
  const stripe = useStripe();
  const elements = useElements();

  async function handleSubmit(event: React.FormEvent) {
    event.preventDefault();
    if (!stripe || !elements) return;
    const { error } = await stripe.confirmSetup({
      elements,
      confirmParams: { return_url: window.location.href },
      redirect: 'if_required',
    });
    // Sin error, la tarjeta queda guardada cuando UTILIA OS recibe el aviso de Stripe.
    if (error) console.error(error.message);
  }

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button type="submit">Guardar tarjeta</button>
    </form>
  );
}
```

## Listar, elegir la predeterminada y retirar

Cada tarjeta trae `id` (el identificador que usas en el resto de llamadas y al cobrar), `type` (`CARD`, `SEPA_DEBIT` o `LINK`), `brand`, `last4`, `expMonth` y `expYear`, `isDefault`, `status` (`ACTIVE`, `EXPIRED` o `DETACHED`) y `addedVia` (`INTERNAL_DASHBOARD`, `PORTAL` o `EXTERNAL_APP`). En Python, el modelo se llama `StoredPaymentMethod` y los campos van en `snake_case`.

`setDefault` marca una tarjeta como predeterminada y quita la marca a la anterior en la misma operación. Es la que tu interfaz puede proponer primero cuando el usuario paga; no autoriza ningún cobro sin el usuario delante, que siempre se hace con la tarjeta de su autorización de cobro. `remove` la desvincula de Stripe y la deja como `DETACHED`.

### JavaScript

```typescript
const cards = await sdk.paymentMethods.list('user_01HXYZ');
for (const card of cards) {
  console.log(`${card.brand} •••• ${card.last4} ${card.isDefault ? '(predeterminada)' : ''}`);
}

await sdk.paymentMethods.setDefault(cards[1].id, 'user_01HXYZ');
await sdk.paymentMethods.remove(cards[0].id, 'user_01HXYZ');
```

### Python

```python
cards = await sdk.payment_methods.list("user_01HXYZ")
for card in cards:
    marca = "(predeterminada)" if card.is_default else ""
    print(f"{card.brand} •••• {card.last4} {marca}")

await sdk.payment_methods.set_default(cards[1].id, "user_01HXYZ")
await sdk.payment_methods.remove(cards[0].id, "user_01HXYZ")
```

> **Atención: Retirar una tarjeta afecta al cobro automático**
>
> Las autorizaciones de cobro que se apoyaban en esa tarjeta dejan de servir: las suscripciones con cobro automático y los cobros programados que dependían de ella fallarán en su próxima ejecución. Recoge una tarjeta y una autorización nuevas antes del siguiente cobro.

## Siguiente paso

- [Cobros](https://os.utilia.ai/docs/developers/sdk/payments): Cobra una factura con la tarjeta guardada, con el usuario delante.
- [Autorizaciones de cobro](https://os.utilia.ai/docs/developers/sdk/payment-authorizations): El consentimiento que permite cobrar sin el usuario delante.
