Self-hosted license server
What a self-hosted license key server does, what it needs to run, and how to decide between building, renting or running one yourself on Linux or Windows.
Run and sell
Subscription licensing ties a license key’s expiry date to a paid subscription: each paid renewal moves the expiry forward, a canceled or unpaid subscription lets the license run out, and a full refund or a lost dispute revokes it. With Stripe, the license server learns about payments from Stripe’s webhooks and updates licenses on its own. This guide explains the pattern and how the optional Stripe Managed Payments billing of Velsigil’s Studio plan implements it.
Last updated
In a subscription model, the license’s expiry date follows the end of the period the customer has paid for. Your application never talks to the payment provider: it keeps validating its key, and the license server answers “active” or “expired” depending on whether the current period is paid.
Velsigil’s SDKs have no separate grace period: once the paid period ends, validation answers license_expired until a renewal extends the license again. While Stripe retries a failed renewal payment, the subscription is past due and the license is not extended; if a retry succeeds, the paid invoice extends it.
With a plain payment processor, you are the seller: you collect sales tax where you owe it, send receipts and handle disputes. With a merchant of record, the provider is the legal seller to your customer: it takes the payment, handles sales tax, sends receipts and handles disputes, and pays you out.
Stripe Managed Payments is Stripe’s merchant-of-record offering. Whether your business is eligible is Stripe’s decision. Managed Payments subscriptions are created only through Stripe Checkout or Payment Links, and buyers manage their subscriptions and receive receipts through Link.
The Stripe billing of Velsigil’s Studio plan is optional and off by default. Once set up, the panel follows each subscription and keeps its license in step:
| Stripe event | What happens to the license |
|---|---|
| First period paid | A license is created on the mapped plan, starting at purchase and expiring at the end of the paid period. The key is emailed once, with a link to the customer portal. |
| Renewal paid | The expiry moves to the end of the new period. It only ever moves forward. |
| Price switched (upgrade or downgrade) | The license moves to the plan mapped to the new price, up or down: its device limit and features follow that plan, and the change is logged and sent as a license.updated webhook. Devices already in use are never removed; above a lowered limit, new activations are refused on device-locked products and you are notified. Map the new price before the switch; a price of another product is not applied. |
| Renewal payment failing | No extension while the subscription is past due. |
| Canceled or unpaid | No further extensions; the license lapses at its expiry. If Stripe cancels the subscription because of a dispute that was not won, the license is revoked. |
| Full refund | The license is revoked, and the subscription is canceled while “cancel on revoke” is on (the default). A partial refund changes nothing. |
| Dispute opened | The license is suspended. |
| Dispute closed | Won: the suspension is lifted. Lost: the license is revoked, and the subscription is canceled while “cancel on revoke” is on. |
Billing never bans and never refunds, lifts only suspensions it created itself, and leaves a revoked license revoked. Each change appears in the audit log with the actor “Stripe billing” and sends your normal webhooks, such as license.created, license.extended or license.revoked.
One caveat: Stripe does not document refund and dispute webhooks for Managed Payments, so treat that part as unverified until you have tested it in a Stripe sandbox.
Velsigil’s billing includes a US-only hold, on by default. A purchase from a non-US or unknown billing country creates no license: the event is marked as failed for manual review, and a geo_anomaly security event is raised. Nothing is refunded automatically; you decide what to do. The check rests on the billing country the buyer declares.
2025-03-31.basil or later and a restricted API key with minimal permissions./api/billing/stripe/webhook in Stripe and subscribe it to the 13 events the panel handles: checkout.session.completed, checkout.session.async_payment_succeeded, invoice.paid, invoice.payment_failed, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, customer.subscription.paused, customer.subscription.resumed, charge.refunded, charge.dispute.created, charge.dispute.updated and charge.dispute.closed.offer field to the panel’s /api/billing/checkout. The panel checks that the request comes from an allowed origin, creates the Checkout Session and answers with a page that forwards the buyer to Stripe. It writes nothing itself: the license comes only from the webhooks.Stripe, like most webhook senders, may deliver an event more than once and not in order. A handler that applies each event’s contents blindly can extend a license twice or move it backwards. Velsigil’s billing worker runs every 15 seconds, re-fetches the subscription from Stripe instead of trusting the event’s contents, and processes events idempotently, so duplicates and out-of-order events are harmless. The endpoint checks Stripe’s signature with a 300-second tolerance and answers 404 while billing is off.
Stripe retries a failed delivery for three days. Deliveries that never arrive are recovered as well: every hour the panel asks Stripe for the events it handles (Stripe keeps events for 30 days; the restricted key needs “Events: Read” for this) and queues those it never received. Events are stored by their Stripe ID and each subscription is fulfilled once, so a recovered event never creates a second license. The same rule applies to Velsigil’s own webhooks to your systems: they are delivered at least once and possibly out of order, so deduplicate them by delivery ID.
After paying, the buyer receives an email with their license key and a link to your customer portal. They enter the key in your application, and they sign in to the portal with the same key to see the license’s status and expiry, manage devices and download releases. The subscription itself, and its receipts, they manage through Link.
Before going live, run the whole lifecycle in a Stripe sandbox: a first purchase, a renewal, a failed renewal, a cancellation, a full refund and a dispute. Check after each step that the license in the panel, and the answer your application gets, changed as described above. Velsigil’s operations documentation walks through this test.
Stripe Managed Payments is the only payment integration built into Velsigil. Any other shop connects through the REST API with a scoped API key: on a paid order, create keys with POST /api/panel/licenses; on a renewal, extend the license with POST /api/panel/licenses/:id/extend (an extension counts from today if the license has already expired); on a refund, revoke it. With Velsigil’s Studio plan, signed webhooks tell your shop about license events in return. The self-hosted license server guide covers the API and webhooks.
Keep reading
What a self-hosted license key server does, what it needs to run, and how to decide between building, renting or running one yourself on Linux or Windows.
How license keys get shared, forged or replayed, and the defenses that work: signed answers, replay protection, hashed keys, device secrets, rate limits.
How software checks a license without internet: signed offline leases, signed license files, grace periods, clock tampering and what each one trusts.
How hardware-locked licenses bind a key to a device: machine IDs per OS, why hardware IDs can be spoofed, device secrets, limits and customer resets.
Early access
Early access is free. In exchange, we ask for your feedback while we prepare the paid launch. Spots are limited, and we reply to every request by email.
Early access ends when paid subscriptions launch; we will tell members by email before then. To keep using Velsigil after that, you need a subscription. The 30% discount applies to the first 12 months of either plan, paid monthly or yearly.
Opens your email app with a short template: company, what you sell, your platforms and languages, expected license volume and how you plan to deploy. You can also write to hello@velsigil.com.