
Capabilities
- Payment gateway
- Invoicing
- Web, iOS and Android
- Real-time chat
Frontend
Backend
MakerUp
CompletedMakerUp is a marketplace where makers publish their 3D printing projects, manage their printers and filaments, and sell with Stripe handling the money. The platform takes a cut of every sale. It ships as a cross-platform app (Angular 20 + Ionic 8) on top of a NestJS backend with TypeORM and PostgreSQL.
The challenge
The hard part wasn't listing products, it was closing the money loop: charging on behalf of third parties, keeping a commission, never double-recording a sale even when Stripe retries a webhook, and issuing legal invoices with sequential numbering that hold up to an audit.
Implementation
Stripe Connect end to end
Express account onboarding for makers, PaymentIntents carrying a platform application fee and transfer_data to the seller, and the Stripe Payment Element with 3DS confirmation on the front. Buying only unlocks once the maker has passed KYC and charges are enabled.
The amount is derived server-side
The PaymentIntent takes amount, currency and recipient from the project row in the database and never trusts the client. A deliberate decision after finding that trusting the browser's currency could desync amount and currency.
Idempotent webhook as source of truth
A sale is only recorded from the Stripe webhook, with verified signature and a unique key per paymentIntentId: a redelivery never duplicates the purchase or resends emails. It also covers refunds, disputes and cancelled payments.
Legal invoices kept apart from quotes
Two paths that never mix: a real invoice can only be issued from a closed sale, numbered sequentially per maker and year, VAT included and persisted as a PDF; a quote is a client-side PDF that numbers and persists nothing.
httpOnly cookie session, chat included
No token in localStorage: the session lives in an httpOnly cookie and that same cookie authenticates the 1-to-1 chat WebSocket between buyer and maker. Each message's sender is set server-side, so it can't be spoofed.
Catalogs with bulk import
Filaments, printers and brands with non-destructive upsert import from Excel, brand normalisation by slug and aliases, and duplicate merging from the admin panel.
Genuinely cross-platform
One Angular 20 codebase with standalone components, zoneless change detection and signals-based state, packaged through Ionic + Capacitor for web, iOS and Android, and installable as a PWA.
Testing where it hurts
Jest (backend) and Karma (frontend) suites are mandatory on the critical flows — file uploads, sign-up/login, payments and invoices — plus a Puppeteer E2E smoke run against a local environment with Stripe in test mode.