Skip to content

khuzaymaa918/PayPerPlay

Repository files navigation

PayPerPlay

PayPerPlay

Real-time per-second micropayments for video streaming, powered by Solana + USDC

Problem · Solution · Features · How It Works · Quick Start · Architecture · Database · API · Tech Stack

TypeScript Node.js Express SQLite Solana USDC Chrome Extension


The Problem

Streaming platforms today operate on an all-or-nothing model. You pay a flat monthly subscription whether you watch 200 hours or 2. Creators are paid through opaque algorithms that have no direct relationship with how much value a viewer actually got from their content.

This creates three fundamental problems:

Who Problem
Viewers Overpay for content they barely use. Subsidize videos they never finish.
Creators Revenue is decoupled from actual viewer engagement and value delivered.
The Market No honest price signal exists to reflect how much of a video is worth watching.

What if you could pay a creator exactly for what you watched — streamed in real-time, settled on a blockchain, down to the second?


The Solution

PayPerPlay is a Chrome extension + Node.js backend that introduces per-second streaming payments for video content using USDC (a digital dollar stablecoin) on Solana.

Instead of subscriptions, viewers pay micro-amounts in real-time as they watch. Close a video halfway? You pay for half. The price itself is shaped by community engagement — if most people only finish 40% of a video, the price adjusts downward automatically to reflect that.

Why Solana + USDC?

We needed a payment rail that could handle frequent tiny transactions with near-instant settlement and near-zero fees. Solana delivers all three:

Requirement Solana Capability
Micropayments (fractions of a cent) Transactions cost ~$0.00025 in SOL fees
Speed (payments every 5 seconds) ~400ms block time, finality in seconds
Stablecoin support USDC is a native SPL token — 1 USDC = exactly $1
Developer experience First-class @solana/web3.js and @solana/spl-token SDKs
Reliability Battle-tested infrastructure with high uptime

Payments are denominated in USDC so viewers and creators deal in familiar dollar amounts. Every transaction is permanently recorded on Solana with a public transaction signature, viewable on Solana Explorer.


Features

  • Per-second pricing — charges accrue only while the video is actively playing
  • Streaming payments — USDC micropayments sent every 5 seconds via Solana SPL token transfer
  • Community-driven pricing — average watch ratio across all viewers dynamically adjusts the price
  • Multi-platform — works on YouTube and Amazon Prime Video
  • Phantom wallet onboarding — connect your Phantom wallet in one click
  • Session history — popup UI shows full transaction history with amounts, durations, and statuses
  • Admin dashboard — override prices, adjust watch ratios, inspect all sessions
  • On-chain audit trail — every payment has a Solana transaction signature, verifiable at explorer.solana.com
  • Devnet safe — runs entirely on Solana devnet with test USDC, no real money involved

How It Works

The Payment Flow

 Viewer opens a YouTube/Prime Video page
         |
         v
 ┌──────────────────────┐
 │    Pricing Gate       │  Extension fetches price from backend
 │    $3.93 for 32min    │  (base rate × community watch ratio)
 │    0.2001¢ / second   │
 │                       │
 │  [Start Watching]     │
 │  [Decline]            │
 └──────────┬────────────┘
            │  Viewer clicks Start Watching
            v
 ┌──────────────────────────────────────────┐
 │              Active Session               │
 │                                          │
 │  Every 1s:  accumulate watch time        │
 │  Every 5s:  send heartbeat to backend ──────> Backend calculates
 │             with seconds watched         │     prorated USDC amount
 │                                          │          │
 │  Badge shows live cost: [$0.04]          │          v
 │                                          │  Solana SPL Transfer
 │                                          │  Viewer ──> Creator
 │                                          │  tx_hash recorded in DB
 └──────────────┬───────────────────────────┘
                │  Video ends / viewer stops
                v
 ┌───────────────────────────┐
 │    Session Complete        │
 │    Final payment sent      │  Remaining balance settled
 │    Total: $0.18 for 1:32   │  Session marked 'completed'
 └───────────────────────────┘

