Self-Service-Onboarding (arquitectura)¶
El Self-Service-Onboarding permite a los interesados configurar por sí mismos una instancia en la nube de DiKAS sin contacto comercial — a elección como prueba gratuita (Trial) o directamente como instancia de pago en producción. Esta página describe la arquitectura técnica, la configuración y los mecanismos de seguridad. La perspectiva del usuario final se describe en Registro en línea.
Desactivado por defecto (Dark-Rollout)
El embudo público está protegido mediante el flag SelfServiceSignupEnabled y viene
desactivado en el estado de entrega. Mientras el flag sea false, todos los endpoints
públicos devuelven 404. Antes de una activación deben cumplirse los puntos indicados en
Antes de la activación.
Resumen¶
El proceso consta de un asistente de frontend público y una serie de endpoints de backend
anónimos. El aprovisionamiento propiamente dicho pasa por el mismo ILicenseProvisioningService que
también utilizan la vía del portal y la vía de prueba (Trial).
| Área | Ubicación |
|---|---|
| Asistente de frontend | Ruta /signup (PublicOnboardingWizardComponent, app dikas-web) |
| Confirmación por enlace mágico | Ruta /signup/verify |
| Endpoints de backend | /api/v1/onboarding/public/* y /api/v1/trial/start |
| Paquete de funciones | Dikas.Features.Licensing |
El asistente guía a través de siete pasos: Paquete → TSE (nube frente a Swissbit) → Cuenta (correo electrónico, sin contraseña mediante enlace mágico) → Datos de la empresa → Nombre en línea (subdominio) → Aspectos legales y pago → Listo (URL de la instancia y datos de acceso).
Flag de función SelfServiceSignupEnabled¶
El flag es una propiedad del documento singleton OperationalConfig (ID fija operationalconfig) y
solo se puede modificar mediante la base de datos o la interfaz de administración — no existe una
variable de entorno para ello.
Cada endpoint público llama primero a CheckSelfServiceEnabledAsync() y responde con
404 Not Found mientras el flag sea false. Así, el embudo permanece invisible hasta que se activa
de forma deliberada.
Endpoints públicos¶
Todos los endpoints son [AllowAnonymous] y están protegidos por el flag y por las medidas contra
el abuso.
| Método y ruta | Finalidad |
|---|---|
POST /api/v1/onboarding/public/orders |
Crear un pedido borrador anónimo (devuelve un token de pedido secreto) |
PUT /api/v1/onboarding/public/orders/{id} |
Actualizar el borrador (vinculado al token) |
POST /api/v1/onboarding/public/orders/{id}/reserve-name |
Reservar temporalmente el nombre en línea |
POST /api/v1/onboarding/public/register |
Crear un cliente sin contraseña y enviar el enlace mágico |
POST /api/v1/onboarding/public/orders/{id}/bind |
Vincular el pedido a la cuenta verificada mediante el enlace mágico ([Authorize]) |
POST /api/v1/onboarding/public/payment/set |
Crear un SetupIntent/PaymentIntent de Stripe (devuelve ClientSecret) |
POST /api/v1/onboarding/public/payment/webhook |
Webhook de Stripe (confirmación de pago) |
POST /api/v1/onboarding/public/orders/{id}/submit |
Aprovisionar la instancia |
GET /api/v1/onboarding/public/payment-config |
Clave publicable de Stripe para el frontend |
POST /api/v1/trial/start |
Prueba (Trial) directa de autoservicio (sin pedido/cuenta de portal) |
Seguridad / anti-abuso¶
Dado que cada aprovisionamiento genera costes reales (COGS), hay activas varias capas de protección independientes entre sí:
- Honeypot — un campo de formulario oculto; si se rellena, se rechaza silenciosamente con
202 Accepted(sin aprovisionamiento). - Limitador de errores por IP — retardo exponencial, bloqueo tras 10 errores (1 h) o 20 errores (24 h).
- Cuota de aprovisionamiento por IP y 24 h (
MaxPublicProvisionsPerIpPer24h, por defecto 3). Se aplica tanto en el envío público como en el inicio del Trial, y no se reinicia en caso de éxito. Si se supera la cuota, el endpoint responde con429(code: SIGNUP_RATE_LIMITED). - Cuota de envío de correo por IP y hora (
MaxPublicEmailSendsPerIpPerHour, por defecto 5) contra el bombardeo de enlaces mágicos. - Enlace mágico en lugar de la confirmación clásica por correo electrónico; al hacer clic se verifica la dirección y se obtiene un JWT del portal con el que se vincula el pedido.
- IP de cliente real — detrás del Ingress, la IP de origen se determina mediante
ForwardedHeaders. Las redes de confianza (KnownNetworks) deben configurarse en el despliegue con el CIDR del clúster; de lo contrario, la cuota por IP deja de funcionar.
Cuotas en memoria
Las cuotas se mantienen en memoria (ventana de tiempo móvil). Con varias instancias detrás de un balanceador de carga, cada instancia cuenta por separado; en ese caso hay que prever una sesión persistente (sticky session) o una caché compartida.
Configuración¶
Las opciones se cargan desde la sección Onboarding (OnboardingOptions). Todos los valores tienen
valores por defecto razonables en el código; se pueden sobrescribir mediante variables de entorno
con doble guion bajo.
| Clave | Por defecto | Significado |
|---|---|---|
Onboarding__MaxPublicProvisionsPerIpPer24h |
3 |
Máx. de aprovisionamientos por IP / 24 h (envío + Trial) |
Onboarding__MaxPublicEmailSendsPerIpPerHour |
5 |
Máx. de correos de enlace mágico por IP / hora |
Onboarding__RequireEmailVerifyBeforeProvision |
true (portal) |
Exigir confirmación de correo antes del aprovisionamiento |
Onboarding__RequireOnlinePaymentForNonTrial |
false (portal) |
Exigir un pago en línea real cuando no es Trial |
Pago¶
El pago se procesa a través de Stripe en una cuenta central de DiKAS (no por inquilino).
| Clave | Significado |
|---|---|
Signup__Stripe__SecretKey |
Clave secreta de Stripe (activa el adaptador de pago real) |
Signup__Stripe__PublishableKey |
Clave publicable para el frontend |
Signup__Stripe__WebhookSecret |
Secreto de firma para el webhook |
Sin una SecretKey configurada, está activo un adaptador nulo (dev/test). Los valores de
OnboardingPaymentMethod: SepaLastschrift = 1, StripeSetupIntent = 2 (Trial primero, registrar
la forma de pago), StripePaymentIntent = 3 (pago primero, pago inmediato).
Ciclo de vida y limpieza¶
Dos servicios en segundo plano mantienen limpio el embudo:
Pedidos abandonados¶
OnboardingCleanupService (diario) elimina los pedidos abandonados/no pagados que llevan más de 14
días (CleanupAbandonedOnboardingsCommand, configurable) inactivos — incluidos los borradores
públicos anónimos huérfanos (sin cliente vinculado) — y libera de nuevo su reserva de nombre en
línea. Los pedidos aprovisionados/pagados permanecen intactos.
Trials caducados (dry-run, oculto)¶
TrialDeprovisioningService es un servicio construido de forma deliberadamente cautelosa:
- Encuentra los Trials caducados cuyo periodo de gracia (
GraceDays, por defecto 30) ha transcurrido tras el final del Trial (IsTrial = trueyEndDatemás allá del plazo; las licencias convertidas/pagadas conIsTrial = falsenunca se incluyen). - En DRY-RUN solo registra qué bases de datos de inquilino (
{DatabasePrefix}_maindb,{DatabasePrefix}_gastrocurrent) eliminaría una ejecución real posterior — no elimina nada. - Está completamente desactivado por defecto y solo se ejecuta con
TrialDeprovision__Enabled=true.
| Clave | Por defecto | Significado |
|---|---|---|
TrialDeprovision__Enabled |
false |
Interruptor principal; false = el servicio no se ejecuta en absoluto |
TrialDeprovision__GraceDays |
30 |
Periodo de gracia en días tras el final del Trial |
La eliminación real de la BD no está implementada
La eliminación real de las bases de datos de los inquilinos está deliberadamente sin implementar. Requiere una autorización explícita y un mapeo verificado de nombres de bases de datos, y debería coordinarse con la aplicación del vencimiento (fin del Trial → bloqueo → exportación).
Auditoría¶
Cada aprovisionamiento se registra mediante IAuditLogger y es visible en la vista de auditoría del
operador (GET /api/v1/audit/logs/{date}, solo rol Admin):
| Evento | Disparador |
|---|---|
TRIAL_PROVISIONED |
Inicio correcto de un Trial (con nombre en línea, ID de licencia, duración) |
TRIAL_CONVERTED_PAID |
Conversión de un Trial en una licencia de pago |
La marca de tiempo y la IP del cliente proceden de la solicitud; en los Trials anónimos, el usuario
figura como -. Los datos de acceso no se escriben en la auditoría.
Antes de la activación¶
Antes de poner SelfServiceSignupEnabled en true:
-
ForwardedHeaders:KnownNetworksconfigurado en el despliegue de producción con el CIDR real del clúster (de lo contrario, la cuota por IP no tiene efecto). - Claves de Stripe (
Signup__Stripe__*) configuradas — de lo contrario, un pago «en vivo de inmediato» con un proveedor real sería gratuito. - Envío del enlace mágico y
/signup/verifycompletamente implementados. - Flag activado en
OperationalConfig.