DEV Community

Ape Collective
Ape Collective

Posted on

How to verify Gumroad license keys in an Electron app (and the 3 gotchas nobody warns you about)

If you sell a desktop app on Gumroad, it hands every buyer a license key. But Gumroad stops there — checking that key inside your app is entirely up to you. Here's how to do it properly in Node/Electron, plus the three traps that catch almost everyone.

We'll use gumroad-license-lite, a tiny, zero-dependency, MIT-licensed helper (you can npm install it or just copy its ~120 lines).

  1. Turn on license keys in Gumroad
    On your product, enable "Generate a unique license key per sale," then grab your product_id (in the product settings / API). Every buyer now gets a key on their receipt.

  2. Verify a key
    const { verifyGumroadLicense } = require('gumroad-license-lite');

const result = await verifyGumroadLicense({
productId: 'YOUR_PRODUCT_ID',
licenseKey,
});

if (result.valid) {
unlockApp(result.email);
}
result.valid is true only if the key is real and the sale wasn't refunded, disputed, or a cancelled subscription — not just "does this key exist," which is gotcha #1 below.

  1. Gate your app on launch You don't want to call Gumroad on every launch, and you want the app to survive a flaky connection. LicenseGate caches the result and re-checks periodically:

const path = require('node:path');
const { LicenseGate } = require('gumroad-license-lite');

const gate = new LicenseGate({
productId: 'YOUR_PRODUCT_ID',
storageFile: path.join(app.getPath('userData'), 'license.json'),
recheckEveryDays: 3,
offlineGraceDays: 14,
});

// on your activation screen:
await gate.activate(userEnteredKey);

// on every launch:
const status = await gate.check();
if (!status.licensed) showActivationScreen();
The 3 gotchas

  1. "Valid" isn't the same as "exists." A refunded or charged-back sale still has a real, working key. If you only check that the key exists, people can buy, copy the key, refund, and keep your app forever. Always check the refund / dispute / subscription flags (the helper above does this for you).

  2. The uses counter is global, not per-device. Gumroad tracks a uses count, but it can't tell you which machines — so you can't actually enforce "3 devices per license." One key can quietly unlock a hundred installs.

  3. Offline means locked out. A pure online check fails the moment your user has no internet — on a plane, on hotel wifi — and your paying customer can't open the app. A local cache (like above) softens this, but a plain JSON cache is editable, so it's friction-reduction, not real protection.

When you outgrow the basic check
An online check is genuinely fine for a lot of apps. But when those three gotchas start costing real money, you need cryptographically signed, device-bound tokens that verify offline and automatic lockout on refunds and chargebacks. That's a meaningfully bigger build — a signing server, client SDKs, a refund webhook — so I packaged it as KeyGate for people who'd rather not assemble it from scratch. Either way, the free tool above is the right place to start.

TL;DR
Enable license keys and grab your product_id
Check validity, not just existence
Cache for offline use — but know its limits
Free tool: https://github.com/apecollective/gumroad-license-lite
How do you handle licensing for the apps you sell on Gumroad? Genuinely curious what others are doing.

Top comments (0)