Scroll down
Discuss your project with me
Back to cases

NaromaxShots

NaromaxShots — custom-built photo stock with checkout, protected originals and its own admin

A photographer's own photo stock built from scratch: Next.js, PostgreSQL and private cloud storage, Stripe and PayPal checkout, watermarked previews, RAW originals by signed links and a full admin.

Photo stock from scratch · Stripe + PayPal
Year
2026
Duration
June – July 2026
Industry
Photography — online photo stock
Country
Online store · buyers worldwide
Services
Web Development, E-commerce, Web Design
NaromaxShots — desktop homepage
Desktop · 1440 px
NaromaxShots — mobile homepage
Mobile · iPhone

The results

  • 82

    Photos for sale, each with its own product page and structured data

  • 12

    Upload formats — from JPEG and iPhone HEIC to DNG and camera RAW

  • 5 min

    Lifetime of a signed link to a private original

  • 37 / 37

    End-to-end checks of the purchase, from upload to download

Project overview

NaromaxShots is a photographer’s own photo stock: landscapes, roads and mountains from trips across North America, sold one by one under a simple royalty-free license. The owner did not want a marketplace that takes a cut, or a template shop that chokes on a 30 MB camera file. The brief was a store that belongs to the author entirely — the photos, the buyers, the data and the money.

That turns a “gallery website” into a real e-commerce system. Originals must stay private until they are paid for, previews must be good enough to sell yet useless to steal, every payment must be confirmed on the server, and the owner — a photographer, not a developer — has to run all of it from an admin panel. No CMS or store builder covers that, so every part of it is written from scratch.

My role in the project

I designed and built the whole platform myself, from the database to the admin panel and the deployment. The areas I owned and delivered directly:

  • Architecture: Next.js 15 with strict TypeScript, PostgreSQL through Prisma, Cloudflare R2 with a private bucket for originals and a public CDN for previews, deployment on Railway
  • An image pipeline that accepts JPEG, PNG, WebP, iPhone HEIC, Apple ProRAW DNG and camera RAW from Canon, Nikon, Sony, Fujifilm, Olympus and Panasonic, develops RAW files in WebAssembly, and produces watermarked previews, thumbnails and blur placeholders
  • Payments on two rails — Stripe as the primary one (cards, Apple Pay, Google Pay) and PayPal — with prices taken only from the database and every payment confirmed by the server and by signed webhooks
  • File delivery: the buyer receives the untouched original, byte for byte, RAW included, through short-lived signed links, with a backup link by e-mail
  • A Russian-language admin panel for photos, orders, subscribers, newsletters, statistics and site content, plus a step-by-step owner’s guide
  • A security review against current attack patterns, and repeated code audits of payments, delivery, newsletters and statistics

What I built: the store

  • Home with a full-width slider whose photos, animation (four types), speed, arrows and dots are set in the admin
  • Catalog with search across title, description and location, filters by groups the owner defines, and “load more”
  • Photo page with a watermarked preview up to 3,200 px, a full-screen viewer with 2× and 3× zoom, and an honest note on what the buyer gets: the original’s resolution, file size and format
  • Guest cart and multi-photo checkout without an account, and a wishlist in the header
  • Order page that follows the payment status live; instant download of each original, “Download all” for several photos, and a backup link by e-mail
  • Newsletter sign-up with double opt-in and one-click unsubscribe
  • Product and Offer structured data for every photo, Organization data, canonical URLs, sitemap and robots

What I built: protection of the photos

  • Originals live in a private bucket the public never sees; a download is a signed link that expires in five minutes, and each order allows a limited number of downloads within a limited time — both enforced atomically on the server
  • Every preview carries a tiled watermark and is capped at 3,200 px, thumbnails are marked too, and the whole catalog’s previews can be regenerated when the watermark changes
  • EXIF data is stripped from everything the public can load
  • Uploads are checked by decoding the file, not by trusting its extension, with a guard against decompression bombs

What I built: the admin

  • Dashboard built on live data
  • Drag-and-drop upload with progress for large files, a photo table with inline price editing and bulk actions, and an archive that deletes files from storage for good
  • Filter groups and values for the catalog; editable home texts, legal pages and footer blocks with a rich-text editor
  • Hero slider settings, a video section switch, and storage diagnostics that explain a failed upload in plain words
  • Orders with their history and actions, and CSV exports of orders, statistics and subscribers
  • Newsletters: a markdown editor with photo insertion, a test e-mail, batch sending with automatic UTM tags, and a kill switch
  • Statistics by period: a daily chart, top photos, categories and traffic channels from first-touch UTM attribution
  • Settings with password change, and in-app dialogs that work the same on a tablet as on a desktop

Security and reliability

The admin sits behind a bcrypt-hashed password and a sealed, http-only session with an idle timeout, login throttling with exponential backoff, session revocation and a log of failed attempts. Every admin request is checked twice — in middleware and in the handler — and every change verifies where it came from. Webhooks are verified by signature, deduplicated and checked against the amount and currency on record; abandoned checkouts expire after 24 hours; and the production build refuses to start with placeholder secrets or the test payment driver. Strict security headers (CSP, HSTS, frame protection, Referrer- and Permissions-Policy) and rate limits on login, checkout, downloads and forms complete the picture.

The whole store also runs locally without a single external key, thanks to swappable storage, mail and payment drivers. Quality is guarded by unit tests and an end-to-end test of the complete purchase — upload, publish, search, buy, download (the bytes must match the original), receipt e-mail, order in the statistics: 37 of 37 checks green.

Key screens

NaromaxShots — Catalog: search by title, description and place, filters, and a price and location on every card.
Catalog: search by title, description and place, filters, and a price and location on every card.
NaromaxShots — Photo page: a watermarked preview up to 3,200 px, the real size and format of the original, buy now or add to cart.
Photo page: a watermarked preview up to 3,200 px, the real size and format of the original, buy now or add to cart.
NaromaxShots — Full-screen viewer with 2× and 3× zoom that follows the cursor or finger — every preview stays watermarked.
Full-screen viewer with 2× and 3× zoom that follows the cursor or finger — every preview stays watermarked.
NaromaxShots — Guest cart: several photos in one payment — cards, Apple Pay and Google Pay through Stripe, or PayPal.
Guest cart: several photos in one payment — cards, Apple Pay and Google Pay through Stripe, or PayPal.

Full homepage, desktop

NaromaxShots — full-page website screenshot

Tech stack

  • Next.js 15
  • TypeScript
  • PostgreSQL + Prisma
  • Cloudflare R2
  • Stripe
  • PayPal
  • sharp · LibRaw · libheif (WASM)
  • Resend
  • Railway

Want a similar project done?

Tell me about the task. I will personally look at your project and suggest the right way to deliver it.

Discuss your project with me