Ir al contenido

Suscripción

Ruta: /owner/settings/subscription Gestiona tu plan de suscripción, métodos de pago, historial de facturación y límites de uso.


El módulo de Suscripción te permite administrar completamente tu plan de Factux. Incluye tres secciones principales:

Pestañas: Uso | Historial | Facturación

Componentes principales:

ComponenteDescripción
Resumen de planInformación del plan actual, estado y fecha de renovación
Uso de recursosVisualización de límites y consumo actual
Historial de pagosRegistro de transacciones y facturas de suscripción
Información de facturaciónDatos fiscales y métodos de pago

Factux ofrece tres niveles de suscripción con diferentes límites y características:

PlanPrecioUsuariosFacturas/mesClientesAlmacenamiento
Free0 €/mes110501 GB
Basic19 €/mes310050010 GB
Premium49 €/mesIlimitadoIlimitadoIlimitadoIlimitado

NOTA: Todos los planes incluyen período de prueba de 14 días sin necesidad de tarjeta de crédito.


FunciónDescripciónDocumentación
Ver plan actualConsulta detalles del plan, estado y límitesVer
Cambiar planActualiza o reduce tu suscripción con prorrateoVer
Gestionar pagoActualiza método de pago vía Stripe PortalVer
Ver historialConsulta facturas y transacciones pasadasVer
Monitorear usoRevisa consumo vs. límites del planVer
Cancelar suscripciónCancela al final del período actualVer

┌─────────┐
│ Trialing│
└────┬────┘
│ Finaliza trial
┌────▼────┐
│ Active │─────────────┐
└────┬────┘ │
│ Falta pago │
┌────▼────┐ ┌────▼────┐
│Past Due │ │Canceled │
└────┬────┘ │ FINAL │
│ └─────────┘
┌────▼────┐
│Canceled │
│ FINAL │
└─────────┘

Estados posibles:

EstadoDescripciónAcceso al sistema
trialingPeríodo de prueba activo✅ Completo
activeSuscripción pagada y activa✅ Completo
past_duePago pendiente⚠️ Limitado
canceledSuscripción cancelada❌ Bloqueado

ALERTA: Las suscripciones en estado past_due tienen 7 días para regularizar el pago antes de ser canceladas automáticamente.


Los límites se aplican de forma automática según tu plan activo:

Free:

  • 1 usuario máximo
  • 10 facturas por mes
  • 50 clientes totales
  • 1 GB almacenamiento

Basic:

  • 3 usuarios máximos
  • 100 facturas por mes
  • 500 clientes totales
  • 10 GB almacenamiento

Premium:

  • Usuarios ilimitados
  • Facturas ilimitadas
  • Clientes ilimitados
  • Almacenamiento ilimitado

NOTA: Los límites mensuales se resetean al inicio de cada período de facturación.


Error: “Datos de facturación incompletos”

Sección titulada «Error: “Datos de facturación incompletos”»

Causa: Faltan campos obligatorios para crear suscripción en Stripe.

Solución:

  1. Ve a Configuración > Negocio
  2. Completa todos los campos obligatorios:
    • CIF/NIF válido
    • Dirección completa
    • Código postal y ciudad
    • Teléfono de contacto

Causa: Problema con el método de pago configurado.

Solución:

  1. Haz clic en “Gestionar pago”
  2. Actualiza o añade un método de pago válido
  3. Verifica que la tarjeta tenga fondos suficientes

Error: “Ya tienes una suscripción activa”

Sección titulada «Error: “Ya tienes una suscripción activa”»

Causa: Intentas crear una segunda suscripción activa.

Solución:

  • Usa “Cambiar plan” en lugar de crear nueva suscripción
  • Si el problema persiste, contacta a soporte

Tablas de base de datos
TablaDescripciónColumnas principales
business_subscriptionsSuscripciones activas de negociosbusiness_id, plan_id, status, stripe_subscription_id, current_period_start, current_period_end, trial_end, cancel_at_period_end
subscription_plansPlanes disponiblesname, slug, price, currency, billing_cycle, limits, features, stripe_product_id, stripe_price_id
subscription_audit_logHistorial de cambios de planbusiness_id, old_plan_id, new_plan_id, change_type, effective_date
subscription_notificationsNotificaciones programadasbusiness_id, notification_type, scheduled_for, is_sent

Relaciones:

  • business_subscriptions.business_idbusinesses.id
  • business_subscriptions.plan_idsubscription_plans.id
