Primsell NFT E-Commerce
Node.js Business Logic on PostgreSQL — Checkout, Stripe Connect & Royalty Ledger
Role: Main full-stack developer in a team of 3–5: backend business logic in Node.js on PostgreSQL (Express/Sequelize core API, NestJS payment service), the blockchain background jobs and the React apps. Mentored 2 junior engineers.
What it is. A B2B platform where brands sell NFT-backed products: buyers pay by card through Stripe, the platform mints the NFT on Polygon, and the buyer later burns it to redeem a real-world discount code or ticket that venue staff verify. Brands also earn royalties when their NFTs resell on OpenSea or Rarible.
Backend. Business logic in Node.js on PostgreSQL: an Express + Sequelize core API (~79 REST endpoints, JWT with ACL roles, per-route validation and rate limits, Swagger generated from the route definitions) and a NestJS + MikroORM payment service on Stripe Connect, called with short-lived service tokens. Order, royalty-ledger and contract-deployment lifecycles run on a state-machine workflow engine with guards and lifecycle events; background work runs as cron jobs with a DB-backed job log and a single-instance guard.
Correctness. Checkout overselling was fixed with a row lock and atomic SQL counters. The order-expiry job checks Stripe before expiring, so late payments still complete, and money math moved from floats to decimal.js. Burn-to-redeem is idempotent on the transaction hash, with a recovery job for redeems whose HTTP call never arrived, and brand ↔ customer CRM data is kept by a PostgreSQL trigger plus an idempotent backfill.
Royalty ledger. A resale indexer for OpenSea and Rarible reads checkpointed block windows with an overlap re-scan and adaptive range splitting, and feeds a per-brand ledger whose withdrawals are gated by balance guards and block confirmations.
Frontends. React apps for brands, checkout, redeem and venue verification share a UI kit and API client through a git submodule.
Key Features
- Express + Sequelize core API on PostgreSQL (~79 REST endpoints, JWT + ACL roles, validation and rate limits per route)
- NestJS + MikroORM payment service on Stripe Connect: per-brand accounts, application fees, signed webhooks, decimal.js money math
- Checkout overselling fixed with SELECT … FOR UPDATE on the campaign row and atomic SQL counters
- Royalty ledger fed by an OpenSea/Rarible resale indexer (checkpoints, overlap re-scan, dedup) with confirmation-gated withdrawals
- Idempotent burn-to-redeem on the transaction hash, plus a recovery job for interrupted redeems
- State-machine workflow engine for orders, ledger operations and contract deployment; cron jobs with a DB-backed job log
Tech Stack
Backend
Payments & Blockchain
Frontend
Infra
Challenges & Solutions
Overselling Under Concurrent Checkout
Order creation read the campaign, checked stock in application memory, then wrote the new booked count back. Two concurrent buyers could both pass the check, one increment was lost, and more NFTs were booked than existed.
One READ COMMITTED transaction takes SELECT … FOR UPDATE on the campaign row, checks stock under the lock, inserts the order and bumps the booked count with an atomic SQL update. Release and booked → bought transfers are atomic updates too, and available stock is a derived value that cannot be written directly.
Late Payments vs Order Expiry
Orders hold stock for a limited time. A buyer who paid just after the reservation expired could be charged for an order the system had already released, and amounts computed as floats broke on prices like 19.99.
The expiry job asks Stripe for the PaymentIntent status before expiring an order: paid orders complete, the rest release stock in bulk and cancel their intent so they can never be charged. Money moved to decimal.js minor units, with the platform fee rounded down so it never exceeds the percentage.
Royalty Ledger From On-Chain Resales
Brands earn royalties when their NFTs resell on OpenSea or Rarible, and must be able to withdraw them — without missing events near the chain head or counting a sale twice.
A resale indexer reads block windows from a stored checkpoint, re-scans an overlap, halves the range on provider errors and dedupes by transaction. It feeds a per-brand, per-currency ledger of signed operations whose lifecycle runs on the workflow engine, with a balance guard on withdrawals and N block confirmations before completion.
Redeems That Must Not Be Lost
A buyer burns the NFT on-chain to get a real-world code. If the browser closed after signing, the burn happened but the backend never heard about it.
The client records a redeem intent before signing. The redeem endpoint verifies the receipt and the decoded event and is idempotent on the transaction hash, and a recovery job scans chain events from the intent's block to complete redeems whose HTTP call never arrived.
Brand ↔ Customer CRM Mapping
Brands must see only their own customers, while one wallet can buy from many brands — and the mapping had to cover past purchases too.
A many-to-many table with a composite key, filled by a BEFORE INSERT trigger on purchases with ON CONFLICT DO NOTHING, plus a separate idempotent backfill migration for the history.