---
name: shopify-ingresos-suscripciones
description: "Solo lectura: si vendes productos por suscripción, calcula tu ingreso recurrente mensual (MRR) y anual (ARR), los suscriptores activos y la tasa de cancelación a partir de los contratos de suscripción. Úsala cuando quieras medir la salud de tus suscripciones."
---

## Propósito
Para las tiendas que venden productos por suscripción con las suscripciones nativas de Shopify, esta skill agrupa los contratos de suscripción activos en las métricas estándar tipo SaaS que le importan al equipo de finanzas: ingreso recurrente mensual (MRR), ingreso recurrente anualizado (ARR), cantidad de suscriptores activos, ingreso promedio por suscriptor (ARPU) y una tasa de cancelación (churn) móvil. Solo lectura: no ejecuta mutaciones.

## Requisitos
- Sesión autenticada de Shopify CLI: `shopify store auth --store <tu-tienda>.myshopify.com --scopes read_own_subscription_contracts`
- Las consultas se ejecutan con `shopify store execute`.
- Permisos (scopes) de la API: `read_own_subscription_contracts`

## Parámetros

| Parámetro | Tipo | Obligatorio | Por defecto | Descripción |
|-----------|------|-------------|-------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., tu-tienda.myshopify.com) |
| churn_window_days | integer | no | 30 | Ventana móvil para calcular la cancelación (cancelaciones / suscriptores al inicio) |
| as_of | string | no | hoy (UTC) | Fecha ISO que se usa como etiqueta de la fecha de corte |
| include_paused | bool | no | false | Cuenta los contratos `PAUSED` (pausados) como ingreso recurrente activo |
| format | string | no | human | Formato de salida: `human` o `json` |

## Seguridad

> ℹ️ Skill de solo lectura: no ejecuta mutaciones. **Esta skill solo tiene sentido si la tienda vende productos por suscripción.** Si `subscriptionContracts` devuelve 0 contratos, el reporte termina con un MRR de 0: esa es la respuesta correcta, no un error.

## Modelo de ingreso recurrente

Para cada contrato de suscripción, su aporte al MRR depende del intervalo de cobro:

```
billing_amount = suma(line.currentPrice × line.quantity) por cada línea del contrato
mrr_contribution =
  billing_amount                       si interval = MONTH e intervalCount = 1
  billing_amount / intervalCount       si interval = MONTH (p. ej., cada 3 meses → /3)
  billing_amount × 4.345               si interval = WEEK (promedio de semanas por mes)
  billing_amount × 4.345 / intervalCount si interval = WEEK con intervalCount > 1
  billing_amount / 12                  si interval = YEAR
  billing_amount × 30 / intervalCount  si interval = DAY
```

Totales:
- **MRR** = Σ mrr_contribution de todos los contratos `ACTIVE` (y `PAUSED` si `include_paused`)
- **ARR** = MRR × 12
- **Suscriptores activos** = cantidad de `customer.id` únicos
- **ARPU** = MRR / suscriptores activos
- **Tasa de cancelación (período)** = contratos cancelados dentro de `churn_window_days` ÷ contratos que estaban activos al inicio de la ventana
- **Suscriptores nuevos netos** = contratos nuevos dentro de la ventana − contratos cancelados dentro de la ventana

## Pasos

1. **OPERACIÓN:** `subscriptionContracts` — consulta
   **Entradas:** `first: 250`, selecciona `id`, `status`, `createdAt`, `updatedAt`, `nextBillingDate`, `customer { id, displayName }`, `currencyCode`, `billingPolicy { interval, intervalCount }`, `lines { edges { node { currentPrice { amount, currencyCode }, quantity, productId, variantId, title } } }`, cursor de paginación
   **Resultado esperado:** Todos los contratos de suscripción; pagina hasta que `hasNextPage: false`

2. Clasifica cada contrato por estado: `ACTIVE`, `PAUSED`, `CANCELLED`, `EXPIRED`, `FAILED`

3. Calcula `mrr_contribution` para cada contrato activo (y pausado, si se pidió) con el modelo de arriba

4. Calcula los totales de MRR / ARR / suscriptores / ARPU

5. Para la cancelación: cuenta las cancelaciones cuyo `updatedAt` cae dentro de `churn_window_days` y cuyo estado pasó a `CANCELLED`; cuenta los contratos que existían y estaban `ACTIVE` en `as_of - churn_window_days`

6. Arma un desglose por producto para que finanzas vea qué SKU de suscripción generan el MRR

## Operaciones GraphQL

