Sensse
liveCustom e-commerce, storefront to back-office
Sexual-wellness store in production: catalogue, Pix and card checkout, postcode-based shipping, stock held against the payment clock, and a back-office the owners run themselves.
- Next.js 16
- TypeScript
- Tailwind v4
- PostgreSQL (Supabase)
- Row Level Security
- Mercado Pago
- Melhor Envio
- Vercel
// the case
- problem
- The shop sold over WhatsApp, one conversation at a time — quoting prices, calculating shipping, sending a Pix key, checking it cleared, noting stock in a notebook. That caps sales at however many messages one person can answer, and leaves no history of what sells or where buyers come from.
- solution
- A complete store built from scratch on Next.js and its own Postgres: catalogue with variants and stock, search, favourites, cart, Pix (expiring QR) and card checkout, postcode shipping with same-day local delivery, customer area, an editorial blog with its own publishing panel, and a back-office covering orders, stock with cost and expiry, finance, influencer coupons and cart recovery.
- impact
- In production at sensse.com.br, serving Belém, Ananindeua and Marituba with same-day delivery and the rest of Brazil by post. The whole operation — products, stock, dispatch, finance and content — is run by the owners in the panel, with no developer in the loop.
// my role
Product, architecture, front end, back end, database and deploy — solo. Including the schema and its RLS policies, the payment and carrier integrations, the back-office, and running the deploy.
// overview
Sensse serves Belém and its region with same-day delivery, and the rest of Brazil by post. It was built without an e-commerce platform for two reasons that are the same reason: the category is restricted across most paid-ad channels and some payment gateways, so traffic has to come from search and content — and customer discretion is an engineering requirement, not a preference. An off-the-shelf platform decides on its own what leaves for third-party tools, and here that could not be its decision.
The result is a single Next.js application — storefront, API routes and back-office in one deploy — over Postgres with row-level security. It covers the full cycle: the customer finds a product, pays by Pix or card, stock is reserved while the charge is alive and drawn down when payment confirms; the owner tracks, dispatches, books cost and publishes content from the panel. The decisions worth reading aren't in the screens but in the rules underneath them — where the discount is calculated, what may reach Google, how long a stock reservation survives.
// engineering notes
Sensitive data doesn't leave the house — and the cost was accepted
Sexual health is sensitive data under Brazil's LGPD, so product name, category and id are treated as things that must not reach a third party. Every route is sanitised before any event goes out — /produto/rosa-magica becomes /produto/[slug] — and the page title is replaced by a constant, because gtag attaches both to every event on its own. That was found by testing in the browser, not by reading documentation. The consequence is deliberate and real: Google Analytics will never say which product was viewed. So the site counts for itself, in Postgres, one row per product per day, with no cookie, no IP and no session — which makes the figure anonymous and, as a side effect, lets it see the visitors who declined the cookie banner and are invisible in GA4.
The discount rule lives in exactly one place
The store gives 5% on Pix and accepts influencer coupons. Stacked, a 10% coupon would hand over 15% and, with commission on top, open a quarter of the price. The rule became "the larger one wins, never the sum", written as a pure function called both by the server that charges and by the screen that promises — because when the same calculation lived in two places, it diverged, and a customer who sees one number and pays another opens a ticket. Ties go to the coupon on purpose: the customer pays the same, but only that way does the sale stay traceable to the influencer who brought it. The same review caught a wrong commission base in the database function booking the expense — it would have paid commission on money the customer never paid.
Stock reserved on the same clock as the charge
Generating a Pix charge reserves the stock, and the reservation and the charge expiry share one constant in one file. If they drifted apart you'd get the two worst cases: a customer paying a QR code for an already-cancelled order, or stock held by a dead charge. A cron releases expired reservations every minute, and the final draw-down is FIFO by batch — each intake carries its cost and expiry, which is what lets the finance panel state the real margin on an order instead of estimating it.
The age gate and neutral packaging as requirements that shaped the code
The 18+ gate is the first thing a visitor meets, and whoever leaves there leaves before accepting cookies — invisible to any external analytics. So there's a server-side counter just for the gate: the only way to know how many people the site loses at the door. The neutral-packaging promise became code too: no product name in transactional e-mail, in push, in the subject of a cart-recovery message or in the WhatsApp templates — those templates don't even have a product variable, so the item cannot be named by accident. The rule became structure instead of discipline.
Content as the acquisition channel, not decoration
Because paid advertising is restricted for the category, the editorial layer isn't an accessory — it is the channel. The blog has its own publishing panel (a partner publishes without touching code), categories, articles served from the CDN with revalidation, structured data and a sitemap. And there is exactly one page about delivery in Belém, deliberately one rather than scattering the city through the site, because the store serves all of Brazil and local is a differentiator, not an identity.