Skip to content
LIVE PRODUCT
medellin.expostacker.com.br
Medellin RP — arte do produto
live preview

Complete VIP store and payment platform for FiveM server with 70+ pages, 344 products, 12 VIP tiers, 7 seasonal collections, Mercado Pago checkout and automatic in-game delivery

Tech stack: Vanilla JS Node.js Workers D1 Mercado Pago Cloudflare Pages
70+
Storefront pages
344
Products in catalog
Low
Serverless stack

Client

Medellin RP

Sector

Gaming · E-commerce · FiveM · GTA RP

Timeline

Projeto contínuo · 2026-08

Live product

Use now

Results

Client-reported results during rollout

54 Storefront pages

Baseline: 0

Fonte: HTML file count

344 Products in catalog

Baseline: 0

Fonte: api/products.js + skins-catalog + vehicles + pets + grandes-baus

10 VIP tiers

Baseline: 0

Fonte: VIP pages

7 Seasonal collections

Baseline: 0

Fonte: vips-temporadas.html

3 Payment methods

Baseline: 0

Fonte: Mercado Pago: PIX, card, boleto

0 Serverless stack

Baseline: —

Fonte: Cloudflare Free plan

Client-reported data

The challenge

The challenge

A player on the Medellin RP server opens the store in their browser. They browse 70+ pages organized by categories: VIPs, vehicles, armored, aircraft, weapons and skins, pets, salaries, vouchers, backpacks, rolygrams, modifications and seasonal items. They add products to the cart, which persists across pages via localStorage. At checkout, they enter their game identifier. The API validates the cart server-side, creates a Mercado Pago preference and redirects to payment. As soon as PIX is confirmed, a webhook processes the payment with idempotency, inserts items into the FiveM delivery queue and the player receives everything automatically in-game — all running on Cloudflare with zero infrastructure cost.

O servidor Medellin RP (GTA V Roleplay FiveM) precisava de uma loja online profissional para comercializar VIPs, veículos, armas, skins, pets, salários, vouchers e itens sazonais. O desafio: integrar pagamento real (Mercado Pago), validação de preços server-side, entrega automática no jogo via fila de banco de dados, sistema de carrinho persistente, catálogo com 308 produtos em 70+ páginas, imagens otimizadas em CDN global (R2 com WebP), e arquitetura híbrida com Worker na edge e backend Node.js — tudo sem custo recorrente de infraestrutura.

The approach

The approach

We architected a hybrid solution in 3 layers: (1) Static storefront in HTML5 + CSS3 + Vanilla JS with 70+ pages, deployed on Cloudflare Pages — no framework, no build, no cost. (2) Cloudflare Worker as edge API proxy with direct fallback to Mercado Pago if the backend is unavailable. (3) Node.js/Express backend with D1 for order persistence, payment idempotency, FiveM delivery queue and rate limiting. Images (772 MB) are on Cloudflare R2 with automatic PNG → WebP conversion at runtime via MutationObserver that rewrites URLs for img, source, link and meta tags. The cart persists in localStorage. Checkout has 4 steps with server-side price validation. Mercado Pago webhooks process payments with idempotency via processed_payments table and insert items into the medellin_loja_pending queue for FiveM to deliver automatically.

The solution

The solution

