# Receta: claves de licencia

> Vende una app de escritorio, un CLI, un plugin o una plantilla con claves de licencia. La app verifica la clave sin ningún secreto dentro.

Perly Pay emite una clave cuando el pago se entrega, cuenta las activaciones por dispositivo y responde «¿es válida esta clave para este producto aquí?» con endpoints públicos. El programa que distribuyes usa `LicenseClient`, que no necesita clave secreta.

> **Nota:** Con un asistente: la skill `sell-license-keys` del plugin de Perly Pay hace esta receta en tu proyecto. El ejemplo ejecutable es `examples/license-keys` (MIT) en el repositorio del plugin.

## Cómo funciona

1. **Un producto que emite claves** — `issues_license: true` y `license_activation_limit` (dispositivos por clave), con un precio único, vitalicio, por plazo o una suscripción.
2. **Véndelo** — Un [enlace de pago](https://pay.perly.io/docs/payment-links.md) en tu sitio o una factura por comprador.
3. **Entrega la clave** — Con el webhook `license.created` (su objeto es la clave) o con `licenseKeys.list({ customer })` tras `invoice.fulfilled`. El recibo no lleva la clave.
4. **Activa en la app** — `activate({ key, product, instance_name })` con un nombre que no cambie en ese dispositivo; guarda el id de la activación.
5. **Verifica al arrancar** — `validate({ key, product, instance })`. Un `valid: false` claro bloquea; sin red, la última respuesta buena vale unos días.

**Node**

```js
import { LicenseClient } from "perly-pay";

const licenses = new LicenseClient(); // no secret key, ever
const result = await licenses.activate({ key, product: "prod_…", instance_name: deviceName });
if (!result.valid) showWhy(result.reason); // limit_reached, revoked, expired, …
```

**PHP**

```php
use Perly\Pay\LicenseClient;

$result = (new LicenseClient())->activate(['key' => $key, 'product' => 'prod_…', 'instance_name' => home_url()]);
if (!$result['valid']) echo $result['reason'];
```

## Ejecuta el ejemplo

1. **Instala** — `npm install` en `examples/license-keys` (Node 20.12+).
2. **Clave** — Copia `.env.example` a `.env` y pon una clave de prueba (`sk_test_…`) en `PERLY_PAY_SECRET_KEY`.
3. **Prepara** — `npm run setup` crea el producto y el precio con tu clave e imprime sus ids para `.env`.
4. **Ejecuta** — `npm start` (o `npm run dev`), según diga su README.
5. **Paga sin billetera** — `npm run buy-test -- you@example.com` compra una copia en modo de prueba e imprime la clave; luego `npm run app -- activate <key>` y `npm run app -- pro hello`.

## Errores comunes

- Una clave `sk_…` dentro de la app. Cualquiera puede extraerla; el programa que distribuyes usa solo `LicenseClient`.
- Sin `product`, la llamada se rechaza (`parameter_missing`). Pásalo siempre; la clave de cualquier otra cosa responde `wrong_product`.
- Un `instance_name` que cambia en cada arranque: cada arranque gasta una activación.
