Estudo de Caso 1: Uma Página de Vendas Multilíngue com Pagamentos
A ideia
Uma amiga vende um guia em PDF de US$ 29 para estrangeiros se mudando para a Coreia. Ela queria uma única página que carregasse rápido no mundo todo, falasse inglês, coreano e chinês, e recebesse pagamentos com cartão sem que ela precisasse tocar em um servidor.
A especificação
Mantivemos a especificação em um único parágrafo antes de escrever qualquer código:
Build a single-page marketing site for a $29 digital product.
Three languages (en/ko/zh), auto-detected from the browser but
switchable with a toggle. One "Buy" button that opens Stripe
Checkout and redirects to a thank-you page on success. No backend
server I have to maintain. Must load fast from anywhere.
Note o que a especificação não diz: nenhum , nenhum design system, nenhum analytics, nenhuma captura de e-mail. Cada um desses seria um recurso razoável, e cada um teria atrasado o primeiro lançamento. A disciplina de uma especificação de um parágrafo é, na maior parte, a disciplina de deixar coisas de fora.
A stack
"Nenhum que eu mantenho" mais "rápido no mundo todo" apontava direto para hospedagem estática em uma CDN de borda (Content Delivery Network — cópias dos seus arquivos guardadas em data centers pelo mundo todo, para que cada visitante carregue a partir de um próximo) com uma pequena função para a única coisa que precisa de um segredo: criar a sessão do Stripe. Escolhemos um site estático no Cloudflare Pages com uma única Pages Function. Toda a arquitetura cabe em uma frase — uma página estática que chama uma função que conversa com o Stripe — e essa simplicidade foi o que tornou tudo depurável mais tarde.
O sistema inteiro é três caixas, e só a do meio toca em algum segredo:
┌─────────────┐ POST /api/checkout ┌──────────────┐ secret key ┌────────┐
│ STATIC PAGE │─────────────────────▶│ PAGES │─────────────▶│ STRIPE │
│ (HTML/JS) │◀─────────────────────┤ FUNCTION │◀─────────────┤ │
│ en/ko/zh │ checkout URL │ (the only │ session URL └────────┘
└─────────────┘ │ secret-side)│
served from CDN edge └──────────────┘
Os prompts principais
Começamos amplo e deixamos a IA propor a estrutura:
Scaffold a static landing page (plain HTML/CSS/JS, no framework)
with a language toggle for en/ko/zh. Store the copy in a single
JS object keyed by language. Detect the default from
navigator.language. Keep it to one index.html plus one main.js.
Depois o caminho de pagamento, onde mora o segredo:
Add a Cloudflare Pages Function at /api/checkout that creates a
Stripe Checkout session for a single $29 product and returns the
redirect URL. Read STRIPE_SECRET_KEY from the environment, never
hardcode it. The Buy button should POST to this function and then
window.location to the returned URL.
A frase "never hardcode it" (nunca deixe fixo no código) merece seu lugar. Se deixada por conta própria, uma IA vai facilmente colocar uma chave de exemplo direto no código para fazer o exemplo "funcionar", e uma chave de exemplo tem o hábito de virar uma chave real que vai parar em produção. Dizer isso uma vez, em voz alta, no , sai mais barato do que pegar isso na revisão.
O obstáculo
O primeiro teste ao vivo falhou: o botão de comprar não fazia nada, e o console do navegador mostrava um erro de CORS (Cross-Origin Resource Sharing — a regra do navegador sobre quais sites podem chamar quais outros sites). Não ficamos adivinhando. Colamos o erro exato de volta:
Clicking Buy logs: "Access to fetch at '/api/checkout' blocked
by CORS policy." The function and page are on the same domain.
Here's the function code: [pasted]. What's actually wrong?
A IA identificou na hora: a função estava retornando a do Stripe como (JavaScript Object Notation — um formato de texto simples e legível que programas usam para trocar dados estruturados), mas o fetch estava sendo feito antes de o preflight OPTIONS da função ser tratado, porque tínhamos implantado a página estática e a função em dois projetos diferentes do Pages por engano. O conserto de verdade era sobre a topologia de , não sobre código. Movemos a função para o diretório /functions do mesmo projeto e o erro desapareceu.
Vale a pena parar nessa lição: o sintoma estava no código (um erro de CORS vindo de um fetch), mas a causa estava na infraestrutura (dois projetos em vez de um). Se tivéssemos deixado a IA "consertar o erro de CORS" sem colar o contexto de configuração, ela teria parafusado cabeçalhos Access-Control-Allow-Origin — um conserto que compila, disfarça o sintoma, e deixa o bug de verdade vivo. Cole o erro literal e o contexto ao redor, e não aceite a primeira explicação plausível se ela não bater com a sua configuração.
O lançamento
Adicionamos as chaves reais do Stripe como variáveis de ambiente criptografadas no painel do Pages (nunca no repositório), apontamos o ( Name System, a agenda de endereços da internet que mapeia um nome para um servidor) do domínio dela para o Pages, e fizemos uma compra de teste real de US$ 29 com cartão, e depois reembolsamos. Também clicamos de propósito no caminho de falha — um cartão de teste recusado — para confirmar que ela não foi cobrada e que apareceu uma mensagem sensata em vez de uma tela em branco. No ar em uma tarde. Ela fez sua primeira venda naquela mesma noite.