CASE STUDYCASE FILE · RAIT–SDS · LIVE · SEP 2026

Full-stack build · B2B eCommerce · wholesale distribution

We built Sundor Skin — a gated wholesale platform for authentic skincare in Bangladesh.

Sundor Skin imports authentic skincare — with a deep K-beauty range — and supplies pharmacies, retail shops, salons and online resellers. RAITHub replaced phone-and-WhatsApp ordering with one system: approved buyers see their own wholesale price, order on prepayment or credit, and every step from approval to dispatch is recorded.

Buyer + staff portals146-table PostgreSQL core12 staff rolesEnglish · বাংলা
Sundor Skin wholesale storefront — 'Your Trusted Partner for Authentic Skincare Worldwide' with Apply for Wholesale Access and Explore Products buttons
Live storefront, English view. Wholesale prices are shown only to approved buyers.

The mandate

Wholesale ran on phone calls, WhatsApp and spreadsheets.

The brief was to make ordering easier than calling and to let the business run without spreadsheets — while keeping trade prices private, extending credit safely, and proving that every unit is authentic and in date.

BeforeOrders taken by hand, price lists shared in chats, credit tracked from memory, and no single record of who approved what.
What we shippedA gated portal where each buyer is verified before they see a price, every order is checked against their credit, and every change lands in an append-only audit log.

Scope · two products

One business, two front doors — each on its own database.

wholesale.sundorskin.comLive

B2B wholesale portal

The ordering and distribution system: public catalogue and account application, a buyer portal for approved businesses, and a staff back office for sales, accounts, warehouse and content teams.

sundorskin.comLaunching

Bengali-first content site

The acquisition channel: skincare articles, an ingredient library and concern guides, with scheduled publishing, moderated comments and a double opt-in newsletter. Articles link into the wholesale catalogue and fall back to plain text if it's unreachable.

The build · buyer journey

From application to dispatch, with a human check at the door.

STEP 01

Apply

A business applies from the public site. Nobody sees wholesale prices without an approved account.

STEP 02

Verify & approve

Staff review the application and issue a single-use invitation link. Staff can also onboard a buyer directly.

STEP 03

Tier pricing

Each buyer sits on a Bronze, Silver, Gold or Platinum tier, with optional per-buyer price overrides.

STEP 04

Order & dispatch

Orders are re-priced on the server, checked against credit, then allocated by batch — earliest expiry first — and picked, packed and dispatched.

By the numbers

The scope of the platform — counted, not estimated.

150K+
Lines of code
~87K app TypeScript · ~25K SQL
77
Route pages
38 admin · 14 buyer · 25 public
146
Database tables
Plus 189 SQL functions
76
Migrations
Replayed on an empty DB in CI
530+
Automated tests
Unit · integration · IDOR security
12
Staff roles
Owner to picker, least privilege
88
Permission codes
Checked on every privileged action
8
Payment methods
bKash · Nagad · bank · credit · COD…

Wholesale portal only, counted from the repository in September 2026. Tests exclude 20 additional SQL assertion suites.

The platform

Buyers, sales, accounts and the warehouse — one system of record.

Buyer portal

Ordering that beats a phone call

  • Catalogue with the buyer's own tier price; cart re-priced on the server at every step
  • Order history with full status timeline, printable invoices and returns
  • Saved lists, wishlist, delivery addresses and message threads with staff
  • Bengali and English, with translation keys enforced by the compiler
Pricing · credit · payments

Money rules the client can't bend

  • Credit limits, exposure and terms enforced in the database at submission
  • New delivery addresses wait 24 hours before credit orders can use them
  • bKash and Nagad receipts submitted by the buyer, then verified by staff against the provider
  • Payment methods switched on or off from an admin screen — no deploy needed
Admin · operations

Run the business from one console

  • Application review, buyer management and ordering on a buyer's behalf
  • Invoices with amount in words and a verification QR code; credit notes
  • Reports, stock movements, and settings for payments, shipping and notifications
  • Email and SMS notification templates behind swappable provider adapters
Warehouse · fulfilment

Batch-level stock, earliest expiry first

  • Stock held per batch with expiry; allocation is first-expiring, first-out
  • Pick, pack and dispatch steps, idempotent at the database level
  • Pathao and Steadfast courier adapters for booking and status polling
  • Returns go through authorisation and inspection; restocked goods re-enter their original batch

System architecture

Business rules live in the database — where they can't be skipped.

Server Componentsalmost no client JS
↓
Server Actionsauth · zod · permission
↓
SQL functionscredit · pricing · stock
↓
PostgreSQLrow-level security · audit

No ORM: typed raw SQL over a schema that owns the rules. Credit checks, pricing and stock allocation run as database functions inside one transaction, so a bug in a screen can't oversell stock or bypass a credit limit. The content site runs on a separate database and cannot reach orders or payments.

Core

Next.js 15React 19TypeScriptPostgreSQLnode-pgzodargon2

Platform

VercelNeonCloudflare R2GitHub Actions

Provider adapters

bKashNagadPathaoSteadfastResendSMS gateway

Engineering rigor

Built for money, stock and trust — not just to demo.

Tenant isolation

Every buyer query runs under PostgreSQL row-level security scoped to that buyer. CI fails the build if any buyer-scoped table lacks a policy, and a dedicated IDOR suite tries to read other buyers' data.

Audit & separation of duties

An append-only, hash-chained audit log written in the same transaction as the change it records, partitioned by month. Conflicting permissions are declared and enforced in the database, so no single role holds both sides of a sensitive pair.

Money & stock correctness

Money is never a float — amounts are stored as integer poisha. Packing is idempotent, allocation follows batch expiry, and transactions retry on serialisation conflicts instead of failing the order.

Hardened sign-in

argon2 password hashing, per-IP and per-account rate limits with lockout, one generic error with equal work for unknown users, validated redirects, CSRF protection and a per-request CSP nonce.

The takeaway

From WhatsApp orders to a system of record.

RAITHub took Sundor Skin from a spec to a live wholesale platform — onboarding, tier pricing, credit, payments, fulfilment and a role-based back office — with a separate content site to bring buyers in.

R High-integrity systems, built to survive production.