Guia de desenvolvimento — Todas as 10 fases concluídas. 42/42 testes passaram. Sistema operacional.
Este documento é um guia vivo de desenvolvimento. Ele é atualizado conforme as funcionalidades são implementadas. Se o Claude perder contexto, deve consultar este arquivo para saber o que já foi feito e o que falta.
| Item | Status |
|---|---|
| Domínio paggo.me | ✅ Configurado (DNS Cloudflare + SSL Let’s Encrypt) |
| Servidor Nginx | ✅ Configurado com proxy reverso para API/app/checkout |
| Landing Page (paggo.me) | ✅ Criada em 20/03/2026 |
| Plano Web (paggo.me/plano) | ✅ Publicado em 20/03/2026 |
| Pasta do projeto | ✅ /home/glauko/checkout-paggo/ |
| Web root | ✅ /var/www/paggo/ |
| PostgreSQL | ✅ Banco paggo criado (Docker PostgreSQL 17) |
| Redis | ✅ Conectado ao Redis Docker existente (porta 6379) |
| Backend Node.js | ✅ Rodando na porta 4600 |
| Merchant admin criado | ✅ admin@paggo.me |
| Fase | Descrição | Status | Última Atualização |
|---|---|---|---|
| 0 | Infraestrutura (domínio, servidor, landing) | ✅ Concluída | 20/03/2026 |
| 1 | Fundação — Banco de Dados + Modelos | ✅ Concluída | 20/03/2026 |
| 2 | Gateway Adapters — 5 Gateways | ✅ Concluída | 20/03/2026 |
| 3 | API Backend — Rotas Principais | ✅ Concluída | 20/03/2026 |
| 4 | Engine de Assinaturas + Dunning | ✅ Concluída | 20/03/2026 |
| 5 | Frontend — Página de Checkout | ✅ Concluída | 20/03/2026 |
| 6 | Dashboard Admin | ✅ Concluída | 20/03/2026 |
| 7 | Páginas Públicas | ✅ Concluída | 20/03/2026 |
| 8 | Segurança e Performance | ✅ Concluída | 20/03/2026 |
| 9 | Deploy e Infraestrutura | ✅ Concluída | 20/03/2026 |
| 10 | Testes e Go-Live | ✅ Concluída | 20/03/2026 |
Plataforma de checkout transparente própria, inspirada na Digital Manager Guru, operando no domínio paggo.me. Suporta vendas internacionais em USD, modelo de assinatura recorrente com desconto configurável no primeiro pagamento, e integração com 5 gateways de pagamento. Preparada para escalar $10.000–$20.000/dia.
/home/glauko/checkout-paggo/ ← Código-fonte do projeto
├── MEGA-PLANO-CHECKOUT.md ← Este guia de desenvolvimento
├── server.js ← Entry point Express (porta 4600)
├── package.json ← Dependências Node.js
├── .env ← Variáveis de ambiente
├── migrations/001_initial.sql ← Schema PostgreSQL
├── src/
│ ├── config/ ← database.js, redis.js, constants.js
│ ├── middleware/ ← auth.js, rateLimit.js, validate.js, errorHandler.js
│ ├── gateways/ ← base.js, stripe.js, mercadopago.js, pagbank.js, asaas.js, pagarme.js, index.js
│ ├── routes/ ← auth.js, products.js, checkout.js, subscriptions.js, webhooks.js, customers.js, coupons.js, orders.js, gateways.js, dashboard.js
│ ├── services/ ← payment.js, checkout.js, subscription.js, notification.js, analytics.js
│ ├── jobs/ ← billingWorker.js, dunningWorker.js, recoveryWorker.js, healthWorker.js
│ └── utils/ ← logger.js, crypto.js, currency.js
└── public/
├── checkout/index.html ← Página de checkout SPA (1000 linhas)
├── checkout/success.html ← Página de sucesso
├── checkout/error.html ← Página de erro
└── dashboard/index.html ← Dashboard admin SPA (1295 linhas)
/var/www/paggo/ ← Web root (Nginx)
├── index.html ← Landing page do Paggo.me
└── plano/
├── index.html ← Plano renderizado em HTML
└── MEGA-PLANO-CHECKOUT.md ← Download do plano
[Cloudflare CDN / DNS]
|
[paggo.me - HTTPS]
|
[Nginx Reverse Proxy]
/ \
[Frontend SPA] [Backend API]
(React/Checkout) (Node.js/Express)
|
[PostgreSQL + Redis]
|
[Payment Orchestration Layer]
/ | | | \
[Stripe] [MercadoPago] [PagBank] [Asaas] [Pagar.me]
| Camada | Tecnologia | Justificativa |
|---|---|---|
| Frontend Checkout | HTML/CSS/JS puro (SPA leve) | Ultra-rápido (<1s load), sem framework pesado |
| Backend API | Node.js + Express | Mesmo stack do ConstruX, fácil manutenção |
| Banco Principal | PostgreSQL | ACID compliance para dados financeiros |
| Cache/Sessões | Redis | Rate limiting, idempotência, sessões |
| Fila de Jobs | Bull (Redis-based) | Webhooks, retentativas, dunning |
| Tokenização | Hosted fields dos gateways | PCI DSS scope reduction (SAQ-A) |
| CDN/DNS | Cloudflare | SSL, DDoS, performance |
| Resend ou AWS SES | Transacional (recibos, dunning) |
Tabela: merchants (multi-tenant ready)
CREATE TABLE merchants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
domain VARCHAR(255),
logo_url TEXT,
brand_color VARCHAR(7) DEFAULT '#6C5CE7',
currency VARCHAR(3) DEFAULT 'USD',
timezone VARCHAR(50) DEFAULT 'America/Sao_Paulo',
webhook_url TEXT,
webhook_secret VARCHAR(64),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
Tabela: gateway_configs (credenciais por merchant por gateway)
CREATE TABLE gateway_configs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id UUID REFERENCES merchants(id),
gateway VARCHAR(20) NOT NULL, -- stripe, mercadopago, pagbank, asaas, pagarme
is_active BOOLEAN DEFAULT true,
is_primary BOOLEAN DEFAULT false,
priority INT DEFAULT 0, -- para failover ordering
credentials JSONB NOT NULL, -- { api_key, secret_key, public_key, etc }
supported_methods TEXT[] DEFAULT '{}', -- {credit_card, pix, boleto}
supported_currencies TEXT[] DEFAULT '{USD,BRL}',
config JSONB DEFAULT '{}', -- settings específicos do gateway
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_gw_merchant ON gateway_configs(merchant_id, is_active);
Tabela: products
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id UUID REFERENCES merchants(id),
name VARCHAR(255) NOT NULL,
description TEXT,
type VARCHAR(20) NOT NULL DEFAULT 'subscription', -- one_time, subscription
price_cents INT NOT NULL, -- preço em centavos (ex: 900 = $9.00)
currency VARCHAR(3) DEFAULT 'USD',
-- Subscription fields
billing_cycle VARCHAR(20), -- monthly, quarterly, yearly
billing_interval INT DEFAULT 1, -- a cada N ciclos
trial_days INT DEFAULT 0,
-- Desconto no primeiro pagamento
first_payment_discount_enabled BOOLEAN DEFAULT false,
first_payment_price_cents INT, -- preço do 1º pagamento (ex: 900 = $9)
-- Installments (parcelamento)
max_installments INT DEFAULT 1,
-- Metadata
image_url TEXT,
checkout_title VARCHAR(255),
checkout_description TEXT,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_products_merchant ON products(merchant_id, is_active);
Tabela: coupons
CREATE TABLE coupons (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id UUID REFERENCES merchants(id),
code VARCHAR(50) NOT NULL,
discount_type VARCHAR(10) NOT NULL, -- percent, fixed
discount_value INT NOT NULL, -- percent: 10=10%, fixed: centavos
currency VARCHAR(3) DEFAULT 'USD',
max_uses INT, -- NULL = ilimitado
used_count INT DEFAULT 0,
max_uses_per_customer INT DEFAULT 1,
applies_to_cycles INT DEFAULT 1, -- quantos ciclos de assinatura aplica
valid_from TIMESTAMPTZ,
valid_until TIMESTAMPTZ,
product_ids UUID[], -- NULL = todos os produtos
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE UNIQUE INDEX idx_coupon_code ON coupons(merchant_id, code);
Tabela: customers
CREATE TABLE customers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id UUID REFERENCES merchants(id),
email VARCHAR(255) NOT NULL,
name VARCHAR(255),
document VARCHAR(20), -- CPF/CNPJ/Tax ID
phone VARCHAR(20),
country VARCHAR(2),
city VARCHAR(100),
-- Gateway customer IDs (para 1-click)
gateway_customer_ids JSONB DEFAULT '{}', -- { stripe: "cus_xxx", mercadopago: "xxx" }
-- Payment tokens salvos (para 1-click)
saved_cards JSONB DEFAULT '[]', -- [{ last4, brand, token, gateway, exp }]
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE UNIQUE INDEX idx_customer_email ON customers(merchant_id, email);
Tabela: orders
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id UUID REFERENCES merchants(id),
customer_id UUID REFERENCES customers(id),
product_id UUID REFERENCES products(id),
subscription_id UUID REFERENCES subscriptions(id),
-- Valores
amount_cents INT NOT NULL,
currency VARCHAR(3) DEFAULT 'USD',
discount_cents INT DEFAULT 0,
coupon_id UUID REFERENCES coupons(id),
net_amount_cents INT NOT NULL, -- amount - discount
-- Pagamento
payment_method VARCHAR(20), -- credit_card, pix, boleto
gateway VARCHAR(20),
gateway_transaction_id VARCHAR(255),
gateway_response JSONB,
-- Status
status VARCHAR(20) DEFAULT 'pending',
-- pending, processing, approved, declined, refunded, cancelled, expired
paid_at TIMESTAMPTZ,
refunded_at TIMESTAMPTZ,
-- Installments
installments INT DEFAULT 1,
-- Card info (tokenizado, sem dados sensíveis)
card_last4 VARCHAR(4),
card_brand VARCHAR(20),
-- Tracking
ip_address INET,
user_agent TEXT,
utm_source VARCHAR(100),
utm_medium VARCHAR(100),
utm_campaign VARCHAR(100),
utm_content VARCHAR(100),
utm_term VARCHAR(100),
referrer TEXT,
-- Metadata
metadata JSONB DEFAULT '{}',
idempotency_key VARCHAR(64) UNIQUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_orders_merchant ON orders(merchant_id, status);
CREATE INDEX idx_orders_customer ON orders(customer_id);
CREATE INDEX idx_orders_subscription ON orders(subscription_id);
CREATE INDEX idx_orders_created ON orders(created_at);
CREATE INDEX idx_orders_gateway_tx ON orders(gateway, gateway_transaction_id);
Tabela: subscriptions
CREATE TABLE subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id UUID REFERENCES merchants(id),
customer_id UUID REFERENCES customers(id),
product_id UUID REFERENCES products(id),
-- Pricing
price_cents INT NOT NULL,
currency VARCHAR(3) DEFAULT 'USD',
first_payment_price_cents INT, -- preço diferenciado do 1º pagamento
-- Ciclo
billing_cycle VARCHAR(20) NOT NULL, -- monthly, quarterly, yearly
billing_interval INT DEFAULT 1,
current_period_start TIMESTAMPTZ,
current_period_end TIMESTAMPTZ,
next_billing_at TIMESTAMPTZ,
-- Status
status VARCHAR(20) DEFAULT 'trialing',
-- trialing, active, past_due, paused, cancelled, expired
cancelled_at TIMESTAMPTZ,
cancel_reason TEXT,
-- Trial
trial_end TIMESTAMPTZ,
-- Contadores
billing_count INT DEFAULT 0, -- quantas cobranças já foram feitas
-- Dunning
retry_count INT DEFAULT 0,
last_retry_at TIMESTAMPTZ,
-- Gateway
gateway VARCHAR(20),
gateway_subscription_id VARCHAR(255),
card_token TEXT, -- token do cartão para renovação
card_last4 VARCHAR(4),
card_brand VARCHAR(20),
-- Coupon
coupon_id UUID REFERENCES coupons(id),
coupon_cycles_remaining INT DEFAULT 0,
-- Metadata
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_sub_merchant ON subscriptions(merchant_id, status);
CREATE INDEX idx_sub_customer ON subscriptions(customer_id);
CREATE INDEX idx_sub_next_billing ON subscriptions(next_billing_at) WHERE status IN ('active','past_due');
Tabela: webhook_events (idempotência)
CREATE TABLE webhook_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
gateway VARCHAR(20) NOT NULL,
event_id VARCHAR(255) NOT NULL, -- ID do evento no gateway
event_type VARCHAR(100) NOT NULL,
payload JSONB NOT NULL,
processed BOOLEAN DEFAULT false,
processed_at TIMESTAMPTZ,
error TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE UNIQUE INDEX idx_webhook_event ON webhook_events(gateway, event_id);
Tabela: checkout_sessions (abandono de carrinho)
CREATE TABLE checkout_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
merchant_id UUID REFERENCES merchants(id),
product_id UUID REFERENCES products(id),
-- Dados parciais do comprador
email VARCHAR(255),
name VARCHAR(255),
phone VARCHAR(20),
-- Tracking
step VARCHAR(20) DEFAULT 'started', -- started, email, payment, completed
ip_address INET,
user_agent TEXT,
utm_source VARCHAR(100),
utm_medium VARCHAR(100),
utm_campaign VARCHAR(100),
referrer TEXT,
-- Recovery
recovery_email_sent BOOLEAN DEFAULT false,
recovered BOOLEAN DEFAULT false,
-- Timestamps
started_at TIMESTAMPTZ DEFAULT NOW(),
last_activity_at TIMESTAMPTZ DEFAULT NOW(),
completed_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ DEFAULT NOW() + INTERVAL '24 hours'
);
CREATE INDEX idx_session_merchant ON checkout_sessions(merchant_id, step);
CREATE INDEX idx_session_recovery ON checkout_sessions(recovery_email_sent, completed_at)
WHERE completed_at IS NULL AND recovery_email_sent = false;
Tabela: gateway_health (monitoramento de gateways)
CREATE TABLE gateway_health (
id SERIAL PRIMARY KEY,
gateway VARCHAR(20) NOT NULL,
merchant_id UUID REFERENCES merchants(id),
success_count INT DEFAULT 0,
failure_count INT DEFAULT 0,
avg_latency_ms INT DEFAULT 0,
last_success_at TIMESTAMPTZ,
last_failure_at TIMESTAMPTZ,
is_healthy BOOLEAN DEFAULT true,
period_start TIMESTAMPTZ DEFAULT NOW(),
period_end TIMESTAMPTZ DEFAULT NOW() + INTERVAL '5 minutes'
);
CREATE INDEX idx_gw_health ON gateway_health(gateway, merchant_id, period_start);
- checkout:session:{id} — dados temporários da sessão de checkout (TTL 24h)
- idempotency:{key} — controle de idempotência (TTL 24h)
- rate_limit:{ip} — rate limiting por IP (TTL 1min)
- gateway_health:{gateway}:{merchant_id} — circuit breaker (TTL 5min)
- card_token:{temp_token} — token temporário de cartão (TTL 60s)
/home/glauko/checkout/
├── package.json
├── .env
├── .env.example
├── server.js — Entry point + Express app
├── src/
│ ├── config/
│ │ ├── database.js — PostgreSQL connection pool
│ │ ├── redis.js — Redis connection
│ │ └── constants.js — Enums, status constants
│ ├── middleware/
│ │ ├── auth.js — JWT authentication
│ │ ├── rateLimit.js — Rate limiting
│ │ ├── validate.js — Request validation (Joi/Zod)
│ │ └── errorHandler.js
│ ├── routes/
│ │ ├── auth.js — Login/register merchant
│ │ ├── products.js — CRUD produtos
│ │ ├── checkout.js — Página de checkout + processamento
│ │ ├── subscriptions.js — Gestão de assinaturas
│ │ ├── webhooks.js — Receber webhooks dos gateways
│ │ ├── customers.js — Gestão de clientes
│ │ ├── coupons.js — CRUD cupons
│ │ ├── orders.js — Consulta de pedidos
│ │ ├── gateways.js — Config de gateways
│ │ ├── dashboard.js — API do painel admin
│ │ └── export.js — Exportação de dados
│ ├── gateways/
│ │ ├── index.js — Gateway factory/router
│ │ ├── stripe.js — Adaptador Stripe
│ │ ├── mercadopago.js — Adaptador Mercado Pago
│ │ ├── pagbank.js — Adaptador PagBank
│ │ ├── asaas.js — Adaptador Asaas
│ │ └── pagarme.js — Adaptador Pagar.me
│ ├── services/
│ │ ├── payment.js — Orquestração de pagamento
│ │ ├── subscription.js — Engine de assinaturas
│ │ ├── dunning.js — Retentativas e cobrança
│ │ ├── checkout.js — Lógica de checkout
│ │ ├── recovery.js — Recuperação de carrinho abandonado
│ │ ├── notification.js — Emails/SMS
│ │ └── analytics.js — Métricas e conversão
│ ├── jobs/
│ │ ├── billingWorker.js — Processar cobranças recorrentes
│ │ ├── dunningWorker.js — Retentativas de cobrança falha
│ │ ├── recoveryWorker.js — Enviar emails de recuperação
│ │ └── healthWorker.js — Monitorar saúde dos gateways
│ └── utils/
│ ├── currency.js — Conversão e formatação de moeda
│ ├── crypto.js — Hashing, webhook signatures
│ └── logger.js — Logging estruturado
├── public/
│ ├── checkout/
│ │ ├── index.html — Página de checkout (SPA)
│ │ ├── checkout.css — Estilos do checkout
│ │ └── checkout.js — Lógica frontend do checkout
│ └── dashboard/
│ ├── index.html — Dashboard admin
│ ├── dashboard.css
│ └── dashboard.js
└── migrations/
└── 001_initial.sql — Schema completo
Cada gateway implementa a mesma interface:
class GatewayAdapter {
// Cobranças
async createCharge({ amount_cents, currency, card_token, customer, metadata })
async refundCharge(transaction_id, amount_cents)
// Assinaturas
async createSubscription({ plan, customer, card_token, first_amount_cents })
async cancelSubscription(subscription_id)
async chargeSubscription(subscription_id, amount_cents)
// Tokenização
getPublicKey() // retorna chave pública para o frontend tokenizar
// Clientes
async createCustomer({ email, name, document })
// Webhooks
verifyWebhookSignature(payload, signature, secret)
parseWebhookEvent(payload) // normaliza evento para formato interno
// Health
async healthCheck()
}
API Base: https://api.stripe.com/v1
Auth: Bearer sk_xxx
Frontend: Stripe.js + Elements (hosted fields)
Recursos usados:
- POST /v1/payment_intents → criar cobrança
- POST /v1/customers → criar cliente
- POST /v1/subscriptions → criar assinatura
- POST /v1/subscriptions/{id} → cancelar
- POST /v1/refunds → estornar
Assinatura com desconto no 1º pagamento:
- Criar subscription com `add_invoice_items` para ajustar o 1º valor
- OU usar `coupon` com `duration: once` aplicado na subscription
- OU usar subscription_schedule com phases diferentes
Multi-currency: NATIVO — aceita 135+ moedas
Webhooks: HMAC-SHA256 com whsec_xxx
Taxas: 2.9% + $0.30 (internacional: 3.9% + $0.30)
API Base: https://api.mercadopago.com/v1
Auth: Bearer ACCESS_TOKEN
Frontend: MercadoPago.js + CardForm (tokenização client-side)
Recursos usados:
- POST /v1/payments → criar pagamento (transparente)
- POST /v1/customers → criar cliente
- POST /preapproval → criar assinatura
- PUT /preapproval/{id} → atualizar/cancelar
- GET /v1/payments/{id} → consultar
Assinatura com desconto no 1º pagamento:
- Usar campo `auto_recurring.transaction_amount` para valor normal
- 1º pagamento: criar payment avulso com valor reduzido + preapproval separado
Multi-currency: Limitado — opera na moeda local de cada país
Webhooks: POST notification com header x-signature
Taxas Brasil: 4.98% (crédito), PIX 0.99%
API Base: https://api.pagseguro.com (sandbox: https://sandbox.api.pagseguro.com)
Auth: Bearer token
Frontend: PagBank.js encrypt (cartão encriptado client-side)
Recursos usados:
- POST /orders → criar pedido (checkout transparente)
- POST /orders/{id}/pay → processar pagamento
- GET /orders/{id} → consultar
- Recurrence via charges API
Assinatura com desconto no 1º pagamento:
- Criar charge avulso com valor reduzido
- Configurar recurrence plan para valor normal a partir da 2ª cobrança
Multi-currency: Apenas BRL
Webhooks: notification_urls configuradas no pedido
Taxas: 3.05% crédito (D+30), PIX 1.89%
API Base: https://api.asaas.com/v3 (sandbox: https://sandbox.asaas.com/api/v3)
Auth: access_token no header
Frontend: Formulário próprio → POST para API (tokenização via API)
Recursos usados:
- POST /customers → criar cliente
- POST /payments → criar cobrança (avulsa ou recorrente)
- POST /subscriptions → criar assinatura
- DELETE /subscriptions/{id} → cancelar
- POST /payments/{id}/refund → estornar
Assinatura com desconto no 1º pagamento:
- Campo `externalReference` + lógica manual:
- 1ª cobrança: valor com desconto
- Atualizar subscription value após 1º pagamento confirmado
Multi-currency: Apenas BRL
Webhooks: POST para URL configurada, sem signature (validar por IP)
Taxas: 2.99% + R$0.49 crédito, R$1.99 boleto, R$1.99 PIX
API Base: https://api.pagar.me/core/v5
Auth: Basic base64(sk_xxx:)
Frontend: tokenizecard.js (token expira em 60s)
Recursos usados:
- POST /orders → criar pedido com pagamento
- POST /customers → criar cliente
- POST /plans → criar plano
- POST /subscriptions → criar assinatura
- PATCH /subscriptions/{id} → atualizar/cancelar
Assinatura com desconto no 1º pagamento:
- Usar `increments` ou `discounts` na subscription
- Campo `cycles` no discount para aplicar em N ciclos
Multi-currency: Apenas BRL
Webhooks: POST com header x-hub-signature (HMAC-SHA1)
Rate Limits: GET 4/s default, POST sem limite
Taxas: Negociáveis (geralmente 2.49%–3.19% crédito)
// Roteamento inteligente:
async function routePayment(merchant_id, payment) {
const gateways = await getActiveGateways(merchant_id);
// 1. Filtrar por moeda suportada
const compatible = gateways.filter(g =>
g.supported_currencies.includes(payment.currency)
);
// 2. Filtrar por método de pagamento
const methodCompatible = compatible.filter(g =>
g.supported_methods.includes(payment.method)
);
// 3. Ordenar por prioridade + saúde
const sorted = methodCompatible.sort((a, b) => {
const healthA = getGatewayHealth(a.gateway, merchant_id);
const healthB = getGatewayHealth(b.gateway, merchant_id);
if (!healthA.is_healthy && healthB.is_healthy) return 1;
if (healthA.is_healthy && !healthB.is_healthy) return -1;
return a.priority - b.priority;
});
// 4. Tentar em cascata (transparent retry)
for (const gw of sorted) {
try {
const result = await processPayment(gw, payment);
if (result.approved) return result;
if (result.hard_decline) break; // não tentar outro gateway
} catch (err) {
markGatewayFailure(gw.gateway, merchant_id);
continue; // tentar próximo
}
}
return { approved: false, reason: 'all_gateways_failed' };
}
POST /api/auth/register — Registrar merchant
POST /api/auth/login — Login (retorna JWT)
POST /api/auth/refresh — Refresh token
GET /api/auth/me — Dados do merchant logado
GET /api/products — Listar produtos do merchant
POST /api/products — Criar produto
GET /api/products/:id — Detalhe do produto
PUT /api/products/:id — Atualizar produto
DELETE /api/products/:id — Desativar produto
GET /api/checkout/:product_id/config — Config pública do checkout (nome, preço, logo, gateway public keys)
POST /api/checkout/:product_id/session — Iniciar sessão de checkout (tracking)
PUT /api/checkout/session/:id — Atualizar dados parciais (para abandono)
POST /api/checkout/:product_id/pay — PROCESSAR PAGAMENTO (o endpoint principal)
POST /api/checkout/:product_id/validate-coupon — Validar cupom em tempo real
POST /api/checkout/:product_id/pay — Payload:
{
"customer": {
"email": "user@example.com",
"name": "João Silva",
"document": "12345678900",
"phone": "+5511999999999"
},
"payment": {
"method": "credit_card",
"card_token": "tok_xxxx", // token do gateway
"gateway": "stripe", // gateway preferido (ou auto)
"installments": 1
},
"coupon_code": "LAUNCH10",
"session_id": "sess_xxx",
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "launch"
},
"idempotency_key": "uuid-unique"
}
GET /api/subscriptions — Listar assinaturas
GET /api/subscriptions/:id — Detalhe
PATCH /api/subscriptions/:id — Atualizar (pause, resume, change card)
DELETE /api/subscriptions/:id — Cancelar
GET /api/subscriptions/:id/invoices — Histórico de cobranças
POST /api/webhooks/stripe — Receber eventos Stripe
POST /api/webhooks/mercadopago — Receber eventos Mercado Pago
POST /api/webhooks/pagbank — Receber eventos PagBank
POST /api/webhooks/asaas — Receber eventos Asaas
POST /api/webhooks/pagarme — Receber eventos Pagar.me
Cada endpoint: 1. Verifica assinatura do webhook 2. Checa idempotência (event_id já processado?) 3. Normaliza evento para formato interno 4. Enfileira para processamento assíncrono 5. Retorna 200 OK imediatamente
GET /api/customers — Listar clientes
GET /api/customers/:id — Detalhe com assinaturas e pedidos
PUT /api/customers/:id — Atualizar
GET /api/customers/:id/orders — Histórico de pedidos
GET /api/coupons — Listar cupons
POST /api/coupons — Criar cupom
PUT /api/coupons/:id — Atualizar
DELETE /api/coupons/:id — Desativar
GET /api/orders — Listar pedidos (filtros: status, date, gateway)
GET /api/orders/:id — Detalhe do pedido
POST /api/orders/:id/refund — Solicitar reembolso
GET /api/orders/export — Exportar CSV/JSON
GET /api/gateways — Listar gateways configurados
POST /api/gateways — Adicionar gateway
PUT /api/gateways/:id — Atualizar credenciais/prioridade
DELETE /api/gateways/:id — Remover gateway
POST /api/gateways/:id/test — Testar conexão com gateway
GET /api/gateways/health — Status de saúde de todos os gateways
GET /api/dashboard/overview — KPIs: receita, vendas, MRR, churn, conversão
GET /api/dashboard/revenue — Receita por período (dia/semana/mês)
GET /api/dashboard/conversions — Funil de checkout (sessão→email→pagamento→aprovado)
GET /api/dashboard/gateways — Performance por gateway (aprovação, latência)
GET /api/dashboard/subscriptions — Métricas de assinatura (MRR, churn, LTV)
GET /api/dashboard/geo — Distribuição geográfica de clientes
GET /api/dashboard/sources — Atribuição por UTM/fonte
Roda a cada 1 hora. Consulta subscriptions com next_billing_at <= NOW() e status = 'active'.
Para cada assinatura vencida:
1. Determinar valor:
- Se billing_count == 0 E first_payment_price_cents != null → usar preço reduzido
- Se coupon ativo E coupon_cycles_remaining > 0 → aplicar desconto
- Senão → preço normal
2. Criar order com status 'processing'
3. Cobrar via gateway (usando card_token salvo)
4. Se aprovado:
- Order status → 'approved'
- Subscription → avançar período (current_period_start/end, next_billing_at)
- billing_count++
- coupon_cycles_remaining-- (se aplicável)
- Enviar recibo por email
5. Se recusado:
- Order status → 'declined'
- Subscription status → 'past_due'
- Enfileirar para dunning
Roda a cada 6 horas. Gerencia retentativas de cobranças falhas.
Schedule de retentativas:
- Dia 0: Falha original → email "problema com pagamento"
- Dia 1: 1ª retentativa → email "ação necessária"
- Dia 3: 2ª retentativa → email "urgente"
- Dia 5: 3ª retentativa → email "última chance" + SMS (se phone)
- Dia 7: 4ª retentativa → email final
- Dia 10: Grace period encerrado → status 'cancelled' + email "cancelado"
Para cada retentativa:
1. Buscar subscriptions com status 'past_due'
2. Calcular próximo retry com base no retry_count
3. Tentar cobrar novamente (com transparent retry entre gateways)
4. Se aprovou: restaurar para 'active' + email "pagamento regularizado"
5. Se falhou: incrementar retry_count, agendar próxima
6. Se esgotou: cancelar subscription + email
Roda a cada 30 minutos.
1. Buscar checkout_sessions com:
- step != 'completed'
- email preenchido
- last_activity_at < NOW() - 30min
- recovery_email_sent = false
- expires_at > NOW()
2. Para cada sessão abandonada:
- Enviar email de recuperação com link direto para checkout
- Marcar recovery_email_sent = true
3. Opcionalmente: 2º email após 24h (se configurado pelo merchant)
Roda a cada 5 minutos.
1. Agregar success/failure counts dos últimos 5 minutos por gateway
2. Se failure_rate > 20% OU 5 falhas consecutivas:
- Marcar gateway como unhealthy
- Alertar merchant via webhook
3. Se gateway voltar a funcionar (3 sucesso consecutivos):
- Restaurar healthy
URL: https://paggo.me/c/{product_id} ou https://paggo.me/c/{slug}
Layout single-page otimizado para conversão:
┌─────────────────────────────────────────────┐
│ [Logo Merchant] 🔒 Pagamento Seguro│
├─────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌─────────────────────┐ │
│ │ PRODUTO │ │ FORMULÁRIO │ │
│ │ ─────────── │ │ │ │
│ │ [Imagem] │ │ 📧 Email │ │
│ │ │ │ 👤 Nome Completo │ │
│ │ Nome Produto │ │ 📱 Telefone │ │
│ │ │ │ │ │
│ │ $9.00/mês │ │ 💳 Método Pagamento│ │
│ │ │ │ ┌─ Card ─┬─ PIX ─┐│ │
│ │ ✓ Acesso │ │ │ │ ││ │
│ │ imediato │ │ │ Nº Cartão ││ │
│ │ ✓ Cancelar │ │ │ Val CVV ││ │
│ │ quando │ │ │ Nome no Cartão ││ │
│ │ quiser │ │ └────────┴────────┘│ │
│ │ │ │ │ │
│ │ ┌──────────┐ │ │ 🏷️ Cupom [______] │ │
│ │ │ CUPOM │ │ │ │ │
│ │ │ -$X off │ │ │ ┌───────────────┐ │ │
│ │ └──────────┘ │ │ │ TOTAL: $9.00 │ │ │
│ │ │ │ │ 1º pag: $4.50 │ │ │
│ └──────────────┘ │ │ Depois: $9/mês │ │ │
│ │ └───────────────┘ │ │
│ │ │ │
│ │ [🔒 PAGAR AGORA $9] │ │
│ │ │ │
│ │ 🔒 Dados protegidos │ │
│ │ SSL 256-bit │ │
│ └─────────────────────┘ │
│ │
│ ───────── Trust Badges ───────── │
│ [Stripe] [Visa] [MC] [SSL] [Garantia] │
└─────────────────────────────────────────────┘
Para cada gateway, carregar o SDK JavaScript correspondente: - Stripe: Stripe.js + stripe.elements().create('card') - Mercado Pago: MercadoPago.js + cardForm.create() - PagBank: PagBank.js + PagSeguro.encryptCard() - Pagar.me: tokenizecard.js - Asaas: Formulário próprio → API backend (sem SDK client-side)
O frontend detecta qual gateway será usado (baseado na config do produto) e carrega o SDK apropriado.
// Eventos disparados em cada etapa:
checkout_view → ao carregar a página
checkout_initiate → ao preencher email
checkout_payment → ao selecionar método de pagamento
checkout_submit → ao clicar pagar
checkout_success → pagamento aprovado
checkout_declined → pagamento recusado
// Pixels suportados:
- Facebook Pixel (fbq)
- Google Analytics (gtag)
- Google Ads Conversion
- TikTok Pixel
- Meta CAPI (server-side)
URL: https://paggo.me/admin
POST /api/auth/login → cookie JWT → redirect /admin/dashboard
Dashboard Principal (/admin/dashboard) - KPI Cards: Receita Hoje | Vendas Hoje | MRR | Taxa de Conversão | Churn Rate - Gráfico: Receita dos últimos 30 dias (line chart) - Gráfico: Vendas por gateway (pie chart) - Tabela: Últimas 20 transações (status, valor, cliente, gateway) - Widget: Saúde dos gateways (verde/amarelo/vermelho)
Produtos (/admin/products) - Lista de produtos com toggle ativo/inativo - Modal criar/editar produto com todos os campos - Preview do link de checkout - Copiar link rápido
Pedidos (/admin/orders) - Tabela filtrável (status, data, gateway, método, cliente) - Busca por email ou ID - Detalhe do pedido com timeline (criado → processado → aprovado) - Botão de reembolso - Export CSV
Assinaturas (/admin/subscriptions) - Tabela: assinaturas ativas, past_due, cancelled - Detalhe com histórico de cobranças - Ações: pausar, cancelar, alterar cartão - Métricas: MRR, LTV médio, churn por mês
Clientes (/admin/customers) - Lista de clientes com total gasto e nº de compras - Detalhe com todas as orders e subscriptions - Cartões salvos (last4 + brand apenas)
Cupons (/admin/coupons) - CRUD de cupons - Stats: uso, receita gerada com desconto
Gateways (/admin/gateways) - Configuração de credenciais por gateway - Drag-and-drop para prioridade de failover - Botão “Testar conexão” - Métricas: taxa de aprovação, latência média, volume
Configurações (/admin/settings) - Dados do merchant (logo, cor, domínio) - Webhook URL - Configuração de emails - Configuração de dunning (dias de retentativa, conteúdo)
URL: https://paggo.me/c/{product_id}/success?order={id}
✅ Pagamento Aprovado!
Obrigado, {nome}!
Seu pedido #{order_id} foi confirmado.
📧 Enviamos os detalhes para {email}
[Produto: {nome}]
[Valor: ${amount}]
[Método: Cartão ****1234]
Próxima cobrança: {data} — ${valor}/mês
[Acessar produto →]
URL: https://paggo.me/c/{product_id}/error
❌ Pagamento não aprovado
Possíveis motivos:
• Saldo insuficiente
• Dados incorretos
• Cartão bloqueado
[Tentar novamente →]
[Usar outro método de pagamento →]
URL: https://paggo.me/my (login via magic link por email)
Minhas Assinaturas:
- {Produto} — $9/mês — Ativa — Próximo: 20/Abr
[Alterar cartão] [Cancelar]
Meus Pagamentos:
- 20/Mar — $9.00 — ✅ Aprovado — Cartão ****1234
- 20/Feb — $4.50 — ✅ Aprovado — Cartão ****1234 (1º pagamento)
/health endpoint para monitoramentoPara $20.000/dia a $9/transação ≈ 2.222 transações/dia ≈ ~93/hora ≈ ~1.5/minuto
Uma instância Node.js + PostgreSQL + Redis em um VPS de 4GB RAM pode facilmente lidar com 10x esse volume. Mesmo em picos (Black Friday), a fila Bull absorve os spikes.
VPS (Hetzner ou DigitalOcean):
- 4 vCPU, 8GB RAM, 80GB SSD
- Ubuntu 22.04 LTS
- Node.js 20 LTS
- PostgreSQL 16
- Redis 7
- Nginx (reverse proxy + SSL)
- PM2 (process manager)
paggo.me → A record → IP do servidor
www.paggo.me → CNAME → paggo.me
api.paggo.me → A record → IP do servidor (opcional, pode ser mesmo)
#!/bin/bash
cd /home/glauko/checkout
git pull origin main
npm install --production
npx knex migrate:latest
pm2 reload paggo
- PostgreSQL: pg_dump diário → S3/Cloudflare R2
- Redis: snapshot a cada 6h (RDB)
- .env: backup manual seguro
Para cada gateway (Stripe, Mercado Pago, PagBank, Asaas, Pagar.me): 1. ✅ Criar cobrança única (cartão) → aprovado 2. ✅ Criar cobrança única (cartão) → recusado 3. ✅ Criar assinatura com desconto no 1º pagamento 4. ✅ Renovação automática da assinatura 5. ✅ Cancelar assinatura 6. ✅ Reembolso 7. ✅ Webhook recebido e processado 8. ✅ PIX (gateways que suportam) 9. ✅ Boleto (gateways que suportam)
[ ] Domínio paggo.me apontado para servidor
[ ] SSL/TLS ativo via Cloudflare
[ ] Credenciais de produção configuradas (todos os gateways)
[ ] Webhook URLs de produção registradas em cada gateway
[ ] Backup automático configurado
[ ] PM2 configurado com auto-restart
[ ] Monitoramento ativo (uptime, logs)
[ ] Email transacional configurado (domínio verificado)
[ ] Primeiro produto criado
[ ] Primeiro checkout de teste em produção (cobrança real mínima + reembolso)
| Fase | Descrição | Linhas Estimadas |
|---|---|---|
| 1 | Fundação — Schema DB + Estrutura do projeto | ~600 |
| 2 | Gateway Adapters — 5 integrações de pagamento | ~800 |
| 3 | API Backend — Todas as rotas | ~1200 |
| 4 | Engine de Assinaturas + Dunning + Workers | ~500 |
| 5 | Frontend — Página de Checkout | ~800 |
| 6 | Dashboard Admin — Painel completo | ~1500 |
| 7 | Páginas Públicas — Sucesso, Erro, Portal | ~300 |
| 8 | Segurança + Performance | ~300 |
| 9 | Deploy + Infraestrutura | ~200 |
| 10 | Testes + Go-Live | ~200 |
| TOTAL | ~6.400 linhas |
{
"dependencies": {
"express": "^4.18",
"pg": "^8.11",
"ioredis": "^5.3",
"bull": "^4.12",
"jsonwebtoken": "^9.0",
"bcryptjs": "^2.4",
"stripe": "^14.0",
"mercadopago": "^2.0",
"zod": "^3.22",
"helmet": "^7.1",
"cors": "^2.8",
"compression": "^1.7",
"winston": "^3.11",
"node-cron": "^3.0",
"nodemailer": "^6.9",
"uuid": "^9.0",
"dotenv": "^16.3"
}
}
| Aspecto | Digital Guru | Paggo.me |
|---|---|---|
| Custo | Plano mensal + taxa por venda | Grátis (self-hosted) |
| Controle | Limitado aos recursos deles | Total — código é seu |
| Customização | Checkout pré-definido | 100% customizável |
| Dados | Nos servidores deles | Seus servidores |
| Gateways | 20+ (deles) | 5 integrados + ilimitado futuro |
| Failover | Básico | Transparent retry com routing inteligente |
| Latência | Servidores deles | Seu servidor otimizado |
| Escala | Depende do plano deles | Sem limites (seu infra) |
| Analytics | Dashboard deles | Integrado com ConstruX Analytics |
| Assinatura 1º pgto | Manual | Nativo e configurável |