Document free-license, tiers, seat management, and local verify variants
This commit is contained in:
@@ -6,9 +6,12 @@ Works in modern browsers and Node 18+. No native dependencies; signature verific
|
||||
|
||||
## What you get
|
||||
|
||||
- **Offline verification**: check a license key with just the issuing server's public key. No network.
|
||||
- **Offline verification**: check a license key with just the issuing server's public key. No network. Optional local fingerprint and expiry enforcement.
|
||||
- **Online validation**: live revocation check and fingerprint binding via the service's `/v1/validate` endpoint.
|
||||
- **Purchase flow**: kick off a BTCPay checkout and poll for the issued key.
|
||||
- **Free licenses**: redeem a free-license code, no payment.
|
||||
- **Tiers**: list a product's public tiers for an in-app picker.
|
||||
- **Machine/seat management**: activate, heartbeat, and deactivate seats.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -28,6 +31,21 @@ console.log('licensed for product', ok.productId)
|
||||
|
||||
That's the whole integration. Embed your public key as a string at build time (e.g. Vite's `?raw` import, webpack raw-loader, or just a `const`). If the verifier returns without throwing, the key is real and was issued by you.
|
||||
|
||||
### Local fingerprint and expiry enforcement
|
||||
|
||||
Two offline variants enforce more without a network call:
|
||||
|
||||
```ts
|
||||
// Throws if the key is fingerprint-bound and the machine doesn't match.
|
||||
// (Unbound keys ignore the fingerprint.)
|
||||
verifier.verifyWithFingerprint(keyFromUser, machineFingerprint)
|
||||
|
||||
// Throws `expired` if now is at or past the key's expiry. Perpetual
|
||||
// keys (expiresAt === 0) always pass. No grace-window logic offline;
|
||||
// use `Client.validate` for that.
|
||||
verifier.verifyWithTime(keyFromUser, Math.floor(Date.now() / 1000))
|
||||
```
|
||||
|
||||
## 10-line online check (with revocation + fingerprint)
|
||||
|
||||
```ts
|
||||
@@ -56,6 +74,46 @@ console.log('got license:', key)
|
||||
|
||||
`waitForLicense` polls until the BTCPay invoice settles and the service issues a key. It throws if the invoice expires or becomes invalid.
|
||||
|
||||
## Free licenses
|
||||
|
||||
Redeem a free-license code (the Creator-tier onboarding path) to get a signed key directly, with no BTCPay checkout:
|
||||
|
||||
```ts
|
||||
const { licenseKey } = await client.redeemFreeLicense('my-product', 'CODE-1234')
|
||||
verifier.verify(licenseKey) // offline-verifiable like any issued key
|
||||
```
|
||||
|
||||
Throws if the code is unknown, disabled, expired, for another product, not a free-license code, or capped out. Optional `{ buyerEmail, buyerNote }` third argument is recorded on the issued license.
|
||||
|
||||
## Tiers
|
||||
|
||||
List a product's public tiers (no auth) to build an in-app tier picker that stays in sync with the operator's admin setup:
|
||||
|
||||
```ts
|
||||
const { product, policies } = await client.listPublicPolicies('my-product')
|
||||
for (const p of policies) {
|
||||
console.log(p.name, p.slug, p.priceSats, p.entitlements)
|
||||
}
|
||||
```
|
||||
|
||||
Each policy carries slug, name, price (in the product currency's smallest unit, sats or cents), duration, seat cap, entitlements, and trial/recurring flags. Pass the chosen `slug` to `startPurchase(slug, { policySlug })` so the invoice is priced and the issued license is provisioned for that tier.
|
||||
|
||||
## Machine/seat management
|
||||
|
||||
For per-seat enforcement, manage machines explicitly. All three return a `MachineResponse` (`{ ok, reason?, machineId?, activeCount?, maxMachines? }`):
|
||||
|
||||
```ts
|
||||
await client.activate(key, fingerprint, { hostname, platform }) // claim a seat
|
||||
await client.heartbeat(key, fingerprint) // mark the seat alive
|
||||
await client.deactivate(key, fingerprint, 'user signed out') // free the seat
|
||||
```
|
||||
|
||||
`validate` already binds a seat on first use when you pass a fingerprint; reach for these when you want explicit activate/deactivate lifecycle or periodic liveness pings.
|
||||
|
||||
## Examples
|
||||
|
||||
Runnable end-to-end scripts live in [`examples/`](./examples): `offline-verify.ts` and `online-validate.ts` (the latter walks purchase to `waitForLicense` to `validate`).
|
||||
|
||||
## Browser usage
|
||||
|
||||
Everything here works in the browser too. Drop the library into your React/Svelte/Vue app and run offline verification client-side — no server call needed for the common case.
|
||||
|
||||
Reference in New Issue
Block a user