Pular para o conteúdo

Todos os trabalhosLoja D'Andrade Moda

O pagamento avisado duas vezes.

Loja de moda feminina com catálogo, sacola, frete calculado, pagamento por Pix e cartão e um painel em que a dona cuida de peças, fotos, preços e estoque sem chamar programador. Site, banco e domínio estão na conta dela, não na minha. A venda online abriu em 22/10/2026.

Estado
no ar
Período
08 a 10/2026
Stack
React 19, TypeScript, App Router sobre Vite (vinext), Cloudflare Workers, D1 (SQLite), Drizzle, Mercado Pago, SuperFrete
Endereço
Ver no ar

Feito por mim

Loja, checkout, painel, banco de dados e migração para a conta da cliente.

Veio do cliente

Marca, fotografias e conteúdo do catálogo.

Tente quebrar

Dispare avisos repetidos, atrasados e fora de ordem contra um pedido.

A regra de pagamento da loja, portada para o navegador: pedido pago não desfaz, e o estoque nunca fica negativo.

Esta demonstração precisa de JavaScript. O resto da página funciona sem ele.

Por dentro

O aviso é dica, não verdade

O Mercado Pago avisa quando um pagamento muda. Esse aviso pode chegar repetido, atrasado ou fora de ordem, então o servidor nunca acredita no corpo dele: confere a assinatura em tempo constante, descarta timestamp velho, registra o evento uma vez só e relê o pagamento na API do provedor.

TypeScript
// Ordem dos estados de UM pagamento: posição menor que a do pedido é evento atrasado
export function paymentStatusRank(status: string) {
  switch (status) {
    case 'charged_back': return 60;
    case 'refunded': return 50;
    case 'partially_refunded': return 40;
    case 'paid': case 'payment_in_mediation': return 30;
    case 'payment_rejected': case 'payment_cancelled': case 'payment_expired': return 25;
    case 'payment_authorized': return 20;
    case 'payment_in_process': return 15;
    default: return 10;
  }
}

Pedido pago não volta atrás

A decisão é uma função pura: recebe o pedido e o pagamento, devolve aplicar, ignorar ou alertar. Pendente atrasado não desfaz pago. Pagamento em dobro vira alerta, não pedido novo. Status desconhecido não muda nada. A gravação é uma troca condicional: se outro processo mexeu no pedido antes, relê e decide de novo.

A última peça

A reserva entra no mesmo lote atômico do pedido, e quem garante o saldo é um gatilho no banco, não o código da aplicação. Quando a reserva vence, o cron confere antes se ainda há Pix a caminho, para não revender a peça de quem já pagou.

SQL
CREATE TRIGGER stock_levels_saldo_valido_upd
BEFORE UPDATE OF quantity, reserved ON stock_levels
WHEN NEW.quantity < 0 OR NEW.reserved < 0 OR NEW.reserved > NEW.quantity
BEGIN SELECT RAISE(ABORT, 'DANDRADE_SEM_SALDO'); END;

Feito para a dona, não para mim

Sessão no servidor (só o hash do token vai ao banco), senha com PBKDF2 calibrado ao limite de CPU do plano grátis, trilha de auditoria e limite de tentativas por IP ou rede IPv6. No fim, migrei site, banco e domínio para a conta dela.

Como sei que funciona

249 testes em 21 arquivos, com pagamento e frete simulados: o fetch é trocado e qualquer host desconhecido lança erro, então nenhum teste toca a rede. Só o webhook tem 17, incluindo "fora de ordem: o pendente atrasado do mesmo pagamento não desfaz o pago".

O que isso não faz

  • O ambiente de testes do provedor não funcionou neste projeto. A prova do fluxo é por simulação da API.
  • Fotos ficam no banco enquanto o armazenamento de objetos não é ativado na conta da cliente.

Telas

Página inicial da loja D'Andrade Moda
A vitrine.
Página de produto com cores, tamanhos e sacola
Produto com cor, tamanho e sacola.

O que quebrou

  1. Restaurar estoque com INSERT OR REPLACE apagaria as reservas sem passar pelo gatilho.

    A armadilha ficou documentada no README, junto com os gatilhos.

Créditos: Marca, fotografias e conteúdo do catálogo: D'Andrade Moda. Desenvolvimento, publicação e entrega: João Leite. Pagamento processado pelo Mercado Pago; frete cotado pelo SuperFrete. Exibido com autorização da dona.

Me conta o problema.

Uma mensagem basta para começar. Eu mesmo respondo e, se fizer sentido, mando uma proposta por escrito: o que entra, o que fica de fora, prazo e preço.

WhatsApp (11) 99227-9903