```graphql
# subscriptionContracts:query — validado con api_version 2025-01
query SubscriptionMRR($after: String) {
  subscriptionContracts(first: 250, after: $after) {
    edges {
      node {
        id
        status
        createdAt
        updatedAt
        nextBillingDate
        currencyCode
        customer {
          id
          displayName
          defaultEmailAddress { emailAddress }
        }
        billingPolicy { interval intervalCount minCycles maxCycles }
        deliveryPolicy { interval intervalCount }
        lines(first: 50) {
          edges {
            node {
              id
              productId
              variantId
              title
              quantity
              currentPrice { amount currencyCode }
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

## Seguimiento de la sesión

**Claude DEBE mostrar esta salida en cada etapa. Es obligatorio.**

**Al iniciar**, muestra:
```
╔════════════════════════════════════════════════╗
║  SKILL: Ingresos recurrentes de suscripciones  ║
║  Tienda: <dominio de la tienda>                ║
║  Fecha de corte: <YYYY-MM-DD>                  ║
║  Inicio: <YYYY-MM-DD HH:MM UTC>                ║
╚════════════════════════════════════════════════╝
```

**Después de cada paso**, muestra:
```
[N/TOTAL] <CONSULTA|MUTACIÓN>  <NombreOperación>
          → Parámetros: <resumen breve de las entradas clave>
          → Resultado: <conteo o resultado>
```

**Al terminar**, muestra:

Para `format: human` (por defecto):
```
══════════════════════════════════════════════
REPORTE DE MRR DE SUSCRIPCIONES  (fecha de corte: <fecha>)
  Contratos activos:           <n>
  Contratos pausados:          <n>
  Cancelados (últimos <n> d):  <n>
  ─────────────────────────────
  MRR:                         $<monto>
  ARR:                         $<monto>
  Suscriptores activos:        <n>
  ARPU:                        $<monto>
  Tasa de cancelación (<n> d): <pct>%
  Nuevos netos (<n> d):        <n>

  Productos de suscripción con más MRR:
    "<título>"   Suscr.: <n>   MRR: $<n>
    "<título>"   Suscr.: <n>   MRR: $<n>

  Archivo: subscription_mrr_<fecha>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-ingresos-suscripciones",
  "store": "<dominio>",
  "as_of": "<YYYY-MM-DD>",
  "active_contracts": 0,
  "paused_contracts": 0,
  "cancelled_in_window": 0,
  "mrr": 0,
  "arr": 0,
  "active_subscribers": 0,
  "arpu": 0,
  "churn_rate_pct": 0,
  "net_new": 0,
  "currency": "<moneda de la tienda>",
  "by_product": [],
  "output_file": "subscription_mrr_<fecha>.csv"
}
```

## Formato de salida
Archivo CSV `subscription_mrr_<YYYY-MM-DD>.csv` con las columnas:
`contract_id`, `status`, `customer_id`, `customer_email`, `created_at`, `next_billing_date`, `interval`, `interval_count`, `billing_amount`, `mrr_contribution`, `currency`, `product_titles`

## Manejo de errores
| Error | Causa | Solución |
|-------|-------|----------|
| `THROTTLED` | Se superó el límite de solicitudes de la API | Espera 2 segundos y reintenta hasta 3 veces |
| `subscriptionContracts` vacío | La tienda no tiene productos por suscripción | Termina sin error y reporta un MRR de 0; ese es el valor correcto, no un error |
| Contratos en varias monedas | Cada contrato tiene un `currencyCode` distinto | Agrupa el MRR por moneda; no sumes entre monedas: muéstralas por separado en el resultado |
| Contratos prepagados (con `maxCycles`) | El cliente pagó por adelantado N ciclos | Sigue sumando al MRR mientras esté ACTIVE, pero el contrato terminará después de `maxCycles`; márcalo en el resultado |

## Buenas prácticas
- Esta skill solo tiene sentido si la tienda realmente vende productos por suscripción. Si no manejas suscripciones, el MRR será 0, y eso es correcto.
- Córrela el **primer día de cada mes** para que cada corte del MRR coincida con el cierre mensual de finanzas.
- Sigue el MRR semana a semana para detectar a tiempo picos de pagos fallidos: los contratos `FAILED` que no se recuperan se convierten en cancelaciones en menos de 30 días.
- Las suscripciones anuales prepagadas inflan el ARR sin aumentar el flujo de caja de un mes dado: sepáralas cuando reportes a inversionistas que quieren ver el flujo de caja.
- Combínala con `shopify-riesgo-abandono-clientes` (abandono en compras normales) para contrastar la cancelación de suscripciones con el abandono de clientes de compra única: cada uno merece su propia estrategia de retención.
- Tiendas con varias monedas: reporta el MRR por moneda; convertirlo con la tasa de cambio del día da una cifra engañosa que fluctúa con el tipo de cambio y no con el desempeño del negocio.
