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")

} + @if (!string.IsNullOrEmpty(Model.StripeSubscriptionId)) + { + var canRefund = Model.SubscriptionStartedAt.HasValue && + (DateTime.UtcNow - Model.SubscriptionStartedAt.Value).TotalDays <= 7; + var daysSinceSubscription = Model.SubscriptionStartedAt.HasValue + ? Math.Round((DateTime.UtcNow - Model.SubscriptionStartedAt.Value).TotalDays, 1) + : 0; + +

+ @if (canRefund) + { + + + Você tem direito a reembolso total (7 dias) + + } + else + { + + @if (daysSinceSubscription > 0) + { + + Assinatura há @daysSinceSubscription dias (período de reembolso expirado) + + } + } +

+ } } else { @@ -47,7 +79,7 @@ Gratuito

- + Fazer upgrade

@@ -163,7 +195,7 @@ @if (!isPremium) { @@ -195,6 +227,80 @@ + + + + + + \ 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 @@
  • @Localizer["DeveloperAPI"]
  • - + @Localizer["AcceleratePrice"] @Localizer["CancelAnytime"] diff --git a/Views/Premium/Upgrade.cshtml b/Views/Premium/Upgrade.cshtml deleted file mode 100644 index 9e73637..0000000 --- a/Views/Premium/Upgrade.cshtml +++ /dev/null @@ -1,343 +0,0 @@ -@model QRRapidoApp.Models.ViewModels.UpgradeViewModel -@using Microsoft.Extensions.Localization -@inject IStringLocalizer Localizer -@{ - ViewData["Title"] = "QR Rapido Premium"; -} - -
    -
    -
    - -
    -

    - QR Rapido Premium -

    -

    - @Localizer["PremiumAccelerateProductivity"] -

    -
    - @Localizer["ThreeTimesFaster"] -
    -
    - - - @if (Model.IsAdFreeActive) - { -
    -
    -
    -
    @Localizer["CurrentStatus"]
    -

    - @Localizer["YouHave"] @Model.DaysUntilAdExpiry @Localizer["DaysRemainingNoAds"] - @Localizer["UpgradeNowForever"] -

    -
    -
    -
    - @Model.DaysUntilAdExpiry @Localizer["DaysRemaining"] -
    -
    -
    -
    - } - - -
    -
    -
    -
    -

    - QR Rapido Premium -

    - @Localizer["MostPopularPlan"] -
    -
    -
    - R$ @Model.PremiumPrice.ToString("0.00") -
    -

    @Localizer["PerMonth"]

    - -
    -
    - - @Localizer["UnlimitedQRCodes"] -
    -
    - - @Localizer["UltraFastGeneration04s"] -
    -
    - - @Localizer["NoAdsForever"] -
    -
    - - @Localizer["DynamicQRCodes"] -
    -
    - - @Localizer["RealTimeAnalytics"] -
    -
    - - @Localizer["PrioritySupport"] -
    -
    - - @Localizer["DeveloperAPI"] -
    -
    - - - - - @Localizer["SecurePaymentStripe"] -
    - @Localizer["CancelAnytime"] -
    -
    -
    -
    -
    - - -
    -
    -

    - @Localizer["PlanComparison"] -

    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    @Localizer["Feature"]FreePremium
    @Localizer["QRCodesPerDay"]50 @Localizer["Unlimited"]
    @Localizer["GenerationSpeed"]1.2s0.4s
    @Localizer["Ads"] @Localizer["NoAds"]
    @Localizer["DynamicQRCodes"]
    @Localizer["DetailedAnalytics"]
    @Localizer["PrioritySupport"]
    API access
    -
    -
    -
    - - -
    -
    -

    - @Localizer["SpeedDemonstration"] -

    -
    -
    -
    -
    -
    -
    -
    @Localizer["Competitors"]
    -
    4.5s
    -

    @Localizer["AverageTime"]

    -
    -
    -
    -
    -
    -
    -
    QR Rapido Free
    -
    1.2s
    -

    @Localizer["ThreeTimesFaster"]

    -
    -
    -
    -
    -
    -
    -
    QR Rapido Premium
    -
    0.4s
    -

    @Localizer["ElevenTimesFaster"]

    -
    -
    -
    -
    -
    -
    - - -
    -
    -

    - @Localizer["FAQ"] -

    -
    -
    -
    -
    -

    - -

    -
    -
    - @Localizer["CancelAnytimeAnswer"] -
    -
    -
    -
    -

    - -

    -
    -
    - @Localizer["DynamicQRAnswer"] -
    -
    -
    -
    -

    - -

    -
    -
    - @Localizer["PrioritySupportAnswer"] -
    -
    -
    -
    -
    -
    -
    -
    -
    - -@section Scripts { - -} \ No newline at end of file diff --git a/Views/Shared/_AdSpace.cshtml b/Views/Shared/_AdSpace.cshtml index 31a438d..3039bfd 100644 --- a/Views/Shared/_AdSpace.cshtml +++ b/Views/Shared/_AdSpace.cshtml @@ -95,7 +95,7 @@ else if (User.Identity.IsAuthenticated)
    @Localizer["UpgradePremiumRemoveAds"] - + @Localizer["PremiumBenefitsShort"]
    diff --git a/Views/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml index bd4cfa1..d4ef1dc 100644 --- a/Views/Shared/_Layout.cshtml +++ b/Views/Shared/_Layout.cshtml @@ -289,7 +289,7 @@ } else { -
  • +
  • QR Rapido Premium
  • } diff --git a/Views/Tutoriais/Article.cshtml b/Views/Tutoriais/Article.cshtml new file mode 100644 index 0000000..c7ca847 --- /dev/null +++ b/Views/Tutoriais/Article.cshtml @@ -0,0 +1,290 @@ +@model QRRapidoApp.Models.ViewModels.ArticleViewModel +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; + ViewData["Title"] = Model.Metadata.Title; + var baseUrl = "https://qrrapido.site"; + var articleUrl = $"{baseUrl}/{ViewBag.Culture}/tutoriais/{ViewBag.Slug}"; +} + + + + + + + @Model.Metadata.Title - QR Rapido + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + +
    +

    @Model.Metadata.Title

    + +
    + @Model.Metadata.Author | + @Model.Metadata.Date.ToString("dd MMM yyyy") | + @Model.Metadata.ReadingTimeMinutes min @(ViewBag.Culture == "pt-BR" ? "de leitura" : "de lectura") +
    + + @if (!string.IsNullOrEmpty(Model.Metadata.Image)) + { + @Model.Metadata.Title + } + +

    @Model.Metadata.Description

    +
    + + +
    + @Html.Raw(Model.HtmlContent) +
    + + +
    +

    + + @(ViewBag.Culture == "pt-BR" ? "Última atualização:" : "Última actualización:") + @Model.Metadata.LastMod.ToString("dd MMM yyyy HH:mm") + +

    +
    + + + @if (ViewBag.ShowAds == true) + { +
    + +
    + } +
    + + + +
    +
    + + + + diff --git a/Views/Tutoriais/Index.cshtml b/Views/Tutoriais/Index.cshtml new file mode 100644 index 0000000..2effbae --- /dev/null +++ b/Views/Tutoriais/Index.cshtml @@ -0,0 +1,109 @@ +@model List +@{ + ViewData["Title"] = ViewBag.Culture == "pt-BR" ? "Tutoriais QR Code" : "Tutoriales Código QR"; + Layout = "~/Views/Shared/_Layout.cshtml"; +} + +
    + +
    +
    +

    + + @(ViewBag.Culture == "pt-BR" ? "Tutoriais QR Code" : "Tutoriales Código QR") +

    +

    + @(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") +

    +
    +
    + + + @if (Model.Any()) + { +
    + @foreach (var article in Model) + { +
    +
    + @if (!string.IsNullOrEmpty(article.Image)) + { + @article.Title + } +
    +
    @article.Title
    +

    @article.Description

    + +
    + + @article.Date.ToString("dd MMM yyyy") | + @article.ReadingTimeMinutes min + +
    + + + @(ViewBag.Culture == "pt-BR" ? "Ler Tutorial" : "Leer Tutorial") + + +
    +
    +
    + } +
    + } + else + { + + } + + +
    +
    +
    +
    +

    + @(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!") +

    + + + @(ViewBag.Culture == "pt-BR" ? "Criar QR Code Agora" : "Crear Código QR Ahora") + +
    +
    +
    +
    +
    + + diff --git a/appsettings.Production.json b/appsettings.Production.json index feb0828..002d291 100644 --- a/appsettings.Production.json +++ b/appsettings.Production.json @@ -18,6 +18,24 @@ } } }, + "Stripe": { + "PublishableKey": "pk_live_XXXXX", + "SecretKey": "sk_live_XXXXX", + "WebhookSecret": "whsec_live_XXXXX", + "ProductId": "prod_SnfQTxwE3i8r5L", + "Plans": { + "Monthly": { + "BR": "price_XXXXX_monthly_br_PROD", + "PY": "price_XXXXX_monthly_py_PROD", + "US": "price_XXXXX_monthly_us_PROD" + }, + "Yearly": { + "BR": "price_XXXXX_yearly_br_PROD", + "PY": "price_XXXXX_yearly_py_PROD", + "US": "price_XXXXX_yearly_us_PROD" + } + } + }, "ResourceMonitoring": { "Enabled": true, "IntervalSeconds": 30, diff --git a/appsettings.json b/appsettings.json index 2f9d35b..81d51cb 100644 --- a/appsettings.json +++ b/appsettings.json @@ -24,7 +24,19 @@ "PublishableKey": "pk_test_51Rs42tBeR5IFYUsBooapyDwQTgh6CFuKbya5R3MVDTrdOUKmgiHQYipU0pgOdG5iKogH77RUYIKBJzbCt5BghUOY00xitV5KiN", "SecretKey": "sk_test_51Rs42tBeR5IFYUsBtycRlJJcdwgoMbh8MfQIKIGelBPTQFwDcOn2iCCbw5uG6hnqlpgNAUuFgWRAUUMA8qkABKun00EIx4odDF", "WebhookSecret": "whsec_2e828803ceb48e7865458b0cf332b68535fdff8753d26d69b1c88ea55cb0e482", - "PriceId": "prod_SnfQTxwE3i8r5L" + "ProductId": "prod_SnfQTxwE3i8r5L", + "Plans": { + "Monthly": { + "BR": "price_1Rs45OBeR5IFYUsBfsnOpOiv", + "PY": "price_XXXXX_monthly_py", + "US": "price_XXXXX_monthly_us" + }, + "Yearly": { + "BR": "price_1Rs4AyBeR5IFYUsB8kRSNUIM", + "PY": "price_XXXXX_yearly_py", + "US": "price_XXXXX_yearly_us" + } + } }, "AdSense": { "ClientId": "ca-pub-3475956393038764", diff --git a/wwwroot/images/tutoriais/qr-code-corretor-imoveis-hero.jpg b/wwwroot/images/tutoriais/qr-code-corretor-imoveis-hero.jpg new file mode 100644 index 0000000..fd98130 Binary files /dev/null and b/wwwroot/images/tutoriais/qr-code-corretor-imoveis-hero.jpg differ diff --git a/wwwroot/images/tutoriais/qr-code-wifi-hero.jpg b/wwwroot/images/tutoriais/qr-code-wifi-hero.jpg new file mode 100644 index 0000000..a8d48dc Binary files /dev/null and b/wwwroot/images/tutoriais/qr-code-wifi-hero.jpg differ diff --git a/wwwroot/images/tutoriais/whatsapp-qr-hero.jpg b/wwwroot/images/tutoriais/whatsapp-qr-hero.jpg new file mode 100644 index 0000000..ee4e6d0 Binary files /dev/null and b/wwwroot/images/tutoriais/whatsapp-qr-hero.jpg differ diff --git a/wwwroot/js/qr-speed-generator.js b/wwwroot/js/qr-speed-generator.js index ea243ac..67789e7 100644 --- a/wwwroot/js/qr-speed-generator.js +++ b/wwwroot/js/qr-speed-generator.js @@ -1489,7 +1489,7 @@ class QRRapidoGenerator { Sessão sem anúncios ativa! Tempo restante: ${this.formatTime(timeRemaining)} - Tornar Permanente + Tornar Permanente `; const container = document.querySelector('.container'); @@ -1548,7 +1548,7 @@ class QRRapidoGenerator {