Multi-Vendor Commerce Platform
ShopWiz.mk
A production multi-vendor ecommerce marketplace for Macedonia: bilingual (EN/MK), cPay card payments, vendor dashboards, advanced product variants, and a full admin console.

Executive summary
- Challenge
- Independent Macedonian vendors needed a shared marketplace with their own storefronts, accurate variant-level inventory, per-vendor fulfilment and local card payments, in two languages and priced in Macedonian Denar.
- Solution
- Three React applications (storefront, vendor portal and admin) over one Express/MongoDB API with JWT and Google OAuth, Redis caching, Sharp image processing and cPay card payments with preauthorization, served behind Nginx with PM2 in cluster mode.
- Result
- Live bilingual marketplace at shopwiz.mk. The non-technical co-founder runs the whole store without a developer, down to scheduling a Black Friday theme from a colour picker.
- Our role
- Product, architecture, storefront, vendor portal, admin console, API and infrastructure. Designed and built by Risto Radanliev, RiseGravity's founder, who co-owns it.
- Scope
- Customer storefrontVendor portalAdmin consoleREST APIPayments (cPay)Infrastructure
- Key complexity
- Per-vendor order lifecycles inside a single shared orderCommission model with per-category overrides cascading to subcategoriesVariant combinations that carry their own SKU, price, stock and weightBilingual EN/MK storefront with bulk Excel import and automatic translation
Technical deep dive
Overview
ShopWiz.mk is a production multi‑vendor ecommerce marketplace built for the Macedonian market. Independent vendors apply, get approved, and run their own storefronts under one roof — managing products, orders, and payouts — while shoppers browse a unified catalog with search, faceted filters, comparison, wishlists, and a streamlined checkout. The entire experience is fully bilingual (English / Macedonian) with prices in Macedonian Denar (MKD).

Architecture
- Storefront (
client/): React 18 + Vite + Tailwind; Zustand for state, TanStack Query for data, react‑i18next for EN/MK localization, React Hook Form for forms - Admin (
admin/): a separate React + Vite SPA served onadmin.shopwiz.mkfor platform operators - API (
server/): Node + Express with MongoDB (Mongoose 8); JWT access/refresh auth plus Google OAuth, Sharp image processing, Redis caching, and hardened security middleware - Infra: Nginx reverse proxy across
shopwiz.mk,admin.shopwiz.mk, andapi.shopwiz.mk, with PM2 running the API in cluster mode
[client] → pages: Home, Products, Product, Categories, Category, Brands, Brand,
Vendors, Store, Cart, Compare, Checkout, Account, Wishlist
vendor portal: Dashboard, Products, Orders, Earnings, Customers, Reviews
[admin] → Dashboard, Vendors, Products, Orders, Categories, Brands, Coupons,
Shipping, Pages (CMS), Users, Settings
[server] → routes: products, categories, brands, vendors, cart, orders, coupons,
reviews, wishlist, payments(cPay), auth(JWT+OAuth), admin/*
models: User, Vendor, Product, Category, Brand, Order, OrderItem,
Coupon, Review, Wishlist, Invoice, Payout, ShippingClass
Storefront Features
- Catalog & discovery: hierarchical categories, brand pages, MongoDB text search, faceted listing with price / vendor / brand filters
- Product variants: variant combinations carry their own SKU, price, stock, and weight (not just price deltas) — accurate inventory at the combination level
- Cart & checkout: persistent cart (localStorage, synced on login), product comparison, guest checkout (email + token) alongside account checkout, separate shipping & billing addresses
- Engagement: wishlist with price‑drop alerts, reviews with verified‑purchase badges and threaded vendor↔customer conversations, loyalty tiers (Bronze→Platinum)


Brands & Categories
- Hierarchical categories with ancestor breadcrumbs, denormalized product counts, per‑category default sort, and dedicated icon / card / banner imagery
- Brand catalog with bilingual names and logos, browsable brand landing pages
- Both surfaces are bilingual (EN/MK) and feed the homepage merchandising


Vendor Portal
- Onboarding: vendor application → admin approval / rejection / blocking, store profile (logo, banner, hours, vacation mode, business & bank info)
- Per‑vendor order statuses: each vendor confirms, processes, and ships its portion of a shared order independently, with tracking numbers
- Flexible commission model: a vendor‑level default rate with per‑category overrides that cascade to subcategories
- Operations: earnings & payout history, customer lists, low‑stock alerts, review replies, and bulk Excel import with automatic EN→MK translation
Admin Console
- Dashboard: revenue, orders, AOV, new customers/vendors, refunds, completion rate, revenue‑over‑time, order/payment status, top products & vendors, catalog health, and recent orders
- Management: vendors & approvals, products & inventory, orders & refunds, hierarchical categories, brands
- Marketing & ops: a coupon engine with granular scoping (vendor / product / category / brand, usage limits), weight‑based shipping classes, CMS static pages, users & roles, and platform settings (cPay credentials, email, theme)

Advanced Implementation
Bilingual storefront & data layer
// client — react-i18next (EN/MK) + TanStack Query + Zustand
i18n.use(initReactI18next).init({ resources: { en, mk }, fallbackLng: 'mk' })
const { data } = useQuery(['products', filters], () => api.products.list(filters))
// product variant combinations: per-SKU price, stock, weight
{ attributes: { Color: 'Red', Size: 'M' }, sku: 'XXX-RED-M', price: 1490, stock: 12, weight: 0.4 }
Payments, security & jobs
// server — cPay card gateway with preauthorization (reserve, capture on ship)
app.use(helmet()); app.use(rateLimit({ windowMs: 60_000, max: 300 })); app.use(compression())
router.post('/payments/cpay/init', initPreauth) // → capture on fulfilment
// images: Sharp → WebP + thumbnails; emails: Resend + React Email; invoices: PDFKit
await sharp(buf).resize(1600).webp({ quality: 80 }).toFile(out)
Localization & SEO
- Full EN/MK localization via
client/src/locales, MKD currency formatting throughout - Per‑product SEO fields (title, description, slug) and bilingual category/brand metadata
- Transactional email automation (order, shipping, review, price‑drop) through Resend with React Email templates
What made it work
- A clean three‑app split — storefront, vendor portal, admin — over one well‑modeled API
- Marketplace primitives done properly: per‑vendor order lifecycles, cascading commissions, and combination‑level inventory
- Bilingual and payment‑local from day one (EN/MK + cPay + MKD), purpose‑built for the Macedonian market
Deliverables
- Customer storefront (browse, search, cart, guest & account checkout)
- Vendor portal (store profile, products, orders, payouts, reviews)
- Admin console (vendors, products, orders, coupons, categories, CMS, settings)
- cPay card payment integration with preauthorization
- Bilingual EN/MK experience (react-i18next, MKD pricing)
- Bulk Excel product import with automatic translation