Melhor Configuração Stripe para Negócio de Assinatura: 4 Modelos que Rodamos
Traduzir o seguinte artigo markdown para português brasileiro.
Configurei integrações Stripe provavelmente uma dúzia de vezes nos últimos anos, e aqui está o que aprendi: o tutorial de início rápido é a parte fácil. A parte difícil é executar quatro modelos de preços completamente diferentes na mesma organização, lidar com 30+ moedas de países sem erros de arredondamento e garantir que seus manipuladores de webhook não falhem silenciosamente às 3 da manhã de um sábado.
Este não é outro post "crie uma sessão de checkout em 5 minutos". Vamos analisar quatro modelos de preços de produção que construímos e operamos -- assinaturas em camadas com preços regionais, comissões de marketplace via Stripe Connect, doações recorrentes vinculadas a entidades específicas e pagamentos de serviço únicos. Cada um tem seu próprio conjunto de pegadinhas, e vou compartilhar os padrões de código específicos e decisões de configuração que nos salvaram de bugs dolorosos.
Índice
- Por que uma configuração Stripe não serve para todos
- Modelo 1: Assinaturas em Camadas com Preços Regionais
- Modelo 2: Comissões de Marketplace com Stripe Connect
- Modelo 3: Doações Recorrentes Vinculadas a Entidades
- Modelo 4: Pagamentos de Serviço Únicos
- Arquitetura de Webhook que Realmente Funciona
- Lógica de Reembolso de Pagamento Falhado e Dunning
- O Bug de Moeda com Zero Decimais que Nos Custou Dinheiro
- Comparando os Quatro Modelos
- FAQ