Pricing Formula

totalPrice = (baseCentsPerSecond × durationSeconds) × (avgWatchRatio / 100)

Where:

  • baseCentsPerSecond = 0.2 cents/second (configurable per video)
  • durationSeconds = total length of the video
  • avgWatchRatio = average percentage of the video watched across all viewers (0–100)

Examples:

Video Duration Watch Ratio Price
Popular tutorial (people finish it) 10 min 90% $1.08
Clickbait video (people leave early) 10 min 20% $0.24
Standard video 32 min 98% $3.93

Content that people actually finish costs more. Content people abandon costs less. The market speaks.

Payment Math (USDC on Solana)

USDC has 6 decimal places on Solana. The conversion formula is:

microUsdc = Math.round(amountCents × 10,000)
Amount Micro-USDC Actual USDC
0.2 cents 2,000 0.000002 USDC
1 cent 10,000 0.0001 USDC
100 cents 1,000,000 1.000000 USDC

Quick Start

Prerequisites

  • Node.js >= 18
  • npm >= 9
  • Google Chrome
  • Phantom wallet browser extension (from phantom.app)

1. Clone and install

git clone https://github.com/khuzaymaa918/PayPerPlay.git
cd PayPerPlay
npm install

2. Set up Solana devnet wallets

npx ts-node backend/scripts/solana-setup.ts

This script:

  • Generates two Solana keypairs (viewer + creator)
  • Writes them to .env immediately
  • Checks SOL balances
  • Creates USDC Associated Token Accounts (ATAs) for both wallets if funded

The Solana devnet RPC airdrop is rate-limited. If it fails, fund both wallets manually:

  1. Go to https://faucet.solana.com → select Devnet → airdrop 2 SOL to each address printed by the script
  2. Re-run npx ts-node backend/scripts/solana-setup.ts — it reloads existing keys and creates the ATAs

3. Fund viewer wallet with devnet USDC

1. Go to https://faucet.circle.com
2. Select network: Solana Devnet
3. Paste the Viewer public key printed by the setup script
4. Request USDC — arrives in ~30 seconds

4. Start the backend

npm run dev:backend

The API server starts at http://localhost:4000. SQLite migrations run automatically on first launch. You should see:

[solana] Connected to https://api.devnet.solana.com
[solana] Viewer : <address> (2.4980 SOL)
[PayPerPlay] Backend running on http://localhost:4000

5. Build the extension

npm run build:extension

6. Load the extension in Chrome

  1. Open chrome://extensions
  2. Enable Developer mode (top-right toggle)
  3. Click Load unpacked
  4. Select the extension/dist folder

7. Connect Phantom and onboard

  1. Click the StreamPay extension icon — the onboarding page opens
  2. If Phantom injects into the page, click Connect Phantom
  3. If not (common on extension pages), open Phantom, copy your wallet address, paste it into the input field
  4. Click Connect

8. Watch a video

Open YouTube or Prime Video. Navigate to any video. The StreamPay pricing gate appears over the player. Click Start Watching — USDC payments stream every 5 seconds.

Check the backend terminal to see live transaction logs:

[solana] Payment SUCCESS: 1¢ (0.010000 USDC)
[solana] TX: https://explorer.solana.com/tx/...?cluster=devnet

Architecture

