Files
LNpaywall/specs.md
2025-12-14 23:08:45 -03:00

14 KiB

Paid Link Paywall Platform

Goal Build a product that lets creators turn any link into paid access in under 60 seconds, and embed the paywall on any website. Users do not need to know Nostr exists. No custody of user funds.

Core one-liner Paste a link, set a price, share or embed, get paid.

Non-goals for v1

  • No community features (posts, comments, member feeds)
  • No file hosting (unless optional later)
  • No subscription memberships (one-time purchase only in v1)
  • No complex analytics or funnels
  • No marketplace or discovery directory

Key principles

  • Fastest possible creator onboarding: 1 minute to first paywall
  • Buyer friction is minimal: open link, pay, unlock
  • Works with existing content locations (Notion, Google Docs, PDFs, Loom, YouTube unlisted, private web pages, GitHub, etc.)
  • Embed-first product: creators can drop into Webflow, WordPress, Framer, custom sites
  • Security is token-based and server-verified
  • Payment is non-custodial: platform only checks payment status and grants access

Product surfaces

  1. Creator web app
  • Landing page
  • Auth
  • Creator dashboard
  • Create paywall flow
  • Paywall detail page
  • Sales list
  • Payouts and settings
  • Embed docs page
  1. Buyer paywall pages
  • Hosted paywall page (shareable)
  • Embedded paywall UI (iframe)
  • Embedded button + checkout modal (script)
  1. Developer API
  • Create paywall
  • Update paywall
  • List paywalls
  • Create checkout session
  • Verify access token
  • Webhooks

Roles and permissions

Roles

  • Creator (default)
  • Admin

Creator can

  • Create/update/archive paywalls
  • View sales
  • Configure payout destination
  • Generate embed code
  • Configure access rules

Admin can

  • View all creators
  • View paywalls
  • View sales
  • Manage disputes and refunds references (actual refunds depend on payment provider)
  • Disable creators or paywalls
  • Configure global settings

Data model

Entities

Creator

  • id (uuid)
  • created_at
  • status (active, disabled)
  • display_name
  • email (optional if using email login)
  • auth_providers (password, nostr, google, github)
  • nostr_pubkey (optional)
  • avatar_url
  • default_currency (sats)
  • payout_config (provider-specific)
  • tax_info (future)

Paywall

  • id (uuid)
  • creator_id
  • status (active, archived, disabled)
  • created_at, updated_at
  • title
  • description (short)
  • cover_image_url (optional)
  • original_url (the target link)
  • original_url_type (url, youtube, notion, pdf, loom, other)
  • preview_mode (none, text_preview, image_preview)
  • preview_content (optional)
  • price_sats (integer)
  • access_expiry_seconds (optional; null means no expiry)
  • max_devices (optional; default 3)
  • max_sessions (optional; default 5)
  • allow_embed (boolean)
  • allowed_embed_origins (list of domains, optional)
  • require_email_receipt (boolean)
  • custom_success_message (optional)
  • custom_branding (theme overrides, optional)
  • slug (optional human friendly)

CheckoutSession

  • id (uuid)
  • paywall_id
  • created_at
  • status (pending, paid, expired, canceled)
  • amount_sats
  • payment_provider (lnbits, openNode, strike, etc)
  • payment_request (invoice string or provider id)
  • expires_at
  • buyer_hint (ip hash, user agent hash)

AccessGrant

  • id (uuid)
  • paywall_id
  • created_at
  • expires_at (nullable)
  • status (active, revoked)
  • buyer_id (nullable)
  • token_id (uuid)
  • last_used_at
  • usage_count

Buyer

  • id (uuid)
  • created_at
  • email (optional)
  • email_verified (boolean)
  • nostr_pubkey (optional)
  • notes (optional)

AccessToken

  • token_id (uuid)
  • paywall_id
  • buyer_id (nullable)
  • issued_at
  • expires_at (nullable)
  • scopes (view)
  • signature (server signed)

Sale

  • id (uuid)
  • paywall_id
  • creator_id
  • created_at
  • amount_sats
  • platform_fee_sats
  • net_sats
  • payment_provider
  • provider_reference
  • buyer_id (nullable)
  • status (confirmed, refunded, chargeback, disputed)

WebhookEvent

  • id
  • created_at
  • provider
  • event_type
  • raw_payload
  • processed_at
  • status

AuditLog

  • id
  • created_at
  • actor (creator/admin/system)
  • action
  • resource_type
  • resource_id
  • ip
  • metadata

Authentication

Creator login options

  • Email + password
  • Nostr login (NIP-07 or signer)
  • Google OAuth
  • GitHub OAuth

Buyer login

  • No account required for purchase
  • Optional email input for receipt and re-access
  • Optional Nostr login for portable access later

Session management

  • JWT access token (short)
  • Refresh token (httpOnly cookie)
  • Rate limiting on auth endpoints
  • 2FA is future

Nostr integration

  • If creator uses Nostr login: store pubkey and verify signed challenge.
  • If creator uses email login: system creates and stores a backend keypair to sign certain actions internally (never shown to user). This key is per-creator and encrypted at rest.

Payments

v1 payment methods

  • Lightning only (invoice-based)

Implementation options

  • LNbits as payment backend
  • Alternative provider module interface to swap later

Important constraints

  • Platform does not custody funds: invoice is generated for creator payout wallet or a routed invoice to platform then forwarded is not allowed.

Recommended model

  • Platform maintains a service wallet only for collecting platform fees.

  • Creator config includes their own Lightning Address or LNURLp.

  • For each checkout, platform generates:

    • one invoice to creator for net amount
    • one invoice to platform for fee
    • or a split payment mechanism if supported by provider.

If split is not supported

  • v1 can temporarily charge a platform fee via subscription model instead of per-sale fee to avoid custodial flow.

Fee model

  • Free tier: 10 percent per sale
  • Pro tier: 15 per month + 3 percent per sale

Core flows

Flow A: Creator creates a paywall

Entry points

  • Dashboard primary CTA: Create Paywall
  • Top nav button

Steps

  1. Paste URL

  2. System fetches metadata:

    • page title
    • description
    • favicon
    • open graph image
  3. Creator edits:

    • title
    • short description
    • cover image
  4. Set price

    • sats integer
  5. Set access rules

    • expiry: none / 24h / 7d / 30d / custom
    • max devices: default 3
    • allow embed: on
    • allowed origins: optional list
  6. Generate output

    • Share link
    • Embed code (iframe)
    • Button embed code
  7. Save

Success state

  • Show the paywall URL
  • Copy buttons
  • Preview mode

Validation

  • URL must be https
  • Block localhost and private IP ranges
  • Block obvious malware/phishing domains (basic allow/deny list)

Flow B: Buyer purchases access via hosted paywall page

Entry

Paywall page sections

  • cover image
  • title
  • short description
  • price
  • what you get (simple)
  • pay button
  • trust line (secure payment)

When pay is clicked

  • create checkout session

  • render QR invoice

  • show invoice string copy

  • show countdown timer

  • poll payment status every 2 seconds, max 2 minutes

  • on paid:

    • issue access token
    • redirect to unlocked view

Unlocked view

  • show success message
  • show button: Open content
  • auto redirect after 3 seconds
  • store access token in secure cookie and localStorage token id (for iframe context)

Re-access

  • if cookie exists and token valid: show Open content
  • if token expired: show repurchase prompt

Flow C: Embedded iframe paywall

Creator embeds

States

  • Loading state
  • Locked state
  • Checkout state (inside iframe)
  • Unlocked state

Locked state UI

  • small cover
  • title
  • price
  • Unlock button

Unlock

  • opens checkout inside iframe
  • after paid, iframe switches to unlocked

Unlocked state behaviors Option 1: reveal content within iframe by rendering a secure redirect button Option 2: open target link in new tab with token parameter (less secure) Option 3: show an inline preview plus open link button

Recommended v1

  • Open target link in new tab after payment, and show persistent “Open again” button.

Origin restrictions

  • If creator set allowed origins, embed endpoint checks the request referrer/origin.
  • If mismatch: show error: embedding not allowed.

Flow D: Button embed + modal checkout

Embed snippet

<script src="https://app.domain/js/paywall.js" data-paywall="{id}" data-theme="auto"></script>

Behavior

  • Script finds placeholder element or injects a button.
  • On click, opens modal overlay.
  • Modal shows paywall details and invoice.
  • On paid, modal shows success and “Open content” button.

Constraints

  • Must be CSP-friendly
  • Must not break SSR sites
  • Provide a no-JS fallback link

Flow E: Creator revenue and sales tracking

Dashboard widgets

  • Total revenue (last 7d, 30d)
  • Net earnings
  • Top paywalls
  • Sales count

Sales table

  • date
  • paywall
  • amount
  • fee
  • net
  • status

Filters

  • date range
  • paywall

Exports

  • CSV export v1 optional

Pages and UI spec

Public landing page

Sections

  • Hero headline: “Turn any link into paid access in 60 seconds.”
  • Subheadline: “No uploads. No platform lock-in. Share or embed anywhere.”
  • CTA: “Create a paywall”
  • Demo embed showing locked/unlocked
  • Use cases grid (Notion, Google Docs, PDFs, videos)
  • Pricing
  • FAQ
  • Footer

Design

  • Modern, premium, minimal
  • Dark mode first with light mode toggle
  • Rounded corners, soft shadows
  • Strong typography
  • No crypto jargon

Auth pages

  • Login
  • Signup
  • Provider buttons (Google, GitHub, Nostr)
  • Forgot password

Creator dashboard

Layout

  • Left sidebar

    • Overview
    • Paywalls
    • Sales
    • Embeds
    • Settings
    • Help
  • Top bar

    • Create Paywall
    • Profile menu

Overview page

  • KPI cards
  • Sales chart (simple line)
  • Recent sales list
  • Quick create CTA

Paywalls list

  • Table / cards
  • Status chips
  • Copy link
  • Copy embed
  • Edit
  • Archive

