Suscripciones y Trials
Gestiona planes, suscripciones y períodos de prueba. Soporte para trials individuales y corporativos (B2B).
Planes
Un plan define el precio, duración y acceso de una suscripción:
| Campo | Descripción |
|---|---|
| name | Nombre visible (ej: "Plan Mensual") |
| slug | Identificador único (ej: "plan_mensual") |
| price | Precio en centavos (ej: 2990000 = $29,900 COP) |
| currency | Moneda (COP, USD) |
| interval | Período: monthly, quarterly, yearly, 4-week (28 días exactos, ver abajo) |
| trial_days | Días de prueba gratis (0 = sin trial) |
| features | Lista de features incluidos |
Estados de Suscripción
| Estado | Descripción | Acceso |
|---|---|---|
TRIAL | En período de prueba | ✅ Sí |
ACTIVE | Pago al día | ✅ Sí |
PAUSED | Pausada temporalmente | ❌ No |
CANCELLED | Cancelada por usuario | ❌ No |
EXPIRED | Venció sin renovar | ❌ No |
Trial Individual
Período de prueba gratuito para usuarios individuales:
- Duración configurable por plan (ej: 7 días)
- No requiere tarjeta de crédito
- Acceso completo durante el trial
- Notificación antes de expirar
- Conversión automática a pago si agrega tarjeta
Trial Corporativo (B2B)
Trials especiales para empresas con políticas personalizadas:
| Característica | Individual | Corporativo |
|---|---|---|
| Duración | Fija por plan | Configurable por empresa |
| Verificación | Email personal | Dominio corporativo |
| Límite usuarios | 1 | Ilimitado o por cuota |
| Facturación | Individual | Consolidada a empresa |
Configurar Trial Corporativo
- Crea una Corporate Policy en el dashboard
- Define el dominio (ej: @empresa.com)
- Configura duración y límites
- Usuarios con ese dominio obtienen trial especial
Precio por Fases (Precio de Introducción)
Un plan puede tener un precio de introducción que dura N ciclos de cobro y después pasa a la siguiente fase (o al precio de lista si es la última) — por ejemplo, "50% off los primeros 3 meses, luego precio completo". Cada fase se configura desde el dashboard del tenant (Suscripciones → Planes → Precio por fases): tipo de descuento (porcentaje o precio fijo) y cuántos ciclos de cobro dura.
Consultar el precio vigente (checkout del cliente)
Endpoint público, sin autenticación, para que el frontend del cliente sepa qué precio y mensaje de promoción mostrar sin hardcodearlo a mano:
GET https://engine.paywl.io/edge/plans?domain=<su-dominio>
[
{
"id": "plan-uuid",
"name": "Plan Mensual",
"price_cents": 2990000,
"currency": "COP",
"billing_interval": "monthly",
"phases": [
{
"phase_order": 1,
"discount_type": "percentage",
"discount_value": 50,
"duration_periods": 3,
"effective_price_cents": 1495000
}
]
}
]
Un plan sin precio por fases devuelve phases: []. effective_price_cents ya viene calculado (precio de lista menos el descuento de esa fase) — no hace falta que el frontend haga la cuenta.
Período de Facturación Personalizado (4 semanas)
Además de los intervalos calendario estándar (mensual, trimestral, anual), un plan puede usar interval: "4-week": un ciclo de cobro de exactamente 28 días, no "un mes calendario". Pensado para medios que quieren un cobro semanal-consistente en vez del corrimiento típico de los ciclos mensuales (ej: el 31 de enero cobra distinto que el 28 de febrero).
setDate(+28), no setMonth(+1)) — el ciclo nunca se desalinea con el día de la semana en que empezó.
Códigos de Descuento (Cupones)
Motor de cupones administrable desde el dashboard del tenant (Suscripciones → Cupones), con validación en dos tiempos: una vista previa antes de tokenizar la tarjeta, y la validación autoritativa real al momento del cobro.
| Campo | Descripción |
|---|---|
code | Código que el usuario escribe en el checkout (único por tenant) |
discount_type | percentage (1–100) o fixed_amount (en centavos) |
applies_to | first_period (solo el primer cobro) o recurring (todos los cobros mientras el código siga vigente) |
audience | new, renewal, o both — a qué tipo de checkout aplica |
plan_ids | Lista opcional de planes a los que aplica; null = todos los planes |
max_uses_total / max_uses_per_user | Límites de redención, opcionales |
valid_from / valid_until | Vigencia opcional del código |
Paso previo (opcional): previsualizar el descuento
Antes de montar el widget de Wompi, se puede validar el código que el usuario escribió para mostrarle el descuento en pantalla sin comprometerse todavía:
POST https://engine.paywl.io/edge/validate-discount-code
Content-Type: application/json
{
"domain": "<su-dominio>",
"code": "BIENVENIDA20",
"plan_id": "<uuid del plan>"
}
// Válido
{ "valid": true, "discount_type": "percentage", "discount_value": 20, "discount_preview_cents": 598000 }
// Inválido (código no encontrado, expirado, agotado, etc.)
{ "valid": false, "reason": "Código expirado" }
Aplicarlo en el checkout
Se agrega el mismo code como campo discount_code al formulario de checkout de Wompi (ver Pagos con Wompi, Paso 2) — la validación se repite del lado del servidor al momento de cobrar, así que el preview nunca es la única fuente de verdad.
/edge/validate-discount-code no conoce todavía quién es el usuario en ese punto del flujo, así que max_uses_per_user solo se verifica en la validación real durante la activación — un código puede pasar el preview y aun así ser rechazado al cobrar si ese usuario ya lo usó el máximo de veces permitido.
Monto Variable (Precio Elegido por el Suscriptor)
Un plan puede configurarse con pricing_type: "variable": en vez de un precio fijo, el suscriptor elige cuánto quiere pagar en el checkout, sujeto a un mínimo. Pensado para modelos tipo membresía/donación recurrente (ej. "Círculo Cambio" en Revista Cambio) — mecanismo genérico disponible para cualquier tenant, no exclusivo de un cliente.
| Campo | Descripción |
|---|---|
pricing_type | fixed (default, comportamiento normal) o variable |
price_cents | En un plan variable, deja de ser "el precio" y pasa a ser el mínimo permitido |
custom_amount_cents | Monto que el suscriptor eligió, persistido en la suscripción — el billing cron cobra ese mismo monto en cada renovación |
La capacidad de crear planes de monto variable se activa por tenant — desde el admin root de Paywl (override puntual) o porque el plan de licencia del tenant la incluye por defecto. Si el plan factura contra Siigo y necesita un código de producto específico (siigo_product_code_override), el dashboard del tenant muestra una alerta mientras no esté configurado; hasta que se configure, ese plan simplemente no se ofrece en el checkout público — no bloquea el resto de la plataforma ni genera facturación incorrecta.
Verificación de Estudiantes y Docentes
Precio especial para suscriptores que verifican pertenecer a un dominio de correo académico configurado por el tenant (ej. @universidad.edu.co). La verificación es anual — vence automáticamente y se le pide al suscriptor volver a confirmar su dominio académico cada año.
- Lista de dominios académicos válidos, configurable por tenant desde el dashboard
- Precio especial asociado al plan de estudiante, distinto del precio de lista
- Re-verificación anual automática — no es un descuento permanente sin control
Códigos de Regalo (2x1 / Bono Canjeable)
Un código de regalo activa una suscripción a $0, sin tocar Wompi en absoluto (sin card_token, sin cobro) — reusa el resto del flujo normal de activación (colisión de identidad, entitlement, notificación a HubSpot/Siigo). El plan se resuelve del propio código, no hace falta pasar plan_id.
| Uso | Descripción |
|---|---|
| 2x1 | Un suscriptor activo comparte un código con otra persona, que obtiene la suscripción gratis por el mismo período |
| Bono/gift card B2B | Códigos canjeables entregados en bloque (ej. como beneficio corporativo o promoción de marca) |
discount_code (cupón) y con el monto variable — no tiene sentido combinar "elijo mi propio precio" o un cupón con una activación a $0.
Oferta de Retención al Cancelar
Antes de cancelar de verdad, se le puede preguntar al suscriptor el motivo — y si el tenant configuró una oferta para ese motivo (desde Suscripciones → Ofertas de Retención en el dashboard), mostrarla como alternativa a la cancelación.
| Motivo | Tipo de oferta posible |
|---|---|
too_expensive (es muy caro) | Descuento en la renovación |
not_enough_time (no tengo tiempo de leer) | Pausar la suscripción N días |
content_not_relevant (el contenido no me interesa) | Cambiar a un plan más económico |
other (otro) | Ninguna — cancela directo siempre, por diseño |
Paso 1: previsualizar la oferta
Antes de mostrarle la pantalla de cancelación al usuario, se puede consultar si hay una oferta configurada para el motivo que seleccionó:
POST https://engine.paywl.io/edge/cancellation-offer
Content-Type: application/json
{
"domain": "<su-dominio>",
"user_token": "<token de sesión del usuario>",
"subscription_id": "<uuid>",
"reason": "too_expensive"
}
// Hay oferta configurada
{ "offer": { "type": "discount", "discount_percent": 20, "pause_days": null, "downgrade_plan_id": null } }
// No hay oferta para ese motivo (incluye siempre "other")
{ "offer": null }
Paso 2: confirmar (aceptar la oferta, o cancelar de verdad)
POST https://engine.paywl.io/edge/cancel-subscription
Content-Type: application/json
{
"domain": "<su-dominio>",
"user_token": "<token de sesión del usuario>",
"subscription_id": "<uuid>",
"reason": "too_expensive",
"accept_offer": true
}
Con accept_offer: true (y una oferta configurada para ese motivo), la suscripción no se cancela — sigue activa con el beneficio aplicado. Con accept_offer: false, o si no hay ninguna oferta configurada, se cancela de verdad. En ambos casos se guarda la respuesta de la encuesta para análisis posterior.
Ciclo de Vida
Ejemplo: Crear Suscripción
curl -X POST https://engine.paywl.io/subscriptions/activate \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscriber_id": "user-123",
"plan_id": "plan_mensual",
"payment_reference": "WOMPI-TXN-456"
}'