Add subcription feature 10

This commit is contained in:
Urtzi Alfaro
2026-01-16 23:52:26 +01:00
parent 3a7d57ef90
commit 4b65817b3e
9 changed files with 89 additions and 29 deletions

View File

@@ -10,6 +10,48 @@
7. [Common Issues & Solutions](#common-issues--solutions) 7. [Common Issues & Solutions](#common-issues--solutions)
8. [Production Checklist](#production-checklist) 8. [Production Checklist](#production-checklist)
Flow Without 3DS Required
Step 1: POST /start-registration
├── Create Stripe Customer
├── Create SetupIntent with confirm=True
│ └── Stripe checks: "Does this card need 3DS?" → NO
│ └── SetupIntent status = 'succeeded' immediately
├── Store state: {customer_id, setup_intent_id, etc.}
└── Return: {requires_action: false, setup_intent_id}
Step 2: Frontend sees requires_action=false
└── Immediately calls /complete-registration (no 3DS popup)
Step 3: POST /complete-registration
├── Verify SetupIntent status = 'succeeded' ✓ (already succeeded)
├── Create Subscription with verified payment method
├── Create User, Tenant, etc.
└── Return: {user, tokens, subscription}
Flow With 3DS Required
Step 1: POST /start-registration
├── Create Stripe Customer
├── Create SetupIntent with confirm=True
│ └── Stripe checks: "Does this card need 3DS?" → YES
│ └── SetupIntent status = 'requires_action'
├── Store state: {customer_id, setup_intent_id, etc.}
└── Return: {requires_action: true, client_secret}
Step 2: Frontend sees requires_action=true
├── Shows 3DS popup: stripe.confirmCardSetup(client_secret)
├── User completes 3DS verification
└── Calls /complete-registration
Step 3: POST /complete-registration
├── Verify SetupIntent status = 'succeeded' ✓ (after 3DS)
├── Create Subscription with verified payment method
├── Create User, Tenant, etc.
└── Return: {user, tokens, subscription}
--- ---
## Prerequisites ## Prerequisites

View File

@@ -546,7 +546,16 @@ const NewProfileSettingsPage: React.FC = () => {
setIsLoading(true); setIsLoading(true);
try { try {
await updateProfileMutation.mutateAsync(profileData); // Map the form fields to the expected API format
// The API expects 'full_name' instead of separate 'first_name' and 'last_name'
const profileUpdateData = {
full_name: `${profileData.first_name} ${profileData.last_name}`.trim(),
phone: profileData.phone,
language: profileData.language,
timezone: profileData.timezone
};
await updateProfileMutation.mutateAsync(profileUpdateData);
setIsEditing(false); setIsEditing(false);
showToast.success(t('profile.save_changes')); showToast.success(t('profile.save_changes'));

View File

@@ -169,12 +169,21 @@ const ProfilePage: React.FC = () => {
const handleSaveProfile = async () => { const handleSaveProfile = async () => {
if (!validateProfile()) return; if (!validateProfile()) return;
setIsLoading(true); setIsLoading(true);
try { try {
await updateProfileMutation.mutateAsync(profileData); // Map the form fields to the expected API format
// The API expects 'full_name' instead of separate 'first_name' and 'last_name'
const profileUpdateData = {
full_name: `${profileData.first_name} ${profileData.last_name}`.trim(),
phone: profileData.phone,
language: profileData.language,
timezone: profileData.timezone
};
await updateProfileMutation.mutateAsync(profileUpdateData);
setIsEditing(false); setIsEditing(false);
showToast.success('Perfil actualizado correctamente'); showToast.success('Perfil actualizado correctamente');
} catch (error) { } catch (error) {

View File

@@ -5,7 +5,7 @@
"tenant_id": "A0000000-0000-4000-a000-000000000001", "tenant_id": "A0000000-0000-4000-a000-000000000001",
"name": "Gerente Madrid - Salamanca", "name": "Gerente Madrid - Salamanca",
"email": "gerente.a0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "gerente.a0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "manager", "role": "admin",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 180d", "created_at": "BASE_TS - 180d",
"updated_at": "BASE_TS - 180d" "updated_at": "BASE_TS - 180d"
@@ -15,7 +15,7 @@
"tenant_id": "A0000000-0000-4000-a000-000000000001", "tenant_id": "A0000000-0000-4000-a000-000000000001",
"name": "Empleado Madrid - Salamanca", "name": "Empleado Madrid - Salamanca",
"email": "empleado.a0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "empleado.a0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "user", "role": "member",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 150d", "created_at": "BASE_TS - 150d",
"updated_at": "BASE_TS - 150d" "updated_at": "BASE_TS - 150d"

View File

@@ -5,7 +5,7 @@
"tenant_id": "B0000000-0000-4000-a000-000000000001", "tenant_id": "B0000000-0000-4000-a000-000000000001",
"name": "Gerente Barcelona - Eixample", "name": "Gerente Barcelona - Eixample",
"email": "gerente.b0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "gerente.b0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "manager", "role": "admin",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 180d", "created_at": "BASE_TS - 180d",
"updated_at": "BASE_TS - 180d" "updated_at": "BASE_TS - 180d"
@@ -15,7 +15,7 @@
"tenant_id": "B0000000-0000-4000-a000-000000000001", "tenant_id": "B0000000-0000-4000-a000-000000000001",
"name": "Empleado Barcelona - Eixample", "name": "Empleado Barcelona - Eixample",
"email": "empleado.b0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "empleado.b0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "user", "role": "member",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 150d", "created_at": "BASE_TS - 150d",
"updated_at": "BASE_TS - 150d" "updated_at": "BASE_TS - 150d"

View File

@@ -5,7 +5,7 @@
"tenant_id": "C0000000-0000-4000-a000-000000000001", "tenant_id": "C0000000-0000-4000-a000-000000000001",
"name": "Gerente Valencia - Ruzafa", "name": "Gerente Valencia - Ruzafa",
"email": "gerente.c0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "gerente.c0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "manager", "role": "admin",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 180d", "created_at": "BASE_TS - 180d",
"updated_at": "BASE_TS - 180d" "updated_at": "BASE_TS - 180d"
@@ -15,7 +15,7 @@
"tenant_id": "C0000000-0000-4000-a000-000000000001", "tenant_id": "C0000000-0000-4000-a000-000000000001",
"name": "Empleado Valencia - Ruzafa", "name": "Empleado Valencia - Ruzafa",
"email": "empleado.c0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "empleado.c0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "user", "role": "member",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 150d", "created_at": "BASE_TS - 150d",
"updated_at": "BASE_TS - 150d" "updated_at": "BASE_TS - 150d"

View File

@@ -5,7 +5,7 @@
"tenant_id": "D0000000-0000-4000-a000-000000000001", "tenant_id": "D0000000-0000-4000-a000-000000000001",
"name": "Gerente Seville - Triana", "name": "Gerente Seville - Triana",
"email": "gerente.d0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "gerente.d0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "manager", "role": "admin",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 180d", "created_at": "BASE_TS - 180d",
"updated_at": "BASE_TS - 180d" "updated_at": "BASE_TS - 180d"
@@ -15,7 +15,7 @@
"tenant_id": "D0000000-0000-4000-a000-000000000001", "tenant_id": "D0000000-0000-4000-a000-000000000001",
"name": "Empleado Seville - Triana", "name": "Empleado Seville - Triana",
"email": "empleado.d0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "empleado.d0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "user", "role": "member",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 150d", "created_at": "BASE_TS - 150d",
"updated_at": "BASE_TS - 150d" "updated_at": "BASE_TS - 150d"

View File

@@ -5,7 +5,7 @@
"tenant_id": "E0000000-0000-4000-a000-000000000001", "tenant_id": "E0000000-0000-4000-a000-000000000001",
"name": "Gerente Bilbao - Casco Viejo", "name": "Gerente Bilbao - Casco Viejo",
"email": "gerente.e0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "gerente.e0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "manager", "role": "admin",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 180d", "created_at": "BASE_TS - 180d",
"updated_at": "BASE_TS - 180d" "updated_at": "BASE_TS - 180d"
@@ -15,7 +15,7 @@
"tenant_id": "E0000000-0000-4000-a000-000000000001", "tenant_id": "E0000000-0000-4000-a000-000000000001",
"name": "Empleado Bilbao - Casco Viejo", "name": "Empleado Bilbao - Casco Viejo",
"email": "empleado.e0000000-0000-4000-a000-000000000001@panaderiaartesana.es", "email": "empleado.e0000000-0000-4000-a000-000000000001@panaderiaartesana.es",
"role": "user", "role": "member",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 150d", "created_at": "BASE_TS - 150d",
"updated_at": "BASE_TS - 150d" "updated_at": "BASE_TS - 150d"

View File

@@ -16,7 +16,7 @@
"tenant_id": "80000000-0000-4000-a000-000000000001", "tenant_id": "80000000-0000-4000-a000-000000000001",
"name": "Produccion", "name": "Produccion",
"email": "produccion@panaderiaartesana.es", "email": "produccion@panaderiaartesana.es",
"role": "production_director", "role": "admin",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 300d", "created_at": "BASE_TS - 300d",
"updated_at": "BASE_TS - 300d" "updated_at": "BASE_TS - 300d"
@@ -26,7 +26,7 @@
"tenant_id": "80000000-0000-4000-a000-000000000001", "tenant_id": "80000000-0000-4000-a000-000000000001",
"name": "Compras", "name": "Compras",
"email": "compras@panaderiaartesana.es", "email": "compras@panaderiaartesana.es",
"role": "procurement_manager", "role": "admin",
"is_active": true, "is_active": true,
"created_at": "BASE_TS - 280d", "created_at": "BASE_TS - 280d",
"updated_at": "BASE_TS - 280d" "updated_at": "BASE_TS - 280d"
@@ -37,7 +37,7 @@
"email": "calidad@panaderiaartesana.es", "email": "calidad@panaderiaartesana.es",
"first_name": "Jos\u00e9", "first_name": "Jos\u00e9",
"last_name": "Mart\u00ednez", "last_name": "Mart\u00ednez",
"role": "quality_control", "role": "admin",
"department": "quality", "department": "quality",
"position": "Responsable de Calidad", "position": "Responsable de Calidad",
"phone": "+34 916 123 459", "phone": "+34 916 123 459",
@@ -57,7 +57,7 @@
"email": "logistica@panaderiaartesana.es", "email": "logistica@panaderiaartesana.es",
"first_name": "Laura", "first_name": "Laura",
"last_name": "L\u00f3pez", "last_name": "L\u00f3pez",
"role": "logistics_coord", "role": "admin",
"department": "logistics", "department": "logistics",
"position": "Coordinadora de Log\u00edstica", "position": "Coordinadora de Log\u00edstica",
"phone": "+34 916 123 460", "phone": "+34 916 123 460",
@@ -77,7 +77,7 @@
"email": "maestro1@panaderiaartesana.es", "email": "maestro1@panaderiaartesana.es",
"first_name": "Antonio", "first_name": "Antonio",
"last_name": "S\u00e1nchez", "last_name": "S\u00e1nchez",
"role": "master_baker", "role": "admin",
"department": "production", "department": "production",
"position": "Maestro Panadero Principal", "position": "Maestro Panadero Principal",
"phone": "+34 916 123 461", "phone": "+34 916 123 461",
@@ -97,7 +97,7 @@
"email": "maestro2@panaderiaartesana.es", "email": "maestro2@panaderiaartesana.es",
"first_name": "Isabel", "first_name": "Isabel",
"last_name": "Ruiz", "last_name": "Ruiz",
"role": "master_baker", "role": "admin",
"department": "production", "department": "production",
"position": "Maestra Panadera Senior", "position": "Maestra Panadera Senior",
"phone": "+34 916 123 462", "phone": "+34 916 123 462",
@@ -117,7 +117,7 @@
"email": "almacen1@panaderiaartesana.es", "email": "almacen1@panaderiaartesana.es",
"first_name": "Francisco", "first_name": "Francisco",
"last_name": "Moreno", "last_name": "Moreno",
"role": "warehouse_supervisor", "role": "admin",
"department": "warehouse", "department": "warehouse",
"position": "Supervisor de Almac\u00e9n", "position": "Supervisor de Almac\u00e9n",
"phone": "+34 916 123 463", "phone": "+34 916 123 463",
@@ -137,7 +137,7 @@
"email": "almacen2@panaderiaartesana.es", "email": "almacen2@panaderiaartesana.es",
"first_name": "Carmen", "first_name": "Carmen",
"last_name": "Jim\u00e9nez", "last_name": "Jim\u00e9nez",
"role": "warehouse_supervisor", "role": "admin",
"department": "warehouse", "department": "warehouse",
"position": "Supervisora de Almac\u00e9n Turno Noche", "position": "Supervisora de Almac\u00e9n Turno Noche",
"phone": "+34 916 123 464", "phone": "+34 916 123 464",
@@ -157,7 +157,7 @@
"email": "analisis@panaderiaartesana.es", "email": "analisis@panaderiaartesana.es",
"first_name": "David", "first_name": "David",
"last_name": "Gonz\u00e1lez", "last_name": "Gonz\u00e1lez",
"role": "operations_analyst", "role": "admin",
"department": "operations", "department": "operations",
"position": "Analista de Operaciones", "position": "Analista de Operaciones",
"phone": "+34 916 123 465", "phone": "+34 916 123 465",
@@ -177,7 +177,7 @@
"email": "mantenimiento@panaderiaartesana.es", "email": "mantenimiento@panaderiaartesana.es",
"first_name": "Pedro", "first_name": "Pedro",
"last_name": "D\u00edaz", "last_name": "D\u00edaz",
"role": "maintenance_tech", "role": "admin",
"department": "maintenance", "department": "maintenance",
"position": "T\u00e9cnico de Mantenimiento", "position": "T\u00e9cnico de Mantenimiento",
"phone": "+34 916 123 466", "phone": "+34 916 123 466",
@@ -196,7 +196,7 @@
"email": "turno.dia@panaderiaartesana.es", "email": "turno.dia@panaderiaartesana.es",
"first_name": "Rosa", "first_name": "Rosa",
"last_name": "Navarro", "last_name": "Navarro",
"role": "shift_supervisor", "role": "admin",
"department": "production", "department": "production",
"position": "Supervisora Turno D\u00eda", "position": "Supervisora Turno D\u00eda",
"phone": "+34 916 123 467", "phone": "+34 916 123 467",
@@ -216,7 +216,7 @@
"email": "turno.tarde@panaderiaartesana.es", "email": "turno.tarde@panaderiaartesana.es",
"first_name": "Manuel", "first_name": "Manuel",
"last_name": "Torres", "last_name": "Torres",
"role": "shift_supervisor", "role": "admin",
"department": "production", "department": "production",
"position": "Supervisor Turno Tarde", "position": "Supervisor Turno Tarde",
"phone": "+34 916 123 468", "phone": "+34 916 123 468",
@@ -236,7 +236,7 @@
"email": "turno.noche@panaderiaartesana.es", "email": "turno.noche@panaderiaartesana.es",
"first_name": "Luc\u00eda", "first_name": "Luc\u00eda",
"last_name": "Romero", "last_name": "Romero",
"role": "shift_supervisor", "role": "admin",
"department": "production", "department": "production",
"position": "Supervisora Turno Noche", "position": "Supervisora Turno Noche",
"phone": "+34 916 123 469", "phone": "+34 916 123 469",
@@ -256,7 +256,7 @@
"email": "it@panaderiaartesana.es", "email": "it@panaderiaartesana.es",
"first_name": "Javier", "first_name": "Javier",
"last_name": "Vargas", "last_name": "Vargas",
"role": "it_admin", "role": "admin",
"department": "it", "department": "it",
"position": "Administrador de Sistemas", "position": "Administrador de Sistemas",
"phone": "+34 916 123 470", "phone": "+34 916 123 470",