Skip to content
← All posts
· Delta1 Labs LicensingGuide

Converting a Trial to a Paid License Without a Reinstall

A trial user who pays should become a licensed user in place — same install, same data, no key to re-enter. Here is how clean trial-to-paid conversion works: upgrading the key in place, refreshing the cached entitlements, and designing trial expiry so paying simply restores what was there.

The trial did its job: someone installed your software, used it, and decided to pay. The conversion that follows should be the easiest moment in the whole relationship — and too often it is the clumsiest, demanding a new key by email, a re-entry, sometimes a reinstall, occasionally the discovery that their trial work is gone. None of that is necessary. A trial and a paid license are the same kind of thing — a signed grant of entitlements — so converting one to the other is a change of attributes, not a replacement.

Upgrade the key in place

The cleanest model issues no new key. The trial key the customer already activated becomes their paid license. On the server, when the payment succeeds, you change that license”s tier from the trial tier to the paid one and extend or remove its expiry:

// On a successful payment for a customer who is already on a trial key:
await licenses.Upgrade(trialKey, new UpgradeRequest
{
    Tier     = "pro",      // trial tier → paid tier
    ExpiryUtc = null,      // perpetual; or a renewal date for a subscription
    Seats    = seatsPaid,  // bump the seat count if the plan includes more
});

Nothing about the customer”s machine changes. The key in their app is the same string; only what that key means changed, server-side. Compared to issuing a fresh key, this removes an email, a copy-paste, and an entire class of “I never got my key” support tickets.

If your billing is automated, this upgrade is exactly what a fulfillment webhook should do on a paid event for an order tied to an existing trial — idempotently, keyed on the order reference.

Refresh the cached entitlements

Here is where in-place conversion usually looks broken: the customer pays, re-opens the app, and it still says “Trial — 0 days left.” The upgrade worked; the app is reading a stale cache.

Licensing SDKs cache a signed lease so the app validates offline between check-ins. That cached lease was minted while the key was a trial, so it still carries the trial tier and expiry. The fix is to force a fresh validation after conversion rather than trusting the cache:

// After the user completes payment (or on app resume), re-validate ONLINE.
var info = await client.ActivateAsync(key);     // re-activate pulls a fresh lease
// or: await client.ValidateOnlineAsync(key);

if (info.Status == LicenseStatus.Valid && !info.IsTrial)
{
    ApplyEntitlements(info.Entitlements);  // paid tier's flags + limits now apply
    ShowStatus("Licensed");                // trial banner gone
}

The new lease carries the paid tier”s entitlements, replacing the cached trial lease. Give the user an obvious “I”ve purchased — refresh my license” affordance, and also re-validate on the next launch, so even a user who closes the app before refreshing is upgraded automatically next time.

Trial keytier=trial, expirypayUpgrade in placesame key → tier=prore-validateApp: Licensedfresh lease, paid entitlements

Design expiry so paying just restores

Conversion is not always same-day. Many customers trial, let it lapse, and come back weeks later. What your app does at expiry decides whether that return is a one-click upgrade or a dead end.

The rule: expiry degrades, it never destroys. When a trial ends, fall back to a read-only or free mode and leave every bit of the user”s work in place. Then the comeback is trivial — they pay, the key upgrades, the app re-validates, and the features switch back on over data that was waiting for them. If instead you wipe projects or lock them out of their own files on expiry, you have deleted the single strongest reason they had to buy, and turned “click to upgrade” into “start over.” A trial that expires into read-only converts; a trial that expires into a locked door does not.

This is also why fail-closed for licensing should mean “disable paid features,” not “disable the app.” The customer in the conversion window is your best prospect; the worst thing to do is make their data hostage.

A few details that pay off

  • One trial per customer. Tie the trial to an email or a device so a lapsed trial can”t simply be restarted for free. On conversion, that same identity links the paid license to the trial — which is how you measure trial-to-paid rate later.
  • Carry context forward. Because it is the same install and the same key, settings, projects and preferences survive automatically. Don”t reset anything on upgrade; the whole point is continuity.
  • Make “already paid?” obvious. A single visible action that re-validates turns a confused “it still says trial” into a solved problem without a support round-trip.
  • Handle the expired-then-paid case explicitly. If the trial already lapsed, the upgrade may need to re-activate the key on the current machine; make sure that path refreshes the lease too.

Issuing a trial is how you earn the chance to convert. Converting it well is quieter work — upgrade in place, refresh the lease, protect the data — but it is the difference between a customer who clicks once and one who wonders where their key went. Treat the trial and the paid license as the same object in two states, and the upgrade becomes what it should be: nearly invisible.

Try Nebula.NET

Harden your .NET code in minutes — start with the free edition.