Tipos TypeScript
// Hook useSubscriptionStatus
export interface SubscriptionStatus {
hasSubscription: boolean;
status: 'active' | 'trialing' | 'past_due' | 'canceled' | 'none';
planName?: string;
planSlug?: string;
planId?: string;
plan?: {
name: string;
slug: string;
limits?: Record<string, any>;
};
currentPeriodStart?: Date;
currentPeriodEnd?: Date;
trialEnd?: Date;
trialDaysRemaining?: number;
isTrialing: boolean;
isActive: boolean;
cancelAtPeriodEnd: boolean;
stripeCustomerId?: string;
canAccessStripePortal: boolean;
}
// Tabla subscription_plans
interface SubscriptionPlan {
id: string;
name: string;
slug: string;
price: number;
currency: string;
billing_cycle: 'monthly' | 'yearly';
limits: {
users: number; // -1 = ilimitado
monthly_invoices: number;
customers: number;
storage_gb: number;
};
features: Record<string, boolean>;
stripe_product_id: string | null;
stripe_price_id: string | null;
is_active: boolean;
display_order: number;
}
// Tabla business_subscriptions
interface BusinessSubscription {
id: string;
business_id: string;
plan_id: string;
status: 'active' | 'trialing' | 'past_due' | 'canceled';
stripe_customer_id: string | null;
stripe_subscription_id: string | null;
current_period_start: string;
current_period_end: string;
trial_end: string | null;
cancel_at_period_end: boolean;
custom_limits: Record<string, any> | null;
created_at: string;
updated_at: string;
}
Edge Functions

Propósito: Crea sesión de pago en Stripe o actualiza suscripción existente.

Endpoint: POST /functions/v1/create-checkout-session

Body:

{
business_id: string;
plan_id: string;
success_url?: string;
cancel_url?: string;
}

Validaciones:

  • Datos de facturación completos (CIF, dirección, teléfono)
  • Business debe existir y estar activo
  • Usuario debe tener permisos de owner

Respuestas:

// Caso 1: Nueva suscripción
{
type: 'checkout',
checkout_url: string
}
// Caso 2: Cambio de plan con prorrateo
{
type: 'plan_change',
subscription_updated: true,
message: string,
invoice_url?: string
}
// Caso 3: Error de validación
{
error: string,
validation: {
errors: string[],
warnings: string[],
validated_fields: Record<string, boolean>
}
}

Propósito: Procesa eventos de Stripe (pagos, renovaciones, cancelaciones).

Eventos procesados:

  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.payment_succeeded
  • invoice.payment_failed

Acciones:

  • Actualiza business_subscriptions
  • Crea registros en subscription_audit_log
  • Programa notificaciones en subscription_notifications

Propósito: Cancela suscripción al final del período actual.

Endpoint: POST /functions/v1/cancel-subscription

Body:

{
businessId: string;
}

Comportamiento:

  • Marca cancel_at_period_end = true en Stripe
  • Actualiza registro en base de datos
  • No cancela inmediatamente (acceso hasta fin de período)

Propósito: Calcula prorrateo antes de cambio de plan.

Endpoint: POST /functions/v1/get-proration-preview

Body:

{
business_id: string;
new_plan_id: string;
user_id: string;
}

Respuesta:

{
proration: {
credit_amount: number; // Crédito por tiempo no usado
charge_amount: number; // Cargo por nuevo plan
tax_amount: number; // Impuestos aplicables
total_amount: number; // Total a pagar/reembolsar
}
}
Componentes React principales

Ruta: src/panels/owner/subscriptions/components/SubscriptionManagement.tsx

Descripción: Componente principal que organiza las tres pestañas de gestión.

Subcomponentes:

  • SubscriptionSummaryCard - Resumen del plan actual
  • UsageLimitsCard - Visualización de límites
  • PaymentHistoryTable - Historial de pagos
  • BillingInfoCard - Información de facturación

Ruta: src/panels/owner/subscriptions/components/management/ChangePlanDialog.tsx

Props:

interface ChangePlanDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}

Funcionalidades:

  • Muestra planes disponibles en grid
  • Calcula prorrateo en tiempo real
  • Valida datos de facturación antes de cambio
  • Redirige a Stripe Checkout o actualiza directamente

Validaciones:

  • No permite seleccionar plan actual
  • Verifica datos de negocio completos
  • Muestra preview de prorrateo

Ruta: src/panels/owner/subscriptions/components/management/UsageLimitsCard.tsx

Descripción: Muestra barras de progreso para cada límite del plan.

Métricas monitoreadas:

  • Usuarios activos
  • Facturas del mes
  • Clientes totales
  • Almacenamiento usado