Por que uma configuração Stripe não serve para todos
A documentação Stripe é excelente para negócios de modelo único. Você escolhe assinaturas ou pagamentos únicos, segue o guia e está ao vivo. Mas a maioria dos negócios reais não permanece tão simples por muito tempo.
Operamos em múltiplos produtos: uma plataforma SaaS com assinaturas em camadas, um marketplace que cobra comissões de prestadores, uma iniciativa de caridade com patrocínios recorrentes e um sistema de agendamento de consultas com pagamentos únicos. Cada um desses vive na mesma conta Stripe, mas requer configurações fundamentalmente diferentes para produtos, preços, webhooks e gerenciamento de clientes.
O maior erro que vejo as equipes cometerem é tentar forçar toda sua cobrança em um modelo. Uma arquitetura centrada em assinatura quebra quando você precisa de pagamentos únicos. Uma abordagem apenas com sessão de checkout desmorona quando você precisa de cobrança recorrente com prorrateição. Você precisa pensar sobre sua configuração Stripe como um portfólio de padrões de cobrança.
Se você está construindo algo semelhante -- especialmente em uma arquitetura headless com Next.js ou Astro no frontend -- os padrões aqui economizarão semanas de depuração.
Modelo 1: Assinaturas em Camadas com Preços Regionais
Este é o modelo mais complexo que executamos, e foi aquele que nos ensinou as lições mais dolorosas. A configuração: quatro camadas (Gratuito, Básico, Pro, Premium) com preços que variam em 30+ países.
A Estrutura de Produto no Stripe
No Stripe, cada camada é um Produto separado. Cada Produto tem múltiplos Preços -- um por combinação de moeda/região. Isso é importante: não tente usar um único Preço e fazer conversão de moeda você mesmo. A preços multi-moeda do Stripe é propositalmente construída para isso.
// Configuração de preços regionais
const REGIONAL_PRICING = {
pro: {
USD: { monthly: 2900, yearly: 29000 }, // $29/mês, $290/ano
EUR: { monthly: 2700, yearly: 27000 }, // €27/mês, €270/ano
GBP: { monthly: 2300, yearly: 23000 }, // £23/mês, £230/ano
JPY: { monthly: 4200, yearly: 42000 }, // ¥4.200/mês -- NÃO ¥42.00!
KRW: { monthly: 38000, yearly: 380000 }, // ₩38.000/mês
INR: { monthly: 190000, yearly: 1900000 }, // ₹1.900/mês
BRL: { monthly: 14900, yearly: 149000 }, // R$149/mês
},
// ... repetir para basic, premium
};
Veja esses valores de JPY e KRW? Vou detalhar esse bug depois, mas resumindo: são moedas com zero decimais. Quando você passa 4200 para JPY, Stripe interpreta como ¥4.200 -- não ¥42.00. Se você multiplicar por 100 como faz para USD, você acabou de cobrar de alguém ¥420.000 ($2.800) em vez de ¥4.200 ($28). Pergunte-me como sei.
Lógica de Teste Regional
Nem toda região tem trial gratuito. Aprendemos isso da forma difícil com certos mercados do Sudeste Asiático onde o abuso de trial era significativamente maior que outras regiões. Nossa configuração fica assim:
const TRIAL_CONFIG = {
default_trial_days: 14,
excluded_regions: ['VN', 'PH', 'ID', 'TH', 'MM', 'KH', 'LA'],
reduced_trial_regions: {
IN: 7,
BR: 7,
},
};
function getTrialDays(countryCode) {
if (TRIAL_CONFIG.excluded_regions.includes(countryCode)) {
return 0;
}
return TRIAL_CONFIG.reduced_trial_regions[countryCode]
?? TRIAL_CONFIG.default_trial_days;
}
Isso é passado para a criação de assinatura:
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: regionalPriceId }],
trial_period_days: getTrialDays(customer.address.country),
payment_behavior: 'default_incomplete',
payment_settings: {
save_default_payment_method: 'on_subscription',
},
expand: ['latest_invoice.payment_intent'],
});
Comportamento de Prorrateiação
Quando alguém faz upgrade de Básico para Pro no meio do ciclo, você precisa decidir: ele paga a diferença imediatamente ou no próximo ciclo de cobrança? Usamos create_prorations com pagamento imediato:
const updatedSubscription = await stripe.subscriptions.update(subscriptionId, {
items: [{
id: existingItemId,
price: newPriceId,
}],
proration_behavior: 'create_prorations',
payment_behavior: 'pending_if_incomplete',
});
Para downgrades, agendamos a mudança para o final do período de cobrança. Ninguém quer um cálculo de crédito surpresa em sua fatura.
Portal do Cliente
O Portal do Cliente do Stripe é subestimado. Em vez de construir sua própria UI de gerenciamento de assinatura, configure o portal e redirecione os usuários para lá:
const portalSession = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: `${process.env.APP_URL}/settings/billing`,
});
Configure no Painel do Stripe para permitir mudanças de plano, cancelamento (com pesquisa de motivo de cancelamento -- os dados são ouro) e atualizações de método de pagamento. Isso sozinho nos economizou provavelmente 40 horas de desenvolvimento frontend.
Modelo 2: Comissões de Marketplace com Stripe Connect
Nosso modelo de marketplace usa Stripe Connect para facilitar pagamentos entre clientes e prestadores de serviço. A plataforma cobra uma comissão em cada transação. Esta é a configuração Stripe Connect que a maioria dos tutoriais ignora.
Integração de Prestador
Todo prestador no marketplace precisa de uma conta Stripe Express. O fluxo de integração cria a conta e os redireciona para a integração hospedada do Stripe:
const account = await stripe.accounts.create({
type: 'express',
country: provider.country,
email: provider.email,
capabilities: {
card_payments: { requested: true },
transfers: { requested: true },
},
business_type: 'individual',
metadata: {
provider_id: provider.id,
platform: 'fme',
},
});
const accountLink = await stripe.accountLinks.create({
account: account.id,
refresh_url: `${process.env.APP_URL}/provider/onboarding/refresh`,
return_url: `${process.env.APP_URL}/provider/onboarding/complete`,
type: 'account_onboarding',
});
O detalhe chave: refresh_url é para onde o Stripe envia usuários se o link expirar. Isso acontece com mais frequência do que você pensaria -- se alguém começar a integração em seu telefone, se distrair e voltar depois. Sempre manipule isso com elegância gerando um novo link.
Estrutura de Comissão
Quando um cliente agenda um serviço, criamos um PaymentIntent com um application_fee_amount:
const paymentIntent = await stripe.paymentIntents.create({
amount: bookingAmountInCents,
currency: 'usd',
application_fee_amount: Math.round(bookingAmountInCents * 0.15), // taxa de plataforma de 15%
transfer_data: {
destination: providerStripeAccountId,
},
metadata: {
booking_id: booking.id,
provider_id: provider.id,
customer_id: customer.id,
},
});
A comissão de 15% vai para a plataforma. Os 85% restantes (menos a taxa de processamento do Stripe) vão para a conta Express do prestador.
Agendamento de Pagamentos
Por padrão, Stripe paga contas Express em base contínua. Substituímos isso por pagamentos semanais, o que nos dá um buffer para reembolsos e disputas:
await stripe.accounts.update(providerStripeAccountId, {
settings: {
payouts: {
schedule: {
interval: 'weekly',
weekly_anchor: 'friday',
},
},
},
});
Pagamentos na sexta-feira significam que prestadores veem dinheiro em suas contas bancárias na segunda-feira. É uma pequena coisa, mas importa enormemente para a satisfação e retenção do prestador.
Webhooks Específicos de Connect
Com Stripe Connect, você recebe webhooks tanto para sua plataforma QUANTO para suas contas conectadas. Você precisa de um endpoint de webhook separado para eventos de Connect:
// Endpoint webhook regular
app.post('/webhooks/stripe', handlePlatformWebhooks);
// Endpoint webhook de Connect
app.post('/webhooks/stripe-connect', handleConnectWebhooks);
O manipulador de webhook de Connect precisa verificar o evento de forma diferente e verificar o campo account:
async function handleConnectWebhooks(req, res) {
const event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'],
process.env.STRIPE_CONNECT_WEBHOOK_SECRET // Segredo diferente!
);
const connectedAccountId = event.account;
// Agora manipule o evento no contexto da conta conectada
}

