Sell one-time licenses that unlock Pro in a .NET or Electron app. Verification is a local function call: no HTTP, activation, certificate chain, feature catalog or .NET helper.
Next License sells one-time licenses that unlock Pro in a desktop app. Customers purchase, copy their license key from their account, and paste it into the app. The app checks the key locally and displays who it is registered to and how many seats it covers.
A license is a standard ES256-signed JWT, not an encrypted JWE. The server holds one private key; applications bundle its public key. Verification requires no network, activation, certificate chain, feature catalog or .NET helper process.
A dated license covers builds released on or before updatesThrough. Lifetime covers future builds. Covered versions remain usable forever: there is no token exp and no comparison with today's date. A newer uncovered build can remain in Free mode and offer renewal.
- Run
npm ciinMyApp.Client, thendotnet watchinMyApp. - Run
dotnet run --project MyApp.Licensing.Tool -- jwt ./license-keysfrom the repository root. - Configure
Licensing:LicensePrivateKeyPem,Licensing:LicenseIssuer(defaultacme-studio) and a random base64 32-byteLicensing:ShortKeySaltin server secrets. - Configure Stripe prices and your agreement in Operations. No live prices, agreement or signing key are supplied by default.
- Bundle
license-public.pemwith your app; never distribute the private key.
var result = LicenseJwt.Verify(token, publicKeyPem, "acme-studio", "acme-studio", "2026-09-21");
bool enablePro = result.Valid;
// result.License: Name, Organization, Seats, Edition, UpdatesThrough, Lifetimeimport { verifyLicense } from './MyApp.Licensing.JavaScript/license.mjs';
const result = verifyLicense(token, { publicKey, issuer: 'acme-studio', product: 'acme-studio', buildDate: '2026-09-21' });
const enablePro = result.valid;
// result.license: name, organization, seats, edition, updatesThrough, lifetimedotnet run --project MyApp.SampleApp -- --desktop demonstrates registered details and covered/uncovered/Lifetime keys with disposable signing material. Publish with -p:LicensePublicKeyFile=/absolute/path/license-public.pem -p:AppBuildDate=YYYY-MM-DD.
dotnet test MyApp.Tests includes .NET-to-Node JWT compatibility and payment/renewal/reissue tests. Node is required. npm run build and npm run test:run in MyApp.Client validate the storefront.
The baseline contains 17 licensing tables and no triggers. GitHub owns releases, Stripe owns promotions/payments, and signing keys stay in server configuration. Optional refresh stores no installation history. See the complete table review for retained tables, removed features, integrity rules and database-provider validation limits.
- Copy
.env.exampleto.envin the repository root. SetStripe__SecretKey,Stripe__WebhookSecret, andLicensing__GitHubRepository(owner/repository). Restart the backend after configuration changes. Existing process environment variables take precedence over.env. - Open
/admin/settingsto check configuration. KeepStripe__LiveMode=falsefor a Stripe test key. Configure the ES256 license signing key through server secrets before issuing licenses. - In
/admin/catalog, save the price and currency for each offering you want to sell. Click Create Missing Stripe to provision one-time Stripe products/prices. Review each offering and click Approve for sale. Free is the only plan shown until paid offerings are approved. Editing an amount makes that offering a draft again; existing order evidence is preserved. - Publish your license terms at
/admin/agreements. Configure Stripe to send the licensing payment events to/stripe/webhook(see the existing payment integration documentation). Fulfillment requires a signing key and working webhook configuration. - Publish GitHub releases with Windows, macOS or Linux builds attached.
/admin/releasesand/downloadread the latest 100 public releases from the configured repository. Updater metadata such as.blockmapand.ymlfiles is omitted from download buttons. These listings do not alter signed license entitlement dates. - Use
/admin/licensesto search customers, issue administrative licenses, or update a selected license. Customer lookup fills account identifiers automatically./admin/orderslinks directly to the matching Stripe payment and supports completing order reviews.
The example repository is NetCoreApps/acme-studio. Stripe catalog provisioning is separate from approval; creating prices alone never exposes a paid plan. The optional explicit SoftwareSetupTests.Stripe_test_catalog_provisioning_is_retryable_and_requires_separate_approval test uses LICENSE_STRIPE_ACCEPTANCE_KEY and accepts only a Stripe test key. It provisions an isolated test catalog, checks retries and approval, then archives its Stripe price and product; it does not perform payments.
SQLite, PostgreSQL, MySQL/MariaDB and SQL Server are selected with Database:Provider and ConnectionStrings:DefaultConnection. Both OrmLite and EF Identity use the selected provider. The deployment guide covers next-saas-style Kamal destinations, settings examples, secrets and repeatable live database tests.