┌─────────────────────────────────────────────────────────┐
│                   Chrome Extension (MV3)                  │
│                                                           │
│  ┌──────────────┐  ┌────────────────┐  ┌─────────────┐  │
│  │ Content       │  │ Service Worker │  │ Popup       │  │
│  │ Script        │  │ (Background)   │  │             │  │
│  │               │  │                │  │ - Tx history│  │
│  │ - Gate UI     │  │ - CORS proxy   │  │ - Totals    │  │
│  │ - Meter       │  │ - Wallet store │  │ - Sessions  │  │
│  │ - Badge       │  │ - Badge ctrl   │  │             │  │
│  │ - Platform    │  │                │  │             │  │
│  │   detection   │  │                │  │             │  │
│  └──────┬────────┘  └──────┬─────────┘  └─────────────┘  │
│         │                  │                              │
└─────────┼──────────────────┼──────────────────────────────┘
          │    REST / JSON    │
          └────────┬──────────┘
                   │
      ┌────────────▼───────────────────────────┐
      │       Backend (Express + TypeScript)    │
      │                                        │
      │  ┌──────────┐  ┌──────────────────┐    │
      │  │ Routes   │  │ Services         │    │
      │  │          │  │                  │    │
      │  │ /price   │  │ PricingEngine    │    │
      │  │ /sessions│  │ SolanaPayment    │    │
      │  │ /solana  │  │   Provider       │    │
      │  │ /onboard │  │                  │    │
      │  │ /admin   │  └────────┬─────────┘    │
      │  └──────────┘           │              │
      │                         │              │
      │  ┌──────────────────┐   │              │
      │  │ SQLite Database  │   │              │
      │  │                  │   │              │
      │  │ videos           │   │              │
      │  │ watch_sessions   │   │              │
      │  │ watch_events     │   │              │
      │  │ payment_ledger   │   │              │
      │  └──────────────────┘   │              │
      └────────────────────────-┼──────────────┘
                                │  SPL Token Transfer
                                │
               ┌────────────────▼──────────────────┐
               │         Solana Devnet              │
               │                                    │
               │  Viewer Wallet ──USDC──> Creator   │
               │                                    │
               │  Settlement: ~400ms                │
               │  Fee: ~$0.00025 in SOL             │
               │  Every tx: viewable on Explorer    │
               └────────────────────────────────────┘

Component Breakdown

Component Tech Role
Content Script TypeScript, Shadow DOM Detects videos, renders pricing gate, meters watch time, sends heartbeats
Service Worker Chrome MV3 Background Proxies API calls (CORS bypass), stores Phantom wallet public key
Popup HTML + TypeScript Shows session history and total USDC spent
Onboarding HTML + TypeScript Phantom wallet connect + manual pubkey fallback
Backend API Express.js, TypeScript Pricing, session lifecycle, payment orchestration
Pricing Engine Pure TypeScript function Computes price from base rate × community watch ratio
Payment Provider @solana/web3.js + @solana/spl-token Creates USDC SPL transfers, signs + submits to Solana devnet
Database SQLite (better-sqlite3) Videos, sessions, events, payment ledger

Platform Support

The extension uses a platform abstraction layer to support multiple streaming sites:

Platform Video ID Navigation Detection
YouTube URL param ?v= yt-navigate-finish event
Prime Video ASIN from URL path URL polling

Project Structure

