Node Example — self-hosted license server (BYOS)
Quick summary
This Node 22 example demonstrates self-hosted license enforcement—not a full payment service. It verifies entitlements and device proofs before granting a one-time execution capability.
Try the minimal server with a development store, then test invalid, expired, or replayed requests.
A minimal enforcement host with @yetdapay/license-server — the same wire contract the Worker speaks, run inside your own service. Requires Node 22.
import { createLicenseServer, createSqliteStore } from '@yetdapay/license-server';
import { createWorkerConfig } from '@yetdapay/license-server';
const server = createLicenseServer({
store: createSqliteStore({ file: './.data/license.db' }),
keys: { load: async () => JSON.parse(process.env.DEVPAY_LICENSE_KEYS) },
issuer: 'devpay', // or createWorkerConfig({store, keys}) for iss='devpay-lic'
entitlementTtlSec: 120,
});2
3
4
5
6
7
8
9
Expose two kinds of routes yourself (the package is a library, not an HTTP server):
// POST /api/license/activate — body {key, product_id, public_jwk}
const { entitlement, expires_in, features } = await server.activate(body);
// POST /api/premium/convert — gated paid operation
const ctx = await server.authorize(headers, requestTarget, rawBody);
// ctx carries license + feature context; denials throw LicenseError2
3
4
5
6
Framework helpers:
- h3/Nuxt →
@yetdapay/license-nuxt(defineLicenseEventHandler). - Express/Fastify/Workers/
node:http→@yetdapay/license-nuxt/core'screateLicenseGuard— feed it a 4-field{headers, method, requestTarget, rawBody}descriptor.
Client side, pair with @yetdapay/license-web (activatePath default /api/license/activate already targets this self-hosted shape — no createWorkerConfig needed unless the host is the Worker).
Store adapters
createMemoryStore(seed?) for tests · createSqliteStore({file}) as the reference persistent adapter and the template for a D1/Postgres port (node:sqlite is experimental in Node 22 — port before production).