refactor(support): replace mock data with real costs and simplify stats

Updated support page to use real data instead of mock values, including monthly costs
This commit is contained in:
Evandro Fonseca Junior committed 2025-10-26 18:32:15 -03:00
1 parent 01e97eb078
commit 00501e6699
8 files changed
+235 -150

No files matched your search

+43
View File
@@ -0,0 +1,43 @@
# Variáveis de Ambiente - AbacatePay
## Configuração para Desenvolvimento/Teste
```env
# Chave de API para ambiente de teste
ABACATE_TEST_APIKEY=your_test_api_key_here
# ID da conta AbacatePay
ABACATEPAY_ACCOUNT_ID=your_account_id_here
# URLs da aplicação
NEXT_PUBLIC_BASE_URL=http://localhost:3000
NEXT_PUBLIC_ABACATEPAY_WEBHOOK_URL=http://localhost:3000/api/webhooks/abacatepay
```
## Configuração para Produção
```env
# Chave de API para ambiente de produção (usado automaticamente quando NODE_ENV=production)
ABACATE_APIKEY=your_production_api_key_here
# ID da conta AbacatePay
ABACATEPAY_ACCOUNT_ID=your_account_id_here
# URLs da aplicação
NEXT_PUBLIC_BASE_URL=https://seudominio.com
NEXT_PUBLIC_ABACATEPAY_WEBHOOK_URL=https://seudominio.com/api/webhooks/abacatepay
```
## Como Funciona
O sistema automaticamente escolhe a chave correta baseado no ambiente:
- **Desenvolvimento/Teste**: Usa `ABACATE_TEST_APIKEY`
- **Produção**: Usa `ABACATE_APIKEY` (quando `NODE_ENV=production`)
## Obtenção das Chaves
1. Acesse o painel do AbacatePay
2. Vá em "Configurações" > "API Keys"
3. Gere uma chave para teste e outra para produção
4. Configure as URLs de webhook no painel do AbacatePay
+34 -78
View File
@@ -10,7 +10,7 @@ import {
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Heart, Server, Code, Shield, AlertTriangle } from "lucide-react";
import { Heart } from "lucide-react";
import { getTranslations } from "next-intl/server";
import Link from "next/link";
import { ShareButton } from "@/components/share-button";
@@ -36,44 +36,19 @@ export default async function SupportPage({
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "donation" });
// Dados mockados para demonstração
// Dados REAIS para demonstração
const stats = {
totalSupporters: 1247,
totalRaised: 18542,
monthsOnline: 12,
uptime: 99.8,
monthsOnline: 1,
uptime: 100,
};
const transparencyData = [
{
icon: Server,
title: t("transparency.hosting"),
percentage: 40,
description: t("transparency.hostingDesc"),
color: "text-blue-500",
},
{
icon: Code,
title: t("transparency.development"),
percentage: 35,
description: t("transparency.developmentDesc"),
color: "text-green-500",
},
{
icon: Shield,
title: t("transparency.support"),
percentage: 15,
description: t("transparency.supportDesc"),
color: "text-purple-500",
},
{
icon: AlertTriangle,
title: t("transparency.emergency"),
percentage: 10,
description: t("transparency.emergencyDesc"),
color: "text-orange-500",
},
];
// Custos mensais REAIS
const monthlyCosts = {
vps: 69, // R$ 69/mês
devHours: 40, // média horas/mês
devRate: 50, // R$ 50/hora
total: 2069, // R$ 69 + (40 * 50)
};
const faqItems = [
{
@@ -136,58 +111,39 @@ export default async function SupportPage({
</div>
</BlurFade>
{/* Transparência */}
{/* Custos Reais */}
<BlurFade delay={0.3}>
<div className="mb-20">
<h2 className="text-3xl font-bold text-center mb-8">
{t("transparency.title")}
{t("costs.title")}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl mx-auto">
{transparencyData.map((item, idx) => (
<div
key={idx}
className="p-6 rounded-xl border border-border/50 bg-card backdrop-blur-sm"
>
<div className="flex items-start gap-4">
<div className="p-3 rounded-lg bg-muted/50">
<item.icon className={`w-6 h-6 ${item.color}`} />
</div>
<div className="flex-1">
<div className="flex items-center justify-between mb-2">
<h3 className="font-semibold">{item.title}</h3>
<Badge variant="secondary">{item.percentage}%</Badge>
</div>
<p className="text-sm text-muted-foreground">
{item.description}
</p>
</div>
</div>
</div>
))}
<div className="max-w-2xl mx-auto space-y-4">
<div className="flex justify-between items-center p-4 rounded-lg bg-card border">
<span className="text-lg">{t("costs.vps")}</span>
<Badge variant="secondary" className="text-lg">
{t("costs.vpsValue")}
</Badge>
</div>
<div className="flex justify-between items-center p-4 rounded-lg bg-card border">
<span className="text-lg">{t("costs.dev")}</span>
<Badge variant="secondary" className="text-lg">
{t("costs.devValue")}
</Badge>
</div>
<div className="flex justify-between items-center p-6 rounded-lg bg-primary/10 border-2 border-primary">
<span className="text-xl font-bold">{t("costs.total")}</span>
<Badge className="text-xl bg-primary">
{t("costs.totalValue")}
</Badge>
</div>
</div>
</div>
</BlurFade>
{/* Prova Social */}
{/* Estatísticas Simplificadas */}
<BlurFade delay={0.4}>
<div className="mb-20">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 text-center">
<div>
<p className="text-3xl font-bold text-primary mb-2">
<NumberTicker value={stats.totalSupporters} />
</p>
<p className="text-sm text-muted-foreground">
{t("social.supporters")}
</p>
</div>
<div>
<p className="text-3xl font-bold text-primary mb-2">
R$ <NumberTicker value={stats.totalRaised} />
</p>
<p className="text-sm text-muted-foreground">
{t("social.raised")}
</p>
</div>
<div className="grid grid-cols-2 gap-8 text-center max-w-md mx-auto">
<div>
<p className="text-3xl font-bold text-primary mb-2">
<NumberTicker value={stats.monthsOnline} />
+12 -10
View File
@@ -17,22 +17,24 @@ export async function POST(request: NextRequest) {
);
}
const billing = await abacatePay.createBilling({
const billing = await abacatePay.createDonationBilling({
amount: body.amount,
metadata: {
donorName: body.donorName,
donorEmail: body.donorEmail,
message: body.message,
},
returnUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/apoiar/obrigado`,
completionUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/api/donations/verify`,
donorEmail: body.donorEmail,
donorName: body.donorName,
message: body.message,
});
// AbacatePay pode retornar diferentes formatos
const pixCode = billing.pixCode || billing.qrCode;
const pixQrCode = billing.pixQrCode || billing.qrCode;
const paymentUrl = billing.url || billing.paymentUrl;
return NextResponse.json({
success: true,
billingId: billing.id,
pixCode: billing.pixCode,
pixQrCode: billing.pixQrCode,
paymentUrl, // URL para redirecionar
pixCode,
pixQrCode,
expiresAt: billing.expiresAt,
});
} catch (error) {
+5 -1
View File
@@ -52,7 +52,11 @@ export function DonationCard({
const data: DonationResponse = await response.json();
if (data.success && data.pixCode && data.pixQrCode) {
if (data.success && data.paymentUrl) {
// Redirecionar para página de pagamento do AbacatePay
window.location.href = data.paymentUrl;
} else if (data.success && data.pixCode && data.pixQrCode) {
// Fallback: mostrar modal com QR Code se disponível
setPixData({
code: data.pixCode,
qrCode: data.pixQrCode,
+69 -4
View File
@@ -19,10 +19,13 @@ interface BillingResponse {
id: string;
amount: number;
status: "PENDING" | "PAID" | "EXPIRED" | "CANCELLED";
pixCode: string;
pixQrCode: string;
expiresAt: string;
pixCode?: string;
pixQrCode?: string;
qrCode?: string;
expiresAt?: string;
createdAt: string;
url?: string; // URL para redirecionamento
paymentUrl?: string; // URL alternativa de pagamento
}
class AbacatePayClient {
@@ -61,6 +64,56 @@ class AbacatePayClient {
return response.json();
}
async createDonationBilling(params: {
amount: number;
donorEmail?: string;
donorName?: string;
message?: string;
}): Promise<BillingResponse> {
const requestBody = {
products: [
{
external_id: `donation-${Date.now()}`,
name: "Doação - Tibia Trade Pagodera",
quantity: 1,
price: Math.round(params.amount * 100), // R$ para centavos
},
],
return_url: `${process.env.NEXT_PUBLIC_BASE_URL}/apoiar/obrigado`,
completion_url: `${process.env.NEXT_PUBLIC_BASE_URL}/api/donations/verify`,
};
console.log("🔍 AbacatePay Request URL:", `${this.config.baseUrl}/billing`);
console.log(
"🔍 AbacatePay Request Body:",
JSON.stringify(requestBody, null, 2)
);
const response = await fetch(`${this.config.baseUrl}/billing`, {
method: "POST",
headers: {
Authorization: `Bearer ${this.config.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
console.log("🔍 AbacatePay Response Status:", response.status);
console.log("🔍 AbacatePay Response OK:", response.ok);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
console.error("🔍 AbacatePay Error Response:", errorData);
throw new Error(
`Failed to create billing: ${errorData.message || response.statusText}`
);
}
const result = await response.json();
console.log("🔍 AbacatePay Success Response:", result);
return result;
}
async getBillingStatus(billingId: string): Promise<BillingResponse> {
const response = await fetch(
`${this.config.baseUrl}/billing/${billingId}`,
@@ -92,8 +145,20 @@ class AbacatePayClient {
}
// Instância do cliente (será configurada com variáveis de ambiente)
const isProduction = process.env.NODE_ENV === "production";
const apiKey = isProduction
? process.env.ABACATE_APIKEY || "demo_key"
: process.env.ABACATE_TEST_APIKEY || "demo_key";
// Log para debug (apenas em desenvolvimento)
if (process.env.NODE_ENV !== "production") {
console.log(
`🔧 AbacatePay: Using ${isProduction ? "PRODUCTION" : "TEST"} environment`
);
}
export const abacatePay = new AbacatePayClient({
apiKey: process.env.ABACATEPAY_API_KEY || "demo_key",
apiKey,
accountId: process.env.ABACATEPAY_ACCOUNT_ID || "demo_account",
});
+35 -28
View File
@@ -189,63 +189,70 @@
}
},
"donation": {
"title": "Support Tibia Trade Pagodera",
"subtitle": "We keep the project free with help from people like you",
"title": "Support Project Development",
"subtitle": "Your voluntary donation helps maintain and evolve the platform",
"story": {
"title": "Our Project, Our Community",
"text": "Tibia Trade Pagodera was born from the passion to create a safer and more efficient trading experience for our community. We started as a small project, but with big dreams of revolutionizing how players trade items on the Pagodera server.\n\nOver the months, we faced technical challenges, learned from our community, and constantly evolved. Every line of code was written thinking of you, players who trust our platform for their trades.\n\nToday, we are more than a trading platform - we are a community united by passion for Tibia and mutual trust."
"text": "Tibia Trade Pagodera is developed with dedication to create the best trading experience for our community. Each voluntary donation helps us continue evolving and maintaining service quality.\n\nWe are not a company, we are developers passionate about Tibia who believe in total transparency. Every penny is invested directly in the project.\n\nYour trust and support make all the difference."
},
"transparency": {
"title": "How We Use Your Donations",
"hosting": "Hosting & Infrastructure",
"development": "Development & Improvements",
"support": "Support & Moderation",
"title": "Total Cost Transparency",
"hosting": "VPS Infrastructure",
"development": "Development",
"support": "Maintenance & Support",
"emergency": "Emergency Reserve",
"hostingDesc": "Servers, CDN and infrastructure to keep the platform always online",
"developmentDesc": "New features, fixes and improvements to user experience",
"supportDesc": "Support team and moderation to keep the community safe",
"emergencyDesc": "Reserve for emergencies and critical improvements"
"hostingDesc": "R$ 69/month - Dedicated server to ensure performance",
"developmentDesc": "R$ 50/hour - Real cost of development and improvements",
"supportDesc": "Monitoring, bug fixes and customer service",
"emergencyDesc": "Buffer for unforeseen situations and scalability"
},
"costs": {
"title": "Estimated Monthly Costs",
"vps": "VPS (Server)",
"dev": "Development (average 40h/month)",
"total": "Total Monthly Estimate",
"vpsValue": "R$ 69/month",
"devValue": "R$ 2,000/month",
"totalValue": "R$ 2,069/month"
},
"social": {
"supporters": "supporters",
"raised": "raised",
"months": "months online",
"months": "month online",
"uptime": "uptime"
},
"tiers": {
"coffee": {
"title": "A Coffee",
"description": "Helps with basic costs",
"description": "Equals ~10 minutes of development",
"badge": "Popular"
},
"lunch": {
"title": "Team Lunch",
"description": "Keeps the server running for 1 day",
"description": "Equals ~30 minutes of development",
"badge": "Recommended"
},
"custom": {
"title": "Custom Amount",
"description": "You choose how much you want to support"
"description": "You choose how much you want to contribute"
}
},
"cta": {
"support": "Support with PIX",
"support": "Make Donation",
"share": "Share Project",
"final": "Every help makes a difference",
"subtitle": "Even $5 helps us keep the project alive and free for everyone"
"final": "Every contribution matters",
"subtitle": "Each donation helps us dedicate more time to the project and keep the infrastructure running"
},
"faq": {
"title": "Frequently Asked Questions",
"q1": "Where does my money go?",
"a1": "All donations are used exclusively to maintain and improve the platform. We publish quarterly reports showing exactly how the money was spent.",
"q2": "Do I get anything in return?",
"a2": "The project remains 100% free for everyone. Your reward is knowing you're helping maintain a tool that benefits the entire community.",
"q1": "Is the project free?",
"a1": "The project charges transaction fees to sustain itself. Donations are voluntary for those who want to contribute beyond normal usage fees.",
"q2": "Where does my donation go?",
"a2": "100% invested in the project: server cost (R$ 69/month) and development hours (R$ 50/hour). We maintain total transparency of expenses.",
"q3": "How does PIX payment work?",
"a3": "You scan the QR Code or copy the PIX code and make the payment through your banking app. Payment is instant and secure.",
"q4": "Can I cancel my donation?",
"a4": "Donations are one-time and non-recurring. No cancellation needed - you pay only once for the chosen amount.",
"a3": "You choose the amount, we generate a QR Code or PIX code, you pay through your banking app. Instant and secure payment.",
"q4": "Do I get anything in return?",
"a4": "We don't offer direct benefits. Your donation is voluntary and helps the project evolve for everyone.",
"q5": "Are you a company?",
"a5": "We are an independent project created by players for players. We are not a commercial company, just Tibia enthusiasts."
"a5": "No. We are independent developers passionate about Tibia. Transparency and community are our values."
},
"supporters": {
"title": "Our Amazing Supporters",
+36 -29
View File
@@ -189,63 +189,70 @@
}
},
"donation": {
"title": "Apoie o Tibia Trade Pagodera",
"subtitle": "Mantemos o projeto gratuito com a ajuda de pessoas como você",
"title": "Apoie o Desenvolvimento do Projeto",
"subtitle": "Sua doação voluntária ajuda a manter e evoluir a plataforma",
"story": {
"title": "Nosso Projeto, Nossa Comunidade",
"text": "O Tibia Trade Pagodera nasceu da paixão por criar uma experiência de trading mais segura e eficiente para nossa comunidade. Começamos como um projeto pequeno, mas com grandes sonhos de revolucionar como os jogadores negociam itens no servidor Pagodera.\n\nAo longo dos meses, enfrentamos desafios técnicos, aprendemos com nossa comunidade e evoluímos constantemente. Cada linha de código foi escrita pensando em vocês, jogadores que confiam em nossa plataforma para suas negociações.\n\nHoje, somos mais que uma plataforma de trading - somos uma comunidade unida pela paixão pelo Tibia e pela confiança mútua."
"text": "O Tibia Trade Pagodera é desenvolvido com dedicação para criar a melhor experiência de trading para nossa comunidade. Cada doação voluntária nos ajuda a continuar evoluindo e mantendo a qualidade do serviço.\n\nNão somos uma empresa, somos desenvolvedores apaixonados por Tibia que acreditam em transparência total. Todo centavo é investido diretamente no projeto.\n\nSua confiança e apoio fazem toda a diferença."
},
"transparency": {
"title": "Como Usamos Suas Doações",
"hosting": "Hospedagem & Infraestrutura",
"development": "Desenvolvimento & Melhorias",
"support": "Suporte & Moderação",
"emergency": "Reserva de Emergência",
"hostingDesc": "Servidores, CDN e infraestrutura para manter a plataforma sempre online",
"developmentDesc": "Novas funcionalidades, correções e melhorias na experiência do usuário",
"supportDesc": "Equipe de suporte e moderação para manter a comunidade segura",
"emergencyDesc": "Reserva para emergências e melhorias críticas"
"title": "Transparência Total de Custos",
"hosting": "Infraestrutura VPS",
"development": "Desenvolvimento",
"support": "Manutenção & Suporte",
"emergency": "Reserva para Emergências",
"hostingDesc": "R$ 69/mês - Servidor dedicado para garantir performance",
"developmentDesc": "R$ 50/hora - Custo real de desenvolvimento e melhorias",
"supportDesc": "Monitoramento, correções de bugs e atendimento",
"emergencyDesc": "Buffer para situações imprevistas e escalabilidade"
},
"costs": {
"title": "Custos Mensais Estimados",
"vps": "VPS (Servidor)",
"dev": "Desenvolvimento (média 40h/mês)",
"total": "Total Mensal Estimado",
"vpsValue": "R$ 69/mês",
"devValue": "R$ 2.000/mês",
"totalValue": "R$ 2.069/mês"
},
"social": {
"supporters": "apoiadores",
"raised": "arrecadados",
"months": "meses online",
"months": "mês online",
"uptime": "uptime"
},
"tiers": {
"coffee": {
"title": "Um Café",
"description": "Ajuda com custos básicos",
"description": "Equivale a ~10 minutos de desenvolvimento",
"badge": "Popular"
},
"lunch": {
"title": "Almoço da Equipe",
"description": "Mantém o servidor rodando por 1 dia",
"description": "Equivale a ~30 minutos de desenvolvimento",
"badge": "Recomendado"
},
"custom": {
"title": "Valor Livre",
"description": "Você escolhe quanto quer apoiar"
"description": "Você escolhe quanto quer contribuir"
}
},
"cta": {
"support": "Apoiar com PIX",
"support": "Fazer Doação",
"share": "Compartilhar Projeto",
"final": "Toda ajuda faz diferença",
"subtitle": "Mesmo R$ 5 nos ajuda a manter o projeto vivo e gratuito para todos"
"final": "Toda contribuição é importante",
"subtitle": "Cada doação nos ajuda a dedicar mais tempo ao projeto e manter a infraestrutura funcionando"
},
"faq": {
"title": "Perguntas Frequentes",
"q1": "Para onde vai meu dinheiro?",
"a1": "Todas as doações são usadas exclusivamente para manter e melhorar a plataforma. Publicamos relatórios trimestrais mostrando exatamente como o dinheiro foi gasto.",
"q2": "Recebo algo em troca?",
"a2": "O projeto continua 100% gratuito para todos. Sua recompensa é saber que está ajudando a manter uma ferramenta que beneficia toda a comunidade.",
"q1": "O projeto é gratuito?",
"a1": "O projeto cobra taxas de transação para se manter. As doações são voluntárias para quem deseja contribuir além das taxas normais de uso.",
"q2": "Para onde vai minha doação?",
"a2": "100% investido no projeto: custo de servidor (R$ 69/mês) e horas de desenvolvimento (R$ 50/hora). Mantemos transparência total dos gastos.",
"q3": "Como funciona o pagamento PIX?",
"a3": "Você escaneia o QR Code ou copia o código PIX e faz o pagamento pelo seu app bancário. O pagamento é instantâneo e seguro.",
"q4": "Posso cancelar minha doação?",
"a4": "As doações são únicas e não recorrentes. Não há cancelamento necessário - você paga apenas uma vez pelo valor escolhido.",
"a3": "Você escolhe o valor, geramos um QR Code ou código PIX, você paga pelo seu app bancário. Pagamento instantâneo e seguro.",
"q4": "Recebo algo em troca?",
"a4": "Não oferecemos benefícios diretos. Sua doação é voluntária e ajuda o projeto a evoluir para todos.",
"q5": "Vocês são uma empresa?",
"a5": "Somos um projeto independente criado por jogadores para jogadores. Não somos uma empresa comercial, apenas apaixonados por Tibia."
"a5": "Não. Somos desenvolvedores independentes apaixonados por Tibia. Transparência e comunidade são nossos valores."
},
"supporters": {
"title": "Nossos Incríveis Apoiadores",
+1
View File
@@ -18,6 +18,7 @@ export interface DonationData {
export interface DonationResponse {
success: boolean;
billingId?: string;
paymentUrl?: string;
pixCode?: string;
pixQrCode?: string;
expiresAt?: string;