# Glassh — An Owned Ecommerce for Handmade Resin Pieces, with a 3D David at the Door

> I'm moving Glassh, an epoxy resin workshop in Salta, off a rented storefront and onto an ecommerce it owns: the 44 real products, Argentine payments with a 10% bank transfer discount, customers uploading their own artwork to be printed on the piece, and a 3D home where catalog photos orbit a David bust blowing bubblegum. 26 documented architecture decisions and 49 end-to-end tests before launch is even on the table.

- Human version: https://www.augustosalazar.com.ar/project/glassh-ecommerce-propio-para-piezas-de-resina-artesanal
- Author: Augusto Salazar — https://www.augustosalazar.com.ar/llms.txt
- Status: in progress — running on staging, not launched yet
- Tags: Full Stack, MedusaJS, Three.js, NextJS, TypeScript, Supabase, Railway, Vercel, Playwright, Claude

## The brief

Glassh is a workshop in Salta, Argentina, that makes one-of-a-kind epoxy resin pieces: ashtrays, grinders and coasters, almost always a single unit each. It sold through TiendaNegocio, a rented storefront: no control over its data, no way for a customer to send in their own artwork (that happened over Instagram DMs), and a look that had nothing to do with what the workshop makes.

The goal was not to dress up the rented store but to own one: the real catalog, payments that work in Argentina, in-store pickup, and a home page that tells you right away these pieces look like nothing else.

