Vende complementos
Declara una capacidad en código, ponle precio en el panel de administración y deja que la gente la active, la compre o la reciba con su plan.
Un complemento es una capacidad que tu proyecto enciende para una cuenta: un módulo, un extra de pago, una cuota. El código declara qué capacidades existen; el panel de administración, o el seeder de complementos cuando declaras el complemento mismo en código, decide cómo se vende cada una; la cuenta de facturación responde si una cuenta la tiene. Esta guía cubre el camino completo, desde el método en PlanFeatures hasta el botón que pulsa el usuario.
Enciéndelo
Los complementos viven en el paquete Billing Core, así que el paquete tiene que estar encendido. Los planes no: un complemento gratis funciona sin plan, sin suscripción y sin llave de Stripe.
| Opción | Variable de entorno | Valor por omisión | Qué cambia |
|---|---|---|---|
| Módulo de complementos | FEATURE_ADDONS_ENABLED |
false |
Apagado: /account/add-ons responde 404, el recurso del panel rechaza toda acción, el panel Complementos desaparece de la página del usuario y nada se otorga ni se vende. |
| Billing Core | BILLING_CORE_ENABLED |
true |
Apagado: el paquete no carga modelos, migraciones, rutas ni webhook. |
| Facturación de usuarios | BILLING_USER_ENABLED |
true |
Apagado: las rutas de facturación bajo /account/... no se registran, complementos incluidos. |
| Stripe | STRIPE_KEY, STRIPE_SECRET, STRIPE_WEBHOOK_SECRET |
vacío | Necesarias para vender cualquier cosa. Cada guardado de un complemento o de un precio habla con Stripe. |
Dos preguntas deciden qué se muestra una vez encendida la bandera:
BillingAddon::hasVisibleCatalog()es verdadero cuando la bandera está encendida y la cuenta del contexto de facturación activo puede ver al menos un complemento — uno a la venta, o uno que ya tiene. Esa pregunta, o la del mismo nombre enBillingMeter, muestra el enlace Complementos en el menú de la cuenta, y solo donde ese contexto tiene su propia pantalla de complementos: con la facturación de usuarios apagada no hay enlace. No consulta Stripe.BillingAddon::isActive()pide lo mismo másSTRIPE_SECRET. Decide si la página de inicio pública muestra el catálogo de complementos a los visitantes.
La bandera también programa billing:sync-exchange-rates a diario a las 12:00, el mismo comando que programan los planes, además de billing:expire-trials a las 08:00 y billing:close-meter-periods a las 02:00. La instalación del paquete, las llaves y el webhook están en Activa los planes y el cobro.
Declara la capacidad en código
PlanFeatures declara qué capacidades existen. Abre app/Classes/PlanFeatures.php y agrega un método público por capacidad. El nombre del método es la llave de la capacidad. El valor que devuelve dice si la capacidad ya está liberada en código, así que puedes vender en preventa algo que todavía no enciende nada.
namespace App\Classes;
class PlanFeatures extends BasePlanFeatures
{
public function electronic_wallet()
{
return true;
}
public function white_label()
{
return false; // se vende en preventa, todavía no enciende nada
}
}
BasePlanFeatures lee la clase por reflexión:
| Método | Devuelve |
|---|---|
PlanFeatures::availableFeatures() |
Las llaves, una por método público no estático. |
PlanFeatures::featureOptions() |
['electronic_wallet' => 'Electronic Wallet', ...], la lista que ofrece el formulario del panel. |
PlanFeatures::isReleased('white_label') |
true sólo cuando la llave está declarada y su método devuelve true. |
La clase se conecta en config/billing-core.php bajo user_adapter.plan_features. Weblabor Base viene sin métodos, así que una instalación limpia no muestra el campo Capacidad ni el catálogo. Un complemento puede vender un límite sin una capacidad declarada. Si no tiene límite y su llave guardada ya no está declarada en el código, el formulario la conserva y la marca con "This capability is not declared in code, so the add-on will not switch anything on"; el complemento se puede seguir vendiendo. Si guardas un complemento sin elegir capacidad, la llave se deriva del nombre (Extra Storage se vuelve extra_storage), con un número agregado cuando otro complemento ya la usa (extra_storage_2). Dos complementos nunca comparten llave: el formulario rechaza una llave que ya está tomada.
Declarar la capacidad basta para venderla desde el panel de administración. Para declarar el complemento mismo —su nombre, precios, imágenes y guías— mira Declara el complemento en código.
Créalo en el panel de administración
Ve a Planes → Complementos en el panel de administración, en /admin/billing-addons. El índice lista nombre, llave y si está a la venta, y tiene un botón Categorías que abre /admin/categories/billing-addons, donde creas las categorías por las que filtra el catálogo. Crear abre /admin/billing-addons/create; editar es /admin/billing-addons/{id}/edit. El formulario muestra una advertencia cuando STRIPE_SECRET no está definida: ponla primero, porque guardar un complemento de pago crea o actualiza su producto en Stripe.
| Campo | Obligatorio | Qué hace |
|---|---|---|
| Nombre | Sí | Título de la tarjeta. También el nombre del producto en Stripe. |
| Categorías | No | Múltiples, del tipo de categoría billing-addons. El catálogo filtra por ellas. |
| Descripción | Sí | Texto corto en la tarjeta y en Stripe. |
| Capacidad | No | La llave declarada que este complemento enciende. Sólo se muestra cuando el código declara al menos una. |
| Depende de | No | Otro complemento que la cuenta ya tiene que tener. Mientras no lo tenga, este no se puede activar, probar ni comprar. Vacío significa que no depende de nada. |
| Disponible desde los planes | No | Los planes a los que una cuenta tiene que estar suscrita antes de poder activar, probar o comprar este complemento; se pueden elegir varios. Vacío, mostrado como Cualquier plan, lo vende con cualquier plan. Mira Véndelo sólo con algunos planes. |
| Descripción completa | No | Se muestra en la página de detalle bajo "Acerca de este complemento". |
| Ícono | No | Imagen cuadrada, hasta 5 MB, guardada en la biblioteca de medios bajo General/Add-ons. Se muestra en la tarjeta. |
| Capturas | No | Hasta seis imágenes, hasta 5 MB cada una, guardadas en la biblioteca de medios bajo General/Add-ons; se pueden elegir varias a la vez. Se muestran como galería en la página de detalle. |
| Guías | No | Guías del centro de ayuda que explican el complemento, tantas como necesites. La tarjeta del catálogo enlaza a ellas. Sólo se muestra cuando el centro de ayuda tiene guías. |
| A la venta | Sí | Encendido por omisión. Apagado lo quita del catálogo para quien no lo tiene y rechaza compras nuevas. Quien ya lo tiene lo sigue viendo en su catálogo y en su página, y puede cancelarlo. |
| Contexto | Sólo con dos adaptadores | Ambos, Usuario o Equipo. Filtra qué dueño de facturación lo ve. Oculto cuando sólo está registrado el adaptador user. |
| Vendido de por vida | No | Agrega una tabla Precio de por vida: un importe por país y moneda, pagado una sola vez con Stripe Checkout. |
| Días gratuitos de prueba | No | Acceso completo durante esos días. No se pide tarjeta ni se cobra nada: cuando termina, el complemento se apaga. Cero no ofrece prueba. Sólo un complemento que no se vende por unidad ni por escalones ofrece una. Una vez por cuenta, para siempre. |
| Vendido por unidad | No | El usuario elige cuántas; el precio y los límites se multiplican. Necesita al menos un límite y no se combina con los escalones. |
| Vendido por escalones | No | El usuario elige una de varias opciones. Los precios de suscripción ganan una columna Unidades, un renglón por opción. Necesita al menos un límite. |
| Los escalones se suman | No | Aparece con Vendido por escalones encendido. El usuario compra cada escalón por unidades, y cada unidad de cada escalón suma su cuota en lugar de reemplazar el escalón que ya tiene. Mira Escalones que se suman. |
| Precios de suscripción | No | Una entrada por país: país, moneda y un importe por intervalo de config/pricing.php (monthly y yearly por omisión). Déjalo vacío para una capacidad gratis. |
| Límites | No | Llave de límite de App\Classes\PlanLimits y un valor entero. La cuota que aporta el complemento. El selector también lista las llaves de los medidores a la venta, mira Vende consumo con medidores y paquetes. |
Los precios son precios de Stripe y los precios de Stripe son inmutables. Cada importe que guardas crea un precio recurrente en Stripe; un importe existente no se puede editar. Para cambiar un precio, quita el intervalo y agrégalo de nuevo con el importe nuevo. Un intervalo con suscriptores no se puede quitar. Un complemento con precios tampoco se puede eliminar, porque suscripciones y facturas los referencian: apaga A la venta en su lugar. Eliminar un complemento sin precios archiva su producto en Stripe.
Un complemento solo se vende en la moneda en que ya paga la cuenta, porque Stripe guarda una moneda por cliente: a una cuenta suscrita en pesos nunca se le ofrece un precio en dólares, ni de suscripción ni de por vida. Cuando tus precios dejan fuera la moneda de cuentas que ya están suscritas, la tabla de precios muestra Suscriptores que no pueden comprar este complemento con la moneda y el país que faltan, y la pantalla de edición del plan lista los Complementos que sus suscriptores no pueden comprar. Puedes guardar de todos modos; esas cuentas no pueden comprarlo hasta que un precio en su moneda las cubra.
Un complemento de pago se sincroniza con Stripe: su primer precio mayor a cero crea un producto con el id del complemento en sus metadatos, actualizar el complemento actualiza nombre y descripción, y cada variación de precio crea un precio con unit_amount en centavos, la moneda, y el intervalo y el conteo de config/pricing.php. Un complemento gratuito — uno sin ningún precio mayor a cero — no crea nada en Stripe: se activa y desactiva dentro de la aplicación. Un precio de cero no crea un precio en Stripe, tampoco en un complemento de pago. Un complemento que ya tiene su producto lo conserva, aunque después se quede sin precios de pago.
Haz que un complemento necesite otro
Hay complementos que sólo tienen sentido encima de otro. Elige ese otro en Depende de y este complemento pasa a estar disponible sólo para las cuentas que ya lo tienen. Mientras no lo tengan, su botón dice Requiere y el nombre del que falta, la página de detalle lo dice igual arriba de los bloques, y no se puede activar, probar ni comprar nada: la negativa es la misma se pulse el botón o se repita la petición.
A quien ya tiene el complemento no le afecta: puede desactivarlo, cancelar su línea o cancelar su prueba como siempre pudo, incluso cuando el complemento del que depende ya no está. Obligar a alguien a seguir pagando algo porque un requisito se cayó sería una trampa, no una regla.
La lista ofrece todos los complementos que tienen una capacidad, menos el que estás editando. Los que quitaste de la venta siguen en la lista a propósito: lo que se comprueba es si la cuenta tiene la capacidad, no si se sigue vendiendo, así que uno que dejaste de vender sigue contando para todos los que ya lo tienen.
Sólo se comprueba un paso, y nada impide que escribas un ciclo. Si A necesita B y B necesita C, una cuenta con B puede obtener A sin haber tenido nunca C; y dos complementos que se nombran entre sí se bloquean mutuamente, así que no lo hagas.
Véndelo sólo con algunos planes
Elige uno o más planes en Disponible desde los planes y el complemento sólo lo pueden obtener las cuentas cuya suscripción activa está en uno de ellos. Una cuenta sin suscripción activa no tiene plan, así que tampoco puede obtenerlo. Mientras la cuenta no cambie de plan, el botón dice Requiere el plan y los nombres de los planes, unidos con comas y una "o" final ("Requiere el plan Pro o Business"), y está deshabilitado; la página de detalle dice "Este complemento solo se puede obtener con el plan Pro. Cambia tu plan primero." arriba de los bloques y no ofrece ninguno, y la misma negativa responde cuando se repite la petición. Un plan que ya no existe se deja fuera de los nombres; cuando ya no existe ninguno de los planes elegidos, nadie puede obtener el complemento, y el botón dice No disponible con tu plan.
El plan se comprueba antes que Depende de, así que a una cuenta a la que le faltan los dos se le habla del plan. Igual que la dependencia, decide quién puede obtener el complemento, nunca quién lo conserva: una cuenta que ya lo tiene —comprado antes de que se definiera la lista, o que lo conservó al cambiar de plan— todavía puede desactivarlo, cancelar su línea, cancelar su prueba, bajar su cantidad o pasar a un escalón más barato, pero no puede subir la cantidad ni pasar a un escalón más caro. La entrega de una compra de por vida ya pagada no se comprueba.
La lista ofrece los planes del contexto del complemento. Déjala vacía para vender el complemento con cualquier plan, que es como empieza todo complemento.
Declara el complemento en código
En vez de capturar un complemento a mano en cada entorno, puedes describirlo una sola vez en database/seeders/AddOnSeeder.php. El deploy ya corre los seeders (php artisan db:seed --force), así que el complemento 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 su capacidad, la misma que declara PlanFeatures:
$addons = [
'extra_storage' => [
'name' => 'Extra storage',
'description' => 'Short text shown on the catalog card.',
'long_description' => 'What it does, shown on the detail page.',
'prices' => [
['country' => 'default', 'currency' => 'usd', 'units' => null, 'intervals' => ['monthly' => 9.99, 'once' => 49]],
['country' => 'mx', 'currency' => 'mxn', 'units' => null, 'intervals' => ['monthly' => 179]],
],
'icon' => 'resources/images/addons/extra-storage.png',
'screenshots' => ['resources/images/addons/extra-storage-1.png'],
'help_guides' => ['sell-add-ons'],
'defaults' => ['is_active' => true],
],
];
| Llave | Qué define |
|---|---|
name, description, long_description |
La tarjeta y la página de detalle. |
prices |
Un renglón por país, moneda y unidades. intervals lleva un importe por intervalo; once es el precio de por vida. units es el tamaño del escalón o del paquete, null para un precio simple. |
icon, screenshots |
Rutas a imágenes dentro de tu repositorio, que se suben a la biblioteca de medios bajo General/Add-ons. Una imagen que no cambió no se vuelve a subir. |
help_guides |
Los slugs de las guías del centro de ayuda que enlaza la tarjeta, en orden. |
purchasable_plans |
Los slugs de los planes con los que se puede obtener, el campo Disponible desde los planes: ['pro', 'business']. Fuera de la entrada o vacío, se vende con cualquier plan. |
limits |
La cuota que agrega, por llave de límite: ['disk_space' => 5, 'branches' => 1]. Cada llave debe ser una que el proyecto declara o la de un medidor, la misma lista que ofrece el panel. Un valor que cambió se actualiza en su lugar; una lista vacía los quita todos. |
sells_per_unit, sells_per_tier, stacks_tiers |
Cómo se vende la cuota, mira Por unidad y por escalones. Por escalones, cada renglón de precio de suscripción lleva sus units: un renglón por país, moneda y escalón. stacks_tiers hace que los escalones se sumen y necesita sells_per_tier. |
sells_lifetime, trial_days |
Se vende de por vida (necesita un precio once) y los días de prueba; 0 no ofrece prueba. |
defaults |
Cualquier otra cosa que captura el formulario, como is_active. Se aplica sólo cuando el complemento se crea; después es tuyo para cambiarlo en el panel. |
Seis escalones de almacenamiento en pesos y en dólares, y una sucursal vendida por unidad, se ven así:
'extra_storage' => [
'name' => 'Extra storage',
'description' => 'Pick how much extra room you need.',
'sells_per_tier' => true,
'limits' => ['disk_space' => 5],
'prices' => [
['country' => 'mx', 'currency' => 'mxn', 'units' => 5, 'intervals' => ['monthly' => 49]],
// ... un renglón por escalón, hasta 1000
['country' => 'default', 'currency' => 'usd', 'units' => 5, 'intervals' => ['monthly' => 3]],
// ... un renglón por escalón, hasta 1000
],
],
'extra_branch' => [
'name' => 'Extra branch',
'description' => 'One more branch per unit.',
'sells_per_unit' => true,
'limits' => ['branches' => 1],
'prices' => [
['country' => 'mx', 'currency' => 'mxn', 'units' => null, 'intervals' => ['monthly' => 50]],
],
],
Un módulo que sólo se vende por suscripción no necesita ni limits ni modalidad: le bastan sus precios mensual y anual.
Una llave que dejas fuera de la entrada se queda como está. PlanSeeder funciona igual para los planes; mira Crea y pon precio a un plan.
Lo que el panel te deja cambiar
Un complemento creado por el seeder abre con un aviso Controlado por código. Su nombre, descripción, descripción completa, capacidad, precios, ícono, capturas, guías, planes disponibles, las formas en que se vende (de por vida, días de prueba, por unidad, por escalones) y sus límites son de sólo lectura ahí, los declare o no su entrada, y cada deploy regresa lo que dice el código. Todo lo demás —categorías, A la venta, Depende de— sigue siendo editable.
Un cambio en los límites o en la forma de venta se aplica en el siguiente deploy aunque el complemento tenga suscriptores, igual que si lo guardaras en el panel.
Los precios siguen al código sin revolver Stripe: un importe que no cambió conserva su precio en Stripe, y un importe que cambió lo reemplaza exactamente como lo haría quitarlo y escribir el nuevo en el panel. Un importe con suscriptores se retira en lugar de quitarse: su precio en Stripe se archiva, así que las compras nuevas y las tarjetas usan el importe nuevo, mientras quien ya paga el anterior lo sigue pagando.
Libéralo del código
Cuando quieras manejar un complemento 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 complemento, aunque su entrada siga en el código. Esto no se puede deshacer desde el panel.
Pasar a código un complemento que ya existe
Un complemento que ya existe con la llave de la entrada se toma sólo si todo lo que la entrada declara coincide con lo guardado: los textos, los mismos renglones de precio sin ningún otro precio guardado, los límites, las formas en que se vende, las guías, los planes disponibles y el mismo icono y capturas (los mismos archivos de imagen). Entonces abre con el aviso Controlado por código desde el siguiente deploy, sin tocar sus precios, sus imágenes ni sus suscriptores, y defaults no se aplica. Una imagen más grande que el tamaño máximo del cargador se redujo al subirla, así que ya no coincide con su archivo; deja icon y screenshots fuera de la entrada para adoptarlo y agrégalos después.
Cuando el complemento 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_addons')
->where('feature_key', 'extra_seats')
->whereNull('released_from_code_at')
->update(['managed_by_code' => true]);
Un complemento liberado del código se salta a propósito, y un precio con suscriptores no se puede reemplazar desde el código, igual que en los planes. 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 complemento 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 complemento liberado del código no se vuelve a tomar aunque siga coincidiendo, y uno que el seeder creó y que luego alguien eliminó tampoco se trae de vuelta.
Una entrada que no se puede vender tal como está escrita se omite completa: el complemento se queda como estaba y el deploy imprime el motivo y sigue. Pasa cuando se vende por unidad y por escalones a la vez, vende cuota (por unidad o por escalones) sin ningún límite, se vende por escalones con un renglón de precio de suscripción sin units, nombra un límite que el proyecto no declara y que ningún medidor usa, o se vende de por vida sin precio once.
Las cinco formas en que un usuario obtiene un complemento
Se pueden combinar en el mismo complemento. El acceso se resuelve desde la más fuerte, en este orden:
lifetime > plan > subscription > trial > free
| Origen | Cómo se otorga | Se pierde cuando | Flujo en el catálogo |
|---|---|---|---|
free |
El usuario pulsa Activar | El usuario lo apaga, o el administrador usa Retirar regalo | Sí |
plan |
El plan incluye el complemento | La suscripción deja de estar activa | Sí |
subscription |
El complemento es una línea de la suscripción de Stripe | Se cancela la línea o la suscripción | Sí |
trial |
El usuario pulsa Probar N días, o el administrador otorga una | Pasa ends_at, sin cobrar nada, o el usuario cancela la prueba; la fila se conserva como vencida |
Sí |
lifetime |
El usuario pulsa Comprar de por vida y el webhook confirma el pago, o el administrador lo otorga | Nunca, salvo que lo revoques | Sí |
La inclusión en el plan y las líneas de la suscripción se leen en vivo del pivote del plan y de los ítems de la suscripción, así que no pueden desfasarse. Las activaciones gratis, las pruebas y las compras de por vida son filas en billing_entitlements:
| Columna | Significado |
|---|---|
billing_account_id |
La cuenta que lo tiene. |
feature_key |
La capacidad. |
source |
free, trial o lifetime. Una fila por cuenta, llave y origen. |
status |
active por omisión; pending mientras se confirma un pago de por vida; expired cuando una prueba terminó. Cualquier valor distinto de active se ignora. |
quantity |
Unidades que se tienen, 1 por omisión. |
starts_at, ends_at |
Ambos opcionales. Una fila cuenta sólo entre los dos. |
reference |
Texto libre para tu propio registro, como el id de una sesión de Stripe. |
granted_by, granted_at |
El administrador que lo otorgó a mano, y cuándo. Vacíos para todo lo que hizo el usuario. |
Gratis
Un complemento sin ningún precio mayor a cero es gratis. El catálogo muestra Gratis y un botón Activar. Activarlo escribe un derecho free y no necesita tarjeta, suscripción, plan ni llave de Stripe. Desactivar borra sólo la fila free, así que un plan o una compra que también otorgue la capacidad la sigue otorgando.
Si das un complemento gratuito en prueba desde el panel Complementos de la página de un usuario, su tarjeta del catálogo dice Activo hasta el y la fecha en que termina la prueba, sin botón que pulsar, como dice la página de detalle. Cuando la prueba termina, la tarjeta vuelve a ofrecer Activar; dado de por vida, dice Adquirido.
Si le pones precio a un complemento que alguien ya activó gratis, lo conserva y dejas de ofrecérselo: su tarjeta del catálogo muestra Regalo en lugar del precio y su botón dice Activado y no hace nada, y la página de detalle quita los bloques de suscripción y de por vida y muestra Sin costo para ti donde estaba el precio, junto a la línea que dice que lo tiene por una activación gratuita y que el regalo no se puede quitar desde su cuenta. No puede comprar la versión de pago encima ni desactivarlo, porque Desactivar sólo se muestra en complementos que siguen siendo gratis. Quítaselo desde el panel Complementos de su página de usuario si hace falta: Retirar regalo junto a él.
Incluido en un plan
El formulario del plan tiene un selector múltiple Complementos incluidos que escribe el pivote billing_plan_addon. Mientras la suscripción default de la cuenta esté activa, cada complemento de su plan se otorga con origen plan. Su tarjeta del catálogo y su página de detalle muestran Incluido en tu plan donde iba el precio, y el botón de la tarjeta dice Incluido y no hace nada. Ambas muestran la tarifa debajo solo como referencia («Fuera del plan: $5.00 al mes») cuando el complemento tiene una. Los límites de los complementos incluidos se suman a los límites del plan.
Suscripción
El catálogo muestra el precio del intervalo que la cuenta ya paga y un botón Comprar. Lo que pasa depende de la cuenta:
- Todavía no hay suscripción: se abre un Stripe Checkout con el plan gratis de ese intervalo más el complemento. Debe existir exactamente una variación de plan gratis activa para ese intervalo, o el usuario ve "No free plan available for :interval billing. Please subscribe to a plan first." Si las pruebas gratis están encendidas en
config/pricing.php, la prueba cubre plan y complemento. El éxito regresa al dashboard con?checkout=success; la cancelación regresa a/account/add-ons?checkout=cancel. - Un plan gratis sin una tarjeta con la que cobrar: la página pide solo la tarjeta, con «Agregar una tarjeta para comprar el complemento». No se abre Checkout ni se crea una segunda suscripción. Al guardar la tarjeta, la compra sigue sola como en el punto siguiente: el complemento se suma a la suscripción gratuita, el cobro de hoy cubre solo los días hasta la siguiente renovación, no hay días gratis y la fecha de renovación no se mueve. Cancelar el formulario, o una tarjeta que no se puede guardar, deja la cuenta como estaba.
- Una suscripción de pago, o un plan gratis con tarjeta: el precio del complemento se agrega a la suscripción y se factura de inmediato. La página consulta cada tres segundos hasta que el webhook registra el ítem nuevo, luego se recarga y muestra Cancelar. Con el tiempo real encendido (/help/real-time-with-reverb) no consulta: el webhook avisa a la página en cuanto llega.
Un complemento solo se agrega en el periodo de cobro que ya tiene la suscripción. En una suscripción mensual, el precio anual de un complemento se rechaza con «Tu suscripción se cobra de forma mensual, así que este complemento solo se puede agregar con su precio mensual.»
Pulsar Cancelar quita el precio de la suscripción sin prorrateo. Se rechaza cuando la cuenta quedaría usando más de lo que su cuota permite sin el complemento; mira "Cuando un cambio dejaría la cuenta por encima de su cuota" más abajo. Los webhooks customer.subscription.updated, invoice.paid y customer.subscription.deleted recalculan el acceso de cada complemento con llave en esa cuenta. Ese recálculo pregunta a todos los orígenes, así que un webhook que ya no ve un complemento entre los ítems no puede apagar una capacidad que una compra de por vida o una activación gratis sigue otorgando.
Prueba
Define Días gratuitos de prueba en el complemento y el usuario puede pulsar Probar N días. No se pide tarjeta y nada llega a Stripe: la prueba empieza en cuanto se pulsa el botón. La página de detalle la ofrece junto a Suscribirse y la compra de por vida; la tarjeta del catálogo muestra Probar N días junto a Comprar mientras la cuenta no tiene el complemento y no ha usado su prueba —uno al lado del otro cuando la tarjeta tiene espacio, uno debajo del otro cuando no— y como único botón cuando no hay nada a la venta para el periodo de cobro en pantalla. Cuando la prueba ya corre o ya se usó, la tarjeta ofrece sólo Comprar. Sólo un complemento sencillo ofrece prueba: uno que se vende por unidad o por escalones nunca la ofrece, digan lo que digan sus días. Escribe una fila trial con ends_at y la página de detalle muestra, bajo el título Tu prueba, los días que quedan, la fecha en que termina y que no se cobra nada. Una prueba por cuenta y complemento, para siempre: la fila se conserva como expired cuando termina, y esa fila es la que rechaza una segunda. La prueba también se rechaza cuando la cuenta ya tiene la capacidad por cualquier vía.
El día antes del fin el usuario recibe un aviso, "Tu prueba de :addon termina mañana", que dice que no se cobra nada automáticamente y que suscribirse o pagarlo de una vez desde la página de complementos lo conserva. El día que termina, billing:expire-trials lo apaga: no se cobra nada, el complemento vuelve a no estar comprado y el usuario recibe un aviso que dice que ya no está disponible y que no se cobró nada. Lo que se creó durante la prueba se conserva; una cuenta que queda por encima de su cuota sólo deja de poder crear más. Desde entonces el complemento sólo se obtiene con Suscribirse o pagándolo de una vez, cobrado como cualquier compra. La prueba queda usada y no se puede volver a tomar. Mira "Por vencimiento" más abajo.
Comprar el complemento mientras la prueba corre la reemplaza sin dejar un hueco en el acceso. La fila de la prueba vence en ese momento, sin ningún aviso, y el historial registra que la prueba terminó antes de la línea de la compra: empezó la prueba, terminó la prueba y luego la compra.
El historial de la suscripción registra "Empezó la prueba de :name (hasta el :date)." cuando una prueba empieza, también la que otorga un administrador, y "Terminó la prueba de :name." cuando termina, se cancela o la reemplaza una compra —una sola vez, lo que ocurra primero.
Mientras la prueba corre, la página de detalle ofrece Cancelar prueba. Retira el acceso de inmediato, no cobra nada y no manda ningún aviso —y gasta la prueba, así que después ya no se puede tomar.
La prueba no depende de la forma en que se paga el complemento. Un complemento que sólo se vende de por vida puede ofrecer una, que es justamente probarlo antes de comprarlo.
De por vida
Enciende Vendido de por vida y llena la tabla Precio de por vida, y el catálogo muestra Comprar de por vida. Pulsarlo abre un Stripe Checkout en modo pago y registra la sesión antes de que el usuario se vaya, con una fila lifetime en estado pending. El acceso no se otorga al volver de Stripe: la página de detalle muestra Pago en curso y consulta hasta que el webhook checkout.session.completed confirma el pago y pone la fila en active. Recargar o pulsar dos veces reutiliza la sesión pendiente, así que nada se paga dos veces. Se reutiliza solo en el idioma con que se abrió: después de cambiar el idioma de la aplicación, el siguiente clic abre una sesión nueva en ese idioma.
Mientras el pago está en curso, la página de detalle ofrece Retomar el pago, que vuelve a abrir ese Checkout, y Abandonar, que cierra en Stripe cada Checkout pendiente del complemento, los marca abandoned y borra la fila pending: no se cobra nada, el complemento queda como no comprado y el historial no escribe nada. Una sesión que Stripe ya completó no se abandona; su webhook otorga el acceso como siempre.
Cuando el pago se confirma, el historial de la suscripción registra "Se compró X de por vida por $…" con el monto pagado, hecho por la cuenta. Si se compra durante una prueba, el historial dice: empezó la prueba, terminó la prueba y luego la compra de por vida —cada una una sola vez, aunque Stripe repita su aviso. El acceso que da un administrador con Conceder de por vida no aparece como compra.
Un derecho de por vida gana sobre cualquier otro origen, así que el catálogo muestra Adquirido, la página de detalle muestra Pagado de por vida donde iba el precio, el administrador no puede apagarlo y cancelar el plan no lo toca. Cuando la cuenta además paga el mismo complemento como línea de la suscripción, la página de detalle lo dice y ofrece Cancelar la línea recurrente; nada cambia hasta que el usuario la pulsa. El mismo mecanismo de Checkout, usado para cosas que no son complementos, es el tema de Cobra una vez con Stripe Checkout.
Por unidad y por escalones
Las dos venden cuota, así que las dos necesitan al menos un límite, y no se pueden combinar. Vendido por unidad es el precio lineal: con storage_gb = 5 a 10 al mes, la página de detalle muestra un selector de cantidad, el resumen recalcula el importe y la cuota conforme se mueve, y tres unidades son quince gigabytes a 30. Vendido por escalones es el precio no lineal: la tabla de precios gana una columna Unidades y el usuario elige una opción, de modo que diez gigabytes pueden costar 70 y no 100:
| País | Moneda | Unidades | Mensual | Anual |
|---|---|---|---|---|
| default | mxn | 1 | 10.00 | 100.00 |
| default | mxn | 10 | 70.00 | 700.00 |
| default | mxn | 50 | 250.00 | 2,500.00 |
Cambiar la cantidad (Actualizar) o el escalón (Cambiar) ajusta la línea que ya existe; nunca agrega una segunda. La pasarela prorratea la diferencia. Bajar la cantidad o pasar a un escalón menor se rechaza cuando la cuota menor quedaría por debajo de lo que la cuenta ya usa: la línea se queda como estaba y el mensaje nombra cada capacidad que quedaría por encima, con el consumo y el nuevo máximo. Subir cualquiera de los dos nunca se rechaza por la cuota, pero cobra la diferencia al momento. Si la cuenta ya no cumple el requisito del plan o la dependencia, puede bajar o cancelar su línea actual, pero no elevarla: el selector de cantidad se detiene en lo contratado y el de escalones muestra el actual y los más baratos. Si la tarjeta se rechaza, nada cambia, la línea conserva su cantidad o su escalón, y la página vuelve a mostrar el contratado. Cuando la suscripción tiene un pago pendiente de completar, un cambio de escalón responde primero con eso. Con un escalón contratado, el precio de la página de detalle es el de ese escalón y no el de la primera opción; la tarjeta del catálogo también lo muestra en la pestaña del intervalo que la cuenta paga, y ofrece Cancelar sea cual sea el escalón. La capacidad recurrente se paga contratada, no consumida: quien tiene diez gigabytes paga diez use tres o todos. Para unidades que se gastan y se acaban, mira Vende consumo con medidores y paquetes.
Escalones que se suman
Con Los escalones se suman encendido, un complemento vendido por escalones deja de ser una sola elección. El usuario elige un escalón y una cantidad, y paga el precio del escalón por la cantidad; comprar otro escalón lo agrega junto a los que ya tiene en lugar de reemplazarlos, y comprar un escalón que ya tiene sube su cantidad. Cada unidad de cada escalón suma su cuota, así que dos unidades del escalón de cinco gigabytes y una del de diez dan veinte.
En la página de detalle cada escalón contratado aparece con su propia cantidad, Actualizar y Eliminar, seguido de lo que dan en conjunto, y Agregar más ofrece la siguiente compra. No se ofrece Cambiar: quitar un escalón deja los demás como están. Bajar una cantidad o quitar un escalón se sigue rechazando cuando la cuenta quedaría por encima de su cuota, como se explica abajo.
Cuando un cambio dejaría la cuenta por encima de su cuota
Cada acción que reduce lo que da un complemento se compara con lo que la cuenta ya usa, y se rechaza cuando el consumo quedaría por encima del nuevo máximo:
- Bajar la cantidad o el escalón, en la página de detalle.
- Cancelar un complemento de pago, en la página de detalle o en la tarjeta del catálogo.
- Desactivar un complemento gratis, en la página de detalle o en la tarjeta del catálogo.
- Retirar regalo en el panel de administración.
No cambia nada, y el mensaje nombra cada capacidad que quedaría por encima: "Has excedido el límite para Espacio en disco (12 / 10)." Usar exactamente el nuevo máximo está permitido. Para seguir, la cuenta libera lo que sobra, o consigue la cuota por otra vía antes.
Sólo se saca de la cuenta el complemento que se cancela o se desactiva. El plan, los complementos que incluye, lo que se tiene de por vida o en prueba, y los demás complementos de la suscripción siguen contando. Si ese complemento era lo único que daba la capacidad, el nuevo máximo es 0, no ilimitado. Un complemento que no da ningún límite nunca se rechaza, y tampoco una suscripción cancelada automáticamente por un cargo sin pagar.
Activar y desactivar
Desde el catálogo
Los complementos gratis alternan con Activar y Desactivar. Los de pago con Comprar y Cancelar. La tarjeta también ofrece Probar N días y Comprar de por vida cuando el complemento los tiene. La página de detalle lista cada vía que ofrece el complemento en su propia caja, con Suscribirse (y la cantidad o el escalón), Actualizar o Cambiar sobre una línea que ya se tiene, Cancelar, Cancelar prueba mientras una prueba corre, y Cancelar la línea recurrente cuando conviven una compra de por vida y una línea de suscripción.
Cancelar pide confirmación antes de quitar el complemento, en la tarjeta igual que en la página de detalle; Desactivar en un complemento gratuito también la pide en la página de detalle. Una tarjeta muestra Cancelar para un complemento que la cuenta tiene sea cual sea la pestaña abierta: contratado mensual y visto en la pestaña anual, sigue diciendo Cancelar, con el precio anual en esa pestaña. Una cuenta que ya paga un plan o un complemento no tiene segunda pestaña: los complementos se suman a esa misma suscripción, así que su catálogo muestra sólo su periodicidad. Todo lo que cobra en el momento — Comprar en la tarjeta, Suscribirse, Actualizar y Cambiar en la página de detalle — se rechaza de inmediato cuando la tarjeta se rechaza: el complemento no se agrega, la cantidad o el escalón se quedan como estaban, no queda ninguna factura sin pagar y el mensaje dice lo que respondió el banco.
Una tarjeta cuyo banco pide a quien compra aprobar el pago (3-D Secure) no se rechaza. La pantalla abre Confirma tu pago dentro de la aplicación, y Aprobar pago muestra la autenticación del propio banco. Nada se enciende hasta que el pago se aprueba: aprobado, el complemento, la cantidad o el escalón quedan activos al momento y la pantalla se recarga para decirlo; cancelado, rechazado o fallido, no se cobra nada ni cambia nada, y el mensaje dice que se puede intentar de nuevo. Quien cierra la pestaña con el pago abierto encuentra «Tienes un pago esperando tu aprobación» con Confirmar pago en la página del complemento y en Facturación, hasta que se acaba el plazo del banco (alrededor de un día); entonces desaparece, sin nada cobrado. Mientras un pago espera, cualquier otra compra o cambio en la suscripción se responde con ese mismo aviso. La misma ventana sirve para subir de plan o cambiar el periodo de cobro. En una suscripción que todavía no cuesta nada, como el plan gratuito, el pago por aprobar es sólo la parte que falta hasta la siguiente renovación, cobrada aparte; ya aprobado, el complemento, la cantidad o el escalón se suman a la suscripción sin mover su fecha de inicio ni la de renovación, y desde la renovación se cobra el precio completo. Si el complemento no puede agregarse después de aprobado el pago, el cobro se devuelve completo y el mensaje lo dice; nada cambia en la suscripción.
Tu webhook de Stripe necesita customer.subscription.pending_update_applied y customer.subscription.pending_update_expired para esto; consulta Activa los planes y el cobro.
Cualquiera de ellas que funcione recarga la pantalla y lo confirma con un aviso arriba, así que lo que el complemento habilita o retira — una entrada del menú de la cuenta, una pantalla que antes no estaba — queda en su sitio sin que el usuario recargue nada. La excepción es una compra que aún espera la confirmación del pago: esa pantalla se queda donde está y anuncia el resultado cuando llega la confirmación.
Desde el panel de administración
La página de un usuario en el panel de administración muestra un panel Complementos. Es una lista de sólo lectura: no puedes encender ni apagar un complemento de una cuenta desde el panel de administración. El panel lista los complementos que tiene la cuenta, cada uno con la vía por la que lo tiene, o "Esta cuenta no tiene complementos." Se muestra en la página del usuario, no en el formulario de edición, y guardar el formulario nunca otorga ni quita un complemento. Un complemento de pago que la cuenta tiene sólo porque se lo encendieron a mano gratis — un regalo — lleva junto a él un enlace Retirar regalo. El enlace abre un formulario corto con ese complemento ya elegido; confirmarlo retira el regalo y te devuelve a la página del usuario, con "El regalo se retiró de la cuenta." y el complemento ya fuera de la lista, salvo que la cuenta quedara por encima de su cuota, en cuyo caso se rechaza con el mismo mensaje que vería el dueño de la cuenta y te quedas en el formulario. Un complemento que se tiene por cualquier otra vía no es un regalo y no lleva enlace: cancélalo donde se compró.
Lo que un administrador dio con Conceder de por vida o Conceder una prueba muestra junto al complemento un enlace Quitar acceso. Abre un formulario corto con ese acceso ya elegido; confirmarlo quita el acceso y te devuelve a la página del usuario con "Se quitó el acceso de la cuenta.", salvo que la cuenta quedara por encima de su cuota, en cuyo caso se rechaza con el mismo mensaje que Retirar regalo. Lo que la cuenta compró, aunque sea de por vida, nunca lleva el enlace: un reembolso se hace en Stripe. Cuando la cuenta tiene el mismo complemento por otra vía — incluido en su plan, como línea de suscripción o activado gratis — lo conserva. El acceso quitado se queda en el Historial de accesos como Retirado, con quién lo quitó y cuándo, y una prueba quitada sigue contando como la prueba de la cuenta, así que no puede iniciar otra del mismo complemento.
Para encender un complemento a una cuenta, inicia sesión como esa cuenta y actívalo desde su pantalla Complementos, o usa una de las dos acciones de abajo.
El mismo panel tiene dos acciones, Conceder de por vida y Conceder una prueba (con los días, quince por omisión). Otorgan el complemento como si se hubiera comprado o probado, registrando qué administrador lo hizo y cuándo. Una prueba manual conserva la regla de una sola vez para siempre. El panel de sólo lectura Historial de accesos de abajo lista cada acceso que la cuenta tuvo alguna vez: complemento, origen, estado, fechas y, cuando fue a mano, quién lo otorgó.
Desde código
Todo es un método de la cuenta de facturación. Obtenla desde el dueño:
use WeblaborMx\BillingCore\BillingManager;
$account = $user->billingAccount; // relación
$account = app(BillingManager::class)->context($user, 'user')->account;
| Método | Qué hace |
|---|---|
$account->grantAddOn($key, $source, $attributes = []) |
Crea o actualiza el derecho para esa llave y origen, pone status en active y refresca la caché. $attributes puede llevar quantity, starts_at, ends_at, reference. |
$account->revokeAddOn($key, $source) |
Borra una fila free, o marca cualquier otro origen como expired, y refresca la caché. Los demás orígenes no se tocan. |
$account->startTrial($addon) |
Inicia la prueba que ofrece el complemento. Devuelve ['success' => bool, 'message' => ...]; se rechaza cuando el complemento no ofrece prueba, la prueba ya se usó o la capacidad ya se tiene. No necesita tarjeta. |
$account->grantManually($key, 'lifetime' o 'trial', $days, $admin) |
Lo que llaman las acciones del administrador. Registra granted_by y granted_at. |
$account->confirmLifetime($reference) |
Pone en active la fila de por vida pendiente con esa referencia. Idempotente: una ya activa responde true y no se otorga nada de nuevo. |
$account->toggleAddOn($variation) |
Compra o cancela un complemento de pago, dada una BillingPriceVariation. Devuelve ['success' => bool, 'message' => ..., 'action' => 'buy' o 'cancel'], o ['success' => true, 'url' => ...] cuando hay que abrir un Checkout, o 'action' => 'capture_card' cuando la cuenta está en un plan gratis sin tarjeta con la que cobrar, que es lo que hace que la pantalla la pida. |
$user->toggleAddOn($variation) |
Lo mismo a través del dueño. |
Por vencimiento
Una prueba o cualquier derecho con ends_at deja de contar en el momento en que pasa la fecha, porque hasAddOn() lee las filas cuando se le pregunta. El comando billing:expire-trials, programado a diario a las 08:00 mientras el módulo está encendido, hace el resto: manda el aviso de las pruebas que terminan en un día o menos, una sola vez, y a las pruebas cuyo tiempo se acabó les marca la fila como expired, refresca la caché features descrita más abajo y manda el aviso de que la prueba terminó sin cobrar nada. Nunca cobra. Una prueba ya reemplazada por una compra vence sin aviso; si la compra no la terminó ya, el comando escribe su fin en el historial, una sola vez.
La fila queda como expired fuera cual fuera el desenlace, incluso cuando el usuario canceló la prueba él mismo, que es lo que sostiene una prueba por cuenta y complemento para siempre. Entre el momento en que una prueba termina y la siguiente corrida, hasAddOn() ya dice que no mientras la caché features descrita más abajo todavía dice que sí.
Verifica el acceso en tu código
Pregúntale a la cuenta. Es lo único que conoce todos los orígenes.
| Método | Devuelve |
|---|---|
$account->hasAddOn('electronic_wallet') |
true o false. |
$account->addOnSource('electronic_wallet') |
'lifetime', 'plan', 'subscription', 'trial', 'free' o null. |
$account->addOnQuantity('extra_storage') |
Unidades que se tienen: el mayor entre 1, la cantidad del ítem de la suscripción y el quantity del derecho. 0 cuando no se tiene. |
$account->entitlements() |
Las filas de billing_entitlements; ->granted() acota a las que cuentan ahora. |
$user->hasAddOn('electronic_wallet') |
La misma respuesta, preguntada desde el usuario. |
$user->addOnSource('electronic_wallet') |
El mismo origen, preguntado desde el usuario. |
$user->hasLockedAddOn('white_label') |
true cuando el origen es cualquiera menos free. Sólo para pintar el candado, ver abajo. |
$user->getIncludedAddons() |
Los complementos del plan actual. |
Pregunta con hasAddOn(), en el usuario o en la cuenta. Hay una sola forma de preguntar, lee las cinco fuentes tal como están en ese momento, y es la que decide si una pantalla, un menú o un guardia dejan pasar a alguien.
hasLockedAddOn() no es esa pregunta. Responde false para una capacidad otorgada gratis, porque existe para decirle al panel interno cuándo pintar un candado. Condicionar una pantalla con ella se la esconde justo a quien recibió la capacidad de regalo.
El json features es sólo una caché. Vive en billing_accounts.features, y también en la tabla del dueño cuando esa tabla tiene una columna features, que es como un producto que ya guarda la bandera en su propia tabla mantiene esa columna al día. La tabla users de Weblabor Base no tiene esa columna. Se escribe con refreshFeatureCache() después de cada otorgamiento, revocación y webhook, así que se atrasa en los vencimientos: una prueba que terminó hace una hora ahí todavía se lee como otorgada. Nunca decidas con ella.
Límites: $user->getLimitTotal('activities') suma el límite del plan, los límites de los complementos que son ítems de la suscripción, los límites de los complementos incluidos en el plan y los límites de los complementos que se tienen como free, trial o lifetime. Un complemento ya contado como línea o como incluido no se vuelve a contar como otorgado, así que comprar de por vida lo que el plan incluye cuenta una vez. Una línea vendida por unidad aporta su límite por la cantidad; una línea vendida por escalones aporta las unidades del escalón en lugar del valor del límite.
Lo que ve el usuario
El catálogo es /account/add-ons, accesible desde la entrada Complementos del menú de la cuenta en cuanto un complemento está a la venta. Renderiza el mismo componente que usa la página de inicio, así que ambos se ven igual.
- Un encabezado, "Mejora tu plan", y una píldora que cambia el intervalo. Sólo se ofrecen los intervalos que algún complemento realmente cotiza y que están activos en
config/pricing.php. El intervalo arranca en el que la cuenta ya paga, o mensual. Una cuenta que paga un plan, o un complemento con el plan gratis, no ve la píldora: su catálogo se queda en la periodicidad de su suscripción, y un complemento sin precio en ella dice No disponible. Con el plan gratis sin nada pagado, o sin plan, se ofrecen las dos periodicidades, y la que se elige al comprar fija la periodicidad. - Un buscador sobre nombre y descripción, y un selector de categoría que sólo lista categorías con al menos un complemento a la venta.
- Una tarjeta por complemento a la venta: ícono o una pieza de rompecabezas como relleno, nombre que enlaza al detalle, hasta dos categorías, la descripción, el precio del intervalo y el botón. Los precios se eligen para el país de la cuenta y caen a la entrada de país
default. El precio lleva su periodo: "$9.00 /mes", "$90.00 /año". El precio "de por vida" sólo aparece en un complemento que se vende de por vida. - Un complemento con precios pero ninguno para el intervalo elegido, y que no se vende de por vida, se queda en el catálogo cuando el usuario cambia de intervalo: sin precio, con el botón No disponible y la nota "Este complemento todavía no tiene precio para esa periodicidad." Un complemento con precios sólo para otros países no aparece.
- Ver guía debajo de la tarjeta cuando el complemento tiene una guía, que lleva directo a ella; Ver guías cuando tiene varias, que abre una lista corta para elegir. Nada cuando no tiene ninguna. El mismo enlace aparece en la página de inicio pública.
- La página de detalle,
/account/add-ons/{id}, agrega la galería de capturas, la descripción completa, "Qué incluye", construido a partir de los límites del complemento (en uno vendido por escalones, las unidades del escalón contratado, o del escalón elegido en el selector mientras la cuenta no lo tiene), y un panel lateral con una caja por cada vía para obtenerlo: gratis, incluido, suscripción, prueba y de por vida, cada una con su precio y su condición. Dice por qué vía la cuenta ya lo tiene, los días que quedan de una prueba y Pago en curso mientras una compra de por vida espera su webhook. Cuando la prueba ya se usó y la cuenta no tiene el complemento por otra vía, la caja de prueba dice "La prueba de este complemento ya se usó en esta cuenta." y explica que cada cuenta puede tomar la prueba una sola vez; cuando la cuenta lo tiene por otra vía, la caja dice cuál en su lugar.
El botón dice lo que la cuenta puede hacer:
| Etiqueta | Cuándo |
|---|---|
| Adquirido | El origen es lifetime, o la cuenta lo tiene por su suscripción o por una prueba en curso y el intervalo elegido no tiene precio para él. Deshabilitado. |
| Incluido | El origen es plan. Deshabilitado. |
| Activado | El origen es free en un complemento que tiene precio. Deshabilitado. |
| Próximamente | La capacidad está declarada en código pero todavía no liberada, o el complemento no tiene precio ni capacidad alguna. Deshabilitado, con la nota "Próximamente. Este complemento aún no está disponible." |
| Pago en curso | Una compra de por vida espera su webhook. Deshabilitado, con la nota "Abre el complemento para retomar el pago o abandonarlo." |
| Activar / Desactivar | Complemento gratis, sin activar o ya activado. |
| Comprar / Cancelar | Complemento de pago, sin ser o siendo ya ítem de la suscripción. |
| Comprar de por vida | Se vende de por vida, sin precio recurrente para el intervalo elegido. |
| Probar N días | Ofrece prueba, todavía no usada, y la cuenta no tiene nada más. |
| No disponible | Complemento de pago sin precio para el intervalo elegido, y que no se vende de por vida. Deshabilitado, con una nota. |
| Requiere el plan ... | El complemento sólo está disponible desde algunos planes y la cuenta no está en ninguno. Deshabilitado, con el motivo al lado. |
| No disponible con tu plan | El complemento sólo está disponible desde algunos planes y ya no existe ninguno de ellos. Deshabilitado, con el motivo al lado. |
| Requiere ... | La cuenta no tiene el complemento del que este depende. Deshabilitado, con el motivo al lado. |
En la página de inicio pública el catálogo aparece sólo cuando BillingAddon::isActive() es verdadero y el visitante no está en iOS. Ahí muestra los cuatro complementos que más cuentas tienen, por suscripción o comprados de por vida, con los empates ordenados por nombre. Las tarjetas no llevan botón: un aviso arriba dice "Para contratar un complemento necesitas una cuenta." con enlaces para crearla y para iniciar sesión, y Ver todos debajo abre la página pública de complementos, /es/complementos (/en/add-ons en inglés), que lista igual todos los complementos a la venta en el país del visitante, por nombre. Un complemento sin precio para el intervalo elegido se deja fuera en vez de mostrarse como no disponible.
Lo que no está construido
Devoluciones, disputas y contracargos: un acceso de por vida no se revoca solo cuando el pago se revierte después. El prorrateo de una cuota incluida cuando el plan cambia a media del ciclo. Las unidades que se gastan y se acaban, los paquetes prepagados y el consumo cobrado al cierre del ciclo no son complementos: son medidores, en Vende consumo con medidores y paquetes.