PayPerPlay/
├── backend/
│   ├── src/
│   │   ├── db/
│   │   │   ├── connection.ts          # SQLite setup (WAL mode, foreign keys)
│   │   │   ├── migrate.ts             # Auto-migration runner on startup
│   │   │   └── migrations/            # Numbered SQL migration files
│   │   ├── models/
│   │   │   ├── video.ts               # Video CRUD + pricing queries
│   │   │   ├── session.ts             # Session lifecycle management
│   │   │   └── event.ts               # Watch event logging
│   │   ├── services/
│   │   │   ├── payment-provider.ts    # Solana USDC payment singleton
│   │   │   └── pricing.ts             # Price computation engine
│   │   ├── routes/
│   │   │   ├── price.ts               # GET /api/videos/:id/price
│   │   │   ├── sessions.ts            # Session CRUD + heartbeat handler
│   │   │   ├── solana.ts              # Solana status + payment history
│   │   │   ├── admin.ts               # Admin dashboard + overrides
│   │   │   └── onboarding.ts          # Viewer registration + SOL airdrop
│   │   ├── middleware/
│   │   │   ├── install-id.ts          # X-Install-Id header extraction
│   │   │   └── admin-auth.ts          # Token-based admin auth
│   │   ├── views/
│   │   │   └── admin.html             # Admin dashboard UI
│   │   ├── app.ts                     # Express app configuration
│   │   └── index.ts                   # Server entry point
│   ├── scripts/
│   │   └── solana-setup.ts            # Devnet wallet provisioning script
│   └── data/
│       └── PayPerPlay.db              # SQLite database (auto-created)
│
├── extension/
│   ├── src/
│   │   ├── background/
│   │   │   └── service-worker.ts      # Message handler, Phantom wallet store
│   │   ├── content/
│   │   │   ├── main.ts                # Video detection orchestrator
│   │   │   ├── gate.ts                # Pricing gate overlay (Shadow DOM)
│   │   │   ├── meter.ts               # Watch time accumulation + heartbeat loop
│   │   │   ├── badge.ts               # Extension icon badge with live cost
│   │   │   ├── platform.ts            # Platform abstraction layer
│   │   │   ├── youtube-utils.ts       # YouTube-specific DOM selectors
│   │   │   └── prime-utils.ts         # Prime Video-specific DOM selectors
│   │   ├── popup/
│   │   │   └── popup.ts               # Transaction history popup UI
│   │   ├── onboarding/
│   │   │   └── onboarding.ts          # Phantom connect + manual fallback
│   │   └── shared/
│   │       ├── api-client.ts          # REST client routed via service worker
│   │       ├── types.ts               # Shared TypeScript interfaces
│   │       └── constants.ts           # API URL, intervals, Solana constants
│   └── dist/                          # Built extension (load this in Chrome)
│
├── static/
│   └── extension/
│       ├── manifest.json              # Chrome MV3 manifest
│       ├── popup.html                 # Popup shell
│       ├── onboarding.html            # Onboarding shell
│       └── icons/                     # Extension icons (16/48/128px)
│
├── .env                               # Solana devnet config (git-ignored)
├── .env.example                       # Template showing required variables
└── package.json                       # npm workspaces root

Database Schema

PayPerPlay uses SQLite with WAL mode for concurrent reads. The schema is managed through numbered SQL migrations that run automatically on every backend startup.

videos
  video_id          TEXT  PRIMARY KEY
  title             TEXT
  channel           TEXT
  duration_seconds  INT
  avg_watch_ratio   REAL   -- community average, updated per session
  manual_avg_watch_ratio REAL  -- admin override
  override_price    REAL   -- admin price override in cents

watch_sessions
  session_id        TEXT  PRIMARY KEY
  install_id        TEXT   -- identifies the extension instance
  video_id          TEXT  REFERENCES videos
  status            TEXT   -- active | completed | declined
  price_quoted      REAL   -- price shown at gate (cents)
  price_final       REAL   -- actual amount paid (cents)
  seconds_watched   INT
  amount_streamed   REAL   -- running total paid so far (cents)

watch_events
  event_id          INT   PRIMARY KEY
  session_id        TEXT  REFERENCES watch_sessions
  event_type        TEXT   -- play | pause | seek | heartbeat | end
  timestamp_seconds REAL
  metadata          TEXT

payment_ledger
  id                INT   PRIMARY KEY
  session_id        TEXT  REFERENCES watch_sessions
  amount_cents      REAL   -- payment amount in cents
  rlusd_amount      TEXT   -- USDC amount as string (e.g. "0.010000")
  tx_hash           TEXT   -- Solana transaction signature (base58)
  tx_type           TEXT   -- stream | final | refund
  created_at        TEXT

Every row in payment_ledger maps to one Solana transaction. The tx_hash column stores the base58 Solana signature, viewable at:

https://explorer.solana.com/tx/{tx_hash}?cluster=devnet

Solana Implementation

How payments are sent