**My role:** design, architecture, full-stack development, catalog migration and infrastructure. Dev team: one person, working with Claude Code.  
**Scope:** public store in Spanish, customer accounts, guest checkout, owner admin, payments, shipping, transactional emails, custom orders and a 3D home.  
**Timeline:** first commit on September 16, 2026. Three weeks later: 25 stable commits, 26 ADRs and a 49-case end-to-end suite passing against the compiled build.  
**Stack:** Medusa 2 (Node + TypeScript) · Next.js 15 (App Router, React 19) · Three.js · Supabase PostgreSQL + Supabase Storage · Railway (backend) · Vercel (storefront) · Resend · Playwright · Claude Code  
**URL (staging):** [glassh.vercel.app](https://glassh.vercel.app/ar)

**Caption:** The old store (TiendaNegocio) and where it is going: an owned, dark storefront where the photos of the pieces lead.

## Picking the engine before writing a line

Before the first commit I compared Medusa, Vendure, Saleor, WooCommerce, Payload Commerce and Vercel Commerce against primary sources: license, recent releases, official starters and extension model. GitHub stars did not count. Payload was out because its ecommerce template was still in beta; Vercel Commerce, because it depends on Shopify — the very dependency we wanted out of. I chose **Medusa's official DTC Starter**, pinned to a specific commit, MIT licensed.

The reuse rule is written into the master plan: catalog, stock, reservations, cart, accounts, promotions and admin belong to Medusa. I only build what Argentina and Glassh need: local payments, customer designs, custom orders and the visual layer.

![Reuse map: what comes from Medusa as is, what is adapted from the starter and what is built for Argentina and Glassh](https://www.augustosalazar.com.ar/images/glassh-reutilizacion-en.png)

**Caption:** A reuse map by capability: what is reused as is, what is adapted and what is built.

## Short phases, each with its evidence

I worked from a master plan split into phases, and none closed without real behavior tests, documentation and a commit. Halfway through, the owner asked to shorten the process: phases 5 to 16 were grouped into five delivery blocks (shopping, payments and shipping, custom orders and emails, SEO and migration, launch) without dropping a single acceptance criterion.

Every non-obvious decision became an ADR with its context, consequences and date. There are 26: from the ecommerce engine to why the side menu stopped using Headless UI transitions.

![Timeline from Sep 16 to Oct 5 with commits per day, the milestones of each phase and the five delivery blocks](https://www.augustosalazar.com.ar/images/glassh-fases-en.png)

**Caption:** From starter to staging. Every phase closed with tests and docs; nothing moved forward "because it seemed to work".

## Architectural decisions

**One engine, one database.** Medusa as a modular monolith on PostgreSQL, with the Next.js storefront separate. Server and worker are the same image with different roles, not microservices. For one person, every extra service is one more thing to watch.

**The server decides prices and stock.** The browser never sets an amount. The 10% bank transfer discount is applied by the backend when the payment method is chosen; if someone tries to force it through the native payment-session endpoint, it is rejected when the order completes. A payment is never marked as paid because of a redirect, an uploaded receipt or what the customer says.

**The real catalog, nothing invented.** One script captures the public store and another imports it idempotently through Medusa's native workflows: 44 products, 119 photos and 17 sale prices, with the list price struck through exactly as it was on the original store. One-of-a-kind pieces stay separate products; I did not invent color variants to make the catalog "look tidier".

**Free until it sells.** The owner asked for zero monthly cost during the trial stage. Supabase for the database and photos, Railway for the backend (256 MB heap, about 325 MB of memory measured in production mode) and Vercel for the storefront. All of it built to move to a VPS without rewriting anything.

![Architecture diagram: storefront on Vercel, Medusa on Railway, Postgres and Storage on Supabase, with Resend and Mercado Pago as external services](https://www.augustosalazar.com.ar/images/glassh-arquitectura-en.png)

**Caption:** Storefront on Vercel, Medusa backend on Railway, database and photos on Supabase. One place where price and stock live.

## The home: from "it looks old" to a David blowing bubblegum

The first home was correct and boring. The owner put it bluntly: it looked old, they wanted something cooler, in 3D. The home went through three versions, each one driven by their feedback:

1.  **An abstract sculpture in Three.js**, with palette and pause controls, reduced-motion support and a static fallback.
2.  **"A WOW when you walk in"**: twelve real catalog photos orbit on a tilted ring around an iridescent resin knot with glitter. You can drag them with inertia, they show name and price on hover and open the product on click. Textures go through Next's image optimizer so they are same-origin in production.
3.  **The David bust** at the center: a CC0 scan of an 1899 cast from the Statens Museum for Kunst, optimized from 12,900 triangles to 438 KB, with a clear-coated resin material and a custom shader that runs a lavender → tangerine → mint gradient up its height. Inspired by the catalog's "David Ashtray", the bust blows a pink bubblegum bubble on a 7-second loop. The anchor point was calibrated by hand so the bubble comes out of the lips, not the nose (yes, there was a commit for that).

Everything lazy-loads, pauses in hidden tabs or off screen, is fully released on exit and has its own pixel budget on mobile (30 fps).

![The three home iterations: abstract sculpture, orbit of real photos and David bust blowing bubblegum](https://www.augustosalazar.com.ar/images/glassh-portada-en.png)

**Caption:** Three iterations of the home, each one from a single sentence from the owner. The last one keeps the language of their own pieces.

**The brand.** I tried redrawing Glassh's circular logo and the owner turned it down ("it looks really ugly"). They were right. I kept only the logo's typeface (Fredoka Bold, OFL license) converted to outlines, in acid lime with a neon glow on a near-black background.

## Letting the customer send their own artwork

In the old store, a custom piece started with an Instagram DM. Now the product page has a "Customize it with your design" section: the customer uploads an image, sees it inside the piece's real shape (circle, square or rectangle), drags, zooms or rotates it, and a live indicator tells them whether the quality is good enough to print.

The rules are strict on purpose, to prevent complaints: JPG, PNG or WebP, up to 15 MB and 40 megapixels, and at least 200 effective dpi over the print area (300 counts as optimal). The backend checks everything again: it validates the real format with sharp, fixes EXIF orientation, strips metadata and stores the original in a private bucket. The surcharge is set per product from the admin and cannot be forged, because the native cart routes reject any customization metadata that does not come from the custom workflow. The owner sees every design on the order, with its placement, print size and dpi, and can download the original. Designs that never made it into an order delete themselves after 7 days.

![Product page with the design editor open, the artwork inside the piece's circular area and live print quality, next to the mobile checkout](https://www.augustosalazar.com.ar/images/glassh-editor.jpg)

**Caption:** The design editor on the product page: the image inside the piece's real shape, with live print quality.

## Payments, shipping and emails for Argentina

-   **Bank transfer with a 10% discount**, applied by the server, with bank details configured in the backend and shown on confirmation.
-   **Mercado Pago** as a custom provider on the Orders API, with webhook signature validation and 15 contract tests. It is ready for test credentials; it has not processed a real transaction yet.
-   **In-store pickup** (Av. Belgrano 433, Salta Capital) and home delivery at a provisional rate approved by the owner, until Correo Argentino is integrated.
-   **Transactional emails** of my own in table-based HTML, no dependencies: order received, payment confirmed, ready for pickup, on its way, canceled, welcome and password reset. They go through the notification module with idempotency keys, so a repeated event never sends two emails, and a failed send never breaks the purchase.
-   **Custom orders** outside the automated rules: a public form with a honeypot and a per-IP limit, a private reference image and its own admin section with status, notes and shortcuts to email and WhatsApp.

![Mobile checkout: pickup in Salta capital, bank transfer with 10% off and a summary with the discount applied](https://www.augustosalazar.com.ar/images/glassh-checkout-en.jpg)

**Caption:** Mobile checkout with the discounted transfer and pickup in Salta. The server calculates the amount, always.

![Order received email: dark background, GLASSH in lime, transfer details, summary and a pill button](https://www.augustosalazar.com.ar/images/glassh-email-en.png)

**Caption:** The order received email, rendered from the real template with sample data.

## What the tests found

The Playwright suite runs against the compiled build, not just in development, and that is where the bugs development mode never showed came up:

-   **The side menu stayed open** in 3 or 4 out of 6 runs at 1440 px: a click outside during the 150 ms enter transition was ignored. I removed the Headless UI transitions, moved to CSS keyframes with an instant close and ran the design suite 5 times: 35/35.
-   **The cart counter went stale** after the first "add" in the compiled build, even though the actual cart was fine. The nav now receives the same cart the layout already has instead of running its own query.
-   **The last unit.** With one-of-a-kind pieces, two people buying the same one at once is a real case. A test simulates it, along with price tampering, invalid stock and password recovery that does not reveal which emails exist.

Test products are created and deleted on every run, so the staging database keeps only the real catalog.

![Three findings from the tests: side menu, cart counter and the last unit, with problem, fix and evidence](https://www.augustosalazar.com.ar/images/glassh-pruebas-en.png)

**Caption:** 49 end-to-end cases on the production build. The menu that "sometimes" did not close actually failed 3 out of 6 times.

## AI-assisted development, with written memory

I wrote all the code with Claude Code, and the rule that helped most was keeping context out of the conversation and in the repo: an `AGENTS.md`, a master plan, an implementation status with the exact next task, a decision log and a changelog. Any new session, mine or an agent's, starts by reading those files, and no phase moves forward without evidence.

## Current status and what is left

Glassh is on staging, with the real catalog loaded in Supabase and the backend running on Railway. It does **not replace** the old store yet. Before launch, what is left:

-   Mercado Pago credentials and a real sandbox transaction;
-   verifying the sender domain in Resend;
-   real Correo Argentino rates;
-   the store's own domain and the DNS cutover, with redirects from the old URLs.

![3D home and the 44-piece catalog on mobile](https://www.augustosalazar.com.ar/images/glassh-mobile-en.jpg)

**Caption:** Home and catalog on mobile: the same 3D home, with its own pixel budget.

## Outcomes so far

-   An owned ecommerce on Medusa with the 44 real products, their 119 photos and their sale prices, imported without inventing data.
-   Customer designs with print-quality validation, a configurable surcharge and original downloads for the workshop.
-   Bank transfer with 10% off, in-store pickup and Mercado Pago ready for credentials.
-   A 3D home that shows the real catalog and keeps the humor of the workshop's pieces.
-   Zero monthly infrastructure cost during the trial stage, with a documented path to a VPS.
-   26 ADRs, 49 end-to-end tests and a repository that explains itself.
