feat: Stripe Checkout, Zeptomail, pre-alert linking, perfil 3-tab, carga-pesada landing

- payments: real Stripe Checkout Session (redirect flow), confirmBySession, webhook handler
- notifications: Zeptomail REST email, wa.me WhatsApp links, IntegrationsService injection
- auth: changePassword, updateProfile, disableMfa (TOTP-verified) endpoints
- packages: tryLinkPreAlert() auto-links on create (non-blocking)
- integrations: catalog updated (zeptomail, stripe_webhook_secret; removed sendgrid/whatsapp-api)
- web/portal/perfil: 3-tab layout (datos / contraseña / MFA)
- web/portal/pago: Stripe redirect + ?success=1&session_id= callback, cancelled banner
- web/admin/b2b: fix enum values (EN_COTIZACION, ACEPTADO)
- web/carga-pesada: rich marketing landing (FCL/LCL, 7-step process, sectors, INEN callout)
- tests: fix payments.service.spec (IntegrationsService+ConfigService mocks)
- tests: fix notifications.service.spec (IntegrationsService mock, phone in mockUser)
- all 104 tests passing, API + web builds clean
This commit is contained in:
Lizandro Guarnizo
2026-06-01 20:24:35 -05:00
parent b2f03e654f
commit 98ab5a309c
19 changed files with 979 additions and 134 deletions
+26 -2
View File
@@ -1,8 +1,8 @@
import {
Controller, Post, Get, Body, Req, UseGuards, HttpCode, HttpStatus,
Controller, Post, Get, Patch, Body, Req, UseGuards, HttpCode, HttpStatus,
} from "@nestjs/common";
import { AuthService } from "./auth.service";
import { RegisterDto, LoginDto, RefreshDto, SetupMfaDto } from "./dto/auth.dto";
import { RegisterDto, LoginDto, RefreshDto, SetupMfaDto, ChangePasswordDto, UpdateProfileDto } from "./dto/auth.dto";
import { JwtAuthGuard } from "./guards/auth.guard";
import { CurrentUser } from "./decorators/current-user.decorator";
@@ -45,6 +45,22 @@ export class AuthController {
return this.auth.getProfile(user.id);
}
/** PATCH /api/auth/me — Actualizar nombre / teléfono */
@Patch("me")
@UseGuards(JwtAuthGuard)
updateProfile(@Body() dto: UpdateProfileDto, @CurrentUser() user: any) {
return this.auth.updateProfile(user.id, dto);
}
/** PATCH /api/auth/password — Cambiar contraseña */
@Patch("password")
@UseGuards(JwtAuthGuard)
@HttpCode(HttpStatus.OK)
async changePassword(@Body() dto: ChangePasswordDto, @CurrentUser() user: any) {
await this.auth.changePassword(user.id, dto.oldPassword, dto.newPassword);
return { message: "Contraseña actualizada correctamente." };
}
/** POST /api/auth/mfa/setup — Genera QR para TOTP */
@Post("mfa/setup")
@UseGuards(JwtAuthGuard)
@@ -58,4 +74,12 @@ export class AuthController {
verifyMfa(@Body() dto: SetupMfaDto, @CurrentUser() user: any) {
return this.auth.verifyMfa(user.id, dto.totpCode);
}
/** POST /api/auth/mfa/disable — Desactiva MFA (requiere código TOTP) */
@Post("mfa/disable")
@UseGuards(JwtAuthGuard)
disableMfa(@Body() dto: SetupMfaDto, @CurrentUser() user: any) {
return this.auth.disableMfa(user.id, dto.totpCode);
}
}