The SolanaPaymentProvider singleton in backend/src/services/payment-provider.ts handles all on-chain activity:

  1. On startup, it loads the viewer keypair from VIEWER_SECRET_KEY in .env
  2. On each heartbeat, it calls sendPayment(amountCents):
    • Gets or creates the viewer's Associated Token Account (ATA) for USDC
    • Gets or creates the creator's ATA for USDC
    • Builds an SPL token transferInstruction
    • Signs and submits with sendAndConfirmTransaction
    • Logs the Solana Explorer URL for every transaction

Wallets explained

Wallet Role Who controls it
Viewer wallet (VIEWER_* in .env) Backend hot-wallet that sends USDC payments Backend server
Creator wallet (CREATOR_* in .env) Receives USDC payments Backend server (simulated)
Phantom wallet Your identity in the extension You (via Phantom)

The Phantom wallet is used for user identification only. Actual USDC transfers happen from the backend viewer keypair.

Associated Token Accounts

USDC on Solana lives in Associated Token Accounts (ATAs) — one per wallet, one per token. The setup script creates ATAs for both viewer and creator. If an ATA doesn't exist when a payment is attempted, the payment provider creates it automatically (using the viewer's SOL to pay the ~0.002 SOL rent).


Onboarding

On first install, the extension opens the onboarding page automatically.

Flow

  1. Detect Phantom — the page polls window.solana for up to 2 seconds
  2. Connect — if Phantom is detected, window.solana.connect() is called and the public key is stored
  3. Manual fallback — if Phantom doesn't inject (common on chrome-extension:// pages), a text field appears to paste your Phantom address directly
  4. Register — the backend creates a USDC ATA for the viewer's Phantom address
  5. Status — shows SOL balance, USDC balance, and ATA address
  6. Ready — viewer can navigate to YouTube/Prime Video and start watching

API Reference

Pricing

Method Endpoint Description
GET /api/videos/:id/price?title=&channel=&duration= Get price for a video

Response:

{
  "videoId": "dQw4w9WgXcQ",
  "priceCents": 393,
  "centsPerSecond": 0.2001,
  "avgWatchRatio": 98,
  "durationSeconds": 1964
}

Sessions

Method Endpoint Description
POST /api/sessions Start a new watch session
POST /api/sessions/decline Log a declined video
GET /api/sessions/history Session history for this extension install
POST /api/sessions/:id/events Log a watch event (heartbeat triggers payment)
POST /api/sessions/:id/end End session and send final settlement

All session endpoints require the X-Install-Id header (set automatically by the extension).

Solana

Method Endpoint Description
GET /api/solana/status Connection status, SOL + USDC balances for both wallets
GET /api/solana/payments All payment ledger entries (newest first)
GET /api/solana/payments/:sessionId All payments for a specific session

Status response:

{
  "connected": true,
  "network": "devnet",
  "viewerAddress": "G3Fniz6...",
  "creatorAddress": "FaAesMP...",
  "viewerSolBalance": 2.498,
  "viewerUsdcBalance": "10.000000",
  "creatorUsdcBalance": "0.041200"
}

Onboarding

Method Endpoint Description
POST /api/onboarding/register-viewer Register Phantom pubkey, create USDC ATA
GET /api/onboarding/status?address= SOL balance, USDC balance, ATA address
POST /api/onboarding/airdrop-sol Request 1 devnet SOL (Solana devnet only)

Admin

Method Endpoint Description
GET /admin Admin dashboard (HTML UI)
GET /api/admin/videos List all tracked videos
PUT /api/admin/videos/:id/override Set a fixed price override
PUT /api/admin/videos/:id/ratio Set a manual watch ratio

Admin endpoints require the X-Admin-Token header matching ADMIN_TOKEN in .env.


Configuration

Environment Variables

Generated by backend/scripts/solana-setup.ts. See .env.example for the template.