Modelo 3: Doações Recorrentes Vinculadas a Entidades
Este é para uma iniciativa de caridade animal que estamos construindo -- pense em "patrocinar um animal específico" com doações mensais recorrentes. O doador escolhe um animal, define uma quantidade mensal e recebe atualizações de fotos.
Assinaturas Vinculadas a Entidades
O truque aqui é vincular uma assinatura Stripe a uma entidade específica (animal) em seu banco de dados. Fazemos isso inteiramente através de metadados:
const subscription = await stripe.subscriptions.create({
customer: donorCustomerId,
items: [{
price_data: {
currency: 'usd',
product: sponsorshipProductId,
unit_amount: donorChosenAmount, // Doador escolhe sua quantidade
recurring: {
interval: 'month',
},
},
}],
metadata: {
entity_id: animal.id,
entity_type: 'animal',
entity_name: animal.name,
sponsor_email: donor.email,
},
});
Usar price_data em vez de um Preço pré-criado permite que doadores escolham sua própria quantidade mensal. Isso é mais limpo que criar centenas de objetos Price.
E-mails de Atualização Mensal
Quando invoice.paid é disparado para uma assinatura de patrocínio, acionamos o fluxo de atualização mensal:
async function handleSponsorshipInvoicePaid(invoice) {
const subscription = await stripe.subscriptions.retrieve(invoice.subscription);
const entityId = subscription.metadata.entity_id;
// Coloque na fila o e-mail de atualização mensal com fotos recentes
await emailQueue.add('sponsorship-update', {
donorEmail: subscription.metadata.sponsor_email,
entityId,
invoiceAmount: invoice.amount_paid,
invoicePdf: invoice.invoice_pdf,
});
}
O e-mail inclui o PDF da fatura (Stripe gera isso automaticamente), fotos recentes do animal patrocinado e uma atualização de cuidado. É um pequeno toque que reduz dramaticamente o churn em doações recorrentes.
Manipulando Cancelamentos de Doação
Quando alguém cancela seu patrocínio, você precisa manipular diferentemente de um cancelamento de SaaS. Não há "downgrade" -- é cancelar ou nada. Mas você quer deixar fácil se re-inscrever depois:
async function handleSponsorshipCancellation(subscription) {
const entityId = subscription.metadata.entity_id;
// Marque o patrocínio como inativo, não deletado
await db.sponsorships.update({
where: { stripeSubscriptionId: subscription.id },
data: {
status: 'inactive',
cancelledAt: new Date(),
},
});
// Envie e-mail "vamos sentir sua falta" com link fácil de re-inscrição
await sendCancellationEmail(subscription.metadata.sponsor_email, entityId);
}
Modelo 4: Pagamentos de Serviço Únicos
O modelo mais simples, mas ainda há detalhes que importam. Este padrão é para agendamentos de consultas onde alguém paga uma vez e recebe um serviço -- sem cobrança recorrente.
Sessão de Checkout com Dados de Reserva
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [{
price: consultationPriceId,
quantity: 1,
}],
customer_email: customer.email,
metadata: {
booking_id: booking.id,
service_type: 'consultation',
appointment_date: booking.date.toISOString(),
practitioner_id: booking.practitionerId,
},
success_url: `${process.env.APP_URL}/booking/confirmed?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/booking/${booking.id}`,
expires_after: 1800, // 30 minutos
payment_intent_data: {
metadata: {
booking_id: booking.id,
},
},
});
Duas coisas a notar. Primeira: expires_after impede que sessões de checkout abandonadas permaneçam. Um slot de reserva não deve ser mantido para sempre. Segunda: duplicamos o booking_id em payment_intent_data.metadata porque os metadados do PaymentIntent são separados dos metadados da Sessão de Checkout. Quando você receber o webhook payment_intent.succeeded, você vai querer esse ID de reserva bem ali.
Confirmação de Pagamento + Reserva
Em checkout.session.completed, confirmamos a reserva e enviamos tudo em um único disparo:
async function handleCheckoutComplete(session) {
const bookingId = session.metadata.booking_id;
// Confirme a reserva
const booking = await db.bookings.update({
where: { id: bookingId },
data: {
status: 'confirmed',
paymentSessionId: session.id,
paidAt: new Date(),
},
});
// Envie confirmação para cliente
await sendBookingConfirmation(session.customer_email, booking);
// Notifique o prestador
await notifyPractitioner(booking.practitionerId, booking);
}
Arquitetura de Webhook que Realmente Funciona
Em todos os quatro modelos, webhooks são a espinha dorsal. Aqui está a arquitetura que estabelecemos após muitas sessões de depuração:
const WEBHOOK_HANDLERS = {
'checkout.session.completed': handleCheckoutComplete,
'invoice.paid': handleInvoicePaid,
'invoice.payment_failed': handlePaymentFailed,
'customer.subscription.created': handleSubscriptionCreated,
'customer.subscription.updated': handleSubscriptionUpdated,
'customer.subscription.deleted': handleSubscriptionDeleted,
'account.updated': handleConnectAccountUpdated,
'payment_intent.succeeded': handlePaymentSucceeded,
};
async function webhookHandler(req, res) {
let event;
try {
event = stripe.webhooks.constructEvent(
req.rawBody, // Você precisa do corpo bruto, não JSON analisado
req.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.error('Falha na verificação de assinatura do webhook:', err.message);
return res.status(400).send();
}
// Idempotência: verifique se já processamos este evento
const processed = await db.webhookEvents.findUnique({
where: { stripeEventId: event.id },
});
if (processed) {
return res.status(200).json({ received: true, duplicate: true });
}
const handler = WEBHOOK_HANDLERS[event.type];
if (handler) {
try {
await handler(event.data.object, event);
await db.webhookEvents.create({
data: { stripeEventId: event.id, type: event.type, processedAt: new Date() },
});
} catch (err) {
console.error(`Erro ao processar ${event.type}:`, err);
return res.status(500).send(); // Stripe vai tentar novamente
}
}
res.status(200).json({ received: true });
}
A verificação de idempotência é crítica. Stripe vai retentar webhooks falhados e você absolutamente não quer processar o mesmo evento duas vezes -- especialmente para coisas como criar reservas ou acionar pagamentos.
Lógica de Reembolso de Pagamento Falhado e Dunning
Stripe tem Tentativas Inteligentes integradas, mas você deve camadas sua própria lógica de dunning:
async function handlePaymentFailed(invoice) {
const attemptCount = invoice.attempt_count;
const subscription = await stripe.subscriptions.retrieve(invoice.subscription);
if (attemptCount === 1) {
// Primeira falha: nudge suave
await sendEmail(invoice.customer_email, 'payment-failed-soft', {
updatePaymentUrl: await createPortalLink(invoice.customer),
});
} else if (attemptCount === 2) {
// Segunda falha: mais urgente
await sendEmail(invoice.customer_email, 'payment-failed-urgent', {
updatePaymentUrl: await createPortalLink(invoice.customer),
daysUntilCancellation: 7,
});
} else if (attemptCount >= 3) {
// Aviso final
await sendEmail(invoice.customer_email, 'payment-failed-final', {
updatePaymentUrl: await createPortalLink(invoice.customer),
});
}
}
Configure o cronograma de retempt do Stripe em Painel → Configurações → Assinaturas e e-mails → Gerenciar pagamentos falhados. Usamos 3 retentativas em 14 dias antes do cancelamento.
O Bug de Moeda com Zero Decimais que Nos Custou Dinheiro
Isso merece sua própria seção porque é um bug que morde todos eventualmente. Stripe usa centavos (menor unidade de moeda) para a maioria das moedas. $29.00 se torna 2900. Mas algumas moedas não têm casas decimais.
Aqui estão as moedas com zero decimais que importam:
| Moeda | Código | Exemplo: equivalente a "$29" | O que você passa para o Stripe |
|---|---|---|---|
| Iene Japonês | JPY | ¥4.200 | 4200 (NÃO 420000) |
| Won Coreano | KRW | ₩38.000 | 38000 (NÃO 3800000) |
| Dong Vietnamita | VND | ₫700.000 | 700000 |
| Peso Chileno | CLP | $25.000 | 25000 |
| Guarani Paraguaio | PYG | ₲200.000 | 200000 |
Aqui está a função utilitária que usamos em todos os lugares:
const ZERO_DECIMAL_CURRENCIES = [
'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW',
'MGA', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF',
'XOF', 'XPF',
];
function toStripeAmount(amount, currency) {
const curr = currency.toUpperCase();
if (ZERO_DECIMAL_CURRENCIES.includes(curr)) {
return Math.round(amount); // Já está na menor unidade
}
return Math.round(amount * 100);
}
function fromStripeAmount(stripeAmount, currency) {
const curr = currency.toUpperCase();
if (ZERO_DECIMAL_CURRENCIES.includes(curr)) {
return stripeAmount;
}
return stripeAmount / 100;
}
Use isso em todos os lugares. Na criação de sua checkout, nos manipuladores de webhook, nos displays do painel. Em todos os lugares. A uma vez que você esquece é a vez que você cobra de alguém 100x do que ele esperava.
Comparando os Quatro Modelos
| Aspecto | Subs em Camadas | Marketplace | Doação Recorrente | Pagamento Único |
|---|---|---|---|---|
| Produto Stripe | Múltiplos Produtos, múltiplos Preços por produto | Produto único por tipo de serviço | Produto único, preços dinâmicos | Produto único, Preço fixo |
| Modo de Cobrança | subscription |
payment com Connect |
subscription |
payment |
| Complexidade de Webhook | Alta (eventos de ciclo de vida) | Alta (eventos de Connect) | Média | Baixa |
| Manipulação de Moeda | Matriz de preços regional | Moeda do prestador | Moeda do doador | Moeda única |
| Suporte a Trial | Sim, dependente da região | N/A | N/A | N/A |
| Prorrateiação | Sim, em upgrades | N/A | N/A | N/A |
| Complexidade de Reembolso | Cálculos prorrateiados | Reversão de taxa de plataforma | Reembolso completo simples | Reembolso completo simples |
| Portal do Cliente | Essencial | Não necessário | Bom ter | Não necessário |
| Taxas Stripe (2025) | 2,9% + 30¢ | 2,9% + 30¢ + 0,5% Connect | 2,9% + 30¢ | 2,9% + 30¢ |
FAQ
Quantos Produtos Stripe devo criar para preços em camadas?
Um Produto por camada. Então se você tiver Gratuito, Básico, Pro e Premium, são quatro Produtos. Cada Produto tem múltiplos Preços -- um por combinação de moeda e intervalo de cobrança. Uma camada Pro com cobrança mensal e anual em 10 moedas significa 20 objetos Price naquele Produto único. Parece um monte, mas Stripe manipula bem e mantém seu catálogo organizado.
Posso usar Stripe Checkout para assinaturas com preços regionais?
Sim, mas você precisa determinar a região do cliente antes de criar a Sessão de Checkout para poder passar o ID de Preço correto. Usamos geolocalização de IP (via headers do Cloudflare) para pré-selecionar a moeda, depois deixamos o cliente confirmar ou mudar. Não confie na moeda automática de Checkout -- você quer controle sobre qual Preço eles veem.
Qual é a diferença entre contas Stripe Connect Express e Custom?
Contas Express permitem que Stripe manipule a integração, verificação de identidade e painel para seus prestadores. Contas Custom dão a você controle total, mas exigem que você construa tudo isso você mesmo. Para a maioria dos marketplaces, Express é a escolha certa. Nunca tivemos um caso onde a perda de controle justificasse o custo de engenharia de Custom. Contas Express também manipulam relatórios de impostos (1099s nos EUA) automaticamente, o que é uma vitória massiva de conformidade.
Como manipulo pagamentos de assinatura falhados sem perder clientes?
Camadas três coisas: Tentativas Inteligentes do Stripe (habilitadas no Painel), e-mails de dunning personalizados acionados por webhooks invoice.payment_failed e um período de carência antes do cancelamento. Damos 14 dias em 3 tentativas de reembolso. O primeiro e-mail é amigável ("ei, seu cartão pode ter expirado"), o segundo é urgente e o terceiro é um aviso final. Inclua um link direto para o Portal do Cliente onde eles podem atualizar seu método de pagamento. Isso sozinho recupera cerca de 30-40% dos pagamentos falhados.
Preciso de endpoints de webhook separados para Stripe Connect?
Sim. Eventos de plataforma e eventos de contas de Connect usam diferentes segredos de webhook e diferentes estruturas de eventos. Eventos de Connect incluem um campo account identificando qual conta conectada o evento se relaciona. Registre dois endpoints no Painel do Stripe: um para eventos de plataforma, um para eventos de Connect. Essa separação também torna a depuração muito mais fácil.
O que são moedas com zero decimais e por que devo me importar?
Moedas com zero decimais como JPY (Iene Japonês) e KRW (Won Coreano) não usam unidades fracionárias. Quando Stripe diz "valor em menor unidade de moeda", para USD isso é centavos (2900 = $29.00), mas para JPY é iene (4200 = ¥4.200). Se você multiplicar por 100 como faz para USD, você cobra ¥420.000 em vez de ¥4.200. Sempre use uma função auxiliar que verifica a moeda antes de converter. Stripe mantém a lista oficial de moedas com zero decimais em sua documentação.
Devo usar o Portal do Cliente de Faturamento do Stripe ou construir o meu?
Use o Portal do Cliente para gerenciamento de assinatura a menos que você tenha requisitos de UI muito específicos. Ele manipula mudanças de plano, cancelamentos, atualizações de método de pagamento e histórico de fatura fora da caixa. Você pode personalizar a marca e configurar quais ações são permitidas. Construir seu próprio portal significa manipular cálculos de prorrateiação, tokenização de método de pagamento e fluxos SCA/3D Secure você mesmo. O portal é gratuito -- está incluído nas taxas de assinatura do Stripe.
Como testo preços regionais e manipulação de moeda localmente?
Modo de teste do Stripe suporta todas as moedas. Crie Preços de teste em cada moeda que você planejar suportar, depois use Stripe CLI para encaminhar webhooks para seu servidor local: stripe listen --forward-to localhost:3000/webhooks/stripe. Para testes de moeda com zero decimais especificamente, crie um Preço em JPY e verifique os valores em seus logs do manipulador de webhook antes de fazer deploy. Também mantemos uma suite de testes que executa toStripeAmount e fromStripeAmount contra cada moeda suportada -- ela capturou problemas mais de uma vez.
Se você está construindo um produto baseado em assinatura e precisa de ajuda com a arquitetura de cobrança, ou se está integrando Stripe com um CMS headless, entre em contato. Construímos esses padrões em múltiplos projetos de CMS headless e podemos ajudar você a pular os erros caros. Verifique nossa página de preços para modelos de engagement -- fazemos construções baseadas em projeto e consultoria contínua.