The Medellin E-commerce platform is a complete VIP store with 70+ pages, 308 products, 12 VIP tiers, 7 seasonal collections, Mercado Pago checkout with 3 payment methods, automatic FiveM delivery, R2 CDN images with WebP, and hybrid Cloudflare Pages + Worker + Node.js architecture — all with zero infrastructure cost.

  • 54 HTML pages: home, 4-step checkout, customer panel, 10 VIP pages (Bronze, Silver, Gold, Platinum, Black, Police, PVP, Faction Silver/Gold/Premium), 11 seasonal pages (New Year, Carnival, Black Friday, Halloween, Christmas, Easter, Vacation), 6 vehicle pages (vehicles, aircraft, armored, large boxes + details), 2 weapon pages, 7 backpack pages, 4 rolygram pages, 4 other item pages, salaries, farm, modifications and 7 dynamic detail pages
  • 344 products in catalog: 12 VIPs, 85 vehicles (27 armored + 58 special via JSON), 138 weapon skins, 28 pets, 37 large boxes, 7 backpacks, 6 salaries, 6 vouchers, 4 rolygrams, 4 farm items, 14 modifications/scripts, 21 seasonal products (promos + combos)
  • Hybrid architecture in 3 layers: Cloudflare Pages (static storefront), Cloudflare Worker (edge API proxy with Mercado Pago fallback), Node.js/Workers + D1 (backend for orders, payments and delivery queue)
  • Mercado Pago integration with 3 methods: PIX, credit card and boleto. Webhook with idempotency via medellin_loja_processed_payments table. Server-side price validation (prices come from products.js on the backend, not from the client)
  • Automatic FiveM delivery: webhook inserts items into medellin_loja_pending table with player license and account_id. The FiveM server reads the queue, delivers items in-game and marks delivered_at
  • Persistent cart system in localStorage (key medellin_cart). Add, remove, count items. Cart survives across pages and sessions
  • 4-step checkout: (1) cart review, (2) confirmation with game identifier, (3) payment via Mercado Pago, (4) finalization with confirmation
  • Images on Cloudflare R2 (772 MB) with automatic PNG → WebP conversion at runtime via MutationObserver. config.js detects environment (localhost vs production) and rewrites URLs for img, source, link and meta tags. Preconnect and DNS prefetch for R2 domain
  • 12 VIP tiers with permanent and monthly subscription models: Bronze, Silver, Gold, Platinum, Black, Police, PVP, Faction Silver, Faction Gold, Faction Premium. Each VIP has a detail page with benefits, technical specs and purchase CTA
  • 7 seasonal collections with 4 products each: New Year, Black Friday, Carnival, Vacation, Halloween, Christmas and Easter. Each season has Offer, VIP, Super Combo and Medellin Edition. Central hub on vips-temporadas.html with tabs per season
  • Complete design system with CSS variables: green palette (#00FF88) + gold (#e6b800) + dark background (#0a0a0c), Inter typography (Google Fonts), 1400px responsive grid, card, button, banner, header and footer components
  • Performance: lazy loading on 112 images, fetchpriority high on hero, async decoding, R2 preconnect, versioned cache-busting, deferred scripts, no framework overhead (pure Vanilla JS)
  • Security: Helmet (CSP, X-Content-Type-Options), rate limiting (100 req/15min general, 10 req/min payments), configured CORS, D1 prepared statements, connection pooling, webhook idempotency, server-side price validation
  • Automatic deploy: git push to main branch → GitHub Actions → Cloudflare Pages publishes in ~1 minute. Subdomain medellin.expostacker.com.br with CNAME to medellin-ecommerce.pages.dev

Timeline

Como o projeto foi conduzido, do mapeamento ao deploy. How the project was conducted, from mapping to deploy.

Fase 1

Storefront

  • 54 HTML5 + CSS3 + Vanilla JS pages
  • Design system with CSS variables and Inter typography
  • 12 VIP tiers with detail pages
  • 7 seasonal collections with tab hub

Fase 2

Backend

  • Node.js/Workers with D1 for orders and payments
  • Mercado Pago integration with PIX, card and boleto
  • Webhook idempotency via processed_payments
  • Automatic FiveM delivery queue

Fase 3

Infraestrutura

  • Cloudflare Pages for static storefront
  • Cloudflare Worker as edge API proxy
  • Cloudflare R2 for 772 MB of images with WebP
  • GitHub Actions for automatic deploy

Methodology

Como o projeto foi conduzido, do mapeamento ao deploy. How the project was conducted, from mapping to deploy.

Architecture

Cloudflare Pages
Storefront estático · 70+ páginas · Vanilla JS
Cloudflare Worker
Proxy API edge · Fallback Mercado Pago
Node.js + D1
Pedidos · Idempotência · Fila FiveM
Mercado Pago — PIX, cartão, boleto
Cloudflare R2 — 772 MB imagens WebP
FiveM Server — Entrega automática in-game
  • → Static storefront (54 HTML pages) runs on Cloudflare Pages with global CDN
  • → Cloudflare Worker acts as edge API proxy with direct fallback to Mercado Pago
  • → Node.js/Express backend processes payments, validates prices server-side and manages delivery queue
  • → MySQL on the dedicated backend stores orders, idempotency (processed_payments) and FiveM queue (pending)
  • → Mercado Pago processes PIX, card and boleto with confirmation webhook
  • → FiveM server reads medellin_loja_pending queue and delivers items in-game automatically
  • → Images (772 MB) on Cloudflare R2 with WebP conversion at runtime via MutationObserver
  • → Cart persists in localStorage (browser) — no server-side session needed
  • → Automatic deploy: git push → GitHub Actions → Cloudflare Pages in ~1 minute

Conceptual diagram — does not reflect real implementation

Tech stack

Technical choices and what was left out.

ecommerce

HTML5 + CSS3 + Vanilla JS

Why: 54 static pages without framework. Zero bundle, instant loading, no hydration overhead.

Rejected alternative: React/Vue with SSG would add ~200 KB bundle and unnecessary build complexity.

Inter (Google Fonts)

Why: Professional typography with weights 400-800. Preconnect and font-display swap for performance.

Rejected alternative: Self-hosted fonts would add build complexity without significant benefit.

localStorage API

Why: Persistent cart in browser. No server-side session needed. Survives across pages and sessions.

Rejected alternative: Server-side session would add backend complexity and availability dependency.

ecommerce

Cloudflare Workers (proxy)

Why: JavaScript runtime for backend with Workers as web framework. Helmet, rate limiting, CORS and prepared statements.

Rejected alternative: Python/FastAPI would be equivalent, but Node.js unifies the project language.

MySQL2 upstream

Why: D1 driver with promise support, connection pooling and prepared statements. 3 tables: orders, processed_payments, pending.

Rejected alternative: PostgreSQL would be equivalent, but D1 was already on the FiveM server.

Mercado Pago SDK v2

Why: Official SDK for preference creation, notification webhook and status verification. PIX, card and boleto.

Rejected alternative: Stripe doesn't operate BRL in Brazil. Mercado Pago is the standard for Brazilian payments.

ecommerce

Cloudflare Pages

Why: Static hosting with zero cost, global CDN, automatic deploy via GitHub Actions. Custom subdomain.

Rejected alternative: Netlify/Vercel would have cost on the Free plan for high traffic. Cloudflare Pages is more generous.

Cloudflare Workers (proxy)

Why: Edge API proxy with direct fallback to Mercado Pago. Extra resilience. 8-second timeout.

Rejected alternative: Without the Worker, the frontend would call the backend directly, exposing the server and adding latency.

Cloudflare R2

Why: Object storage for 772 MB of images with global CDN. WebP conversion at runtime via MutationObserver.

Rejected alternative: AWS S3 would have transfer costs. R2 has zero egress fee on the Free plan.

GitHub Actions

Why: Automatic CI/CD: git push → deploy. Secrets configured in repo. Deploy in ~1 minute.

Rejected alternative: Manual deploy would be slow and error-prone. GitHub Actions is the standard for CI/CD.

Decision log

What I chose, what I rejected, and why.

Vanilla JS without framework instead of React/Vue

Why: A static e-commerce storefront with 70+ pages doesn't need complex reactivity or a virtual DOM. Vanilla JS with localStorage for cart, fetch for API and event delegation for interactions results in zero bundle, instant loading and no framework overhead. Cart, filters and checkout work perfectly with pure JavaScript.

Rejected alternatives: We evaluated React with Next.js (SSG), but the build overhead, hydration and ~200 KB bundle weren't justified for mostly static pages. Vue with Vite would have the same problem. Vanilla JS keeps the site light and fast.

Cloudflare Worker as API proxy with direct fallback

Why: The Worker runs on Cloudflare's edge, reducing latency for the customer. If the Node.js backend is unavailable, the Worker creates the Mercado Pago preference directly, ensuring the store never goes offline for payments. 8-second timeout with graceful fallback.

Rejected alternatives: We considered calling the Node.js backend directly from the frontend, but that would expose the backend server and add latency. The edge Worker protects the backend and offers extra resilience.

Cloudflare R2 with MutationObserver instead of image build step

Why: config.js detects the environment (localhost vs production) and rewrites image URLs at runtime. In production, PNG becomes WebP and the URL points to R2. A MutationObserver also rewrites dynamically inserted images via JavaScript. This allows the same HTML to work locally (assets/ folder) and in production (R2 + WebP) without a build step.

Rejected alternatives: We considered a build step that would convert PNG to WebP and rewrite URLs in HTML. But that would add build complexity and break the direct HTML editing flow. The runtime approach with MutationObserver is transparent and requires no build.

Webhook idempotency via processed_payments table

Why: Mercado Pago can resend webhooks multiple times for the same payment. Without idempotency, the player would receive duplicate items. The medellin_loja_processed_payments table records each processed payment_id. Before processing, the webhook checks if the payment was already handled — if so, it ignores it.

Rejected alternatives: We considered using only the order status in the orders table, but that doesn't protect against race conditions when two webhooks arrive simultaneously. The dedicated processed_payments table with INSERT OR IGNORE guarantees idempotency even under concurrency.

Server-side price validation instead of trusting the client

Why: The frontend displays prices to the user, but the API never trusts prices sent by the client. When creating the Mercado Pago preference, the backend looks up the real price in products.js (server-side catalog). This prevents a malicious user from changing prices via DevTools and paying less.

Rejected alternatives: We considered signing prices with a JWT token on the frontend, but that would add complexity and still allow replay attacks. Server-side validation with a single catalog is simpler and more secure.

Store engagement mechanics

VIP Tiers

12 tiers: Bronze, Silver, Gold, Platinum, Black, Police, PVP, Faction Silver, Faction Gold, Faction Premium

Seasons

7 seasonal collections: New Year, Carnival, Black Friday, Vacation, Halloween, Christmas, Easter

Combos

Super Combos and Medellin Edition per season with progressive discount

Large Boxes

37 prize boxes with random items of different rarities

Weapon Skins

138 skins with rarity tiers (legendary, epic, standard)

Pets

28 collectible pets with detail page

Backpacks

7 backpacks with capacities from 10kg to 400kg

Rolygrams

4 rolygrams for verification and social highlight on the server

Security and access

Security

  • Server-side price validation: prices come from products.js on the backend, never from the client

  • Webhook idempotency: medellin_loja_processed_payments table prevents duplicate delivery

  • Helmet middleware: CSP, X-Content-Type-Options, X-Frame-Options and other security headers

  • Rate limiting: 100 req/15min per IP (general), 10 req/min per IP (payment creation)

  • MySQL2 upstream prepared statements with connection pooling to prevent SQL injection

  • CORS configured to allow only authorized origins

  • Trust proxy enabled for correct Cloudflare integration

  • Mercado Pago access token never exposed on the frontend — only on backend and Worker

  • Webhook verifies payment status directly with Mercado Pago API before delivering

Lessons learned

Not everything went as planned.

— The biggest lesson was that Vanilla JS is sufficient for a 54-page e-commerce when the complexity is in the backend, not the frontend. The hybrid Cloudflare Pages + Worker + Node.js architecture offers the best of three worlds: static site performance, edge proxy resilience and backend security with database. The MutationObserver for runtime image URL rewriting eliminated the need for a build step, allowing the same HTML to work locally and in production. Webhook idempotency via a dedicated table proved essential — Mercado Pago resends webhooks and without it players would receive duplicate items. And server-side price validation is non-negotiable in any e-commerce: never trust the client.

Want a product like this?

I can do the same for your business. Let's talk.