Variable Description
SOLANA_RPC_URL Solana RPC endpoint (default: https://api.devnet.solana.com)
SOLANA_WS_URL Solana WebSocket endpoint
USDC_MINT Devnet USDC mint address (4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU)
VIEWER_PUBLIC_KEY Viewer wallet public key (base58)
VIEWER_SECRET_KEY Viewer wallet secret key (JSON array of 64 bytes)
CREATOR_PUBLIC_KEY Creator wallet public key (base58)
CREATOR_SECRET_KEY Creator wallet secret key (JSON array of 64 bytes)
PORT Backend port (default: 4000)
ADMIN_TOKEN Secret token for admin dashboard access

Extension Constants (extension/src/shared/constants.ts)

Constant Value Description
API_BASE http://localhost:4000/api Backend URL
HEARTBEAT_INTERVAL_MS 5000 How often payments are sent (ms)
METER_TICK_MS 1000 Watch time accumulation tick (ms)
SOLANA_NETWORK devnet Solana network identifier
USDC_MINT 4zMMC9... Devnet USDC mint address
USDC_DECIMALS 6 USDC decimal places on Solana

Development

Backend (with hot reload)

npm run dev:backend

Uses nodemon + ts-node. Auto-restarts on any .ts or .sql file change. SQLite database is created at backend/data/PayPerPlay.db on first run.

Extension

npm run build:extension

Uses esbuild for sub-second bundling. After building, go to chrome://extensions and click the reload button on the StreamPay card.

Re-run wallet setup (if needed)

npx ts-node backend/scripts/solana-setup.ts

Reloads existing keys from .env on re-runs — will not regenerate keypairs if .env already exists. Run this after funding wallets with SOL to complete ATA creation.

Check Solana status

curl http://localhost:4000/api/solana/status | json_pp

Admin dashboard

http://localhost:4000/admin

Tech Stack

Layer Technology Why
Language TypeScript (strict) Type safety across the entire stack
Backend Express.js Lightweight, well-understood HTTP framework
Database SQLite via better-sqlite3 Zero-config, single-file, WAL mode for concurrency
Blockchain Solana (devnet) Fast settlement (~400ms), near-zero fees, ideal for micropayments
Payments @solana/web3.js + @solana/spl-token Official Solana SDKs for keypair management and SPL token transfers
Stablecoin USDC (SPL token) USD-pegged, 6 decimals, widely used — viewers pay in real dollars
Wallet Phantom Most popular Solana wallet, simple window.solana API
Extension Chrome Manifest V3 Modern extension platform with service workers
Build esbuild Sub-second extension builds
UI Shadow DOM Style isolation — gate overlay can't be broken by host page CSS
Monorepo npm workspaces Single install, shared dependencies

Roadmap

  • Mainnet deployment with real USDC
  • Direct Phantom signing (viewer pays from their own wallet, not backend keypair)
  • Creator dashboard with earnings analytics
  • Multi-creator payment splitting
  • Firefox and Edge extension support
  • Creator-set pricing tiers
  • Viewer reputation and loyalty rewards
  • Additional platform support (Twitch, Vimeo, Netflix)

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Run npx ts-node backend/scripts/solana-setup.ts to provision devnet wallets
  4. Make your changes
  5. Build and test the extension in Chrome
  6. Commit: git commit -m 'Add my feature'
  7. Push: git push origin feature/my-feature
  8. Open a Pull Request

License

MIT License. See LICENSE for details.


Acknowledgements

  • Solana — the blockchain powering PayPerPlay's payment layer
  • USDC — Circle's USD stablecoin for real-dollar micropayments
  • @solana/web3.js — official Solana JavaScript SDK
  • @solana/spl-token — SPL token transfer library
  • Phantom — Solana wallet used for viewer identity
  • esbuild — blazing-fast bundler for the Chrome extension
  • better-sqlite3 — synchronous SQLite for Node.js

Built for a fairer streaming economy — pay only for what you watch.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors