Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

Nuskha (نسخة)

Single-product ebook storefront for Indonesian communities: QRIS payment via Midtrans, per-buyer PDF watermarking (social DRM), and expiring download links. One master PDF in, personalized stamped copies out.

Nuskha — Arabic for "a copy / manuscript". Every buyer gets their own. Rename freely; the code doesn't care.

Why

Marketplace platforms (KDP, Google Play Books) can't do members-only sales. International direct-sales platforms (Payhip, Gumroad) do automatic PDF stamping but only take card/PayPal. Indonesian platforms (Lynk.id, Mayar) take QRIS but don't stamp. This app does both, self-hosted.

Flow

  1. Buyer opens the buy page, enters name + email, clicks pay.
  2. App creates a Midtrans QRIS transaction (Snap), buyer scans and pays.
  3. Midtrans fires a payment notification webhook. App verifies the SHA-512 signature, checks settlement status, records the order (idempotent — Midtrans retries notifications).
  4. App emails the buyer a signed download link (expires in 72h, limited download count).
  5. On download, the master PDF is stamped on the fly with the buyer's email and order ID (small gray footer text, every page) plus restricted PDF permissions, then streamed. No per-buyer copies stored.
  6. Buyers can re-request their link via a resend page (enter order email).

Stack

  • Java 25, Spring Boot 4, Maven
  • Thymeleaf + Alpine-CSP (fetch() for partial updates) — no SPA
  • PostgreSQL + Flyway
  • PDFBox 3.x for stamping and permissions
  • Midtrans Snap API (QRIS) — sandbox for dev
  • SMTP for transactional mail (authenticated domain: SPF/DKIM/DMARC required)

Project layout

src/main/java/.../nuskha/
  order/        # Order entity, repository, service, buy page controller
  payment/      # Midtrans client, webhook controller, signature verification
  delivery/     # Download link signing, stamping service, download controller
  mail/         # Transactional email
  config/       # Product config (title, price, master PDF path), app config
src/main/resources/
  templates/    # buy.html, thanks.html, resend.html
  db/migration/ # Flyway

Configuration

All product-specific values are configuration, not code — the app is a reusable single-product storefront:

Property Description
nuskha.product.title Book title shown on buy page
nuskha.product.price Price in IDR
nuskha.product.master Path to master PDF
nuskha.download.ttl Link validity (default 72h)
nuskha.download.max Max downloads per order (default 5)
midtrans.server-key Midtrans server key
midtrans.client-key Midtrans client key
midtrans.production false = sandbox
spring.mail.* SMTP settings

Secrets via environment variables. No secrets in the repo, ever.

Development

# Postgres via Docker/OrbStack, or rely on Testcontainers for tests
docker run -d --name nuskha-pg -e POSTGRES_PASSWORD=nuskha -p 5432:5432 postgres:17

# Midtrans sandbox keys from https://dashboard.sandbox.midtrans.com
export MIDTRANS_SERVER_KEY=SB-Mid-server-...
export MIDTRANS_CLIENT_KEY=SB-Mid-client-...

./mvnw spring-boot:run

Webhook testing locally: use the Midtrans dashboard notification tester, or tunnel (ssh -R / cloudflared) to expose the webhook endpoint.

Testing

@SpringBootTest with Testcontainers (real PostgreSQL). No mocks. Tests are functional-workflow-oriented: create order → simulate signed Midtrans notification → assert mail queued and link works → download → assert stamp present in PDF (extract text with PDFBox, check footer content) → assert link expiry and max-download enforcement.

Deployment

Single JAR on any small VPS behind a reverse proxy (Caddy/nginx, TLS required — Midtrans webhooks are HTTPS-only). Systemd unit or container, your choice.

Threat model: why watermarking, not DRM

Nuskha deliberately uses social DRM (per-buyer watermarking) instead of encryption-based DRM. This is a considered position, not a shortcut.

DRM doesn't work, and isn't really for piracy anyway

Hard DRM (Kindle KFX, Apple FairPlay, Adobe ACS) requires the reader's device to decrypt the book in order to display it — the keys are always on hostile territory, which is why every major scheme is routinely stripped by off-the-shelf tools. The platforms keep DRM anyway because its real function is library lock-in: a customer with hundreds of DRM'd purchases can't switch ecosystems, so every next purchase defaults to the same store. The anti-piracy effect is a leaky side effect. Meanwhile DRM punishes exactly the people who paid: proprietary readers, no lending, books that die with the account.

The industry precedent for going the other way

Publishers who control their own distribution keep converging on watermarked, DRM-free files:

  • Pottermore sold the Harry Potter EPUBs DRM-free with Booxtream watermarking — multiple redundant marks per copy (visible obfuscated transaction ID on the copyright page, hidden serials in image attributes, CSS markers, randomized internal filenames) — on the most piracy-attractive catalog imaginable.
  • Tor Books dropped DRM entirely in 2012 and reported no measurable increase in piracy. Baen has been DRM-free since the early 2000s.
  • O'Reilly, Springer, Pragmatic Bookshelf, No Starch ship PDF/EPUB stamped with the buyer's email. In several European markets (Netherlands, Germany, Italy, Sweden), watermarking has displaced DRM as the norm.

How the deterrent actually works

A watermark is a sensor plus a credible threat, not a lock:

  1. The visible footer stops the casual leak. Forwarding a file with your own email and order ID on every page feels like leaving your name at the scene. That covers the realistic threat: a member sharing the PDF into a chat group.
  2. The hidden mark (order ID in XMP metadata) punishes half-measures. Stripping the visible footer isn't enough, and the leaker can't easily be sure they found everything. The deterrent is the doubt, not the robustness.
  3. Enforcement is an option you buy, not a cost you pay. Big publishers pair watermarks with crawling services (Digimarc Guardian, Link-Busters) and takedowns; at community scale, "monitoring" is noticing a file circulating, and "enforcement" is a private message to the person whose email is on page one. The watermark works even if that message is never sent, because the buyer can't know whether anyone is watching.

What we honestly concede

  • A technical attacker can strip both marks: parse the content streams, clean the metadata, or simply buy two copies and diff them (every byte that differs is the watermark). No watermarking scheme survives collusion; the academic fixes (Tardos codes) aren't worth deploying here.
  • The PDF permission flags (StandardProtectionPolicy) are a speed bump, trivially removed. UI copy must never call any of this "copy protection".
  • The defense is proportionate to the threat: this is a members-only community product. Anyone diffing two purchases to launder a clean copy has spent more effort than the book costs — and in a community of known members, taken a social risk far larger than the technical one. The threat model is gossip-scale; so is the defense.

Design consequence: disclose the stamping on the buy page. The deterrent works best when the buyer reads the warning before purchase, and buyers who know upfront don't feel ambushed.

Non-goals (v1)

  • EPUB watermarking (later: EPUB is a ZIP of XHTML, inject colophon + footer)
  • Multi-product catalog
  • User accounts / login
  • "Real" DRM — stamping is social DRM; a screenshot defeats anything stronger anyway

License

Apache-2.0

About

Ebook Marketplace Platform

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors