Crea y pon precio a un plan
Arma un plan en el panel de administración, ponle precio por país e intervalo, y sigue lo que un suscriptor puede hacer con él.
Un plan es aquello a lo que un usuario se suscribe. Esta guía cubre crear uno desde el panel de administración, sus precios, cómo se retira de la venta, y cada movimiento que un suscriptor puede hacer: suscribirse, subir, bajar, cancelar, volver. Da por hecho que el cobro está encendido; si la barra lateral del panel no tiene grupo Planes, empieza por Activa los planes y el cobro.
Cómo se guarda un plan
Un plan es una fila en billing_plans y un Stripe Product, nombrado APP_NAME - Nombre del plan. Sus precios son filas en billing_prices, una por país y moneda, y cada una tiene una fila en billing_price_variations por intervalo de cobro, con el monto y el id del Stripe Price. Una suscripción apunta a un Stripe Price, así que el plan actual de un usuario se resuelve a partir del precio del elemento de la suscripción, nunca de un id de plan guardado en la suscripción.
Un usuario tiene una suscripción, llamada default, con exactamente un precio de plan y cero o más precios de complementos. Todos los helpers asumen esa forma.
Crea el plan
Ve a Planes, Planes, Crear. El formulario tiene seis bloques, seguidos de dos tarjetas de sólo lectura.
| Bloque | Campos | Notas |
|---|---|---|
| Información del plan | Nombre, Descripción | Ambos obligatorios. Son lo que muestra la tarjeta del plan. Un selector Contexto aparece sólo cuando hay un segundo adaptador de cobro registrado, para restringir el plan a usuarios o al otro dueño. Mientras los tickets o el chat en vivo están encendidos, el bloque también tiene los interruptores Soporte prioritario y Chat de soporte. |
| Características | Una línea cada una, agregada con Agregar | Cada línea se vuelve una palomita en la tarjeta. Puramente descriptivas; nada las lee. |
| Precios | Una variación de precio por país | Detallado abajo. Obligatorio: al menos una variación con al menos un monto. |
| Complementos incluidos | Complementos incluidos en este plan | Capacidades que el plan otorga sin línea extra en la suscripción. Ver Vende complementos. |
| Medidores incluidos | Medidor e Incluido en cada ciclo, una fila por medidor, agregada con Agregar medidor | Los medidores prepago y pospago. Estar en esta lista es lo que permite a los suscriptores del plan usar el medidor; el monto es lo que el plan trae en cada ciclo. Cuando se acaba, un medidor prepago bloquea hasta que se compra más y uno pospago cobra el excedente; 0 significa que cada unidad se cobra desde la primera. Ver Vende consumo con medidores y paquetes. |
| Capacidad y límites | Capacidad y Permitido, una fila por cuota, agregada con Agregar capacidad | Las cuotas son los métodos públicos de App\Classes\PlanLimits y los medidores de capacidad. Nada aquí bloquea por sí solo: el kit muestra al cliente su consumo contra lo que tiene, y tu código decide dónde aplica la revisión. Ver Límites de plan y consumo. |
Un medidor o una cuota puede aparecer una sola vez entre las dos listas. Debajo de ellas, Medidores pospago y Medidores de capacidad son de sólo lectura: para cada medidor activo de ese tipo dicen si el plan, tal como se guardó por última vez, lo declara y cuánto incluye o permite, y avisan cuando un medidor de capacidad es uno que el proyecto no mide, así que su consumo siempre se lee en cero.
Guardar crea el Stripe Product y un Stripe Price por cada monto, y redirige a la lista de planes. Si falta STRIPE_SECRET el formulario lo dice en una advertencia arriba; ponlo antes de crear planes, porque cada precio es un objeto de Stripe.
El plan también recibe un slug a partir de su nombre. Es metadato interno que se escribe en el Stripe Product y no se muestra en ningún lado, salvo como la llave de un plan declarado en código.
Precios
Haz clic en Agregar variación de precios. Cada variación es un País y una Moneda, con un campo de monto por intervalo. Un plan que se vende a 199 MXN al mes en México y a 12 USD al mes en todos lados son dos variaciones: MX - Mexico en MXN, y Todo en USD. Cada país puede aparecer una vez por plan.
- País es
Todo(guardado comodefault) o uno de los países listados. El usuario ve la variación de su país cuando existe, yTodoen caso contrario. El país sale delcountry_codedel usuario, luego de la detección de país, luego dedefault. Ahí se detiene: a un usuario cuyo país no tiene variación y para el que tampoco hay variaciónTodono se le muestra nada, nunca el monto de otro país en la moneda de otro país. Las mayúsculas dan igual:MXymxson el mismo país. - Moneda es uno de los códigos ISO listados. Es la moneda en la que cobra Stripe; no hay conversión al momento de la venta.
- Cada monto crea un Stripe Price recurrente para ese intervalo. Los Stripe Price son inmutables, así que una vez guardado el campo queda deshabilitado. Para cambiar un monto, quita el precio con la cruz arriba del campo y escribe el nuevo: el Stripe Price viejo se archiva y se crea uno nuevo. La cruz sólo se ofrece mientras ninguna suscripción usa ese precio.
- El botón de basura quita la variación completa; sólo se ofrece cuando ninguno de sus precios tiene suscriptores.
- Un monto de
0es un plan gratuito. Deja uno: cuando aplican varios, una cuenta nueva queda en el que tiene precio para su propio país antes que en unoTodo, y en el más antiguo de ésos, así que los demás nunca se reparten al registrarse. La lista de planes te avisa cuando existe más de un plan gratuito.
Las columnas de intervalo salen de config/pricing.php, monthly y yearly por omisión; agregar una entrada ahí agrega una columna aquí, y la lista de monedas, la de países y los tipos de cambio se explican en Monedas, intervalos y tipos de cambio.
Una vez guardada una variación, el botón de ojo junto a ella abre un modal con su número de suscriptores, sus precios y una gráfica, y un enlace Abrir página completa a /admin/billing-plans/{id}/pricing/{country}.
Declara un plan en código
En vez de capturar un plan a mano en cada entorno, puedes describirlo una sola vez en database/seeders/PlanSeeder.php. El deploy ya corre los seeders (php artisan db:seed --force), así que el plan se crea en el primer deploy y se mantiene al día en cada uno de los siguientes. Weblabor Base entrega el seeder vacío.
Cada entrada lleva como llave el slug del plan:
$plans = [
'pro' => [
'name' => 'Pro',
'description' => 'Short text shown on the pricing page.',
'prices' => [
['country' => 'default', 'currency' => 'usd', 'units' => null, 'intervals' => ['monthly' => 19, 'yearly' => 190]],
['country' => 'mx', 'currency' => 'mxn', 'units' => null, 'intervals' => ['monthly' => 349]],
],
'defaults' => ['features' => ['Everything in Basic']],
],
];
name, description y prices se mantienen al paso del código; cada renglón de precios es una variación de precio, con un monto por intervalo. defaults lleva cualquier otra cosa que captura el formulario, como las líneas de características, y se aplica sólo cuando el plan se crea; después es tuyo para cambiarlo en el panel. Una llave que dejas fuera de la entrada se queda como está. Los complementos se pueden declarar igual; mira Vende complementos.
Lo que el panel te deja cambiar
Un plan creado por el seeder abre con un aviso Controlado por código. Su nombre, descripción y precios son de sólo lectura ahí, y cada deploy los regresa a lo que dice el código. Las características, los complementos incluidos, los medidores incluidos y las filas de capacidad siguen siendo editables.
Los precios siguen al código sin revolver Stripe: un monto que no cambió conserva su Stripe Price, y un monto que cambió lo reemplaza exactamente como lo haría quitarlo y escribir el nuevo. Un monto con suscriptores se retira en lugar de quitarse: su Stripe Price se archiva, así que las ventas nuevas, las tarjetas de planes y los cambios de plan usan el monto nuevo, mientras quien ya paga el anterior lo sigue pagando y sigue viendo el plan como su plan actual. Un precio retirado nunca se ofrece como destino; usa Migrar suscriptores, más abajo, para moverlos al nuevo.
Libéralo del código
Cuando quieras manejar un plan a mano de ahí en adelante, pulsa Liberar del código en el aviso y confirma. Todos los campos se vuelven editables y el seeder no vuelve a tocar ese plan, aunque su entrada siga en el código. Esto no se puede deshacer desde el panel.
Pasar a código un plan que ya existe
Un plan que ya existe con el slug de la entrada —creado en el panel, o antes de que existiera el seeder— se toma sólo si todo lo que la entrada declara coincide con lo guardado: el nombre, la descripción y los mismos renglones de precio (país, moneda, unidades y monto), sin ningún otro precio guardado. Entonces abre con el aviso Controlado por código desde el siguiente deploy, sin tocar sus precios ni sus suscriptores, y defaults no se aplica. Escribe la entrada a partir de lo que muestra el panel para adoptar un plan que ya vendes.
Cuando el plan difiere de la entrada y quieres que el código lo controle de todos modos, márcalo con una migración de tu proyecto; desde el siguiente deploy el seeder lo trata como suyo y pone en su lugar todo lo que la entrada declara:
DB::table('billing_plans')
->where('slug', 'pro')
->whereNull('released_from_code_at')
->update(['managed_by_code' => true]);
Un plan liberado del código se salta a propósito. Cuida los precios: uno que tiene suscriptores nunca se elimina, así que un monto distinto al de la entrada no se puede reemplazar desde el código, igual que no se puede desde el panel. Pasa primero a esos suscriptores, o escribe la entrada con los montos que el plan ya tiene. La misma receta para un medidor, con su migración completa, está en Pasar a código un medidor que difiere de la entrada.
Lo que el seeder nunca toca
Un plan que difiere en algo de lo que la entrada declara nunca se toma: el seeder lo deja en paz y el deploy imprime qué campos difieren. Un plan liberado del código no se vuelve a tomar aunque siga coincidiendo, y un plan que el seeder creó y que luego alguien eliminó tampoco se trae de vuelta.
Qué ve un país sin precio
Ponerle precio a dos países y olvidar la variación Todo es el error más fácil de cometer aquí, y antes era invisible: al visitante le quedaba una pantalla vacía y nadie se enteraba. Ahora cada pantalla dice qué está pasando, y nunca se cotiza ni se cobra a la tarifa de otro país.
| Dónde | Qué ve el usuario |
|---|---|
| La página de planes, y el bloque de precios del landing | "Aún no hay planes para tu país", con una línea que explica que no se han publicado precios para su país y lo invita a escribirte. |
| La página de detalle de un complemento | "No está a la venta en tu país" en lugar del precio, y cada vía para obtenerlo — suscripción, escalones, por unidad, de por vida — dice "Este complemento aún no está a la venta en tu país" y ofrece un botón deshabilitado. |
| El catálogo de complementos de la cuenta | El complemento queda fuera de la lista. El buscador y los filtros de categoría siguen en pantalla, así que una búsqueda que no encontró nada se puede limpiar. |
| La tarjeta de un medidor | "Todavía no hay paquetes de unidades en oferta en tu país" en lugar de los botones de compra. |
| El consumo que se cobra al cerrar el ciclo | "No cobrado: no hay precio para tu país". El consumo se sigue midiendo, y el periodo cerrado aparece como No facturado; a Stripe no se le manda nada. |
Un plan con precio sólo para otros países también aparece como No disponible en el panel de administración, en lugar de Gratis, que era lo que decía antes.
Avisos en el panel de administración
Tres avisos te dicen que un catálogo está dejando a alguien fuera. Ninguno te impide guardar: vender sólo en dos países es una decisión que tienes derecho a tomar.
- En cada formulario de precios — planes, complementos y medidores — cuando las variaciones cubren países con nombre y ninguna es
Todo: "No hay precio para el resto del mundo". - En la lista de planes, cuando ningún plan del catálogo tiene variación
Todo: quien viva en cualquier otro país no ve ningún plan y no se puede suscribir. - En la lista de planes, cuando existe más de un plan gratuito, explicando cuál le toca a una cuenta nueva.
El primero responde por el formulario que tienes enfrente; el segundo hace la pregunta que ningún plan por sí solo puede contestar, porque un plan que se vende sólo en México es perfectamente legítimo mientras algún otro plan cubra a los demás.
Dónde aparece el plan
Un plan está en venta en el momento en que se guarda con un Stripe Price. Se muestra en /account/plans para usuarios con sesión y en la sección de precios de la página de inicio, bajo el selector de intervalo. El selector lista sólo los intervalos que al menos un plan tiene con precio y que están marcados como activos en config/pricing.php; un usuario que ya está suscrito ve seleccionado su propio intervalo.
Retira un plan de la venta
No hay un interruptor de "en venta" en los planes; el catálogo es lo que tiene un Stripe Price. Existen tres operaciones, y cada una se niega cuando rompería a un suscriptor:
- Quitar un precio: la cruz sobre el monto, sólo mientras nadie está suscrito a él. El Stripe Price se archiva, así que ya no puede comprarse, y el plan deja de mostrarse para ese intervalo o país.
- Borrar el plan: desde la lista de planes, sólo cuando ya no le quedan precios. El Stripe Product se archiva y la fila se borra de forma suave.
- Migrar a los suscriptores: en la página de precios, Migrar suscriptores abre un modal donde marcas las suscripciones a mover, o ninguna para moverlas todas, y eliges un plan destino y uno de sus precios. El movimiento corre en un job encolado,
MigrateSubscriptionsToPlan, así que la cola debe estar corriendo; cada suscripción se cambia al precio destino, conservando los complementos que existen en la misma moneda e intervalo, con el prorrateo por omisión de Stripe. No se envía ningún correo a los suscriptores.
Así que la secuencia para retirar un plan que la gente usa es: crea el reemplazo, migra los suscriptores a él, quita los precios viejos, borra el plan viejo. Las suscripciones y las facturas siguen referenciando los Stripe Price archivados, y por eso el borrado está protegido.
Acuerda un precio con una cuenta
Con una cuenta se puede acordar un precio menor que el de lista, para un plan, un complemento que se vende por suscripción o un paquete de un medidor. En la página del usuario del panel de administración, el panel Precios acordados lista los que tiene la cuenta, o dice que no tiene ninguno, y Agregar un precio acordado pide:
- Precio: el precio de lista que reemplaza — el producto, su intervalo y su importe.
- Precio acordado: en la misma moneda, y menor que el precio de lista; si no, se rechaza.
- Duración: para siempre, hasta que se quite; un número de cobros (cada compra de un paquete es uno), tras los cuales la cuenta vuelve al precio de lista; o hasta una fecha, después de la cual un cobro es a precio de lista.
Un paquete se cobra al precio acordado en un solo pago, sin suscripción. Un plan o un complemento se cobra al precio de lista con un descuento de Stripe por la diferencia, hecho sólo para esa cuenta y esa línea, así que las demás líneas de la suscripción no cambian. Una línea que empieza mientras el precio acordado está vigente — al suscribirse al plan, al cambiar a él o a su intervalo, o al agregar el complemento — lleva el descuento desde su primera factura; cuando ese primer pago pasa por Stripe Checkout, no se ofrece el campo de código promocional. Un cambio a un plan más barato que espera a la renovación hace lo mismo: si la cuenta tiene un precio acordado para ese plan e intervalo, la renovación ya se cobra a ese precio, y si no, al precio de lista de ese plan. Una cuenta que ya está suscrita paga el precio acordado desde su siguiente renovación, sin cobrar ni devolver nada por el periodo en curso. Si cambia el precio de lista, el descuento se recalcula para que la cuenta siga pagando lo acordado. Un precio acordado pertenece al producto y al intervalo para los que se agregó: una cuenta que pasa a otro plan, o a un intervalo sin precio acordado, paga el precio de lista de lo nuevo. Para nada de esto se crea un producto ni un precio en Stripe.
La cuenta ve su precio acordado, y cuánto dura, en lugar del precio de lista en sus planes, complementos y paquetes. El panel muestra de cada uno el precio de lista, el precio acordado, la duración, cuántos cobros se han aplicado, y quién lo agregó y cuándo; Eliminar termina uno, y la cuenta paga el precio de lista desde su siguiente cobro. Uno que termina por sus cobros o por su fecha muestra cuándo terminó. Sólo los administradores que pueden dar complementos ven el panel.
Abona o quita saldo a favor de una cuenta
El saldo a favor es un monto que la cuenta tiene a su favor: Stripe lo descuenta por sí solo de las siguientes facturas de la cuenta, hasta el total de cada factura, y guarda lo que sobra para la que sigue. En la página del usuario del panel de administración, el panel Saldo a favor muestra el saldo disponible, cuánto sale la siguiente factura sin él y con él, y cada movimiento con su fecha, monto, motivo y quién lo hizo.
- Haz clic en Abonar saldo para darle saldo a la cuenta, o en Quitar saldo para retirar saldo que todavía no ha usado. Quitar saldo sólo aparece mientras la cuenta tiene saldo.
- Escribe el Monto. Va en la moneda de facturación de la cuenta, que el campo nombra; al quitar, no puede ser más que el saldo que queda sin usar.
- Escribe el Motivo. Es obligatorio, y la cuenta lo lee junto al movimiento, así que escríbelo para ella.
- Guarda. El panel muestra el nuevo movimiento y el saldo disponible después de él.
Si Stripe no acepta el cambio, no cambia nada y el mensaje dice por qué.
El saldo paga sólo facturas de la suscripción. Un paquete que se compra con Stripe Checkout es un pago único, no una factura, así que se cobra completo sin importar el saldo. Cada vez que una factura usa parte del saldo, el panel lo registra como Aplicado a una factura, por Sistema.
La cuenta ve una tarjeta Saldo a favor en su pantalla de cobro en cuanto tiene saldo o algún movimiento: el monto disponible, de cuánto a cuánto pasa su siguiente pago, lo que le queda para el siguiente cuando el saldo es mayor que la factura, sus movimientos con su motivo, firmados Por Administración en lugar de con tu nombre, y un aviso de que el saldo no aplica a la compra de paquetes.
Sólo los administradores que pueden dar complementos ven el panel. En un producto donde se cobra a los equipos y no a los usuarios, el panel va en la página del equipo, donde el producto lo agrega a su ficha de equipo.
Consulta el historial de la suscripción de una cuenta
En la página del usuario del panel de administración, el panel Historial de la suscripción lista cada cambio de la suscripción de la cuenta, del más reciente al más antiguo, cada uno como una frase con su fecha: el plan con el que empezó y a qué precio, cada cambio de plan, precio, nivel, complemento o cantidad con el precio de antes y el de después, las cancelaciones, reanudaciones y el final, los precios acordados que empiezan y terminan, un cambio del día de renovación, los pagos fallidos y los recuperados, los pagos recibidos con lo que cubrieron, y el saldo a favor abonado, quitado o aplicado.
Cada línea termina con quién hizo el cambio: Por y el nombre de tu colega para lo que hizo un administrador, Por La cuenta para lo que hizo la propia cuenta, y Por Sistema para lo que hizo Stripe o una tarea programada. Cuando el cambio generó un cobro, la línea dice cuánto y hasta cuándo, por ejemplo Cobro de $120.00, proporcional hasta el 15 de marzo de 2027; un pago que no pagó ningún cambio propio, como una renovación, es una línea aparte. No hay que escribir nada a mano: cada cambio hecho desde la cuenta, desde el panel de administración o desde Stripe se registra una sola vez.
La cuenta ve el mismo historial en su pantalla de cobro, firmado Por ti, Por Administración o Por Sistema, nunca con tu nombre. Una cuenta que ya tenía suscripción cuando se encendió el historial lee El historial detallado empieza el y esa fecha: antes de ella, el historial sólo tiene lo que ya se guardaba —el inicio, los cambios de plan, la cancelación y el final, los pagos y los movimientos de saldo a favor— y esas líneas anteriores aparecen cuando la cola las procesa.
Sólo los administradores que pueden dar complementos ven el panel. En un producto donde se cobra a los equipos y no a los usuarios, va en la página del equipo, donde el producto lo agrega a su ficha de equipo.
Sigue a los suscriptores
La página de precios de cada variación lista a sus suscriptores con nombre, correo, estatus y fecha de inicio, y grafica Nuevas suscripciones por día, Suscripciones acumuladas e Ingresos estimados por día, esta última siendo nuevas suscripciones por el monto promedio de esa variación. La lista de usuarios tiene una columna Plan actual y un filtro Plan. El tablero del panel tiene las métricas globales de ingresos y suscripciones descritas en Activa los planes y el cobro.
Qué puede hacer el suscriptor
Toda acción vive en las tarjetas de planes de /account/plans; el botón de cada tarjeta depende de la suscripción del usuario. La lógica es PlanService en el paquete, que desde tu propio código alcanzas como $user->subscribeToPlan($variation), $user->changeSubscriptionPlan($variation) y $user->subscription()->cancel().
Suscribirse
Sin suscripción cada tarjeta dice Suscribirse. Un plan gratuito se suscribe al instante, sin tarjeta. Un plan de pago abre Stripe Checkout; al terminar, Stripe devuelve al usuario a /account/plans?checkout=success, que muestra "Pago exitoso" y lo manda al tablero mientras el webhook crea la suscripción local. Checkout muestra un campo de código promocional cuando los cupones están encendidos; ver Pruebas gratuitas y cupones.
Un usuario nuevo queda suscrito a un plan gratuito al registrarse: el que tiene precio para su propio país cuando existe, el Todo en caso contrario, y el más antiguo de ellos cuando aplican varios. Un plan gratuito cuyo Stripe Price nunca se creó se salta. Abrir /account/plans repite esa revisión para un usuario que no tiene ninguna suscripción, y no cambia nada para quien sí la tiene: el plan al que alguien se suscribió sigue siendo suyo aunque después su país deje de coincidir con el catálogo en el que se le puso precio. El país sigue decidiendo qué precios se le muestran. Cuando una suscripción termina, la cuenta vuelve sola a su plan gratuito, y cuando no puede se le dice por qué en cada pantalla; ver Cuando tu cuenta se queda sin plan.
Como el precio depende de él, a un usuario sin país en su perfil se le detecta uno en su siguiente página, y sólo va a dar a /account y ahí se queda cuando esa detección no responde nada: entonces cualquier otra página lo regresa, y sólo le queda cerrar sesión. El panel de administración nunca se ve afectado, ni tampoco un proyecto que no vende suscripciones. Ver Detecta el país del visitante.
Cambiar de plan
Cada plan que se puede elegir dice Cambiar plan. Un plan más barato programado para la renovación muestra Inicia el {date}. Mientras se pueda deshacer una cancelación, el plan actual muestra Reanudar plan. Antes de enviar nada a Stripe, los límites del plan destino se comparan con el consumo actual del usuario; exceder uno detiene el cambio con el mensaje "Has excedido el límite para ...". Después:
| De | A | Qué pasa |
|---|---|---|
| Gratuito | Gratuito | Se intercambia el precio. |
| Gratuito | De pago, sin tarjeta guardada | Se abre Checkout con el nuevo plan más los complementos actuales, y la suscripción gratuita sigue corriendo mientras el usuario está afuera pagando. Cerrar la pestaña, echarse para atrás, o un banco que nunca confirma, lo dejan en el plan que ya tenía. |
| De pago | De pago, más caro, mismo intervalo | swapAndInvoice: la diferencia prorrateada se cobra ahora. Una tarjeta rechazada anula el cambio: el usuario se queda en el plan que tenía, no queda ninguna factura sin pagar y el mensaje dice lo que respondió el banco. Lo mismo vale desde un plan gratuito con tarjeta guardada. Cuando el banco pide al usuario aprobar el pago, Confirma tu pago se abre dentro de la aplicación y el nuevo plan se aplica sólo cuando se aprueba; consulta Vende complementos. |
| De pago | De pago, más barato, mismo intervalo | El plan actual sigue activo durante el periodo ya pagado. El plan más barato comienza en la siguiente renovación, sin crédito por el tiempo restante. |
| De pago | Gratuito | Se cancela al final del periodo; el usuario conserva el plan de pago hasta entonces y no se le cobra de nuevo. |
| Cualquiera | Intervalo distinto | La suscripción de Stripe se actualiza directamente con proration_behavior: always_invoice, con cada complemento mapeado a su precio en el nuevo intervalo. Falla si un complemento no tiene precio en ese intervalo, o si no hay un método de pago válido registrado. Un pago que el banco pide aprobar lo espera en la misma ventana. |
Los complementos de la suscripción se conservan en todos los cambios. Las bajadas se miden por monto: un destino más barato que el precio del plan actual es una bajada.
Una cuenta nunca carga dos suscripciones. Llegue como llegue una nueva — el Checkout que Stripe confirma, el plan gratuito que se le entrega a quien no tiene ninguno, un complemento que abre la primera suscripción — la que venía corriendo se termina en el momento en que la nueva queda confirmada, y no antes.
Cancelar y el periodo de gracia
La tarjeta del plan de pago actual dice Cancelar. Cancelar programa la suscripción para terminar al final del periodo, así que el usuario conserva el acceso hasta la fecha que pagó. Durante ese periodo de gracia la tarjeta del plan ofrece Reanudar plan, con la fecha hasta la que la suscripción seguirá activa, y la pantalla de cobro muestra Cancelación programada:
- Pulsar Reanudar plan quita la cancelación pendiente, así que el mismo plan se renueva con normalidad. Elegir otro plan de pago también reactiva la suscripción.
- Elegir un plan gratuito se rechaza: la suscripción ya está terminando.
- Cuando pasa la fecha, la suscripción termina y el usuario queda solo en el plan gratuito de su país. Cuando no lo hay, o su consumo rebasa sus límites, todas las tarjetas vuelven a decir Suscribirse. En ambos casos la pantalla de cobro muestra cuándo se canceló y quién lo hizo.
La fecha de cancelación, su motivo y el usuario que hizo clic se guardan en la suscripción (canceled_at, cancellation_reason, canceled_by). Una cancelación hecha en el Dashboard de Stripe llega por el webhook y se muestra como "vía Stripe".
Pagos fallidos
Cuando Stripe no pudo cobrar una renovación, la suscripción queda past_due, y sigue contando como activa: el plan, sus complementos y sus fechas quedan intactos durante un periodo de gracia de BILLING_PAST_DUE_GRACE_DAYS días, 15 por omisión, contados desde el cobro fallido. La pantalla de cobro muestra Pago fallido con esa fecha límite y un botón Reintentar pago, la tarjeta del plan dice Reintentar pago, y el usuario recibe un aviso, y otro BILLING_PAST_DUE_WARNING_DAYS días (3) antes del final. Si para entonces no se pagó nada, billing:enforce-past-due, programado a diario a las 03:00, cancela la suscripción y registra el monto como un saldo que la cuenta tiene que liquidar antes de seguir. Un cobro fallido por el consumo cerrado al final de un ciclo abre el mismo periodo de gracia. Cuando el primer pago nunca se completó, la suscripción queda incomplete y no cuenta como activa: /app redirige a los planes, y la tarjeta dice Reintentar pago. Reintentar paga la factura abierta con la tarjeta predeterminada, o manda al usuario a la página de factura alojada de Stripe cuando el banco pide autenticación. Un solo reintento atiende todo lo que se debe, incluidos los periodos de consumo cerrados, y reporta el pago como pendiente cuando alguna parte no pasó.
La pantalla de cobro
/account/billing muestra el estado de la suscripción como una etiqueta, el nombre del plan, el número de complementos activos, el inicio del periodo actual y la fecha del siguiente pago leídos en vivo desde Stripe, el Precio actual por mes de todo lo que lleva la suscripción, una lista plegable de los complementos con sus precios, y bajo Últimos Pagos las últimas diez facturas pagadas con fecha, monto, estatus y lo que cubrió cada una; los montos se formatean con CASHIER_CURRENCY_LOCALE. La tarjeta Historial de la suscripción lista los últimos diez cambios de la suscripción, y Ver historial completo abre todos, como describe Consulta el historial de la suscripción de una cuenta. Ver en un pago abre Detalle del pago, con cada línea de la factura y su total, y Descargar recibo cuando Stripe tiene el PDF de la factura. La misma pantalla muestra Pago fallido con Reintentar pago después de un cobro fallido, Reanudar plan mientras hay una cancelación programada, y el crédito que espera en la siguiente factura. El recibo, y la página de Stripe que abre Reintentar pago, llegan en el idioma de la aplicación y no en el del navegador.
/account/payment-methods lista las tarjetas guardadas con marca, últimos cuatro dígitos y vencimiento. Una tarjeta se agrega mediante Stripe Elements contra un SetupIntent, así que ningún número de tarjeta llega a tu servidor; la primera tarjeta se vuelve predeterminada automáticamente. Una tarjeta puede volverse predeterminada cuando hay más de una, y eliminarse salvo que sea la última o la predeterminada de una suscripción activa.
Eventos para tu código
Dos eventos dejan que el resto de la aplicación reaccione sin tocar el cobro:
WeblaborMx\BillingCore\Events\SubscriptionPaiden cada factura pagada, con el monto, la moneda, el id de la factura y el id de la suscripción. El kit lo escucha para pagar recompensas de referidos y registrar un evento de tracking.WeblaborMx\BillingCore\Events\PlanChangedcuando un usuario cambia a un plan de pago.
Ambos llevan el contexto de cobro; revisa $event->adapterKey() === 'user' antes de actuar sobre un usuario, para que un listener siga siendo correcto si más adelante se agrega otro dueño de cobro.