Alertas:

  • ⚠️ Amarillo: 75-90% del límite
  • 🔴 Rojo: 90-100% del límite
  • ✅ Verde: menos de 75% del límite
Integración con Stripe

Variables de entorno:

STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PUBLISHABLE_KEY=pk_live_...

Factux usa Stripe Customer Portal para:

  • Actualizar método de pago
  • Ver historial de facturas
  • Descargar recibos
  • Gestionar datos de facturación

Acceso:

// Genera URL del portal
const { data } = await supabase.functions.invoke('create-portal-session', {
body: { business_id: currentBusinessId }
});
// Redirige al portal
window.location.href = data.url;

Endpoint: https://[proyecto].supabase.co/functions/v1/stripe-webhook

Seguridad:

  • Verifica firma de Stripe con STRIPE_WEBHOOK_SECRET
  • Valida duplicados usando stripe_event_id
  • Registra todos los eventos en logs

Eventos críticos:

// Pago exitoso
'invoice.payment_succeeded' → status = 'active'
// Pago fallido
'invoice.payment_failed' → status = 'past_due'
// Suscripción cancelada
'customer.subscription.deleted' → status = 'canceled'
Schemas y validaciones

Función: validateBusinessBillingInfo

Ubicación: supabase/functions/_shared/customer-data-validator.ts

Campos validados:

interface ValidationResult {
isValid: boolean;
errors: string[];
warnings: string[];
validated_fields: {
tax_id: boolean;
legal_name: boolean;
address: boolean;
city: boolean;
postal_code: boolean;
country: boolean;
phone: boolean;
email: boolean;
};
}

Reglas de validación:

  1. CIF/NIF (tax_id):

    • Formato: NIF (8 dígitos + letra), CIF (letra + 7 dígitos + control), NIE (X/Y/Z + 7 dígitos + letra)
    • Validación de dígito de control
  2. Teléfono:

    • Formato: +34 seguido de 9 dígitos
    • Prefijos válidos: 6XX, 7XX, 8XX, 9XX
  3. Dirección:

    • Mínimo 5 caracteres
    • No puede estar vacía
  4. Código postal:

    • España: 5 dígitos (01XXX-52XXX)
    • Otros países: mínimo 3 caracteres

Descripción: Obtiene límites efectivos combinando plan + límites custom.

Uso:

const { data } = await supabase.rpc('get_business_effective_limits', {
p_business_id: 'uuid-del-negocio'
});
// Retorna:
{
users: number,
monthly_invoices: number,
customers: number,
storage_gb: number,
// ... otros límites
}

Lógica:

  1. Lee subscription_plans.limits
  2. Aplica business_subscriptions.custom_limits si existen
  3. Retorna objeto combinado con prioridad a custom

  1. Usuario en /owner/settings/subscription
  2. Clic en “Cambiar plan”
  3. Selecciona plan Premium
  4. Sistema calcula prorrateo (muestra preview)
  5. Clic en “Confirmar cambio”
  6. Si datos incompletos → muestra diálogo de validación
  7. Si datos OK → redirige a Stripe Checkout
  8. Usuario completa pago en Stripe
  9. Webhook actualiza suscripción a Premium
  10. Usuario es redirigido a /subscription/success
  1. Usuario en pestaña “Facturación”
  2. Clic en “Cancelar suscripción”
  3. Confirma en diálogo de advertencia
  4. Edge function marca cancel_at_period_end = true
  5. Usuario mantiene acceso hasta fin de período
  6. Al finalizar período, webhook cambia status a canceled

¿Puedo cambiar de plan en cualquier momento?

Sí, puedes cambiar entre planes en cualquier momento. El sistema aplicará prorrateo automático: se calcula el crédito por el tiempo no usado del plan actual y se carga la diferencia del nuevo plan.

¿Qué pasa si mi pago falla?

Si un pago falla, tu suscripción pasa a estado past_due. Tienes 7 días para actualizar tu método de pago. Durante este tiempo, tendrás acceso limitado al sistema. Si no se regulariza el pago, la suscripción será cancelada automáticamente.

¿Los límites se acumulan si no los uso?

No, los límites mensuales (como facturas/mes) se resetean al inicio de cada período de facturación. Los límites totales (como clientes) son acumulativos.

¿Puedo recuperar una suscripción cancelada?

Sí, puedes crear una nueva suscripción en cualquier momento desde el módulo de Suscripción. Sin embargo, se tratará como una nueva suscripción con un nuevo período de prueba si aplica.