Create paywall page

  • Stepper UI (Paste link -> Details -> Price -> Access -> Output)

Paywall detail page

  • Preview hosted page
  • Settings panels
  • Copy buttons
  • Embed origins list
  • Regenerate embed codes

Sales page

  • Sales table
  • Export

Embeds page

  • Docs and copy-paste snippets
  • Testing sandbox

Settings

  • Profile
  • Payout destination
  • Branding (logo, primary color)
  • Domain/whitelabel (future)
  • Security (sessions)

Buyer hosted paywall page

  • Responsive
  • Minimal checkout
  • QR centered
  • Clear instructions
  • After pay: success state

Look and feel

Theme

  • Primary color: choose one brand color; allow creator override later
  • Background: dark #0b0f14 style
  • Cards: slightly lighter panels
  • Typography: modern sans
  • Spacing: generous

Components

  • Buttons: large, rounded, high contrast
  • Inputs: simple
  • Toasts for copy actions
  • Skeleton loaders

Microcopy tone

  • Clear, short, confident
  • Avoid crypto words
  • Use “sats” not BTC

Accessibility

  • AA contrast
  • keyboard navigable
  • screen reader labels

Backend architecture

Recommended stack

  • FastAPI or Node/Express
  • Postgres
  • Redis for session and rate limit
  • Worker queue for webhook processing

Services

  1. API service
  • REST endpoints
  • auth
  • paywall CRUD
  • checkout sessions
  • access verification
  1. Webhook worker
  • provider webhooks
  • marks CheckoutSession paid
  • issues AccessGrant
  • records Sale
  1. Embed service
  • serves iframe content
  • verifies origin
  • uses access token

Security

  • Strict input validation
  • SSRF protection on metadata fetch
  • Rate limits
  • IP throttling on checkout creation
  • Signed tokens (JWT with rotating keys)
  • Encrypt secrets at rest

Metadata fetcher

  • Fetch open graph tags
  • Timeout 3 seconds
  • Only allow https
  • Block private ranges

API endpoints (v1)

Auth

  • POST /auth/signup
  • POST /auth/login
  • POST /auth/logout
  • POST /auth/refresh
  • POST /auth/oauth/{provider}/start
  • POST /auth/oauth/{provider}/callback
  • POST /auth/nostr/challenge
  • POST /auth/nostr/verify

Paywalls

  • POST /paywalls
  • GET /paywalls
  • GET /paywalls/{id}
  • PATCH /paywalls/{id}
  • POST /paywalls/{id}/archive

Checkout

  • POST /paywalls/{id}/checkout
  • GET /checkout/{session_id}

Access

  • POST /access/verify
  • POST /access/revoke

Public

  • GET /p/{slug_or_id} (render)
  • GET /embed/{id} (render)
  • GET /js/paywall.js (button script)

Webhooks

  • POST /webhooks/{provider}

Admin

  • GET /admin/creators
  • GET /admin/paywalls
  • GET /admin/sales
  • POST /admin/paywalls/{id}/disable

Token and access logic

On payment confirmed

  • Create AccessGrant
  • Issue AccessToken JWT Claims
  • token_id
  • paywall_id
  • buyer_id (optional)
  • iat
  • exp (optional)

Storage

  • httpOnly cookie: access_jwt
  • localStorage: token_id (for embed communication)

Verification

  • /access/verify checks signature and status
  • checks revoked
  • checks expiry
  • checks max device fingerprint count

Device fingerprint

  • hash of user agent + stable client id cookie
  • do not use invasive fingerprinting

Limits

  • max devices default 3
  • if exceeded: prompt to “Reset devices” (creator can allow)

Abuse prevention

Threats

  • Link sharing
  • Invoice replay
  • Token theft
  • Bots spamming checkout sessions
  • SSRF via URL fetch

Controls

  • Access tokens tied to limited devices
  • Session expiry and revocation
  • Rate limit checkout creation
  • Webhook idempotency
  • Block private network URL fetch

Observability and operations

Logging

  • request id
  • creator id
  • paywall id
  • checkout session id
  • webhook event id

Metrics

  • checkouts created
  • conversion rate
  • webhook latency
  • embed loads
  • errors

Alerts

  • webhook failures
  • payment provider downtime

Deployment

Environments

  • dev
  • staging
  • production

Secrets

  • provider keys
  • jwt signing keys
  • db password

CDN

  • cache static assets
  • do not cache paywall pages with personal state

v1 acceptance criteria

Creator

  • Can sign up and create a paywall in under 60 seconds
  • Can copy share link and embed code
  • Can see sales appear after payments

Buyer

  • Can pay via lightning invoice and unlock within seconds
  • Can revisit link and still access if not expired

Embed

  • Iframe works on standard websites
  • Button script opens modal checkout

Security

  • Tokens are signed and validated server-side
  • Metadata fetch is protected against SSRF

Roadmap after v1

v1.1

  • Email receipts
  • Custom success message
  • Better origin controls

v1.2

  • Whitelabel domains
  • Team accounts

v2

  • Subscriptions
  • Bundles
  • Creator pages
  • Affiliate links