Skip to content

All workD'Andrade Moda store

The payment notice came twice.

Women's fashion store with a catalog, a bag, calculated shipping, payment by Pix (the Brazilian instant payment) and card, and a panel where the owner manages pieces, photos, prices and stock without calling a programmer. Site, database and domain are in her account, not mine. Online sales opened on October 22, 2026.

Status
live
Period
Aug to Oct 2026
Stack
React 19, TypeScript, App Router on Vite (vinext), Cloudflare Workers, D1 (SQLite), Drizzle, Mercado Pago, SuperFrete
Link
See it live

Done by me

Store, checkout, panel, database and migration to the client's account.

From the client

Brand, photographs and catalog content.

Try to break it

Fire repeated, late and out-of-order notices at an order.

The store payment rule, ported to the browser: a paid order does not go back, and stock never goes negative.

This demo needs JavaScript. The rest of the page works without it.

Inside

The notice is a hint, not the truth

Mercado Pago sends a notice when a payment changes. That notice can arrive repeated, late or out of order, so the server never trusts its body: it checks the signature in constant time, drops an old timestamp, records the event only once and reads the payment again from the provider API.

TypeScript
// Order of the states of ONE payment: a rank below the order's rank is a late event
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;
  }
}

A paid order does not go back

The decision is a pure function: it takes the order and the payment, and returns apply, ignore or alert. A late pending does not undo paid. A double payment becomes an alert, not a new order. An unknown status changes nothing. The write is a conditional swap: if another process touched the order first, it reads again and decides again.

The last piece

The reservation goes in the same atomic batch as the order, and a trigger in the database guarantees the balance, not the application code. When the reservation expires, the cron first checks whether a Pix is still on its way, so it does not resell a piece someone has already paid for.

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;

Built for the owner, not for me

Server-side session (only the token hash goes to the database), password with PBKDF2 tuned to the CPU limit of the free plan, an audit trail and an attempt limit per IP or IPv6 network. In the end, I migrated site, database and domain to her account.

How I know it works

249 tests in 21 files, with payment and shipping simulated: fetch is replaced and any unknown host throws an error, so no test touches the network. The webhook alone has 17, including one where a late pending from the same payment does not undo paid.

What this does not do

  • The provider's test environment did not work on this project. The proof of the flow is by simulating the API.
  • Photos stay in the database until object storage is turned on in the client's account.

Screens

Home page of the D'Andrade Moda store
The storefront.
Product page of the store
A product page.

What broke

  1. Restoring stock with INSERT OR REPLACE would erase reservations without going through the trigger.

    The trap was documented in the README, along with the triggers.

Credits: Brand, photographs and catalog content: D'Andrade Moda. Development, publishing and delivery: João Leite. Payments processed by Mercado Pago; shipping quoted by SuperFrete. Shown with the owner's permission.

Tell me the problem.

One message is enough to start. I reply myself and, if it makes sense, I send a written proposal: what is in, what is out, deadline and price.

WhatsApp +55 11 99227-9903