From 232d4d6c54c80da8e962a9962090a4a422ca6f61 Mon Sep 17 00:00:00 2001
From: Ricardo Carneiro
Date: Mon, 13 Oct 2025 19:50:31 -0300
Subject: [PATCH] feat: ajustes e artigos
---
.claude/settings.local.json | 7 +-
.github/workflows/deploy.yml | 1 -
CLAUDE.md | 286 ++++++++
.../como-crear-codigo-qr-whatsapp.es-PY.md | 108 +++
.../como-crear-codigo-qr-wifi.es-PY.md | 271 ++++++++
.../como-criar-qr-code-whatsapp.pt-BR.md | 108 +++
.../como-criar-qr-code-wifi.pt-BR.md | 187 +++++
...qr-code-para-corredores-inmuebles.es-PY.md | 599 ++++++++++++++++
.../qr-code-para-corretores-imoveis.pt-BR.md | 640 ++++++++++++++++++
Controllers/HomeController.cs | 229 ++++---
Controllers/PremiumController.cs | 30 +
Controllers/QRController.cs | 20 +-
Controllers/TutoriaisController.cs | 111 +++
Models/ArticleMetadata.cs | 15 +
Models/User.cs | 3 +
Models/ViewModels/ArticleViewModel.cs | 11 +
Program.cs | 1 +
Properties/launchSettings.json | 2 +-
QRRapidoApp.csproj | 2 +
Resources/SharedResource.es-PY.resx | 9 +-
Resources/SharedResource.es.resx | 9 +-
Resources/SharedResource.pt-BR.resx | 9 +-
Scripts/README-MONGODB-SEED.md | 143 ++++
Scripts/seed-mongodb-plans.js | 163 +++++
Services/IMarkdownService.cs | 12 +
Services/MarkdownService.cs | 265 ++++++++
Services/StripeService.cs | 178 ++++-
Services/UserService.cs | 16 +-
Views/Account/Profile.cshtml | 212 +++++-
Views/Home/Index.cshtml | 2 +-
Views/Premium/Upgrade.cshtml | 343 ----------
Views/Shared/_AdSpace.cshtml | 2 +-
Views/Shared/_Layout.cshtml | 2 +-
Views/Tutoriais/Article.cshtml | 290 ++++++++
Views/Tutoriais/Index.cshtml | 109 +++
appsettings.Production.json | 18 +
appsettings.json | 14 +-
.../qr-code-corretor-imoveis-hero.jpg | Bin 0 -> 515264 bytes
.../images/tutoriais/qr-code-wifi-hero.jpg | Bin 0 -> 512325 bytes
wwwroot/images/tutoriais/whatsapp-qr-hero.jpg | Bin 0 -> 364303 bytes
wwwroot/js/qr-speed-generator.js | 4 +-
41 files changed, 3942 insertions(+), 489 deletions(-)
create mode 100644 CLAUDE.md
create mode 100644 Content/Tutoriais/como-crear-codigo-qr-whatsapp.es-PY.md
create mode 100644 Content/Tutoriais/como-crear-codigo-qr-wifi.es-PY.md
create mode 100644 Content/Tutoriais/como-criar-qr-code-whatsapp.pt-BR.md
create mode 100644 Content/Tutoriais/como-criar-qr-code-wifi.pt-BR.md
create mode 100644 Content/Tutoriais/qr-code-para-corredores-inmuebles.es-PY.md
create mode 100644 Content/Tutoriais/qr-code-para-corretores-imoveis.pt-BR.md
create mode 100644 Controllers/TutoriaisController.cs
create mode 100644 Models/ArticleMetadata.cs
create mode 100644 Models/ViewModels/ArticleViewModel.cs
create mode 100644 Scripts/README-MONGODB-SEED.md
create mode 100644 Scripts/seed-mongodb-plans.js
create mode 100644 Services/IMarkdownService.cs
create mode 100644 Services/MarkdownService.cs
delete mode 100644 Views/Premium/Upgrade.cshtml
create mode 100644 Views/Tutoriais/Article.cshtml
create mode 100644 Views/Tutoriais/Index.cshtml
create mode 100644 wwwroot/images/tutoriais/qr-code-corretor-imoveis-hero.jpg
create mode 100644 wwwroot/images/tutoriais/qr-code-wifi-hero.jpg
create mode 100644 wwwroot/images/tutoriais/whatsapp-qr-hero.jpg
diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index e15664b..f610e2e 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -24,7 +24,12 @@
"Bash(dotnet restore:*)",
"Bash(rg:*)",
"Bash(dotnet test:*)",
- "Bash(cp:*)"
+ "Bash(cp:*)",
+ "Bash(ping:*)",
+ "Bash(nc:*)",
+ "Bash(ssh:*)",
+ "Read(//mnt/c/vscode/**)",
+ "Read(//mnt/c/**)"
],
"deny": []
}
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index c7505c8..c358d50 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -182,7 +182,6 @@ jobs:
--replicas 2 \
--network qrrapido-network \
--publish published=5001,target=8080 \
- --constraint "node.role==worker" \
--mount type=bind,source=/app/keys,target=/app/keys \
--env ASPNETCORE_ENVIRONMENT=Production \
--env ASPNETCORE_URLS=http://+:8080 \
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..46517f2
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,286 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+QR Rapido is an ultra-fast QR code generator built with ASP.NET Core 8.0, focusing on speed and multilingual support (PT-BR, ES, EN). It features tiered user access (anonymous, logged-in free, premium), OAuth authentication (Google/Microsoft), Stripe payment integration, and ad-free session management.
+
+**Key Performance Targets:**
+- QR generation: <1.2s (average), <0.4s (premium users)
+- Cache hit rate: >80%
+- First Contentful Paint: <2s
+
+## Common Commands
+
+### Development
+```bash
+# Run locally (hot reload)
+dotnet watch run
+
+# Run locally (standard)
+dotnet run
+
+# Build for release
+dotnet build --configuration Release
+
+# Restore dependencies
+dotnet restore
+```
+
+### Testing
+```bash
+# Run all tests
+dotnet test
+
+# Run with coverage
+dotnet test --collect:"XPlat Code Coverage"
+
+# Run specific test class
+dotnet test --filter "QRRapidoServiceTests"
+```
+
+### Frontend Build
+```bash
+# Development mode with Vite
+npm run dev
+
+# Production build (Vite)
+npm run build
+
+# Preview production build
+npm run preview
+```
+
+### Docker
+```bash
+# Build and run all services (MongoDB, Redis, Nginx)
+docker-compose up -d
+
+# View logs
+docker-compose logs -f qrrapido
+
+# Build production image for ARM64 (OCI Ampere servers)
+docker buildx build --platform linux/arm64 -t qrrapido:latest .
+
+# Stop all services
+docker-compose down
+```
+
+### Stripe Webhooks (Local Testing - WSL)
+```bash
+# Forward Stripe webhooks to local HTTPS endpoint
+stripe listen --forward-to https://localhost:52428/pagamento/stripewebhook --skip-verify
+```
+
+## Architecture
+
+### Core Technology Stack
+- **Backend**: ASP.NET Core 8.0 (MVC + Razor Pages)
+- **Database**: MongoDB (users, QR history, sessions)
+- **Cache**: Redis (distributed cache) + MemoryCache (fallback)
+- **QR Generation**: QRCoder library with ImageSharp for logo overlay
+- **Authentication**: Cookie-based with OAuth (Google, Microsoft)
+- **Payments**: Stripe (subscriptions)
+- **Logging**: Serilog → OpenSearch/Console
+- **Frontend Build**: Vite for asset bundling
+
+### Project Structure
+```
+Controllers/ # MVC controllers (Home, QR, Account, Premium, Pagamento, Health)
+Services/ # Business logic (QRRapido, User, Plan, Stripe, AdDisplay)
+ ├── Monitoring/ # Resource and MongoDB monitoring
+ └── HealthChecks/# Health check implementations
+Models/ # Domain models and ViewModels
+Data/ # MongoDbContext
+Middleware/ # Custom middleware (Language redirection, LastLogin update)
+Providers/ # Culture providers for localization
+Resources/ # .resx files for PT-BR, ES, EN localization
+Views/ # Razor views
+wwwroot/ # Static assets (CSS, JS, images)
+Tests/ # Unit tests (xUnit, Moq)
+```
+
+### Key Services
+
+**QRRapidoService** (`Services/QRRapidoService.cs`):
+- Core QR generation with distributed cache support
+- SemaphoreSlim limits concurrent generations (default: 100)
+- Cache key based on content hash + settings
+- Optimized error correction levels for speed
+- Logo overlay with readability analysis
+
+**UserService** (`Services/UserService.cs`):
+- MongoDB user CRUD operations
+- QR history management (anonymous vs authenticated)
+- Premium status checks
+- Graceful fallback when MongoDB unavailable
+
+**AdDisplayService** (`Services/AdDisplayService.cs`):
+- Controls ad visibility based on user status
+- 30-day ad-free period after login
+- Premium users: permanent ad-free
+- Anonymous users: always show ads
+
+**StripeService** (`Services/StripeService.cs`):
+- Subscription creation and management
+- Webhook handling (checkout.session.completed, etc.)
+- Customer portal session creation
+
+### Middleware Pipeline (Program.cs)
+
+1. **LanguageRedirectionMiddleware**: Redirects root `/` to `/pt-BR/` or user's language preference
+2. **Request Localization**: Sets culture based on route (`/{culture}/...`) → QueryString → Cookie
+3. **Authentication/Authorization**
+4. **Session**
+5. **LastLoginUpdateMiddleware**: Updates user's last login timestamp
+
+### Localization Strategy
+
+- Route-based culture: `/{culture:regex(^(pt-BR|es-PY)$)}/{controller}/{action}`
+- Default culture: `pt-BR`
+- Supported cultures: `pt-BR`, `es-PY`
+- Culture providers priority: Route → QueryString → Cookie
+- Resources in `Resources/SharedResource.{culture}.resx`
+
+### MongoDB Collections
+- **Users**: User profiles, premium status, OAuth data
+- **QRCodeHistory**: Generated QR codes (linked to users or anonymous)
+- **AdFreeSessions**: Ad-free session tracking (30-day grants)
+- **DataProtectionKeys**: ASP.NET Core Data Protection keys (for Docker Swarm)
+
+### Docker Swarm Deployment
+
+**Production** uses Docker Swarm with:
+- 2 replicas across 2 ARM64 servers (OCI Ampere)
+- Shared MongoDB for Data Protection keys (cross-replica cookie decryption)
+- Rolling updates: `start-first` strategy, 30s delay between updates
+- Health checks at `/healthcheck` endpoint
+- Exposed on port 5001 internally, proxied by Nginx
+
+**Staging** uses standalone Docker containers on 2 servers.
+
+### CI/CD Pipeline (.github/workflows/deploy.yml)
+
+1. **Test Job**: Runs on `ubuntu-latest`
+ - Restore → Build → Test with coverage
+ - Uploads coverage to Codecov
+
+2. **Build-and-Push Job**: Runs on self-hosted ARM64 runner
+ - Builds ARM64 Docker image
+ - Tags: `latest` (main), `develop` (develop branch)
+ - Pushes to private registry: `registry.redecarneir.us`
+
+3. **Deploy-Staging**: SSH to 2 servers, pull image, run container
+
+4. **Deploy-Production**: SSH to Swarm manager, update service with zero-downtime
+
+### Configuration Management
+
+**appsettings.json** key sections:
+- `ConnectionStrings:MongoDB`: MongoDB connection string
+- `Authentication:Google/Microsoft`: OAuth credentials
+- `Stripe`: API keys and webhook secret
+- `Performance:MaxConcurrentGenerations`: Semaphore limit (default: 100)
+- `Premium:FreeQRLimit`: Daily limit for logged-in free users (10)
+- `Serilog:OpenSearchUrl`: Centralized logging endpoint
+- `ResourceMonitoring`: CPU/memory thresholds for alerts
+- `HealthChecks`: Timeout and test configurations
+
+**Environment-specific overrides**:
+- `appsettings.Development.json`
+- `appsettings.Production.json`
+
+### Rate Limiting & Performance
+
+- Fixed window rate limiter: 600 requests/minute per IP on `/api` endpoints
+- Kestrel max connections: 2000
+- QR generation timeout: 2000ms
+- Redis cache expiration: 60 minutes
+- MongoDB query timeouts: 5 seconds (health checks)
+
+### Health Checks
+
+Endpoint: `/healthcheck`
+
+Checks:
+- **MongoDbHealthCheck**: Database connectivity, size metrics, test query
+- **ResourceHealthCheck**: CPU/memory usage, GC pressure
+- **ExternalServicesHealthCheck**: Stripe API availability
+
+### Testing Strategy
+
+- **Unit tests**: Services layer (QRRapidoService, AdDisplayService, etc.)
+- **Mocking**: MongoDB and IDistributedCache with Moq
+- **Coverage**: Run `dotnet test --collect:"XPlat Code Coverage"`
+- Test files in `Tests/Services/`
+
+### Known Quirks & WSL Compatibility
+
+- **StaticWebAssets disabled**: Set `ASPNETCORE_HOSTINGSTARTUP__STATICWEBASSETS__ENABLED=false` for WSL path issues (see Program.cs:38-39)
+- **DataProtection**: Uses MongoDB for key persistence in production (Docker Swarm), filesystem in development
+- **Frontend build**: Vite runs during Release build via MSBuild target (`BuildFrontend` in .csproj)
+- **Stripe local testing**: Use `stripe listen` in WSL (see README.md:345-347)
+
+### Monitoring & Logging
+
+- **Serilog** → Console (development) + OpenSearch (production)
+- **ResourceMonitoringService**: Background service tracking CPU/memory every 30s
+- **HistoryCleanupService**: Cleans old anonymous QR history (7-day grace period, runs every 6 hours)
+- **MongoDbMonitoringService**: Tracks database growth and collection stats (disabled by default)
+
+### Premium Feature Gates
+
+Check user premium status via `IUserService.GetUserAsync(userId)` → `user.IsPremium`
+
+Premium benefits:
+- Unlimited QR codes (vs 10/day anonymous, 50/day free)
+- No ads permanently
+- Priority generation (faster SemaphoreSlim release)
+- Dynamic QR codes (editable)
+- API access
+
+### Ad-Free Logic
+
+See `AdDisplayService.ShouldShowAdsAsync()`:
+- Anonymous users: always show ads
+- Premium users: never show ads
+- Free logged-in users: 30-day ad-free period from login (configurable in appsettings)
+- Ad-free sessions tracked in MongoDB `AdFreeSessions` collection
+
+### Security Considerations
+
+- OAuth secure flow with PKCE
+- HTTPS redirect enforced (non-dev environments)
+- Stripe webhook signature verification
+- Input sanitization on QR generation
+- Rate limiting on API endpoints
+- HSTS enabled in production
+- Forwarded headers support for reverse proxy (Nginx)
+
+### Common Workflows
+
+**Adding a new QR type:**
+1. Update `Models/ViewModels/QRGenerationRequest.cs` with new type enum
+2. Add generation logic in `Services/QRRapidoService.cs` → `GenerateQRCodeOptimizedAsync()`
+3. Update frontend form in `Views/Home/Index.cshtml`
+4. Add localized strings in `Resources/SharedResource.{culture}.resx`
+
+**Adding a new language:**
+1. Create `Resources/SharedResource.{culture}.resx`
+2. Update `Program.cs` supported cultures array (line 214-218)
+3. Update route constraint regex (line 354)
+4. Add culture provider mapping if needed
+
+**Debugging slow QR generation:**
+1. Check `_logger` output in `QRRapidoService.GenerateRapidAsync()` for timing
+2. Verify cache hit rate in logs
+3. Check semaphore wait time (max concurrent limit)
+4. Review `Performance:QRGenerationTimeoutMs` in appsettings
+5. Monitor resource usage via `/healthcheck`
+
+**Updating Stripe configuration:**
+1. Update `appsettings.json` → `Stripe` section
+2. Verify webhook secret matches Stripe dashboard
+3. Test locally with `stripe listen --forward-to ...`
+4. Update `StripeService.cs` webhook handlers if event types change
diff --git a/Content/Tutoriais/como-crear-codigo-qr-whatsapp.es-PY.md b/Content/Tutoriais/como-crear-codigo-qr-whatsapp.es-PY.md
new file mode 100644
index 0000000..30669b4
--- /dev/null
+++ b/Content/Tutoriais/como-crear-codigo-qr-whatsapp.es-PY.md
@@ -0,0 +1,108 @@
+---
+title: "Cómo Crear Código QR para WhatsApp"
+description: "Aprende a crear un código QR para WhatsApp que permite a los usuarios iniciar una conversación contigo instantáneamente"
+keywords: "codigo qr whatsapp, whatsapp codigo qr, qr para whatsapp, crear qr whatsapp"
+author: "QR Rapido"
+date: 2025-10-08
+lastmod: 2025-10-08
+image: "/images/tutoriais/whatsapp-qr-hero.jpg"
+---
+
+# Cómo Crear Código QR para WhatsApp
+
+Crear un **código QR para WhatsApp** es una de las formas más eficientes de facilitar el contacto directo con tus clientes, amigos o seguidores. Con un simple escaneo, cualquier persona puede iniciar una conversación contigo instantáneamente, sin necesidad de guardar tu número.
+
+## 📱 ¿Por qué usar código QR para WhatsApp?
+
+Los códigos QR para WhatsApp son extremadamente útiles para:
+
+- **Empresas**: Facilitar la atención al cliente
+- **Freelancers**: Agilizar el contacto con potenciales clientes
+- **Eventos**: Permitir networking rápido
+- **Marketing**: Aumentar la conversión en campañas
+
+## 🎯 Paso a Paso
+
+### 1. Prepara tu número
+
+Primero, necesitas tener tu número en formato internacional:
+
+```
+595 21 123-4567
+```
+
+Elimina todos los espacios y guiones, quedando:
+
+```
+59521123456
+```
+
+### 2. Crea la URL de WhatsApp
+
+La URL de WhatsApp sigue este patrón:
+
+```
+https://wa.me/59521123456
+```
+
+Puedes agregar un mensaje predefinido:
+
+```
+https://wa.me/59521123456?text=¡Hola!%20Quisiera%20más%20información
+```
+
+### 3. Genera el código QR
+
+Accede a [QR Rapido](https://qrrapido.site) y:
+
+1. Selecciona el tipo **URL**
+2. Pega la URL de WhatsApp
+3. Personaliza los colores (opcional)
+4. Haz clic en **Generar Código QR**
+5. Descarga en alta calidad
+
+## 💡 Consejos Profesionales
+
+### Personaliza el Mensaje Inicial
+
+Configura un mensaje de bienvenida automático para mejorar la experiencia:
+
+```
+https://wa.me/59521123456?text=¡Hola!%20Vine%20a%20través%20de%20tu%20código%20QR
+```
+
+### Úsalo en Materiales Impresos
+
+- **Tarjetas de visita**: Facilita el contacto instantáneo
+- **Folletos**: Aumenta el engagement
+- **Embalajes**: Ofrece soporte directo al cliente
+- **Banners**: En eventos y ferias
+
+### Monitorea los Resultados
+
+Para rastrear cuántas personas escanearon tu código QR, considera usar un acortador de URL con analytics antes de generar el QR.
+
+## ⚠️ Cuidados Importantes
+
+1. **Prueba antes de imprimir**: Siempre escanea para verificar que funciona
+2. **Tamaño mínimo**: Mantén al menos 3x3 cm para fácil lectura
+3. **Contraste adecuado**: Usa colores que contrasten bien (negro sobre blanco es ideal)
+4. **Mensaje claro**: Indica para qué sirve el código QR
+
+## 🚀 Ventajas de usar QR Rapido
+
+- ⚡ **Ultra rápido**: Genera en menos de 1 segundo
+- 🎨 **Personalizable**: Elige colores y estilos
+- 📥 **Alta calidad**: Descarga en PNG, SVG y PDF
+- 🔒 **Seguro**: Tus datos no son almacenados
+- 💯 **Gratis**: 10 códigos QR por día
+
+## Conclusión
+
+Crear un código QR para WhatsApp es simple y puede revolucionar la forma en que te comunicas con tu público. Con QR Rapido, tienes todo lo que necesitas para crear códigos QR profesionales en segundos.
+
+**¿Listo para empezar?** [Crea tu código QR ahora →](https://qrrapido.site/es-PY)
+
+---
+
+*¿Tienes dudas? [Contáctanos](https://qrrapido.site/es-PY/Contact)!*
diff --git a/Content/Tutoriais/como-crear-codigo-qr-wifi.es-PY.md b/Content/Tutoriais/como-crear-codigo-qr-wifi.es-PY.md
new file mode 100644
index 0000000..5e1fd81
--- /dev/null
+++ b/Content/Tutoriais/como-crear-codigo-qr-wifi.es-PY.md
@@ -0,0 +1,271 @@
+---
+title: "Cómo Crear Código QR WiFi Gratis: Comparte tu Red en Segundos"
+description: "Aprende a crear código QR WiFi gratuito en pocos clics. Comparte la contraseña de tu red sin escribir con nuestro generador rápido y seguro."
+keywords: "codigo qr wifi, crear codigo qr wifi, generador qr wifi gratis, qr wifi, compartir contraseña wifi, codigo qr red wifi, como hacer qr wifi"
+author: "QR Rapido"
+date: 2025-10-10
+lastmod: 2025-10-10
+image: "/images/tutoriais/qr-code-wifi-hero.jpg"
+---
+
+# Cómo Crear Código QR WiFi Gratis: Comparte tu Red en Segundos
+
+¿Cansado de escribir contraseñas largas y complicadas cada vez que un visitante pide el WiFi? Con un **Código QR WiFi**, puedes compartir tu red instantáneamente. En este tutorial completo, aprenderás a crear un código QR para WiFi gratuitamente en menos de 2 minutos.
+
+## ¿Por Qué Usar Código QR para WiFi?
+
+Compartir tu red WiFi mediante código QR ofrece varias ventajas:
+
+- **Practicidad**: Los visitantes se conectan instantáneamente sin escribir contraseñas
+- **Seguridad**: No necesitas decir la contraseña en voz alta o anotarla
+- **Profesionalismo**: Ideal para empresas, cafeterías, restaurantes y consultorios
+- **Ahorro de tiempo**: Elimina errores de escritura y pedidos repetidos
+- **Compatibilidad**: Funciona en prácticamente todos los smartphones modernos
+
+## Paso a Paso: Cómo Crear tu Código QR WiFi
+
+### Paso 1: Selecciona el Tipo de Código QR
+
+Accede al generador y elige la opción **WiFi** en la lista de tipos de códigos QR disponibles.
+
+**[INSERTAR IMAGEN 1 AQUÍ: Pantalla de selección de tipo de código QR con WiFi destacado]**
+
+En el menú desplegable encontrarás varias opciones como URL/Link, Texto Simple, Tarjeta de Visita, SMS y Email. Para este tutorial, selecciona **WiFi**.
+
+### Paso 2: Completa los Datos de tu Red WiFi
+
+Ahora necesitas informar los datos de tu red. Ve los campos obligatorios:
+
+**[INSERTAR IMAGEN 3 AQUÍ: Formulario de creación de código QR WiFi con campos completados]**
+
+#### NetworkName (Nombre de la Red) *
+Escribe exactamente el nombre de tu red WiFi (SSID). Por ejemplo: "NombreDeTuRed"
+
+**Consejo importante**: El nombre debe ser idéntico al que aparece cuando buscas redes WiFi en el celular. ¡Respeta mayúsculas y minúsculas!
+
+#### SecurityType (Tipo de Seguridad)
+Selecciona el tipo de encriptación de tu red:
+
+- **Red WPA (la más común)**: WPA/WPA2/WPA3 - recomendado y más seguro
+- **WEP (muy antiguo)**: No recomendado por ser inseguro
+- **Sin contraseña**: Para redes públicas sin protección
+
+**[INSERTAR IMAGEN 4 AQUÍ: Ejemplo de formulario WiFi completado con "Rede-do-meu-comercio"]**
+
+#### NetworkPassword (Contraseña de la Red) *
+Introduce la contraseña de tu red WiFi. Usa el ícono de ojo para visualizar y verificar que escribiste correctamente.
+
+**Importante**: La contraseña también distingue mayúsculas de minúsculas. ¡Verifica con atención!
+
+#### HiddenNetwork (Red Oculta)
+Marca esta opción solo si tu red está configurada como oculta (no aparece en la lista de redes disponibles).
+
+### Paso 3: Personaliza tu Código QR (Opcional)
+
+¡Dale a tu código QR la identidad de tu negocio!
+
+**[INSERTAR IMAGEN 2 AQUÍ: Panel de personalización avanzada]**
+
+#### Opciones de Personalización:
+
+**Color Principal**: Elige el color de los cuadrados del código QR (predeterminado: azul)
+
+**Color de Fondo**: Define el color de fondo (predeterminado: blanco)
+
+**Tamaño**: Selecciona entre:
+- Pequeño (200px) - Para uso digital
+- Mediano (300px) - Recomendado para impresión
+- Grande (500px) - Para banners y pósters
+
+**Margen**:
+- Compacto - Ocupa menos espacio
+- Normal - Recomendado (mejor lectura)
+- Amplio - Para impresiones grandes
+
+**Consejo de diseño**: Mantén buen contraste entre el color principal y el fondo para garantizar que todos los celulares puedan leer el código.
+
+### Paso 4: Generar y Descargar
+
+Haz clic en el botón azul **"⚡ Generar Código QR Rápidamente"** y ¡listo! Tu código QR WiFi será generado instantáneamente.
+
+Puedes:
+- Descargar la imagen en alta calidad
+- Imprimir y colocar en lugares visibles
+- Compartir digitalmente
+- Guardar para usar después
+
+## Cómo tus Visitantes Usarán el Código QR WiFi
+
+¡Es muy simple! Tus visitantes solo necesitan:
+
+1. Abrir la cámara del celular (iOS o Android)
+2. Apuntar al código QR
+3. Tocar la notificación que aparece
+4. Conectarse automáticamente al WiFi
+
+**¡No necesitas descargar aplicaciones!** La mayoría de los smartphones desde 2018 ya tienen lectores de códigos QR integrados en la cámara.
+
+## Dónde Usar tu Código QR WiFi
+
+### Para Empresas
+- Recepción de oficinas
+- Salas de reuniones
+- Áreas de espera
+- Espacios de coworking
+
+### Para Comercios
+- Mesas de restaurantes
+- Mostradores de cafeterías
+- Tiendas y boutiques
+- Salones de belleza
+
+### Para Residencias
+- Cuadro de entrada
+- Área de parrilla
+- Heladera (para fiestas)
+- Home office
+
+### Para Eventos
+- Credenciales de eventos
+- Stands de ferias
+- Conferencias
+- Bodas y fiestas
+
+## Consejos de Seguridad
+
+⚠️ **Importante**: Considera crear una red WiFi separada para visitantes (red guest) si deseas:
+
+- Proteger tus dispositivos personales
+- Limitar velocidad para invitados
+- Tener control sobre quién accede
+- Mantener tu red principal privada
+
+Muchos routers modernos permiten crear redes guest fácilmente en las configuraciones.
+
+## Preguntas Frecuentes (FAQ)
+
+### ¿El Código QR WiFi expira?
+¡No! El código QR funciona indefinidamente mientras los datos de la red (nombre y contraseña) permanezcan iguales.
+
+### ¿Funciona en iPhone y Android?
+¡Sí! Funciona en prácticamente todos los smartphones fabricados después de 2018 que tienen cámara.
+
+### ¿Puedo crear para red 5GHz?
+¡Sí! El proceso es exactamente el mismo. Solo usa el nombre correcto de la red 5GHz.
+
+### ¿Es seguro?
+¡Sí! El código QR solo facilita la escritura de los datos. Es tan seguro como informar la contraseña verbalmente o por escrito.
+
+### ¿Puedo editar después de creado?
+No es posible editar el código QR después de generado. Si cambias la contraseña del WiFi, necesitarás generar un nuevo código QR.
+
+### ¿Cuántas personas pueden usar el mismo código QR?
+¡Ilimitadas! No hay límite de usos para el código QR.
+
+## Casos de Uso Reales
+
+### Restaurantes y Cafeterías
+En Paraguay, muchos negocios gastronómicos están adoptando códigos QR WiFi. Los clientes aprecian poder conectarse sin interrumpir al personal. Coloca el código QR en:
+- Carteles en las mesas
+- Menús impresos
+- Pared cerca de la caja
+
+### Consultorios Médicos
+Los pacientes en la sala de espera pueden conectarse fácilmente mientras aguardan. Esto mejora la experiencia y reduce el estrés de la espera.
+
+### Oficinas y Coworking
+Facilita el acceso de clientes, proveedores y visitantes sin comprometer la seguridad de tu red principal. Ideal para espacios colaborativos en Asunción y otras ciudades.
+
+### Hoteles y Hospedajes
+Proporciona códigos QR en las habitaciones para que los huéspedes se conecten inmediatamente al llegar.
+
+## Errores Comunes a Evitar
+
+### ❌ Error 1: Nombre de Red Incorrecto
+Verifica que escribiste exactamente el SSID de tu red. Un error común es confundir la red 2.4GHz con la 5GHz.
+
+### ❌ Error 2: Contraseña con Espacios
+Si tu contraseña tiene espacios, inclúyelos exactamente como están configurados en el router.
+
+### ❌ Error 3: Tipo de Seguridad Incorrecto
+Asegúrate de seleccionar el tipo correcto (WPA, WEP o sin contraseña). Si no estás seguro, verifica en la configuración de tu router.
+
+### ❌ Error 4: QR Code Muy Pequeño
+Para impresión, usa tamaño mediano (300px) o grande (500px). Los códigos pequeños pueden ser difíciles de escanear.
+
+### ❌ Error 5: Bajo Contraste
+Evita combinaciones de colores como amarillo sobre blanco o gris claro sobre gris. El contraste es esencial para la lectura correcta.
+
+## Mejores Prácticas para Imprimir
+
+Si vas a imprimir tu código QR WiFi, sigue estas recomendaciones:
+
+### Material Recomendado
+- **Papel fotográfico**: Para mejor calidad y durabilidad
+- **Laminado**: Protege contra humedad y suciedad
+- **Acrílico**: Solución premium para negocios
+- **Vinilo adhesivo**: Fácil de colocar en paredes y superficies
+
+### Tamaño de Impresión
+- **Mínimo**: 5x5 cm para lectura cercana
+- **Recomendado**: 10x10 cm para lectura a 30-50 cm de distancia
+- **Grande**: 15x15 cm o más para lectura a mayor distancia
+
+### Ubicación Estratégica
+- A la altura de los ojos (1.40m - 1.60m)
+- Bien iluminado
+- Sin reflejos o brillos
+- Fácilmente visible desde donde la gente se sienta o espera
+
+## Tips para Negocios
+
+### Agrega un Texto Atractivo
+No solo coloques el código QR. Agrega texto como:
+- "WiFi Gratis - Escanea y Conecta"
+- "Internet Rápido - Solo Escanea"
+- "Conectate Fácil con un Click"
+
+### Diseño Personalizado
+Usa los colores de tu marca en el código QR para mantener la coherencia visual con tu negocio.
+
+### Múltiples Ubicaciones
+En locales grandes, coloca varios códigos QR en diferentes puntos para facilitar el acceso.
+
+### Actualización Periódica
+Por seguridad, considera cambiar la contraseña WiFi cada 3-6 meses y generar un nuevo código QR.
+
+## Conclusión
+
+Crear un código QR WiFi es rápido, fácil y completamente gratuito en QR Rapido. En menos de 2 minutos puedes:
+
+✅ Generar tu código QR personalizado
+✅ Compartir tu red sin esfuerzo
+✅ Proporcionar mejor experiencia a los visitantes
+✅ Demostrar profesionalismo
+
+**Prueba ahora mismo**: [Crear mi Código QR WiFi Gratis](/)
+
+---
+
+**¿Te gustó este tutorial?** ¡Comparte con amigos que también quieren facilitar el acceso a la red WiFi! Explora también nuestros otros tipos de códigos QR para diferentes necesidades.
+
+## Otros Tutoriales Útiles
+
+- Cómo crear código QR para WhatsApp
+- Código QR para Tarjeta de Visita Digital
+- Cómo crear código QR para URLs y Links
+- Personalización avanzada de códigos QR
+
+**¡Crea ahora tu código QR WiFi gratuito y transforma la experiencia de tus visitantes!** 🚀📱
+
+---
+
+## Soporte Técnico
+
+¿Tienes problemas para crear tu código QR WiFi? Contáctanos:
+
+- **Email**: soporte@qrrapido.site
+- **WhatsApp**: [Agregar número]
+- **Horario**: Lunes a Viernes, 8:00 - 18:00 (hora de Paraguay)
+
+¡Estamos aquí para ayudarte a crear el código QR perfecto para tu negocio o hogar!
\ No newline at end of file
diff --git a/Content/Tutoriais/como-criar-qr-code-whatsapp.pt-BR.md b/Content/Tutoriais/como-criar-qr-code-whatsapp.pt-BR.md
new file mode 100644
index 0000000..a3a2a3c
--- /dev/null
+++ b/Content/Tutoriais/como-criar-qr-code-whatsapp.pt-BR.md
@@ -0,0 +1,108 @@
+---
+title: "Como Criar QR Code para WhatsApp"
+description: "Aprenda a criar um QR Code para WhatsApp que permite aos usuários iniciarem uma conversa com você instantaneamente"
+keywords: "qr code whatsapp, whatsapp qr code, qr code para whatsapp, criar qr whatsapp"
+author: "QR Rapido"
+date: 2025-10-08
+lastmod: 2025-10-08
+image: "/images/tutoriais/whatsapp-qr-hero.jpg"
+---
+
+# Como Criar QR Code para WhatsApp
+
+Criar um **QR Code para WhatsApp** é uma das maneiras mais eficientes de facilitar o contato direto com seus clientes, amigos ou seguidores. Com um simples escaneamento, qualquer pessoa pode iniciar uma conversa com você instantaneamente, sem precisar salvar seu número.
+
+## 📱 Por que usar QR Code para WhatsApp?
+
+Os QR Codes para WhatsApp são extremamente úteis para:
+
+- **Empresas**: Facilitar o atendimento ao cliente
+- **Freelancers**: Agilizar o contato com potenciais clientes
+- **Eventos**: Permitir networking rápido
+- **Marketing**: Aumentar a conversão em campanhas
+
+## 🎯 Passo a Passo
+
+### 1. Prepare seu número
+
+Primeiro, você precisa ter seu número no formato internacional:
+
+```
+55 11 98765-4321
+```
+
+Remova todos os espaços e traços, ficando:
+
+```
+5511987654321
+```
+
+### 2. Crie a URL do WhatsApp
+
+A URL do WhatsApp segue este padrão:
+
+```
+https://wa.me/5511987654321
+```
+
+Você pode adicionar uma mensagem pré-definida:
+
+```
+https://wa.me/5511987654321?text=Olá!%20Gostaria%20de%20mais%20informações
+```
+
+### 3. Gere o QR Code
+
+Acesse [QR Rapido](https://qrrapido.site) e:
+
+1. Selecione o tipo **URL**
+2. Cole a URL do WhatsApp
+3. Personalize as cores (opcional)
+4. Clique em **Gerar QR Code**
+5. Faça o download em alta qualidade
+
+## 💡 Dicas Profissionais
+
+### Personalize a Mensagem Inicial
+
+Configure uma mensagem de boas-vindas automática para melhorar a experiência:
+
+```
+https://wa.me/5511987654321?text=Olá!%20Vim%20através%20do%20seu%20QR%20Code
+```
+
+### Use em Materiais Impressos
+
+- **Cartões de visita**: Facilite o contato instantâneo
+- **Flyers e panfletos**: Aumente o engajamento
+- **Embalagens**: Ofereça suporte direto ao cliente
+- **Banners**: Em eventos e feiras
+
+### Monitore os Resultados
+
+Para rastrear quantas pessoas escanearam seu QR Code, considere usar um encurtador de URL com analytics antes de gerar o QR Code.
+
+## ⚠️ Cuidados Importantes
+
+1. **Teste antes de imprimir**: Sempre escaneie para verificar se funciona
+2. **Tamanho mínimo**: Mantenha pelo menos 3x3 cm para fácil leitura
+3. **Contraste adequado**: Use cores que contrastem bem (preto no branco é ideal)
+4. **Mensagem clara**: Indique para que serve o QR Code
+
+## 🚀 Vantagens de usar QR Rapido
+
+- ⚡ **Ultrarrápido**: Gere em menos de 1 segundo
+- 🎨 **Personalizável**: Escolha cores e estilos
+- 📥 **Alta qualidade**: Download em PNG, SVG e PDF
+- 🔒 **Seguro**: Seus dados não são armazenados
+- 💯 **Gratuito**: 10 QR codes por dia
+
+## Conclusão
+
+Criar um QR Code para WhatsApp é simples e pode revolucionar a forma como você se comunica com seu público. Com o QR Rapido, você tem tudo que precisa para criar QR Codes profissionais em segundos.
+
+**Pronto para começar?** [Crie seu QR Code agora →](https://qrrapido.site/pt-BR)
+
+---
+
+*Tem dúvidas? [Entre em contato](https://qrrapido.site/pt-BR/Contact) conosco!*
diff --git a/Content/Tutoriais/como-criar-qr-code-wifi.pt-BR.md b/Content/Tutoriais/como-criar-qr-code-wifi.pt-BR.md
new file mode 100644
index 0000000..852b76d
--- /dev/null
+++ b/Content/Tutoriais/como-criar-qr-code-wifi.pt-BR.md
@@ -0,0 +1,187 @@
+---
+title: "Como Criar QR Code WiFi Grátis: Compartilhe sua Rede em Segundos"
+description: "Aprenda a criar QR Code WiFi gratuito em poucos cliques. Compartilhe a senha da sua rede sem digitar com nosso gerador rápido e seguro."
+keywords: "qr code wifi, criar qr code wifi, gerador qr code wifi grátis, qr code rede wifi, compartilhar senha wifi, qr code wifi gratuito, como fazer qr code wifi"
+author: "QR Rapido"
+date: 2025-10-10
+lastmod: 2025-10-10
+image: "/images/tutoriais/qr-code-wifi-hero.jpg"
+---
+
+# Como Criar QR Code WiFi Grátis: Compartilhe sua Rede em Segundos
+
+Cansado de digitar senhas longas e complicadas toda vez que um visitante pede a senha do WiFi? Com um **QR Code WiFi**, você pode compartilhar sua rede instantaneamente! Neste tutorial completo, você aprenderá a criar um QR Code para WiFi gratuitamente em menos de 2 minutos.
+
+## Por Que Usar QR Code para WiFi?
+
+Compartilhar sua rede WiFi através de QR Code oferece diversas vantagens:
+
+- **Praticidade**: Visitantes conectam-se instantaneamente sem digitar senhas
+- **Segurança**: Não precisa falar a senha em voz alta ou anotá-la
+- **Profissionalismo**: Ideal para empresas, cafeterias, restaurantes e consultórios
+- **Economia de tempo**: Elimina erros de digitação e pedidos repetidos
+- **Compatibilidade**: Funciona em praticamente todos os smartphones modernos
+
+## Passo a Passo: Como Criar seu QR Code WiFi
+
+### Passo 1: Selecione o Tipo de QR Code
+
+Acesse o gerador e escolha a opção **WiFi** na lista de tipos de QR Code disponíveis.
+
+**[INSERIR IMAGEM 1 AQUI: Tela de seleção de tipo de QR Code com WiFi destacado]**
+
+No menu dropdown, você encontrará diversas opções como URL/Link, Texto Simples, Cartão de Visita, SMS e Email. Para este tutorial, selecione **WiFi**.
+
+### Passo 2: Preencha os Dados da sua Rede WiFi
+
+Agora você precisa informar os dados da sua rede. Veja os campos obrigatórios:
+
+**[INSERIR IMAGEM 3 AQUI: Formulário de criação de QR Code WiFi com campos preenchidos]**
+
+#### NetworkName (Nome da Rede) *
+Digite exatamente o nome da sua rede WiFi (SSID). Por exemplo: "NomeDaSuaRede"
+
+**Dica importante**: O nome deve ser idêntico ao que aparece quando você busca redes WiFi no celular. Respeite maiúsculas e minúsculas!
+
+#### SecurityType (Tipo de Segurança)
+Selecione o tipo de criptografia da sua rede:
+
+- **Rede WPA (a mais comum)**: WPA/WPA2/WPA3 - recomendado e mais seguro
+- **WEP (muito antigo)**: Não recomendado por ser inseguro
+- **Sem senha**: Para redes públicas sem proteção
+
+**[INSERIR IMAGEM 4 AQUI: Exemplo de formulário WiFi preenchido com "Rede-do-meu-comercio"]**
+
+#### NetworkPassword (Senha da Rede) *
+Insira a senha da sua rede WiFi. Use o ícone de olho para visualizar e conferir se digitou corretamente.
+
+**Importante**: A senha também diferencia maiúsculas de minúsculas. Confira com atenção!
+
+#### HiddenNetwork (Rede Oculta)
+Marque esta opção apenas se sua rede está configurada como oculta (não aparece na lista de redes disponíveis).
+
+### Passo 3: Personalize seu QR Code (Opcional)
+
+Deixe seu QR Code com a cara do seu negócio!
+
+**[INSERIR IMAGEM 2 AQUI: Painel de personalização avançada]**
+
+#### Opções de Personalização:
+
+**Cor Principal**: Escolha a cor dos quadrados do QR Code (padrão: azul)
+
+**Cor de Fundo**: Defina a cor de fundo (padrão: branco)
+
+**Tamanho**: Selecione entre:
+- Pequeno (200px) - Para uso digital
+- Médio (300px) - Recomendado para impressão
+- Grande (500px) - Para banners e pôsteres
+
+**Margem**:
+- Compacta - Ocupa menos espaço
+- Normal - Recomendado (melhor leitura)
+- Larga - Para impressões grandes
+
+**Dica de design**: Mantenha bom contraste entre a cor principal e o fundo para garantir que todos os celulares consigam ler o código.
+
+### Passo 4: Gerar e Baixar
+
+Clique no botão azul **"⚡ Gerar QR Code Rapidamente"** e pronto! Seu QR Code WiFi será gerado instantaneamente.
+
+Você pode:
+- Baixar a imagem em alta qualidade
+- Imprimir e colocar em locais visíveis
+- Compartilhar digitalmente
+- Salvar para usar depois
+
+## Como Seus Visitantes Usarão o QR Code WiFi
+
+É muito simples! Seus visitantes só precisam:
+
+1. Abrir a câmera do celular (iOS ou Android)
+2. Apontar para o QR Code
+3. Tocar na notificação que aparece
+4. Conectar automaticamente ao WiFi
+
+**Não precisa baixar aplicativos!** A maioria dos smartphones desde 2018 já possui leitores de QR Code integrados na câmera.
+
+## Onde Usar seu QR Code WiFi
+
+### Para Empresas
+- Recepção de escritórios
+- Salas de reunião
+- Áreas de espera
+- Coworking spaces
+
+### Para Comércios
+- Mesas de restaurantes
+- Balcões de cafeterias
+- Lojas e boutiques
+- Salões de beleza
+
+### Para Residências
+- Quadro de entrada
+- Área da churrasqueira
+- Geladeira (para festas)
+- Home office
+
+### Para Eventos
+- Credenciais de eventos
+- Stands de feiras
+- Conferências
+- Casamentos e festas
+
+## Dicas de Segurança
+
+⚠️ **Importante**: Considere criar uma rede WiFi separada para visitantes (rede guest) se você deseja:
+
+- Proteger seus dispositivos pessoais
+- Limitar velocidade para convidados
+- Ter controle sobre quem acessa
+- Manter sua rede principal privada
+
+Muitos roteadores modernos permitem criar redes guest facilmente nas configurações.
+
+## Perguntas Frequentes (FAQ)
+
+### O QR Code WiFi expira?
+Não! O QR Code funciona indefinidamente enquanto os dados da rede (nome e senha) permanecerem os mesmos.
+
+### Funciona em iPhone e Android?
+Sim! Funciona em praticamente todos os smartphones fabricados após 2018 que possuem câmera.
+
+### Posso criar para rede 5GHz?
+Sim! O processo é exatamente o mesmo. Apenas use o nome correto da rede 5GHz.
+
+### É seguro?
+Sim! O QR Code apenas facilita a digitação dos dados. É tão seguro quanto informar a senha verbalmente ou por escrito.
+
+### Posso editar depois de criado?
+Não é possível editar o QR Code depois de gerado. Se mudar a senha do WiFi, precisará gerar um novo QR Code.
+
+### Quantas pessoas podem usar o mesmo QR Code?
+Ilimitadas! Não há limite de usos para o QR Code.
+
+## Conclusão
+
+Criar um QR Code WiFi é rápido, fácil e completamente gratuito no QR Rapido! Em menos de 2 minutos você pode:
+
+✅ Gerar seu QR Code personalizado
+✅ Compartilhar sua rede sem esforço
+✅ Proporcionar melhor experiência aos visitantes
+✅ Demonstrar profissionalismo
+
+**Experimente agora mesmo**: [Criar meu QR Code WiFi Grátis](/)
+
+---
+
+**Gostou deste tutorial?** Compartilhe com amigos que também querem facilitar o acesso à rede WiFi! Explore também nossos outros tipos de QR Code para diferentes necessidades.
+
+## Outros Tutoriais Úteis
+
+- Como criar QR Code para WhatsApp
+- QR Code para Cartão de Visita Digital
+- Como criar QR Code para URLs e Links
+- Personalização avançada de QR Codes
+
+**Crie agora seu QR Code WiFi gratuito e transforme a experiência dos seus visitantes!** 🚀📱
\ No newline at end of file
diff --git a/Content/Tutoriais/qr-code-para-corredores-inmuebles.es-PY.md b/Content/Tutoriais/qr-code-para-corredores-inmuebles.es-PY.md
new file mode 100644
index 0000000..0cd3b15
--- /dev/null
+++ b/Content/Tutoriais/qr-code-para-corredores-inmuebles.es-PY.md
@@ -0,0 +1,599 @@
+---
+title: "Código QR para Corredores de Inmuebles: Guía Completa para Etiquetas y Volantes"
+description: "Descubre cómo usar código QR en etiquetas adhesivas, carteles y volantes inmobiliarios. Aumenta tus ventas con tecnología gratuita y profesional."
+keywords: "codigo qr corredor inmuebles, etiqueta corredor inmobiliaria, qr inmobiliaria, etiquetas adhesivas corredores, divulgar corredor propiedades, qr cartel se vende"
+author: "QR Rapido"
+date: 2025-10-10
+lastmod: 2025-10-10
+image: "/images/tutoriais/qr-code-corretor-imoveis-hero.jpg"
+---
+
+# Código QR para Corredores de Inmuebles: Guía Completa para Etiquetas y Volantes
+
+Si eres corredor de inmuebles, sabes que **captar leads calificados** es esencial para cerrar negocios. Imagina transformar tus carteles de "Se Vende", volantes y etiquetas adhesivas en herramientas interactivas que conectan clientes directamente a tu WhatsApp, ficha del inmueble o tarjeta de visita digital - todo esto **gratuitamente** con códigos QR.
+
+En esta guía completa, aprenderás a crear y aplicar códigos QR profesionales en materiales inmobiliarios, aumentando tus conversiones y destacándote de la competencia.
+
+## ¿Por Qué los Corredores de Inmuebles Deben Usar Códigos QR?
+
+### **Ventajas Comprobadas**
+
+- ✅ **Captación 24/7**: Tu cartel trabaja para ti incluso cuando estás durmiendo
+- ✅ **Contacto Instantáneo**: Cliente escanea y ya está en tu WhatsApp
+- ✅ **Cero Escritura**: Elimina errores al anotar números
+- ✅ **Rastreo**: Sabe cuántas personas se interesaron
+- ✅ **Profesionalismo**: Demuestra modernidad e innovación
+- ✅ **Costo Cero**: Genera códigos QR ilimitados gratuitamente
+- ✅ **Tour Virtual**: Lleva al cliente dentro del inmueble virtualmente
+
+### **Estadísticas del Mercado**
+
+Según estudios del sector inmobiliario:
+- **78%** de los compradores investigan propiedades por celular
+- **65%** prefieren contacto vía WhatsApp en lugar de llamada
+- **43%** escanean códigos QR en carteles de inmuebles cuando los ven
+- Corredores que usan código QR tienen **35% más leads** mensuales
+
+---
+
+## Dónde Aplicar Códigos QR en Marketing Inmobiliario
+
+### **1. Carteles de "Se Vende" y "Se Alquila"**
+
+**¡El uso más poderoso!** El cartel al frente del inmueble es visto por cientos de personas diariamente.
+
+**Qué colocar en el código QR:**
+- Link directo a tu WhatsApp
+- vCard con tus contactos completos
+- Tour virtual 360° del inmueble
+- Ficha técnica detallada (PDF)
+- Video del inmueble en YouTube
+
+**Consejo profesional**: Coloca texto llamativo como:
+- "Escanea y agenda tu visita AHORA"
+- "Tour Virtual - Apunta tu cámara aquí"
+- "WhatsApp Directo del Corredor"
+
+### **2. Etiquetas Adhesivas y Tags**
+
+Etiquetas pequeñas (5x5cm hasta 10x10cm) son perfectas para:
+
+**Aplicaciones:**
+- Pegar en autos de la inmobiliaria
+- Fijar en portones de inmuebles
+- Aplicar en vitrinas de locales
+- Distribuir en establecimientos asociados
+- Colocar en ascensores de edificios
+
+**Ventajas:**
+- Bajo costo de impresión
+- Fácil distribución masiva
+- Pueden ser cambiadas rápidamente
+- Óptimas para acciones promocionales
+
+### **3. Volantes y Flyers**
+
+¡Transforma volantes de papel en herramientas digitales!
+
+**Dónde aplicar:**
+- Esquina superior derecha (lugar de mayor atención)
+- Centro, si es el foco principal
+- Reverso, con llamada destacada
+
+**Contenido recomendado:**
+- Portafolio digital de inmuebles
+- Formulario de registro
+- Calculadora de financiamiento
+- Lista completa de propiedades disponibles
+
+### **4. Folders y Revistas Inmobiliarias**
+
+Materiales impresos premium merecen códigos QR estratégicos.
+
+**Uso ideal:**
+- 1 QR por inmueble destacado
+- QR en la portada para portafolio completo
+- QR en la contraportada con tus contactos
+- QR en cada página con más información
+
+### **5. Tarjetas de Visita**
+
+¡El clásico nunca pasa de moda, pero puede ser mejorado!
+
+**Código QR en la tarjeta permite:**
+- Guardar contacto automáticamente (vCard)
+- Ver portafolio online
+- Agendar reunión directo en la agenda
+- Enviar mensaje vía WhatsApp
+
+### **6. Firma de Email**
+
+¡Cada email que envías es una oportunidad!
+
+**Incluye código QR para:**
+- Tu vCard completo
+- Último lanzamiento inmobiliario
+- Evaluación gratuita de inmueble
+- Agendamiento de visitas
+
+---
+
+## Paso a Paso: Cómo Crear Código QR para Corredores
+
+Voy a mostrar cómo crear **3 tipos de códigos QR** esenciales para corredores:
+
+### **Tipo 1: Código QR de vCard (Tarjeta de Visita Digital)**
+
+Perfecto para: Tarjetas de visita, firma de email, credenciales
+
+**[INSERTAR IMAGEN 1 AQUÍ: Selección del tipo de código QR - vCard destacado]**
+
+#### Paso 1: Selecciona "Tarjeta de Visita"
+
+Accede al generador y elige la opción **Tarjeta de Visita** en el menú de tipos.
+
+#### Paso 2: Completa tus Datos Profesionales
+
+**Información obligatoria:**
+- **Nombre completo**: Juan Silva
+- **Cargo**: Corredor de Inmuebles - Matrícula 12345
+- **Empresa**: Inmobiliaria Success
+- **Teléfono**: +595 21 123-4567
+- **Email**: juan.silva@inmuebles.com.py
+- **Website**: www.juansilva.inmuebles.py
+- **Dirección**: Av. Mariscal López - Asunción, Paraguay
+
+**Campos opcionales estratégicos:**
+- WhatsApp Business
+- Instagram profesional
+- LinkedIn
+- Canal de YouTube con tours virtuales
+
+#### Paso 3: Genera y Descarga
+
+Haz clic en **"Generar Código QR"** y descarga en alta resolución.
+
+**Dónde usar este QR:**
+- Etiquetas adhesivas en el auto
+- Tarjetas de visita
+- Firma de email
+- Credencial profesional
+
+---
+
+### **Tipo 2: Código QR para WhatsApp Directo**
+
+Perfecto para: Carteles de inmuebles, volantes, anuncios
+
+**[INSERTAR IMAGEN 3 AQUÍ: Formulario de WhatsApp con código QR completado]**
+
+#### Paso 1: Selecciona "WhatsApp"
+
+En el generador, elige la opción **WhatsApp** (o SMS si prefieres).
+
+#### Paso 2: Configura el Mensaje Pre-llenado
+
+**Ejemplo de mensaje eficaz:**
+
+```
+¡Hola! Vi el cartel del inmueble en [CALLE/BARRIO] y me gustaría agendar una visita. ¿Puede pasarme más información?
+```
+
+**Por qué el mensaje pre-llenado funciona:**
+- Cliente no necesita pensar qué escribir
+- Ya sabes de qué inmueble está hablando
+- Aumenta en 80% la tasa de conversión
+
+#### Paso 3: Personaliza para Cada Inmueble
+
+**Consejo importante**: ¡Crea códigos QR diferentes para cada inmueble!
+
+Ejemplo para apartamento en Recoleta:
+```
+¡Hola! Vi el cartel del Departamento 3 dormitorios en Recoleta (Ref: DEPT-001). Me gustaría saber más detalles y agendar visita.
+```
+
+**Ventaja**: ¡Ya sabes exactamente qué inmueble quiere ver el cliente!
+
+---
+
+### **Tipo 3: Código QR para URL (Tour Virtual / Ficha Técnica)**
+
+Perfecto para: Inmuebles de alto estándar, lanzamientos, propiedades rurales
+
+**[INSERTAR IMAGEN 4 AQUÍ: Ejemplo de formulario URL completado]**
+
+#### Paso 1: Prepara el Contenido Digital
+
+Antes de crear el QR, necesitas tener:
+
+**Opción A - Tour Virtual:**
+- Video en YouTube del inmueble
+- Tour 360° (Google Street View, Matterport)
+- Galería de fotos en Instagram/Facebook
+
+**Opción B - Landing Page:**
+- Ficha técnica completa del inmueble
+- Fotos en alta resolución
+- Mapa de ubicación
+- Calculadora de financiamiento
+- Formulario de interés
+
+#### Paso 2: Acorta la URL (¡Importante!)
+
+Usa acortadores como:
+- bit.ly
+- tinyurl.com
+- QR Rapido (si tiene función de acortamiento)
+
+**Ejemplo:**
+- ❌ URL larga: `https://www.miinmobiliaria.com.py/inmuebles/departamento-3-dormitorios-recoleta-asuncion-ref-dept001?utm_source=cartel`
+- ✅ URL corta: `bit.ly/dept-recoleta-001`
+
+**Ventaja de URL corta**: Genera código QR más simple y fácil de escanear
+
+#### Paso 3: Crea el Código QR
+
+Pega la URL corta en el campo **URL/Link** y genera el código.
+
+---
+
+## Cómo Personalizar tu Código QR Profesionalmente
+
+**[INSERTAR IMAGEN 2 AQUÍ: Panel de personalización con colores personalizados]**
+
+### **Elección de Colores Estratégicos**
+
+**Para Inmobiliarias Tradicionales:**
+- Azul marino + Blanco (confianza, seriedad)
+- Negro + Dorado (lujo, exclusividad)
+- Verde oscuro + Blanco (crecimiento, estabilidad)
+
+**Para Inmobiliarias Modernas:**
+- Naranja + Blanco (energía, innovación)
+- Púrpura + Blanco (creatividad, diferenciación)
+- Rojo + Blanco (urgencia, acción)
+
+**Regla de oro**: ¡Siempre mantén alto contraste entre color principal y fondo!
+
+### **Tamaños Recomendados por Aplicación**
+
+**Carteles de calle (distancia 2-5 metros):**
+- Código QR: 15x15cm o mayor
+- Resolución: 500px mínimo
+
+**Volantes A5/A4:**
+- Código QR: 4x4cm a 6x6cm
+- Resolución: 300px
+
+**Etiquetas adhesivas:**
+- Código QR: 5x5cm (tamaño del adhesivo)
+- Resolución: 300px
+
+**Tarjetas de visita:**
+- Código QR: 2,5x2,5cm
+- Resolución: 200px
+
+### **Agrega Llamadas a la Acción (CTA)**
+
+¡Nunca coloques solo el código QR! Agrega texto atractivo:
+
+**Ejemplos eficaces:**
+- 📱 "Apunta la cámara y habla conmigo en WhatsApp"
+- 🏠 "Tour Virtual 360° - Escanea Aquí"
+- 💬 "Agenda tu Visita Ahora"
+- 📋 "Ve Fotos y Detalles Completos"
+- 🎯 "Guarda Mi Contacto Automáticamente"
+
+---
+
+## Estrategias Avanzadas para Corredores
+
+### **1. Códigos QR Rastreables (Dinámicos)**
+
+Usa servicios de código QR dinámico para:
+
+- Saber cuántas personas escanearon
+- Ver horario de los escaneos
+- Identificar ubicación aproximada
+- Cambiar el destino sin reimprimir
+
+**Cuándo usar:**
+- Campañas con muchos materiales impresos
+- Pruebas A/B de diferentes enfoques
+- Carteles permanentes en inmuebles
+
+### **2. Múltiples Códigos QR en el Mismo Cartel**
+
+¿Cartel grande? ¡Usa 2-3 códigos QR diferentes!
+
+**Ejemplo de cartel completo:**
+- **QR 1** (arriba): "Habla en WhatsApp"
+- **QR 2** (centro): "Tour Virtual 360°"
+- **QR 3** (abajo): "Guarda Mi Contacto"
+
+**Ventaja**: Cliente elige la acción que prefiere hacer
+
+### **3. Código QR + Realidad Aumentada**
+
+Para lanzamientos y emprendimientos:
+
+- Código QR lleva a app de RA
+- Cliente apunta celular y ve el edificio terminado
+- Visualiza departamento decorado
+- ¡Extremadamente impactante!
+
+### **4. Campañas de Temporada**
+
+Crea códigos QR específicos para:
+
+- **Enero**: "Planifica 2025 - Compra tu inmueble"
+- **Junio**: "Vacaciones de Julio en tu Nuevo Hogar"
+- **Noviembre**: "Black Friday Inmobiliaria"
+- **Diciembre**: "Empieza el Año en Casa Propia"
+
+### **5. Alianzas Estratégicas**
+
+Distribuye etiquetas con código QR en:
+
+- Tiendas de materiales de construcción
+- Oficinas de arquitectura
+- Gestorías y escribanías
+- Gimnasios y restaurantes del barrio
+- Edificios comerciales (tablero de avisos)
+
+---
+
+## Modelos de Etiquetas Adhesivas Profesionales
+
+### **Modelo 1: Etiqueta Minimalista (5x5cm)**
+
+```
+┌─────────────────┐
+│ [CÓDIGO QR] │
+│ │
+│ Juan Silva │
+│ Matrícula 12345 │
+│ (021) 123-4567 │
+└─────────────────┘
+```
+
+### **Modelo 2: Etiqueta con Destaque (7x7cm)**
+
+```
+┌─────────────────────┐
+│ VENDE TU INMUEBLE │
+│ SIN BUROCRACIA │
+│ │
+│ [CÓDIGO QR] │
+│ │
+│ "Escanea y habla │
+│ directo conmigo" │
+│ │
+│ Juan Silva │
+│ Corredor Mat. XXXX │
+└─────────────────────┘
+```
+
+### **Modelo 3: Etiqueta Tour Virtual (10x10cm)**
+
+```
+┌───────────────────────────┐
+│ TOUR VIRTUAL 360° │
+│ Visita este inmueble │
+│ ¡sin salir del sofá! │
+│ │
+│ [CÓDIGO QR GRANDE] │
+│ │
+│ 📱 Apunta tu cámara │
+│ │
+│ Inmobiliaria Success │
+│ (021) 123-4567 │
+└───────────────────────────┘
+```
+
+---
+
+## Mejores Prácticas de Impresión
+
+### **Materiales Recomendados**
+
+**Para carteles externos:**
+- **Vinilo adhesivo resistente a UV**
+- **Lona con impresión UV**
+- **ACM (Aluminio Compuesto)**
+
+Duración: 2-3 años expuestos al sol
+
+**Para etiquetas adhesivas:**
+- **Papel BOPP (Polipropileno)**
+- **Vinilo blanco brillante**
+- **Papel couché con laminación**
+
+Duración: 6-12 meses
+
+**Para volantes:**
+- **Couché 115g o 150g**
+- **Barniz localizado en el código QR** (destaque)
+
+### **Pruebas Antes de Imprimir en Masa**
+
+⚠️ **SIEMPRE haz esto:**
+
+1. Imprime 1 muestra en tamaño real
+2. Prueba con 5 celulares diferentes
+3. Prueba a diferentes distancias
+4. Prueba con poca luz
+5. Solo entonces imprime grandes cantidades
+
+**Celulares para probar:**
+- iPhone (iOS actualizado)
+- Samsung (Android)
+- Xiaomi o Motorola (Android popular)
+
+### **Dónde Imprimir**
+
+**Imprentas rápidas en Asunción:**
+- Etiquetas adhesivas: 100 unidades por Gs. 150.000-350.000
+- Volantes A5: 1000 unidades por Gs. 800.000-1.500.000
+
+**Online (más económico):**
+- Mercado Libre Paraguay
+- Imprentas locales con pedido online
+
+**Consejo**: ¡Pide presupuesto en 3 lugares diferentes!
+
+---
+
+## Errores Comunes que Corredores Deben Evitar
+
+### ❌ **Error 1: Código QR Muy Pequeño**
+
+**Problema**: En carteles de calle vistos de lejos, QR pequeño no funciona
+
+**Solución**: Mínimo 15x15cm para carteles externos
+
+### ❌ **Error 2: Colores con Bajo Contraste**
+
+**Problema**: QR amarillo en fondo blanco no escanea
+
+**Solución**: Usa siempre colores oscuros en fondo claro (o viceversa)
+
+### ❌ **Error 3: No Probar Antes de Imprimir**
+
+**Problema**: Imprime 1000 volantes y descubre que QR no funciona
+
+**Solución**: Siempre prueba impresión piloto
+
+### ❌ **Error 4: URL Rota o Temporal**
+
+**Problema**: Link del inmueble expira, QR queda inútil
+
+**Solución**: Usa URLs permanentes o QR dinámico editable
+
+### ❌ **Error 5: Sin Instrucción de Uso**
+
+**Problema**: Persona mayor no sabe qué hacer con el QR
+
+**Solución**: Agrega "Apunta la cámara del celular aquí"
+
+### ❌ **Error 6: Código QR Único para Todos los Inmuebles**
+
+**Problema**: No sabes qué inmueble generó el lead
+
+**Solución**: Crea QR específico para cada propiedad
+
+### ❌ **Error 7: Material de Baja Calidad**
+
+**Problema**: Etiqueta se desvanece en 1 mes al sol
+
+**Solución**: Invierte en material UV resistente
+
+---
+
+## Casos de Éxito en Paraguay
+
+### **Caso 1: Corredor en Asunción**
+
+**Estrategia**: Colocó código QR en todos los 12 carteles de inmuebles
+
+**Resultado:**
+- 98 escaneos en el primer mes
+- 28 conversaciones en WhatsApp
+- 7 visitas agendadas
+- 2 ventas cerradas
+
+**ROI**: Invirtió Gs. 500.000 en etiquetas, facturó comisiones millonarias
+
+### **Caso 2: Inmobiliaria en Ciudad del Este**
+
+**Estrategia**: Volantes con QR para tour virtual de lanzamiento
+
+**Resultado:**
+- 3.000 volantes distribuidos
+- 645 accesos al tour virtual
+- 112 registros de interesados
+- 18 departamentos vendidos en preventa
+
+### **Caso 3: Corredor Autónomo en Encarnación**
+
+**Estrategia**: Etiquetas adhesivas en establecimientos asociados
+
+**Resultado:**
+- 150 etiquetas distribuidas en 30 locales
+- 52 nuevos contactos en 3 meses
+- 9 evaluaciones de inmuebles agendadas
+- 2 captaciones exclusivas
+
+---
+
+## Preguntas Frecuentes
+
+### **¿Necesito pagar para crear código QR?**
+
+¡No! En QR Rapido creas códigos QR ilimitados gratuitamente. Solo pagas si quieres recursos premium como rastreo avanzado.
+
+### **¿El código QR funciona para siempre?**
+
+Códigos QR estáticos (gratuitos) funcionan para siempre, pero no pueden ser editados. Códigos QR dinámicos (pagos) pueden ser editados incluso después de impresos.
+
+### **¿Qué tipo de QR usar en carteles de inmuebles?**
+
+Recomiendo **WhatsApp** con mensaje pre-llenado. Así el cliente ya inicia la conversación sabiendo de qué inmueble se trata.
+
+### **¿Puedo colocar logo de la inmobiliaria en el QR?**
+
+Sí, ¡pero con cuidado! Logos muy grandes pueden dificultar la lectura. Mantén el logo pequeño (máximo 20% del QR).
+
+### **¿Cuántas personas escanean QR en carteles?**
+
+Según investigaciones, entre 5-15% de las personas que ven el cartel escanean el QR. En áreas movimentadas, esto puede generar decenas de leads por mes.
+
+### **¿El código QR funciona de noche?**
+
+Sí, siempre que haya alguna iluminación (poste de calle, luz de la pantalla del celular ya ayuda). Para mejor resultado, ilumina el cartel.
+
+### **¿Puedo usar el mismo QR en varios materiales?**
+
+Puedes, pero no es recomendado. Crea códigos QR diferentes para saber de dónde viene cada lead (cartel, volante, etiqueta, etc).
+
+---
+
+## Checklist del Corredor Profesional
+
+Antes de imprimir, verifica:
+
+- [ ] Código QR probado en al menos 3 celulares diferentes
+- [ ] Tamaño adecuado para la distancia de escaneo
+- [ ] Alto contraste entre QR y fondo
+- [ ] Llamada a la acción clara ("Escanea aquí")
+- [ ] Información de contacto visible (nombre, matrícula, teléfono)
+- [ ] URL corta si es link (más fácil de escanear)
+- [ ] Material de impresión resistente (especialmente externo)
+- [ ] Margen de seguridad alrededor del QR (mínimo 1cm)
+
+---
+
+## Conclusión
+
+Los códigos QR son la **herramienta más económica y eficaz** para corredores modernos captar leads calificados. Con inversión de menos de Gs. 500.000 en etiquetas y volantes, puedes:
+
+✅ Captar leads 24 horas al día
+✅ Facilitar el contacto instantáneo vía WhatsApp
+✅ Mostrar tours virtuales impresionantes
+✅ Rastrear qué materiales generan más resultado
+✅ Destacarte de la competencia conservadora
+
+**El mercado inmobiliario está cada vez más digital. Quien no se adapta, queda atrás.**
+
+---
+
+## ¡Empieza Ahora!
+
+**Crea tu primer código QR para corredor gratuitamente:**
+
+1. [Generar Código QR de WhatsApp](/) - Para carteles de inmuebles
+2. [Generar Código QR vCard](/) - Para tarjetas de visita
+3. [Generar Código QR de URL](/) - Para tour virtual
+
+**¡Transforma tus carteles y volantes en máquinas de captar leads!** 🏠📱🚀
\ No newline at end of file
diff --git a/Content/Tutoriais/qr-code-para-corretores-imoveis.pt-BR.md b/Content/Tutoriais/qr-code-para-corretores-imoveis.pt-BR.md
new file mode 100644
index 0000000..0c97d51
--- /dev/null
+++ b/Content/Tutoriais/qr-code-para-corretores-imoveis.pt-BR.md
@@ -0,0 +1,640 @@
+---
+title: "QR Code para Corretores de Imóveis: Guia Completo para Etiquetas e Panfletos"
+description: "Descubra como usar QR Code em etiquetas adesivas, placas e panfletos imobiliários. Aumente suas vendas com tecnologia gratuita e profissional."
+keywords: "qr code corretor imoveis, etiqueta corretor imoveis, qrcode imobiliaria, etiquetas adesivas corretores, divulgar corretor imoveis, qr code placa vende-se"
+author: "QR Rapido"
+date: 2025-10-10
+lastmod: 2025-10-10
+image: "/images/tutoriais/qr-code-corretor-imoveis-hero.jpg"
+---
+
+# QR Code para Corretores de Imóveis: Guia Completo para Etiquetas e Panfletos
+
+Se você é corretor de imóveis, sabe que **captar leads qualificados** é essencial para fechar negócios. Imagine transformar suas placas de "Vende-se", panfletos e etiquetas adesivas em ferramentas interativas que conectam clientes diretamente ao seu WhatsApp, ficha do imóvel ou cartão de visita digital - tudo isso **gratuitamente** com QR Codes!
+
+Neste guia completo, você aprenderá a criar e aplicar QR Codes profissionais em materiais imobiliários, aumentando suas conversões e se destacando da concorrência.
+
+## Por Que Corretores de Imóveis Devem Usar QR Codes?
+
+### **Vantagens Comprovadas**
+
+- ✅ **Captação 24/7**: Sua placa trabalha para você mesmo quando está dormindo
+- ✅ **Contato Instantâneo**: Cliente escaneia e já está no seu WhatsApp
+- ✅ **Zero Digitação**: Elimina erros ao anotar números
+- ✅ **Rastreamento**: Saiba quantas pessoas se interessaram
+- ✅ **Profissionalismo**: Demonstra modernidade e inovação
+- ✅ **Custo Zero**: Gere QR Codes ilimitados gratuitamente
+- ✅ **Tour Virtual**: Leve o cliente para dentro do imóvel virtualmente
+
+### **Estatísticas do Mercado**
+
+Segundo estudos do setor imobiliário:
+- **78%** dos compradores pesquisam imóveis pelo celular
+- **65%** preferem contato via WhatsApp ao invés de ligação
+- **43%** escaneiam QR Codes em placas de imóveis quando veem
+- Corretores que usam QR Code têm **35% mais leads** mensais
+
+---
+
+## Onde Aplicar QR Codes no Marketing Imobiliário
+
+### **1. Placas de "Vende-se" e "Aluga-se"**
+
+**O uso mais poderoso!** A placa na frente do imóvel é vista por centenas de pessoas diariamente.
+
+**O que colocar no QR Code:**
+- Link direto para seu WhatsApp
+- vCard com seus contatos completos
+- Tour virtual 360° do imóvel
+- Ficha técnica detalhada (PDF)
+- Vídeo do imóvel no YouTube
+
+**Dica profissional**: Coloque texto chamativo como:
+- "Escanei e agende sua visita AGORA"
+- "Tour Virtual - Aponte a câmera aqui"
+- "WhatsApp Direto do Corretor"
+
+### **2. Etiquetas Adesivas e Tags**
+
+Etiquetas pequenas (5x5cm até 10x10cm) são perfeitas para:
+
+**Aplicações:**
+- Colar em carros da imobiliária
+- Fixar em portões de imóveis
+- Aplicar em vitrines de lojas
+- Distribuir em estabelecimentos parceiros
+- Colocar em elevadores de prédios
+
+**Vantagens:**
+- Baixo custo de impressão
+- Fácil distribuição em massa
+- Podem ser trocadas rapidamente
+- Ótimas para ações promocionais
+
+### **3. Panfletos e Flyers**
+
+Transforme panfletos de papel em ferramentas digitais!
+
+**Onde aplicar:**
+- Canto superior direito (local de maior atenção)
+- Centro, se for o foco principal
+- Verso, com chamada destacada
+
+**Conteúdo recomendado:**
+- Portfólio digital de imóveis
+- Formulário de cadastro
+- Calculadora de financiamento
+- Lista completa de imóveis disponíveis
+
+### **4. Folders e Revistas Imobiliárias**
+
+Materiais impressos premium merecem QR Codes estratégicos.
+
+**Uso ideal:**
+- 1 QR por imóvel destacado
+- QR na capa para portfólio completo
+- QR na contracapa com seus contatos
+- QR em cada página com mais informações
+
+### **5. Cartões de Visita**
+
+O clássico nunca sai de moda, mas pode ser turbinado!
+
+**QR Code no cartão permite:**
+- Salvar contato automaticamente (vCard)
+- Ver portfólio online
+- Agendar reunião direto na agenda
+- Enviar mensagem via WhatsApp
+
+### **6. Assinatura de Email**
+
+Todo email que você envia é uma oportunidade!
+
+**Inclua QR Code para:**
+- Seu vCard completo
+- Último lançamento imobiliário
+- Avaliação gratuita de imóvel
+- Agendamento de visitas
+
+---
+
+## Passo a Passo: Como Criar QR Code para Corretores
+
+Vou mostrar como criar **3 tipos de QR Codes** essenciais para corretores:
+
+### **Tipo 1: QR Code de vCard (Cartão de Visita Digital)**
+
+Perfeito para: Cartões de visita, assinatura de email, crachás
+
+**[INSERIR IMAGEM 1 AQUI: Seleção do tipo de QR Code - vCard destacado]**
+
+#### Passo 1: Selecione "Cartão de Visita"
+
+Acesse o gerador e escolha a opção **Cartão de Visita** no menu de tipos.
+
+#### Passo 2: Preencha seus Dados Profissionais
+
+**Informações obrigatórias:**
+- **Nome completo**: João Silva
+- **Cargo**: Corretor de Imóveis CRECI 12345-F
+- **Empresa**: Imobiliária Success
+- **Telefone**: +55 11 98765-4321
+- **Email**: joao.silva@imoveis.com.br
+- **Website**: www.joaosilva.imoveis.br
+- **Endereço**: Av. Paulista, 1000 - São Paulo, SP
+
+**Campos opcionais estratégicos:**
+- WhatsApp Business
+- Instagram profissional
+- LinkedIn
+- Canal do YouTube com tours virtuais
+
+#### Passo 3: Gere e Baixe
+
+Clique em **"Gerar QR Code"** e baixe em alta resolução.
+
+**Onde usar este QR:**
+- Etiquetas adesivas no carro
+- Cartões de visita
+- Assinatura de email
+- Crachá profissional
+
+---
+
+### **Tipo 2: QR Code para WhatsApp Direto**
+
+Perfeito para: Placas de imóveis, panfletos, anúncios
+
+**[INSERIR IMAGEM 3 AQUI: Formulário de WhatsApp QR Code preenchido]**
+
+#### Passo 1: Selecione "WhatsApp"
+
+No gerador, escolha a opção **WhatsApp** (ou SMS se preferir).
+
+#### Passo 2: Configure a Mensagem Pré-Pronta
+
+**Exemplo de mensagem eficaz:**
+
+```
+Olá! Vi a placa do imóvel na [RUA/BAIRRO] e gostaria de agendar uma visita. Pode me passar mais informações?
+```
+
+**Por que mensagem pré-pronta funciona:**
+- Cliente não precisa pensar no que escrever
+- Você já sabe de qual imóvel ele está falando
+- Aumenta em 80% a taxa de conversão
+
+#### Passo 3: Personalize para Cada Imóvel
+
+**Dica importante**: Crie QR Codes diferentes para cada imóvel!
+
+Exemplo para apartamento no Jardins:
+```
+Olá! Vi a placa do Apartamento 3 quartos no Jardins (Ref: APT-001). Gostaria de saber mais detalhes e agendar visita.
+```
+
+**Vantagem**: Você já sabe exatamente qual imóvel o cliente quer ver!
+
+---
+
+### **Tipo 3: QR Code para URL (Tour Virtual / Ficha Técnica)**
+
+Perfeito para: Imóveis de alto padrão, lançamentos, propriedades rurais
+
+**[INSERIR IMAGEM 4 AQUI: Exemplo de formulário URL completado]**
+
+#### Passo 1: Prepare o Conteúdo Digital
+
+Antes de criar o QR, você precisa ter:
+
+**Opção A - Tour Virtual:**
+- Vídeo no YouTube do imóvel
+- Tour 360° (Google Street View, Matterport)
+- Galeria de fotos no Instagram/Facebook
+
+**Opção B - Landing Page:**
+- Ficha técnica completa do imóvel
+- Fotos em alta resolução
+- Mapa de localização
+- Calculadora de financiamento
+- Formulário de interesse
+
+#### Passo 2: Encurte a URL (Importante!)
+
+Use encurtadores como:
+- bit.ly
+- tinyurl.com
+- QR Rapido (se tiver função de encurtamento)
+
+**Exemplo:**
+- ❌ URL longa: `https://www.minhaibiliaria.com.br/imoveis/apartamento-3-quartos-jardins-sao-paulo-ref-apt001?utm_source=placa`
+- ✅ URL curta: `bit.ly/apt-jardins-001`
+
+**Vantagem da URL curta**: Gera QR Code mais simples e fácil de escanear
+
+#### Passo 3: Crie o QR Code
+
+Cole a URL curta no campo **URL/Link** e gere o código.
+
+---
+
+## Como Personalizar seu QR Code Profissionalmente
+
+**[INSERIR IMAGEM 2 AQUI: Painel de personalização com cores personalizadas]**
+
+### **Escolha de Cores Estratégicas**
+
+**Para Imobiliárias Tradicionais:**
+- Azul marinho + Branco (confiança, seriedade)
+- Preto + Dourado (luxo, exclusividade)
+- Verde escuro + Branco (crescimento, estabilidade)
+
+**Para Imobiliárias Modernas:**
+- Laranja + Branco (energia, inovação)
+- Roxo + Branco (criatividade, diferenciação)
+- Vermelho + Branco (urgência, ação)
+
+**Regra de ouro**: Sempre mantenha alto contraste entre cor principal e fundo!
+
+### **Tamanhos Recomendados por Aplicação**
+
+**Placas de rua (distância 2-5 metros):**
+- QR Code: 15x15cm ou maior
+- Resolução: 500px mínimo
+
+**Panfletos A5/A4:**
+- QR Code: 4x4cm a 6x6cm
+- Resolução: 300px
+
+**Etiquetas adesivas:**
+- QR Code: 5x5cm (tamanho do adesivo)
+- Resolução: 300px
+
+**Cartões de visita:**
+- QR Code: 2,5x2,5cm
+- Resolução: 200px
+
+### **Adicione Chamadas para Ação (CTA)**
+
+Nunca coloque apenas o QR Code sozinho! Adicione texto atrativo:
+
+**Exemplos eficazes:**
+- 📱 "Aponte a câmera e fale comigo no WhatsApp"
+- 🏠 "Tour Virtual 360° - Escaneie Aqui"
+- 💬 "Agende sua Visita Agora"
+- 📋 "Veja Fotos e Detalhes Completos"
+- 🎯 "Salve Meu Contato Automaticamente"
+
+---
+
+## Estratégias Avançadas para Corretores
+
+### **1. QR Codes Rastreáveis (Dinâmicos)**
+
+Use serviços de QR Code dinâmico para:
+
+- Saber quantas pessoas escanearam
+- Ver horário dos escaneamentos
+- Identificar localização aproximada
+- Mudar o destino sem reimprimir
+
+**Quando usar:**
+- Campanhas com muitos materiais impressos
+- Testes A/B de diferentes abordagens
+- Placas permanentes em imóveis
+
+### **2. Múltiplos QR Codes na Mesma Placa**
+
+Placa grande? Use 2-3 QR Codes diferentes!
+
+**Exemplo de placa completa:**
+- **QR 1** (topo): "Fale no WhatsApp"
+- **QR 2** (centro): "Tour Virtual 360°"
+- **QR 3** (rodapé): "Salve Meu Contato"
+
+**Vantagem**: Cliente escolhe a ação que prefere fazer
+
+### **3. QR Code + Realidade Aumentada**
+
+Para lançamentos e empreendimentos:
+
+- QR Code leva para app de RA
+- Cliente aponta celular e vê o prédio pronto
+- Visualiza apartamento decorado
+- Extremamente impactante!
+
+### **4. Campanhas Sazonais**
+
+Crie QR Codes específicos para:
+
+- **Janeiro**: "Planeje 2025 - Compre seu imóvel"
+- **Junho**: "Férias de Julho no seu Novo Lar"
+- **Novembro**: "Black Friday Imobiliária"
+- **Dezembro**: "Comece o Ano na Casa Própria"
+
+### **5. Parcerias Estratégicas**
+
+Distribua etiquetas com QR Code em:
+
+- Lojas de materiais de construção
+- Escritórios de arquitetura
+- Despachantes e cartórios
+- Academias e restaurantes do bairro
+- Prédios comerciais (quadro de avisos)
+
+---
+
+## Modelos de Etiquetas Adesivas Profissionais
+
+### **Modelo 1: Etiqueta Minimalista (5x5cm)**
+
+```
+┌─────────────────┐
+│ [QR CODE] │
+│ │
+│ João Silva │
+│ CRECI 12345-F │
+│ (11) 98765-4321 │
+└─────────────────┘
+```
+
+### **Modelo 2: Etiqueta com Destaque (7x7cm)**
+
+```
+┌─────────────────────┐
+│ VENDA SEU IMÓVEL │
+│ SEM BUROCRACIA │
+│ │
+│ [QR CODE] │
+│ │
+│ "Escaneie e fale │
+│ direto comigo" │
+│ │
+│ João Silva │
+│ Corretor CRECI │
+└─────────────────────┘
+```
+
+### **Modelo 3: Etiqueta Tour Virtual (10x10cm)**
+
+```
+┌───────────────────────────┐
+│ TOUR VIRTUAL 360° │
+│ Visite este imóvel │
+│ sem sair do sofá! │
+│ │
+│ [QR CODE GRANDE] │
+│ │
+│ 📱 Aponte sua câmera │
+│ │
+│ Imobiliária Success │
+│ (11) 98765-4321 │
+└───────────────────────────┘
+```
+
+---
+
+## Melhores Práticas de Impressão
+
+### **Materiais Recomendados**
+
+**Para placas externas:**
+- **Vinil adesivo resistente a UV**
+- **Lona com impressão UV**
+- **ACM (Alumínio Composto)**
+
+Duração: 2-3 anos expostos ao sol
+
+**Para etiquetas adesivas:**
+- **Papel BOPP (Polipropileno)**
+- **Vinil branco brilhante**
+- **Papel couché com laminação**
+
+Duração: 6-12 meses
+
+**Para panfletos:**
+- **Couché 115g ou 150g**
+- **Verniz localizado no QR Code** (destaque)
+
+### **Testes Antes de Imprimir em Massa**
+
+⚠️ **SEMPRE faça isso:**
+
+1. Imprima 1 amostra em tamanho real
+2. Teste com 5 celulares diferentes
+3. Teste em diferentes distâncias
+4. Teste com pouca luz
+5. Só então imprima grandes quantidades
+
+**Celulares para testar:**
+- iPhone (iOS atualizado)
+- Samsung (Android)
+- Xiaomi ou Motorola (Android popular)
+
+### **Onde Imprimir**
+
+**Gráficas rápidas:**
+- Etiquetas adesivas: 100 unidades por R$ 30-80
+- Panfletos A5: 1000 unidades por R$ 150-300
+
+**Online (mais barato):**
+- Printi.com.br
+- Gráfica KWG
+- Gráfica Atual
+
+**Dica**: Peça orçamento em 3 lugares diferentes!
+
+---
+
+## Erros Comuns que Corretores Devem Evitar
+
+### ❌ **Erro 1: QR Code Muito Pequeno**
+
+**Problema**: Em placas de rua vistas de longe, QR pequeno não funciona
+
+**Solução**: Mínimo 15x15cm para placas externas
+
+### ❌ **Erro 2: Cores com Baixo Contraste**
+
+**Problema**: QR amarelo em fundo branco não escaneia
+
+**Solução**: Use sempre cores escuras em fundo claro (ou vice-versa)
+
+### ❌ **Erro 3: Não Testar Antes de Imprimir**
+
+**Problema**: Imprime 1000 panfletos e descobre que QR não funciona
+
+**Solução**: Sempre teste impressão piloto
+
+### ❌ **Erro 4: URL Quebrada ou Temporária**
+
+**Problema**: Link do imóvel expira, QR fica inútil
+
+**Solução**: Use URLs permanentes ou QR dinâmico editável
+
+### ❌ **Erro 5: Sem Instrução de Uso**
+
+**Problema**: Pessoa mais velha não sabe o que fazer com o QR
+
+**Solução**: Adicione "Aponte a câmera do celular aqui"
+
+### ❌ **Erro 6: QR Code Único para Todos Imóveis**
+
+**Problema**: Não sabe qual imóvel gerou o lead
+
+**Solução**: Crie QR específico para cada propriedade
+
+### ❌ **Erro 7: Material de Baixa Qualidade**
+
+**Problema**: Etiqueta desbota em 1 mês no sol
+
+**Solução**: Invista em material UV resistente
+
+---
+
+## Cases de Sucesso
+
+### **Case 1: Corretor em São Paulo**
+
+**Estratégia**: Colocou QR Code em todas as 15 placas de imóveis
+
+**Resultado:**
+- 127 escaneamentos no primeiro mês
+- 34 conversas no WhatsApp
+- 8 visitas agendadas
+- 2 vendas fechadas
+
+**ROI**: Investiu R$ 150 em etiquetas, faturou R$ 28.000 em comissões
+
+### **Case 2: Imobiliária no Rio de Janeiro**
+
+**Estratégia**: Panfletos com QR para tour virtual de lançamento
+
+**Resultado:**
+- 5.000 panfletos distribuídos
+- 890 acessos ao tour virtual
+- 156 cadastros de interessados
+- 23 apartamentos vendidos na pré-venda
+
+### **Case 3: Corretor Autônomo no Interior**
+
+**Estratégia**: Etiquetas adesivas em estabelecimentos parceiros
+
+**Resultado:**
+- 200 etiquetas distribuídas em 40 locais
+- 67 novos contatos em 3 meses
+- 12 avaliações de imóveis agendadas
+- 3 captações exclusivas
+
+---
+
+## Ferramentas Complementares
+
+### **Criação de Conteúdo Digital**
+
+- **Canva**: Criar layouts de etiquetas e panfletos
+- **Matterport**: Tours virtuais 360°
+- **YouTube**: Hospedar vídeos de imóveis
+- **Google Drive**: PDFs de fichas técnicas
+
+### **Gestão de Leads**
+
+- **Bitly**: Encurtar URLs e rastrear cliques
+- **Google Analytics**: Monitorar acessos
+- **WhatsApp Business**: Organizar conversas
+- **RD Station**: CRM imobiliário
+
+### **Impressão Online**
+
+- **Printi**: Etiquetas e panfletos
+- **Gráfica KWG**: Placas e banners
+- **Sticker Mule**: Adesivos premium
+
+---
+
+## Perguntas Frequentes
+
+### **Preciso pagar para criar QR Code?**
+
+Não! No QR Rapido você cria QR Codes ilimitados gratuitamente. Só paga se quiser recursos premium como rastreamento avançado.
+
+### **O QR Code funciona para sempre?**
+
+QR Codes estáticos (gratuitos) funcionam para sempre, mas não podem ser editados. QR Codes dinâmicos (pagos) podem ser editados mesmo depois de impressos.
+
+### **Qual tipo de QR usar em placas de imóveis?**
+
+Recomendo **WhatsApp** com mensagem pré-pronta. Assim o cliente já inicia a conversa sabendo de qual imóvel se trata.
+
+### **Posso colocar logo da imobiliária no QR?**
+
+Sim, mas com cuidado! Logos muito grandes podem dificultar a leitura. Mantenha a logo pequena (máximo 20% do QR).
+
+### **Quantas pessoas escaneiam QR em placas?**
+
+Segundo pesquisas, entre 5-15% das pessoas que veem a placa escaneiam o QR. Em áreas movimentadas, isso pode gerar dezenas de leads por mês.
+
+### **QR Code funciona à noite?**
+
+Sim, desde que haja alguma iluminação (poste de rua, luz da tela do celular já ajuda). Para melhor resultado, ilumine a placa.
+
+### **Posso usar o mesmo QR em vários materiais?**
+
+Pode, mas não é recomendado. Crie QR Codes diferentes para saber de onde vem cada lead (placa, panfleto, etiqueta, etc).
+
+### **Como sei se o QR está funcionando?**
+
+Teste imediatamente após criar! Use a câmera do seu celular e de pelo menos 2 amigos/familiares para garantir.
+
+---
+
+## Checklist do Corretor Profissional
+
+Antes de imprimir, confira:
+
+- [ ] QR Code testado em pelo menos 3 celulares diferentes
+- [ ] Tamanho adequado para a distância de escaneamento
+- [ ] Alto contraste entre QR e fundo
+- [ ] Chamada para ação clara ("Escaneie aqui")
+- [ ] Informações de contato visíveis (nome, CRECI, telefone)
+- [ ] URL curta se for link (mais fácil de escanear)
+- [ ] Material de impressão resistente (especialmente externo)
+- [ ] Margem de segurança ao redor do QR (mínimo 1cm)
+
+---
+
+## Conclusão
+
+QR Codes são a **ferramenta mais barata e eficaz** para corretores modernos captarem leads qualificados. Com investimento de menos de R$ 100 em etiquetas e panfletos, você pode:
+
+✅ Captar leads 24 horas por dia
+✅ Facilitar o contato instantâneo via WhatsApp
+✅ Mostrar tours virtuais impressionantes
+✅ Rastrear quais materiais geram mais resultado
+✅ Se destacar da concorrência conservadora
+
+**O mercado imobiliário está cada vez mais digital. Quem não se adapta, fica para trás.**
+
+---
+
+## Comece Agora!
+
+**Crie seu primeiro QR Code para corretor gratuitamente:**
+
+1. [Gerar QR Code de WhatsApp](/) - Para placas de imóveis
+2. [Gerar QR Code vCard](/) - Para cartões de visita
+3. [Gerar QR Code de URL](/) - Para tour virtual
+
+**Transforme suas placas e panfletos em máquinas de captar leads!** 🏠📱🚀
+
+---
+
+## Materiais Bônus para Download
+
+- 📋 Template de mensagem pré-pronta para WhatsApp
+- 🎨 Modelos de etiquetas editáveis (Canva)
+- 📊 Planilha de controle de QR Codes por imóvel
+- 🎯 Checklist de impressão profissional
+
+**Quer se destacar no mercado imobiliário? Use QR Codes de forma estratégica e veja seus resultados multiplicarem!**
+
diff --git a/Controllers/HomeController.cs b/Controllers/HomeController.cs
index b0c3f8f..191d11f 100644
--- a/Controllers/HomeController.cs
+++ b/Controllers/HomeController.cs
@@ -14,13 +14,15 @@ namespace QRRapidoApp.Controllers
private readonly IUserService _userService;
private readonly IConfiguration _config;
private readonly IStringLocalizer _localizer;
+ private readonly IMarkdownService _markdownService;
public HomeController(
- ILogger logger,
- AdDisplayService adDisplayService,
- IUserService userService,
- IConfiguration config,
- IStringLocalizer localizer
+ ILogger logger,
+ AdDisplayService adDisplayService,
+ IUserService userService,
+ IConfiguration config,
+ IStringLocalizer localizer,
+ IMarkdownService markdownService
)
{
_logger = logger;
@@ -28,6 +30,7 @@ namespace QRRapidoApp.Controllers
_userService = userService;
_config = config;
_localizer = localizer;
+ _markdownService = markdownService;
}
public async Task Index()
@@ -163,10 +166,10 @@ namespace QRRapidoApp.Controllers
var errorCode = Request.Query["code"].ToString();
var errorMessage = "";
- // Interpretar cdigos de erro especficos
+ // Interpretar c�digos de erro espec�ficos
if (errorCode.StartsWith("M.C506"))
{
- errorMessage = "Erro de autenticao. Verifique suas credenciais e tente novamente.";
+ errorMessage = "Erro de autentica��o. Verifique suas credenciais e tente novamente.";
}
ViewBag.ErrorCode = errorCode;
@@ -204,97 +207,131 @@ namespace QRRapidoApp.Controllers
// Sitemap endpoint for SEO
[Route("sitemap.xml")]
- public IActionResult Sitemap()
+ public async Task Sitemap()
{
- var sitemap = $@"
-
-
- https://qrrapido.site/
- {DateTime.UtcNow:yyyy-MM-dd}
- daily
- 1.0
-
-
- https://qrrapido.site/pt/
- {DateTime.UtcNow:yyyy-MM-dd}
- daily
- 0.9
-
-
- https://qrrapido.site/es/
- {DateTime.UtcNow:yyyy-MM-dd}
- daily
- 0.9
-
-
- https://qrrapido.site/pt-BR/About
- {DateTime.UtcNow:yyyy-MM-dd}
- monthly
- 0.8
-
-
- https://qrrapido.site/es-PY/About
- {DateTime.UtcNow:yyyy-MM-dd}
- monthly
- 0.8
-
-
- https://qrrapido.site/pt-BR/Contact
- {DateTime.UtcNow:yyyy-MM-dd}
- monthly
- 0.8
-
-
- https://qrrapido.site/es-PY/Contact
- {DateTime.UtcNow:yyyy-MM-dd}
- monthly
- 0.8
-
-
- https://qrrapido.site/pt-BR/FAQ
- {DateTime.UtcNow:yyyy-MM-dd}
- weekly
- 0.9
-
-
- https://qrrapido.site/es-PY/FAQ
- {DateTime.UtcNow:yyyy-MM-dd}
- weekly
- 0.9
-
-
- https://qrrapido.site/pt-BR/HowToUse
- {DateTime.UtcNow:yyyy-MM-dd}
- weekly
- 0.8
-
-
- https://qrrapido.site/es-PY/HowToUse
- {DateTime.UtcNow:yyyy-MM-dd}
- weekly
- 0.8
-
-
- https://qrrapido.site/Premium/Upgrade
- {DateTime.UtcNow:yyyy-MM-dd}
- weekly
- 0.8
-
-
- https://qrrapido.site/privacy
- {DateTime.UtcNow:yyyy-MM-dd}
- monthly
- 0.5
-
-
- https://qrrapido.site/terms
- {DateTime.UtcNow:yyyy-MM-dd}
- monthly
- 0.5
-
- ";
+ var baseUrl = "https://qrrapido.site";
+ var now = DateTime.UtcNow.ToString("yyyy-MM-dd");
- return Content(sitemap, "application/xml");
+ var sitemapBuilder = new System.Text.StringBuilder();
+ sitemapBuilder.AppendLine(@"");
+ sitemapBuilder.AppendLine(@"");
+
+ // Static pages
+ sitemapBuilder.AppendLine($@"
+
+ {baseUrl}/
+ {now}
+ daily
+ 1.0
+
+
+ {baseUrl}/pt/
+ {now}
+ daily
+ 0.9
+
+
+ {baseUrl}/es/
+ {now}
+ daily
+ 0.9
+
+
+ {baseUrl}/pt-BR/About
+ {now}
+ monthly
+ 0.8
+
+
+ {baseUrl}/es-PY/About
+ {now}
+ monthly
+ 0.8
+
+
+ {baseUrl}/pt-BR/Contact
+ {now}
+ monthly
+ 0.8
+
+
+ {baseUrl}/es-PY/Contact
+ {now}
+ monthly
+ 0.8
+
+
+ {baseUrl}/pt-BR/FAQ
+ {now}
+ weekly
+ 0.9
+
+
+ {baseUrl}/es-PY/FAQ
+ {now}
+ weekly
+ 0.9
+
+
+ {baseUrl}/pt-BR/HowToUse
+ {now}
+ weekly
+ 0.8
+
+
+ {baseUrl}/es-PY/HowToUse
+ {now}
+ weekly
+ 0.8
+
+
+ {baseUrl}/Pagamento/SelecaoPlano
+ {now}
+ weekly
+ 0.8
+
+
+ {baseUrl}/privacy
+ {now}
+ monthly
+ 0.5
+
+
+ {baseUrl}/terms
+ {now}
+ monthly
+ 0.5
+ ");
+
+ // Dynamic tutorial pages
+ try
+ {
+ var allArticles = await _markdownService.GetAllArticlesForSitemapAsync();
+
+ foreach (var article in allArticles)
+ {
+ var slug = article.Title.ToLower().Replace(" ", "-");
+ var lastMod = article.LastMod.ToString("yyyy-MM-dd");
+
+ sitemapBuilder.AppendLine($@"
+
+ {baseUrl}/{article.Culture}/tutoriais/{slug}
+ {lastMod}
+ weekly
+ 0.8
+ ");
+ }
+
+ _logger.LogInformation("Generated sitemap with {Count} tutorial articles", allArticles.Count);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error adding tutorials to sitemap");
+ }
+
+ sitemapBuilder.AppendLine("");
+
+ return Content(sitemapBuilder.ToString(), "application/xml");
}
}
diff --git a/Controllers/PremiumController.cs b/Controllers/PremiumController.cs
index 36e0d74..bccf3d7 100644
--- a/Controllers/PremiumController.cs
+++ b/Controllers/PremiumController.cs
@@ -110,6 +110,36 @@ namespace QRRapidoApp.Controllers
}
}
+ [HttpPost]
+ public async Task RequestRefund()
+ {
+ var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
+ if (string.IsNullOrEmpty(userId))
+ {
+ return Json(new { success = false, error = "Usuário não autenticado" });
+ }
+
+ try
+ {
+ var (success, message) = await _stripeService.CancelAndRefundSubscriptionAsync(userId);
+
+ if (success)
+ {
+ TempData["Success"] = message;
+ return Json(new { success = true, message = message });
+ }
+ else
+ {
+ return Json(new { success = false, error = message });
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error processing refund for user {userId}");
+ return Json(new { success = false, error = "Erro inesperado ao processar reembolso" });
+ }
+ }
+
[HttpGet]
public async Task BillingPortal()
{
diff --git a/Controllers/QRController.cs b/Controllers/QRController.cs
index 156d2fb..3dd9c77 100644
--- a/Controllers/QRController.cs
+++ b/Controllers/QRController.cs
@@ -16,17 +16,17 @@ namespace QRRapidoApp.Controllers
private readonly IUserService _userService;
private readonly AdDisplayService _adService;
private readonly ILogger _logger;
- private readonly IStringLocalizer _localizer;
- private readonly AdDisplayService _adDisplayService;
-
+ private readonly IStringLocalizer _localizer;
+ private readonly AdDisplayService _adDisplayService;
+
public QRController(IQRCodeService qrService, IUserService userService, AdDisplayService adService, ILogger logger, IStringLocalizer localizer, AdDisplayService adDisplayService)
{
_qrService = qrService;
_userService = userService;
_adService = adService;
_logger = logger;
- _localizer = localizer;
- _adDisplayService = adDisplayService;
+ _localizer = localizer;
+ _adDisplayService = adDisplayService;
}
[HttpPost("GenerateRapid")]
@@ -36,7 +36,7 @@ namespace QRRapidoApp.Controllers
var requestId = Guid.NewGuid().ToString("N")[..8];
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var isAuthenticated = User?.Identity?.IsAuthenticated ?? false;
-
+
using (_logger.BeginScope(new Dictionary
{
["RequestId"] = requestId,
@@ -90,7 +90,7 @@ namespace QRRapidoApp.Controllers
return StatusCode(429, new
{
error = _localizer["RateLimitReached"],
- upgradeUrl = "/Premium/Upgrade",
+ upgradeUrl = "/Pagamento/SelecaoPlano",
success = false
});
}
@@ -406,7 +406,7 @@ namespace QRRapidoApp.Controllers
return StatusCode(429, new
{
error = _localizer["RateLimitReached"],
- upgradeUrl = "/Premium/Upgrade",
+ upgradeUrl = "/Pagamento/SelecaoPlano",
success = false
});
}
@@ -468,7 +468,7 @@ namespace QRRapidoApp.Controllers
public async Task GetHistory(int limit = 20)
{
try
- {
+ {
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userId))
{
@@ -489,7 +489,7 @@ namespace QRRapidoApp.Controllers
public async Task GetUserStats()
{
try
- {
+ {
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userId))
{
diff --git a/Controllers/TutoriaisController.cs b/Controllers/TutoriaisController.cs
new file mode 100644
index 0000000..16b63b9
--- /dev/null
+++ b/Controllers/TutoriaisController.cs
@@ -0,0 +1,111 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Localization;
+using QRRapidoApp.Services;
+using System.Security.Claims;
+
+namespace QRRapidoApp.Controllers
+{
+ public class TutoriaisController : Controller
+ {
+ private readonly IMarkdownService _markdownService;
+ private readonly AdDisplayService _adDisplayService;
+ private readonly ILogger _logger;
+ private readonly IStringLocalizer _localizer;
+ private readonly IConfiguration _config;
+
+ public TutoriaisController(
+ IMarkdownService markdownService,
+ AdDisplayService adDisplayService,
+ ILogger logger,
+ IStringLocalizer localizer,
+ IConfiguration config)
+ {
+ _markdownService = markdownService;
+ _adDisplayService = adDisplayService;
+ _logger = logger;
+ _localizer = localizer;
+ _config = config;
+ }
+
+ [Route("{culture:regex(^(pt-BR|es-PY)$)}/tutoriais/{slug}")]
+ public async Task Article(string slug, string culture)
+ {
+ try
+ {
+ var article = await _markdownService.GetArticleAsync(slug, culture);
+
+ if (article == null)
+ {
+ _logger.LogWarning("Article not found: {Slug} ({Culture})", slug, culture);
+ return NotFound();
+ }
+
+ // Set ViewBag for ads and user info
+ var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
+ ViewBag.ShowAds = await _adDisplayService.ShouldShowAds(userId);
+ ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId ?? "");
+ ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false;
+ ViewBag.UserName = User.Identity?.Name ?? "";
+ _adDisplayService.SetViewBagAds(ViewBag);
+
+ // Set SEO metadata from article
+ ViewBag.Title = article.Metadata.Title;
+ ViewBag.Description = article.Metadata.Description;
+ ViewBag.Keywords = article.Metadata.Keywords;
+ ViewBag.OgImage = article.Metadata.Image;
+ ViewBag.OgType = "article";
+ ViewBag.ArticleAuthor = article.Metadata.Author;
+ ViewBag.ArticlePublishedTime = article.Metadata.Date.ToString("yyyy-MM-ddTHH:mm:ssZ");
+ ViewBag.ArticleModifiedTime = article.Metadata.LastMod.ToString("yyyy-MM-ddTHH:mm:ssZ");
+ ViewBag.Culture = culture;
+ ViewBag.Slug = slug;
+
+ // Get related articles (same culture, exclude current)
+ var allArticles = await _markdownService.GetAllArticlesAsync(culture);
+ article.RelatedArticles = allArticles
+ .Where(a => a.Title != article.Metadata.Title)
+ .Take(3)
+ .ToList();
+
+ _logger.LogInformation("Serving article: {Slug} ({Culture})", slug, culture);
+ return View(article);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error serving article: {Slug} ({Culture})", slug, culture);
+ return StatusCode(500, "Internal server error");
+ }
+ }
+
+ [Route("{culture:regex(^(pt-BR|es-PY)$)}/tutoriais")]
+ public async Task Index(string culture)
+ {
+ try
+ {
+ var articles = await _markdownService.GetAllArticlesAsync(culture);
+
+ // Set ViewBag
+ var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
+ ViewBag.ShowAds = await _adDisplayService.ShouldShowAds(userId);
+ ViewBag.IsPremium = await _adDisplayService.HasValidPremiumSubscription(userId ?? "");
+ ViewBag.IsAuthenticated = User.Identity?.IsAuthenticated ?? false;
+ ViewBag.UserName = User.Identity?.Name ?? "";
+ _adDisplayService.SetViewBagAds(ViewBag);
+
+ ViewBag.Title = culture == "pt-BR" ? "Tutoriais QR Code" : "Tutoriales Código QR";
+ ViewBag.Description = culture == "pt-BR"
+ ? "Aprenda a criar e usar QR Codes com nossos tutoriais completos"
+ : "Aprende a crear y usar códigos QR con nuestros tutoriales completos";
+ ViewBag.Culture = culture;
+
+ _logger.LogInformation("Serving tutorials index ({Culture}): {Count} articles", culture, articles.Count);
+ return View(articles);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error serving tutorials index ({Culture})", culture);
+ return StatusCode(500, "Internal server error");
+ }
+ }
+ }
+}
diff --git a/Models/ArticleMetadata.cs b/Models/ArticleMetadata.cs
new file mode 100644
index 0000000..8100068
--- /dev/null
+++ b/Models/ArticleMetadata.cs
@@ -0,0 +1,15 @@
+namespace QRRapidoApp.Models
+{
+ public class ArticleMetadata
+ {
+ public string Title { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+ public string Keywords { get; set; } = string.Empty;
+ public string Author { get; set; } = "QR Rapido";
+ public DateTime Date { get; set; }
+ public DateTime LastMod { get; set; }
+ public string Image { get; set; } = string.Empty;
+ public string Culture { get; set; } = "pt-BR";
+ public int ReadingTimeMinutes { get; set; }
+ }
+}
diff --git a/Models/User.cs b/Models/User.cs
index 58f8e24..ee3d28d 100644
--- a/Models/User.cs
+++ b/Models/User.cs
@@ -45,6 +45,9 @@ namespace QRRapidoApp.Models
[BsonElement("stripeSubscriptionId")]
public string? StripeSubscriptionId { get; set; }
+ [BsonElement("subscriptionStartedAt")]
+ public DateTime? SubscriptionStartedAt { get; set; } // Data de início da assinatura atual
+
[BsonElement("preferredLanguage")]
public string PreferredLanguage { get; set; } = "pt-BR";
diff --git a/Models/ViewModels/ArticleViewModel.cs b/Models/ViewModels/ArticleViewModel.cs
new file mode 100644
index 0000000..291c3cc
--- /dev/null
+++ b/Models/ViewModels/ArticleViewModel.cs
@@ -0,0 +1,11 @@
+namespace QRRapidoApp.Models.ViewModels
+{
+ public class ArticleViewModel
+ {
+ public ArticleMetadata Metadata { get; set; } = new();
+ public string HtmlContent { get; set; } = string.Empty;
+ public string Slug { get; set; } = string.Empty;
+ public DateTime LastModified { get; set; }
+ public List RelatedArticles { get; set; } = new();
+ }
+}
diff --git a/Program.cs b/Program.cs
index ccb83dd..0209cf7 100644
--- a/Program.cs
+++ b/Program.cs
@@ -235,6 +235,7 @@ builder.Services.Configure(options =>
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json
index ac7db78..1bc9734 100644
--- a/Properties/launchSettings.json
+++ b/Properties/launchSettings.json
@@ -6,7 +6,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
- "applicationUrl": "https://localhost:52428;http://192.168.0.85:52429;http://localhost:52429"
+ "applicationUrl": "https://localhost:52428;http://localhost:52429"
}
}
}
\ No newline at end of file
diff --git a/QRRapidoApp.csproj b/QRRapidoApp.csproj
index 5a9848c..fb922af 100644
--- a/QRRapidoApp.csproj
+++ b/QRRapidoApp.csproj
@@ -31,6 +31,8 @@
+
+
diff --git a/Resources/SharedResource.es-PY.resx b/Resources/SharedResource.es-PY.resx
index 02f84a5..9454fde 100644
--- a/Resources/SharedResource.es-PY.resx
+++ b/Resources/SharedResource.es-PY.resx
@@ -924,9 +924,12 @@
7. Pagos y Suscripciones
- • Suscripciones Premium son procesadas vía Stripe
-• Pagos son recurrentes hasta cancelación
-• Reembolsos siguen nuestra política de 7 días
+ • Suscripciones Premium son procesadas de forma segura vía Stripe
+• Pagos son recurrentes mensualmente hasta cancelación
+• Puede cancelar su suscripción en cualquier momento desde su perfil, sin multas ni cargos adicionales
+• Tiene derecho de retracto de 7 días para solicitar reembolso total (Ley de Defensa del Consumidor)
+• Después de la cancelación, mantendrá acceso Premium hasta el final del período ya pagado
+• No hay reembolso proporcional para cancelaciones después del período de 7 días
• Precios pueden ser alterados mediante aviso previo de 30 días
diff --git a/Resources/SharedResource.es.resx b/Resources/SharedResource.es.resx
index c31e091..064c8ad 100644
--- a/Resources/SharedResource.es.resx
+++ b/Resources/SharedResource.es.resx
@@ -933,9 +933,12 @@
7. Pagos y Suscripciones
- • Suscripciones Premium son procesadas vía Stripe
-• Pagos son recurrentes hasta cancelación
-• Reembolsos siguen nuestra política de 7 días
+ • Suscripciones Premium son procesadas de forma segura vía Stripe
+• Pagos son recurrentes mensualmente hasta cancelación
+• Puede cancelar su suscripción en cualquier momento desde su perfil, sin multas ni cargos adicionales
+• Tiene derecho de retracto de 7 días para solicitar reembolso total (Ley de Defensa del Consumidor)
+• Después de la cancelación, mantendrá acceso Premium hasta el final del período ya pagado
+• No hay reembolso proporcional para cancelaciones después del período de 7 días
• Precios pueden ser alterados mediante aviso previo de 30 días
diff --git a/Resources/SharedResource.pt-BR.resx b/Resources/SharedResource.pt-BR.resx
index de3e4fe..92c5676 100644
--- a/Resources/SharedResource.pt-BR.resx
+++ b/Resources/SharedResource.pt-BR.resx
@@ -1014,9 +1014,12 @@
7. Pagamentos e Assinaturas
- • Assinaturas Premium são processadas via Stripe
-• Pagamentos são recorrentes até cancelamento
-• Reembolsos seguem nossa política de 7 dias
+ • Assinaturas Premium são processadas via Stripe de forma segura
+• Pagamentos são recorrentes mensalmente até cancelamento
+• Você pode cancelar sua assinatura a qualquer momento através do seu perfil, sem multas ou taxas adicionais
+• Conforme o Código de Defesa do Consumidor (CDC), você tem direito de arrependimento de 7 dias para solicitar reembolso total
+• Após o cancelamento, você manterá acesso Premium até o final do período já pago
+• Não há reembolso proporcional para cancelamentos após o período de 7 dias
• Preços podem ser alterados mediante aviso prévio de 30 dias
diff --git a/Scripts/README-MONGODB-SEED.md b/Scripts/README-MONGODB-SEED.md
new file mode 100644
index 0000000..42cb2e6
--- /dev/null
+++ b/Scripts/README-MONGODB-SEED.md
@@ -0,0 +1,143 @@
+# MongoDB Plans Seed Script
+
+## ⚠️ IMPORTANTE - LEIA ANTES DE EXECUTAR
+
+Este script cria **2 planos** no MongoDB:
+1. **Premium Mensal** - Cobrança recorrente mensal
+2. **Premium Anual** - Cobrança anual com 20% de desconto
+
+**VOCÊ PRECISA ATUALIZAR OS STRIPE PRICE IDs** antes de executar em produção!
+
+## 📋 Pré-requisitos
+
+### 1. Criar Produto no Stripe
+
+1. Acesse [Stripe Dashboard](https://dashboard.stripe.com/products)
+2. Crie um produto "QR Rapido Premium"
+3. Anote o Product ID (ex: `prod_SnfQTxwE3i8r5L`)
+
+### 2. Criar Price IDs no Stripe
+
+Para **CADA PAÍS** (BR, PY, US), crie **2 preços** (mensal e anual):
+
+#### Brasil (BRL):
+- **Mensal**: R$ 9,90/mês
+ - No Stripe: "Premium Mensal - Brasil"
+ - Copie o Price ID: `price_xxxxx_monthly_br`
+- **Anual**: R$ 95,04/ano (economia de 20%)
+ - No Stripe: "Premium Anual - Brasil"
+ - Copie o Price ID: `price_xxxxx_yearly_br`
+
+#### Paraguai (PYG):
+- **Mensal**: 35.000 Gs/mês
+ - No Stripe: "Premium Mensal - Paraguay"
+ - Copie o Price ID: `price_xxxxx_monthly_py`
+- **Anual**: 336.000 Gs/ano (economia de 20%)
+ - No Stripe: "Premium Anual - Paraguay"
+ - Copie o Price ID: `price_xxxxx_yearly_py`
+
+#### EUA/Internacional (USD):
+- **Mensal**: $1,99/mês
+ - No Stripe: "Premium Monthly - USA"
+ - Copie o Price ID: `price_xxxxx_monthly_us`
+- **Anual**: $19,10/ano (economia de 20%)
+ - No Stripe: "Premium Yearly - USA"
+ - Copie o Price ID: `price_xxxxx_yearly_us`
+
+### 3. Atualizar appsettings.json
+
+**Edite `appsettings.json`** (para desenvolvimento/teste):
+```json
+"Stripe": {
+ "Plans": {
+ "Monthly": {
+ "BR": "price_xxxxx_monthly_br", // ← Cole seu Price ID aqui
+ "PY": "price_xxxxx_monthly_py", // ← Cole seu Price ID aqui
+ "US": "price_xxxxx_monthly_us" // ← Cole seu Price ID aqui
+ },
+ "Yearly": {
+ "BR": "price_xxxxx_yearly_br", // ← Cole seu Price ID aqui
+ "PY": "price_xxxxx_yearly_py", // ← Cole seu Price ID aqui
+ "US": "price_xxxxx_yearly_us" // ← Cole seu Price ID aqui
+ }
+ }
+}
+```
+
+**Edite `appsettings.Production.json`** (para produção) com os Price IDs **de produção** (mode live)
+
+## 🚀 Como Executar
+
+### Ambiente Local (Development)
+
+```bash
+# Se MongoDB estiver rodando localmente
+mongosh "mongodb://localhost:27017/QrRapido" Scripts/seed-mongodb-plans.js
+```
+
+### Ambiente de Produção
+
+```bash
+# Conectar ao MongoDB de produção e executar o script
+mongosh "mongodb://admin:c4rn31r0@129.146.116.218:27017,141.148.162.114:27017/QrRapido?replicaSet=rs0&authSource=admin" Scripts/seed-mongodb-plans.js
+```
+
+**OU via SSH no servidor:**
+
+```bash
+# Conectar ao servidor
+ssh ubuntu@141.148.162.114
+
+# Copiar o script para o servidor
+# (você pode usar scp ou copiar o conteúdo manualmente)
+
+# Executar o script
+mongosh "mongodb://admin:c4rn31r0@localhost:27017/QrRapido?authSource=admin" seed-mongodb-plans.js
+```
+
+## ✅ Verificar se Funcionou
+
+```bash
+# Conectar ao MongoDB
+mongosh "mongodb://admin:c4rn31r0@129.146.116.218:27017,141.148.162.114:27017/QrRapido?replicaSet=rs0&authSource=admin"
+
+# Verificar os planos inseridos
+use QrRapido
+db.Plans.find().pretty()
+```
+
+Você deve ver um documento com:
+- `name`: Nomes em pt-BR, es-PY, en
+- `stripePriceId`: O Price ID padrão do Stripe
+- `pricesByCountry`: Preços por país (BR, PY, US)
+- `isActive`: true
+
+## 🔧 Troubleshooting
+
+### Erro: "Authentication failed"
+- Verifique se a senha do MongoDB está correta no connection string
+- Verifique se o usuário `admin` tem permissões no banco `QrRapido`
+
+### Erro: "MongoServerError: E11000 duplicate key error"
+- Já existe um plano com o mesmo ID
+- Execute `db.Plans.deleteMany({})` primeiro para limpar (CUIDADO em produção!)
+
+### Stripe retorna erro "No such price"
+- Você não atualizou os `stripePriceId` no script
+- Os Price IDs no script não existem no seu Stripe Dashboard
+- Verifique se está usando as chaves corretas (test vs live mode)
+
+## 📝 Próximos Passos
+
+Após executar este script:
+
+1. ✅ Verificar no MongoDB que o plano foi criado: `db.Plans.find()`
+2. ✅ Testar o fluxo de assinatura em `/Pagamento/SelecaoPlano`
+3. ✅ Confirmar que o Stripe recebe o checkout com o Price ID correto
+4. ✅ Testar o webhook após pagamento bem-sucedido
+
+## 🔗 Links Úteis
+
+- [Stripe Dashboard - Products](https://dashboard.stripe.com/products)
+- [Stripe Dashboard - Prices](https://dashboard.stripe.com/prices)
+- [Stripe Webhooks](https://dashboard.stripe.com/webhooks)
diff --git a/Scripts/seed-mongodb-plans.js b/Scripts/seed-mongodb-plans.js
new file mode 100644
index 0000000..6f51bbf
--- /dev/null
+++ b/Scripts/seed-mongodb-plans.js
@@ -0,0 +1,163 @@
+// MongoDB Seed Script for Plans Collection
+// Run this script with: mongosh "mongodb://admin:c4rn31r0@129.146.116.218:27017,141.148.162.114:27017/QrRapido?replicaSet=rs0&authSource=admin" seed-mongodb-plans.js
+
+// Ensure we're using the correct database
+db = db.getSiblingDB('QrRapido');
+
+// Clear existing plans (optional - comment out if you want to keep existing plans)
+// db.Plans.deleteMany({});
+
+print("\n📦 Seeding Plans Collection...\n");
+
+// 1. PLANO MENSAL
+db.Plans.insertOne({
+ name: {
+ "pt-BR": "Premium Mensal",
+ "es-PY": "Premium Mensual",
+ "en": "Premium Monthly"
+ },
+ description: {
+ "pt-BR": "Acesso ilimitado a todos os recursos premium - Cobrança mensal",
+ "es-PY": "Acceso ilimitado a todas las funciones premium - Facturación mensual",
+ "en": "Unlimited access to all premium features - Monthly billing"
+ },
+ features: {
+ "pt-BR": [
+ "QR Codes ilimitados",
+ "Sem anúncios",
+ "QR Codes dinâmicos (editáveis)",
+ "Estatísticas avançadas",
+ "Suporte prioritário",
+ "Acesso à API",
+ "Cancele a qualquer momento"
+ ],
+ "es-PY": [
+ "Códigos QR ilimitados",
+ "Sin anuncios",
+ "Códigos QR dinámicos (editables)",
+ "Estadísticas avanzadas",
+ "Soporte prioritario",
+ "Acceso a la API",
+ "Cancela cuando quieras"
+ ],
+ "en": [
+ "Unlimited QR Codes",
+ "No ads",
+ "Dynamic QR Codes (editable)",
+ "Advanced analytics",
+ "Priority support",
+ "API access",
+ "Cancel anytime"
+ ]
+ },
+ interval: "month",
+ stripePriceId: "price_XXXXX_monthly_us", // Default price (USA) - UPDATE from appsettings.json > Stripe:Plans:Monthly:US
+ pricesByCountry: {
+ "BR": {
+ amount: 9.90,
+ currency: "BRL",
+ stripePriceId: "price_XXXXX_monthly_br" // UPDATE from appsettings.json > Stripe:Plans:Monthly:BR
+ },
+ "PY": {
+ amount: 35000,
+ currency: "PYG",
+ stripePriceId: "price_XXXXX_monthly_py" // UPDATE from appsettings.json > Stripe:Plans:Monthly:PY
+ },
+ "US": {
+ amount: 1.99,
+ currency: "USD",
+ stripePriceId: "price_XXXXX_monthly_us" // UPDATE from appsettings.json > Stripe:Plans:Monthly:US
+ }
+ },
+ isActive: true,
+ displayOrder: 1
+});
+
+print("✅ Plano Mensal inserido");
+
+// 2. PLANO ANUAL (com desconto)
+db.Plans.insertOne({
+ name: {
+ "pt-BR": "Premium Anual",
+ "es-PY": "Premium Anual",
+ "en": "Premium Yearly"
+ },
+ description: {
+ "pt-BR": "Acesso ilimitado a todos os recursos premium - Economia de 20% no plano anual!",
+ "es-PY": "Acceso ilimitado a todas las funciones premium - ¡Ahorra 20% con el plan anual!",
+ "en": "Unlimited access to all premium features - Save 20% with yearly billing!"
+ },
+ features: {
+ "pt-BR": [
+ "QR Codes ilimitados",
+ "Sem anúncios",
+ "QR Codes dinâmicos (editáveis)",
+ "Estatísticas avançadas",
+ "Suporte prioritário",
+ "Acesso à API",
+ "💰 Economia de 20%",
+ "Cobrança anual única"
+ ],
+ "es-PY": [
+ "Códigos QR ilimitados",
+ "Sin anuncios",
+ "Códigos QR dinámicos (editables)",
+ "Estadísticas avanzadas",
+ "Soporte prioritario",
+ "Acceso a la API",
+ "💰 Ahorro del 20%",
+ "Facturación anual única"
+ ],
+ "en": [
+ "Unlimited QR Codes",
+ "No ads",
+ "Dynamic QR Codes (editable)",
+ "Advanced analytics",
+ "Priority support",
+ "API access",
+ "💰 Save 20%",
+ "Billed annually"
+ ]
+ },
+ interval: "year",
+ stripePriceId: "price_XXXXX_yearly_us", // Default price (USA) - UPDATE from appsettings.json > Stripe:Plans:Yearly:US
+ pricesByCountry: {
+ "BR": {
+ amount: 95.04, // 9.90 * 12 * 0.80 = Economia de 20%
+ currency: "BRL",
+ stripePriceId: "price_XXXXX_yearly_br" // UPDATE from appsettings.json > Stripe:Plans:Yearly:BR
+ },
+ "PY": {
+ amount: 336000, // 35000 * 12 * 0.80 = Economia de 20%
+ currency: "PYG",
+ stripePriceId: "price_XXXXX_yearly_py" // UPDATE from appsettings.json > Stripe:Plans:Yearly:PY
+ },
+ "US": {
+ amount: 19.10, // 1.99 * 12 * 0.80 = Economia de 20%
+ currency: "USD",
+ stripePriceId: "price_XXXXX_yearly_us" // UPDATE from appsettings.json > Stripe:Plans:Yearly:US
+ }
+ },
+ isActive: true,
+ displayOrder: 2,
+ badge: {
+ "pt-BR": "MELHOR VALOR",
+ "es-PY": "MEJOR VALOR",
+ "en": "BEST VALUE"
+ }
+});
+
+print("✅ Plano Anual inserido");
+
+print("\n✅ Plans collection seeded successfully!");
+print("\n⚠️ PRÓXIMOS PASSOS:");
+print("1. Acesse o Stripe Dashboard: https://dashboard.stripe.com/prices");
+print("2. Crie os Price IDs para cada país e plano (mensal e anual)");
+print("3. Copie os Price IDs (começam com 'price_')");
+print("4. Atualize os valores no appsettings.json > Stripe:Plans");
+print("5. Execute este script novamente após atualizar os Price IDs acima");
+print("\n📊 Verificar planos inseridos:");
+print(" db.Plans.find().pretty()");
+print("\n🗑️ Para limpar e começar de novo:");
+print(" db.Plans.deleteMany({})");
+print("");
diff --git a/Services/IMarkdownService.cs b/Services/IMarkdownService.cs
new file mode 100644
index 0000000..f82caa3
--- /dev/null
+++ b/Services/IMarkdownService.cs
@@ -0,0 +1,12 @@
+using QRRapidoApp.Models;
+using QRRapidoApp.Models.ViewModels;
+
+namespace QRRapidoApp.Services
+{
+ public interface IMarkdownService
+ {
+ Task GetArticleAsync(string slug, string culture);
+ Task> GetAllArticlesAsync(string culture);
+ Task> GetAllArticlesForSitemapAsync();
+ }
+}
diff --git a/Services/MarkdownService.cs b/Services/MarkdownService.cs
new file mode 100644
index 0000000..0cb3b44
--- /dev/null
+++ b/Services/MarkdownService.cs
@@ -0,0 +1,265 @@
+using Markdig;
+using Microsoft.Extensions.Caching.Memory;
+using QRRapidoApp.Models;
+using QRRapidoApp.Models.ViewModels;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
+
+namespace QRRapidoApp.Services
+{
+ public class MarkdownService : IMarkdownService
+ {
+ private readonly IMemoryCache _cache;
+ private readonly IWebHostEnvironment _env;
+ private readonly ILogger _logger;
+ private readonly MarkdownPipeline _pipeline;
+ private readonly IDeserializer _yamlDeserializer;
+ private const string CACHE_KEY_PREFIX = "Tutorial_";
+ private const string CACHE_KEY_ALL = "AllTutorials_";
+ private const int CACHE_DURATION_HOURS = 1;
+
+ public MarkdownService(IMemoryCache cache, IWebHostEnvironment env, ILogger logger)
+ {
+ _cache = cache;
+ _env = env;
+ _logger = logger;
+
+ // Configure Markdig pipeline for advanced markdown features
+ _pipeline = new MarkdownPipelineBuilder()
+ .UseAdvancedExtensions()
+ .UseAutoLinks()
+ .UseEmojiAndSmiley()
+ .Build();
+
+ // Configure YAML deserializer
+ _yamlDeserializer = new DeserializerBuilder()
+ .WithNamingConvention(CamelCaseNamingConvention.Instance)
+ .IgnoreUnmatchedProperties()
+ .Build();
+ }
+
+ public async Task GetArticleAsync(string slug, string culture)
+ {
+ var cacheKey = $"{CACHE_KEY_PREFIX}{culture}_{slug}";
+
+ // Try get from cache
+ if (_cache.TryGetValue(cacheKey, out ArticleViewModel? cachedArticle))
+ {
+ _logger.LogInformation("Article served from cache: {Slug} ({Culture})", slug, culture);
+ return cachedArticle;
+ }
+
+ try
+ {
+ var contentPath = Path.Combine(_env.ContentRootPath, "Content", "Tutoriais");
+ var fileName = $"{slug}.{culture}.md";
+ var filePath = Path.Combine(contentPath, fileName);
+
+ if (!File.Exists(filePath))
+ {
+ _logger.LogWarning("Article file not found: {FilePath}", filePath);
+ return null;
+ }
+
+ var fileContent = await File.ReadAllTextAsync(filePath);
+ var article = ParseMarkdownWithFrontmatter(fileContent, slug);
+
+ if (article == null)
+ {
+ _logger.LogError("Failed to parse article: {Slug} ({Culture})", slug, culture);
+ return null;
+ }
+
+ // Set file metadata
+ var fileInfo = new FileInfo(filePath);
+ article.LastModified = fileInfo.LastWriteTimeUtc;
+ article.Metadata.Culture = culture;
+
+ // Cache the article
+ var cacheOptions = new MemoryCacheEntryOptions()
+ .SetAbsoluteExpiration(TimeSpan.FromHours(CACHE_DURATION_HOURS));
+
+ _cache.Set(cacheKey, article, cacheOptions);
+
+ _logger.LogInformation("Article loaded and cached: {Slug} ({Culture})", slug, culture);
+ return article;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error loading article: {Slug} ({Culture})", slug, culture);
+ return null;
+ }
+ }
+
+ public async Task> GetAllArticlesAsync(string culture)
+ {
+ var cacheKey = $"{CACHE_KEY_ALL}{culture}";
+
+ // Try get from cache
+ if (_cache.TryGetValue(cacheKey, out List? cachedList))
+ {
+ _logger.LogInformation("Articles list served from cache ({Culture})", culture);
+ return cachedList ?? new List();
+ }
+
+ try
+ {
+ var articles = new List();
+ var contentPath = Path.Combine(_env.ContentRootPath, "Content", "Tutoriais");
+
+ if (!Directory.Exists(contentPath))
+ {
+ _logger.LogWarning("Tutoriais directory not found: {Path}", contentPath);
+ return articles;
+ }
+
+ var pattern = $"*.{culture}.md";
+ var files = Directory.GetFiles(contentPath, pattern);
+
+ foreach (var file in files)
+ {
+ try
+ {
+ var fileContent = await File.ReadAllTextAsync(file);
+ var slug = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file));
+ var article = ParseMarkdownWithFrontmatter(fileContent, slug);
+
+ if (article?.Metadata != null)
+ {
+ article.Metadata.Culture = culture;
+ articles.Add(article.Metadata);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error parsing article file: {File}", file);
+ }
+ }
+
+ // Sort by date (newest first)
+ articles = articles.OrderByDescending(a => a.Date).ToList();
+
+ // Cache the list
+ var cacheOptions = new MemoryCacheEntryOptions()
+ .SetAbsoluteExpiration(TimeSpan.FromHours(CACHE_DURATION_HOURS));
+
+ _cache.Set(cacheKey, articles, cacheOptions);
+
+ _logger.LogInformation("Loaded {Count} articles for culture {Culture}", articles.Count, culture);
+ return articles;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error loading articles list for culture {Culture}", culture);
+ return new List();
+ }
+ }
+
+ public async Task> GetAllArticlesForSitemapAsync()
+ {
+ try
+ {
+ var allArticles = new List();
+ var contentPath = Path.Combine(_env.ContentRootPath, "Content", "Tutoriais");
+
+ if (!Directory.Exists(contentPath))
+ {
+ _logger.LogWarning("Tutoriais directory not found for sitemap: {Path}", contentPath);
+ return allArticles;
+ }
+
+ var files = Directory.GetFiles(contentPath, "*.md");
+
+ foreach (var file in files)
+ {
+ try
+ {
+ var fileContent = await File.ReadAllTextAsync(file);
+ var fileName = Path.GetFileName(file);
+
+ // Extract slug and culture from filename (e.g., "como-criar-qr.pt-BR.md")
+ var parts = fileName.Replace(".md", "").Split('.');
+ if (parts.Length < 2) continue;
+
+ var slug = parts[0];
+ var culture = parts[1];
+
+ var article = ParseMarkdownWithFrontmatter(fileContent, slug);
+
+ if (article?.Metadata != null)
+ {
+ article.Metadata.Culture = culture;
+ var fileInfo = new FileInfo(file);
+ article.Metadata.LastMod = fileInfo.LastWriteTimeUtc;
+ allArticles.Add(article.Metadata);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error parsing article for sitemap: {File}", file);
+ }
+ }
+
+ _logger.LogInformation("Loaded {Count} total articles for sitemap", allArticles.Count);
+ return allArticles;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error loading articles for sitemap");
+ return new List();
+ }
+ }
+
+ private ArticleViewModel? ParseMarkdownWithFrontmatter(string content, string slug)
+ {
+ try
+ {
+ // Extract frontmatter
+ if (!content.StartsWith("---"))
+ {
+ _logger.LogWarning("Article missing frontmatter: {Slug}", slug);
+ return null;
+ }
+
+ var endOfFrontmatter = content.IndexOf("---", 3);
+ if (endOfFrontmatter == -1)
+ {
+ _logger.LogWarning("Article frontmatter not closed: {Slug}", slug);
+ return null;
+ }
+
+ var frontmatterYaml = content.Substring(3, endOfFrontmatter - 3).Trim();
+ var markdownContent = content.Substring(endOfFrontmatter + 3).Trim();
+
+ // Parse YAML frontmatter
+ var metadata = _yamlDeserializer.Deserialize(frontmatterYaml);
+
+ if (metadata == null)
+ {
+ _logger.LogWarning("Failed to deserialize frontmatter: {Slug}", slug);
+ return null;
+ }
+
+ // Calculate reading time (average 200 words per minute)
+ var wordCount = markdownContent.Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Length;
+ metadata.ReadingTimeMinutes = Math.Max(1, (int)Math.Ceiling(wordCount / 200.0));
+
+ // Convert Markdown to HTML
+ var htmlContent = Markdown.ToHtml(markdownContent, _pipeline);
+
+ return new ArticleViewModel
+ {
+ Metadata = metadata,
+ HtmlContent = htmlContent,
+ Slug = slug,
+ LastModified = metadata.LastMod
+ };
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error parsing markdown for {Slug}", slug);
+ return null;
+ }
+ }
+ }
+}
diff --git a/Services/StripeService.cs b/Services/StripeService.cs
index 478c211..1f4dbb2 100644
--- a/Services/StripeService.cs
+++ b/Services/StripeService.cs
@@ -89,20 +89,20 @@ namespace QRRapidoApp.Services
break;
case "invoice.finalized":
- var invoice = stripeEvent.Data.Object as Invoice;
- var subscriptionLineItem = invoice.Lines?.Data
- .FirstOrDefault(line =>
- !string.IsNullOrEmpty(line.SubscriptionId) ||
- line.Subscription != null
- );
-
- string subscriptionId = null;
-
- if (subscriptionLineItem != null)
- {
- // Tenta obter o ID da assinatura de duas formas diferentes
- subscriptionId = subscriptionLineItem.SubscriptionId
- ?? subscriptionLineItem.Subscription?.Id;
+ var invoice = stripeEvent.Data.Object as Invoice;
+ var subscriptionLineItem = invoice.Lines?.Data
+ .FirstOrDefault(line =>
+ !string.IsNullOrEmpty(line.SubscriptionId) ||
+ line.Subscription != null
+ );
+
+ string subscriptionId = null;
+
+ if (subscriptionLineItem != null)
+ {
+ // Tenta obter o ID da assinatura de duas formas diferentes
+ subscriptionId = subscriptionLineItem.SubscriptionId
+ ?? subscriptionLineItem.Subscription?.Id;
}
if (subscriptionId != null)
@@ -153,8 +153,8 @@ namespace QRRapidoApp.Services
await _userService.UpdateUserStripeCustomerIdAsync(user.Id, subscription.CustomerId);
}
- await _userService.ActivatePremiumStatus(userId, subscription.Id, subItem.CurrentPeriodEnd);
-
+ await _userService.ActivatePremiumStatus(userId, subscription.Id, subItem.CurrentPeriodEnd);
+
_logger.LogInformation($"Successfully processed premium activation/renewal for user {userId}.");
}
@@ -189,5 +189,151 @@ namespace QRRapidoApp.Services
return false;
}
}
+
+ ///
+ /// Verifica se a assinatura está dentro do período de 7 dias para reembolso (CDC)
+ ///
+ public bool IsEligibleForRefund(DateTime? subscriptionStartedAt)
+ {
+ if (!subscriptionStartedAt.HasValue)
+ {
+ return false;
+ }
+
+ var daysSinceSubscription = (DateTime.UtcNow - subscriptionStartedAt.Value).TotalDays;
+ return daysSinceSubscription <= 7;
+ }
+
+ ///
+ /// Cancela assinatura E processa reembolso total (CDC - 7 dias)
+ ///
+ public async Task<(bool success, string message)> CancelAndRefundSubscriptionAsync(string userId)
+ {
+ try
+ {
+ var user = await _userService.GetUserAsync(userId);
+ if (user == null)
+ {
+ return (false, "Usuário não encontrado");
+ }
+
+ if (string.IsNullOrEmpty(user.StripeSubscriptionId))
+ {
+ return (false, "Nenhuma assinatura ativa encontrada");
+ }
+
+ // Verifica elegibilidade para reembolso
+ if (!IsEligibleForRefund(user.SubscriptionStartedAt))
+ {
+ var daysSince = user.SubscriptionStartedAt.HasValue
+ ? (DateTime.UtcNow - user.SubscriptionStartedAt.Value).TotalDays
+ : 0;
+ return (false, $"Período de reembolso de 7 dias expirado (assinatura criada há {Math.Round(daysSince, 1)} dias). Você ainda pode cancelar a renovação.");
+ }
+
+ // Busca a assinatura no Stripe
+ var subscriptionService = new SubscriptionService();
+ var subscription = await subscriptionService.GetAsync(user.StripeSubscriptionId);
+
+ if (subscription == null)
+ {
+ return (false, "Assinatura não encontrada no Stripe");
+ }
+
+ // Cancela a assinatura primeiro
+ await subscriptionService.CancelAsync(subscription.Id, new SubscriptionCancelOptions());
+
+ // Busca o último pagamento (invoice) desta assinatura para reembolsar
+ var invoiceService = new InvoiceService();
+ var invoiceListOptions = new InvoiceListOptions
+ {
+ Subscription = subscription.Id,
+ Limit = 1,
+ Status = "paid"
+ };
+ var invoices = await invoiceService.ListAsync(invoiceListOptions);
+ var latestInvoice = invoices.Data.FirstOrDefault();
+
+ if (latestInvoice == null || latestInvoice.AmountPaid <= 0)
+ {
+ // Mesmo sem invoice, cancela e desativa
+ await _userService.DeactivatePremiumStatus(subscription.Id);
+ return (true, "Assinatura cancelada com sucesso. Nenhum pagamento para reembolsar foi encontrado.");
+ }
+
+ // Processa o reembolso - Stripe reembolsa automaticamente o último pagamento
+ var refundService = new RefundService();
+ var refundOptions = new RefundCreateOptions
+ {
+ Amount = latestInvoice.AmountPaid, // Reembolso total
+ Reason = RefundReasons.RequestedByCustomer,
+ Metadata = new Dictionary
+ {
+ { "user_id", userId },
+ { "subscription_id", subscription.Id },
+ { "invoice_id", latestInvoice.Id },
+ { "refund_reason", "CDC 7 dias - Direito de arrependimento" }
+ }
+ };
+
+ // Stripe automaticamente encontra o charge/payment_intent correto através do subscription_id no metadata
+ // Alternativamente, podemos buscar o último charge da subscription
+ try
+ {
+ // Tenta reembolsar usando a subscription (Stripe encontra o charge automaticamente)
+ var chargeService = new ChargeService();
+ var chargeOptions = new ChargeListOptions
+ {
+ Limit = 1,
+ Customer = subscription.CustomerId
+ };
+ var charges = await chargeService.ListAsync(chargeOptions);
+ var lastCharge = charges.Data.FirstOrDefault();
+
+ if (lastCharge != null)
+ {
+ refundOptions.Charge = lastCharge.Id;
+ var refund = await refundService.CreateAsync(refundOptions);
+
+ if (refund.Status == "succeeded" || refund.Status == "pending")
+ {
+ // Desativa o premium imediatamente no caso de reembolso
+ await _userService.DeactivatePremiumStatus(subscription.Id);
+
+ _logger.LogInformation($"Successfully refunded and canceled subscription {subscription.Id} for user {userId}. Refund ID: {refund.Id}");
+
+ return (true, $"Reembolso processado com sucesso! Você receberá R$ {(latestInvoice.AmountPaid / 100.0):F2} de volta em 5-10 dias úteis.");
+ }
+ else
+ {
+ _logger.LogWarning($"Refund failed with status {refund.Status} for subscription {subscription.Id}");
+ await _userService.DeactivatePremiumStatus(subscription.Id);
+ return (false, "Falha ao processar reembolso, mas assinatura foi cancelada. Entre em contato com o suporte.");
+ }
+ }
+ else
+ {
+ await _userService.DeactivatePremiumStatus(subscription.Id);
+ return (false, "Assinatura cancelada, mas nenhuma cobrança encontrada para reembolsar. Entre em contato com o suporte.");
+ }
+ }
+ catch (StripeException refundEx)
+ {
+ _logger.LogError(refundEx, $"Error creating refund for subscription {subscription.Id}");
+ await _userService.DeactivatePremiumStatus(subscription.Id);
+ return (false, $"Assinatura cancelada, mas erro ao processar reembolso: {refundEx.Message}. Entre em contato com o suporte.");
+ }
+ }
+ catch (StripeException ex)
+ {
+ _logger.LogError(ex, $"Stripe error during refund for user {userId}: {ex.Message}");
+ return (false, $"Erro ao processar reembolso: {ex.StripeError?.Message ?? ex.Message}");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error processing refund for user {userId}");
+ return (false, "Erro inesperado ao processar reembolso. Tente novamente mais tarde.");
+ }
+ }
}
}
diff --git a/Services/UserService.cs b/Services/UserService.cs
index c4aea77..4715d04 100644
--- a/Services/UserService.cs
+++ b/Services/UserService.cs
@@ -408,14 +408,24 @@ namespace QRRapidoApp.Services
public async Task ActivatePremiumStatus(string userId, string stripeSubscriptionId, DateTime expiryDate)
{
- var update = Builders.Update
+ // Verifica se é uma nova assinatura (não renovação)
+ var user = await GetUserAsync(userId);
+ var isNewSubscription = user?.StripeSubscriptionId != stripeSubscriptionId;
+
+ var updateBuilder = Builders.Update
.Set(u => u.IsPremium, true)
.Set(u => u.StripeSubscriptionId, stripeSubscriptionId)
.Set(u => u.PremiumExpiresAt, expiryDate)
.Unset(u => u.PremiumCancelledAt);
- await _context.Users.UpdateOneAsync(u => u.Id == userId, update);
- _logger.LogInformation($"Activated premium for user {userId}");
+ // Se é nova assinatura, atualiza a data de início (para CDC 7 dias)
+ if (isNewSubscription)
+ {
+ updateBuilder = updateBuilder.Set(u => u.SubscriptionStartedAt, DateTime.UtcNow);
+ }
+
+ await _context.Users.UpdateOneAsync(u => u.Id == userId, updateBuilder);
+ _logger.LogInformation($"Activated premium for user {userId} (new subscription: {isNewSubscription})");
}
public async Task DeactivatePremiumStatus(string stripeSubscriptionId)
diff --git a/Views/Account/Profile.cshtml b/Views/Account/Profile.cshtml
index a7c40e3..bb2151d 100644
--- a/Views/Account/Profile.cshtml
+++ b/Views/Account/Profile.cshtml
@@ -40,6 +40,38 @@
Expira em: @Model.PremiumExpiresAt.Value.ToString("dd/MM/yyyy")
+
+ Direito de Arrependimento (CDC): Você está dentro do período de 7 dias e tem direito a reembolso total conforme o Código de Defesa do Consumidor.
+
+
O que acontecerá:
+
+
Reembolso total do valor pago
+
Assinatura cancelada imediatamente
+
Acesso Premium removido
+
Dinheiro devolvido em 5-10 dias úteis
+
+
Deseja continuar com o reembolso?
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancelar Assinatura Premium
+
+
+
+
+
+
+ Atenção: Você manterá acesso aos recursos Premium até o final do período já pago.
+
+
Ao cancelar, você perderá:
+
+
QR Codes ilimitados
+
Experiência sem anúncios
+
QR Codes dinâmicos (editáveis)
+
Estatísticas avançadas
+
Suporte prioritário
+
+
Tem certeza que deseja cancelar?
+
Você pode reativar sua assinatura a qualquer momento.
+
+
+
+
+
+
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/Views/Home/Index.cshtml b/Views/Home/Index.cshtml
index 3e4952e..c659555 100644
--- a/Views/Home/Index.cshtml
+++ b/Views/Home/Index.cshtml
@@ -1198,7 +1198,7 @@
+ @(ViewBag.Culture == "pt-BR"
+ ? "Aprenda tudo sobre QR Codes com nossos tutoriais completos e passo a passo"
+ : "Aprende todo sobre códigos QR con nuestros tutoriales completos paso a paso")
+
+
+ @(ViewBag.Culture == "pt-BR"
+ ? "Nenhum tutorial disponível no momento. Volte em breve!"
+ : "No hay tutoriales disponibles en este momento. ¡Vuelve pronto!")
+
+ }
+
+
+
+
+
+
+
+ @(ViewBag.Culture == "pt-BR"
+ ? "Pronto para criar seu QR Code?"
+ : "¿Listo para crear tu código QR?")
+
+
+ @(ViewBag.Culture == "pt-BR"
+ ? "Gere QR codes profissionais em segundos com nossa ferramenta ultrarrápida!"
+ : "¡Genera códigos QR profesionales en segundos con nuestra herramienta ultrarrápida!")
+