You’ve spent the last eighteen months building a VST plugin, a CAD extension, or maybe a scientific computing tool. Every feature has been earned line by line. The code compiles, the customers are waiting, and you’re finally ready to ship.
Then you run into the last wall: licensing.
You need to make sure only paying customers can run your software. You need to handle activations, trials, maybe upgrades and refunds. And somewhere on your list is a nagging question: “How hard can this really be? Can I just build it myself?”
It’s a fair question. You’re a C++ developer who just finished something far more complex than a license check. You can write a server endpoint. You can hash a serial number. You can probably spin up a database in an afternoon. The DIY instinct is rational.
This guide won’t tell you what to do. It will show you what’s actually behind the word “licensing” — what you’d be signing up to build, maintain, and support for the lifetime of your product. After that, the decision is yours.
What Licensing Actually Includes
Before we talk about building it, let’s define what “it” is. Most developers start with a mental model that looks like this: generate a key, check the key on startup, done. That’s maybe 5% of a working licensing system.
Here’s what actually sits under the word “licensing”:
- Key generation and distribution — How are keys created, stored, and delivered to the customer? Email? Purchase confirmation page? Re-deliverable when the customer loses them?
- Dealer distribution — Not every customer buys direct. Resellers and dealers are a legitimate sales channel for commercial software, especially in B2B and region-specific markets. Dealers need their own inventory of keys, their own activation flow, and visibility into what they’ve sold.
- Activation — The first time a user enters their key, something happens on your side: the key gets bound to their account, their device, or both. This is not the same as validation.
- Validation — Every time the app runs, it needs to confirm the license is still valid. Online? Offline? Cached? How stale is too stale?
- Hardware binding — If a key isn’t bound to a device, one customer can share it with a thousand. Binding requires a stable host ID and a way to handle the inevitable: motherboard swaps, new computers, reinstalls.
- Trial periods — Time-limited access before purchase. Two quieter questions hide inside this. First, the trial end has to be stored securely on the client — inside a tamper-resistant, encrypted file — so a user can’t just delete a counter and start over. Second, if the trial has expired but the machine is offline, how does the application find out? The client can enforce an end date it already has, but the trial still has to touch the server at some point, or “time-limited” means nothing.
- Subscriptions — Licenses that expire unless payment continues. Needs coordination with your billing provider’s webhooks and a clean handoff when a renewal fails.
- Bundles — At some point you’ll want to sell three SKUs together as a single package for a single price, because bundling is a real marketing lever. That means one serial that activates three products — a different shape than one key per SKU, with its own rules for partial activation, upgrades, and revocation.
- Upgrades and version gating — Customer bought v1. v2 is out. Is it free for them? Paid? A discounted upgrade? Now extend that to upgrades sold through your dealers: the dealer needs to know who’s eligible, at what price, and what happens to the old license when the new one activates.
- Revocation — Refunds, chargebacks, fraud, stolen keys. You need to be able to kill a license cleanly without breaking legitimate customers.
- Offline operation — Some of your customers will run your software on machines that never touch the internet. Studios, air-gapped labs, bandwidth-constrained regions. Offline isn’t an edge case for C++ desktop software — it’s a common requirement.
- Multi-seat licenses — A single purchase that activates on five machines. Seat limits, seat management, and admin tooling.
- Support and self-service — “I got a new computer.” “I can’t find my key.” “My license stopped working.” These requests arrive daily once you have customers.
Not every product needs every piece on this list. A single-SKU developer tool has very different needs than a multi-tier CAD suite sold through dealers across three continents. Before you write a single line of licensing code, the most valuable thing you can do is figure out which of these items apply to you — and which you can safely leave out. Building against a clear picture of your own needs is always easier than building against the full generality of everyone else’s.
The DIY Path
If you decide to build your own licensing system, here’s the terrain you’ll cross. Most DIY projects move through three stages, and each one solves a real problem while leaving a real problem open.
Starting Point — Hardcoded Comparison
The simplest possible check: compare the entered serial against a known value.
const std::string expectedSerial = "XXXX-XXXX-XXXX-XXXX";
bool isLicenseValid(const std::string& serial) {
return serial == expectedSerial;
}
This is the mental model most developers start with, and it solves exactly one thing: a user who didn’t read a key anywhere can’t guess it. That’s the end of its usefulness. The expected serial sits in plain text inside your binary — visible in any hex editor. Every copy of your app ships with the same valid key. There’s no way to issue different keys to different customers, no way to revoke a key, no way to tell one device from another.
You won’t ship this. But it’s worth naming as the floor, because every additional capability you want is a departure from this point.
Local Encryption
The next step is usually to stop storing recognizable strings. Encrypt the valid-serial list (or a hash of it) with AES-256, embed the ciphertext in the binary, and decrypt at runtime.
// At build time, you produce an encrypted blob of valid serial hashes.
// At runtime, you decrypt and compare.
std::vector<std::string> loadValidSerialHashes() {
std::vector<uint8_t> ciphertext = readEmbeddedBlob();
std::vector<uint8_t> key = obfuscatedKey(); // still lives in your binary
std::vector<uint8_t> iv = obfuscatedIV();
std::vector<uint8_t> plain = aes256_cbc_decrypt(ciphertext, key, iv);
return parseHashList(plain);
}
bool isLicenseValid(const std::string& serial) {
auto hashes = loadValidSerialHashes();
return std::find(hashes.begin(), hashes.end(), sha256(serial)) != hashes.end();
}
This solves casual inspection. A string search through your binary no longer reveals the valid keys. But two harder problems remain. The decryption key has to exist in the binary at some point, because the application itself needs to read the list. Obfuscating the key slows discovery but doesn’t eliminate it. More importantly, the list is frozen at build time. You can’t revoke a serial after a refund, can’t bind a license to a device, can’t count active installations, and can’t issue a new key to a new customer without shipping a new build.
Encryption is a useful layer, but it doesn’t change the shape of what a licensing system can do. For that, you have to move the decision off the client.
Server-Side Validation
This is the step where a licensing system stops being a check and starts being a system.
The shape of the request:
// Pseudocode — real code needs TLS, retry, timeout, error handling.
std::string hostId = computeHostId(); // you have to implement this.
std::string serial = enteredByUser;
json request = {
{"app_id", APP_ID},
{"api_key", API_KEY},
{"product", PRODUCT_CODE},
{"serial", serial},
{"host_id", hostId}
};
auto response = httpsPost("https://your-api.example.com/activate", request);
if (response["status"] == "valid") {
// Store the signed license locally so the customer can work
// offline for as long as you allow.
storeSignedLicense(response["license"]);
}
With a server in the picture, you can finally revoke licenses, enforce device limits, issue trials, run upgrades, and see what’s active in the field. The decision-making has moved to a machine you control.
What this example doesn’t show is everything outside the endpoint: the server that answers it, the database behind the server, the dashboard that creates licenses, the webhook that issues them on purchase, the admin tooling that lets your support team release a device binding for a customer with a new laptop, the offline mode for customers without internet, the signed-response handling on the client, the certificate rotation, the retry logic. That’s the work behind a single httpsPost. That’s also the subject of the next section.
A Brief Note on Client-Side Reality
Before we talk about cost, one quick reality check. Any code that runs on a customer’s machine can, in principle, be inspected. That’s physics, not a flaw — and it’s true whether you write the licensing yourself or use a service. The goal of a licensing system isn’t perfect security; it’s reasonable friction. It buys you time, keeps honest customers honest, and makes the licensed path easier than the alternative. We’ll cover anti-tampering and binary protection in depth in their own articles. For the question in front of us — “should I build this myself?” — what matters is that DIY and third-party systems live in the same reality. The choice is about time and scope, not about whether impossible security is on the table.
The Real Cost of Building It Yourself
DIY licensing has a real cost, and it’s rarely the cost developers estimate at the start. It’s not the weekend you imagine. Here’s what the bill actually looks like.
Infrastructure. You need a server that’s up when your customers launch your app — which is, unpredictably, all the time. That means a TLS certificate and its renewal, uptime monitoring, database backups, rate limiting against abuse, and a deployment pipeline. None of this is hard individually. All of it adds up to ongoing work that has nothing to do with your product.
E-commerce integration. You won’t be building your own checkout — that’s what WooCommerce, Shopify, Paddle, and FastSpring exist for. The real work lives on the seam between your store and your licensing system. When a purchase completes, a license needs to exist and land in the customer’s inbox within seconds. When a refund or chargeback happens, that license needs to die before the customer launches your app again. Webhook reliability, retry logic, idempotent license creation, and a way to reconcile when the store and the licensing system disagree — that’s the integration bill. This is also where your choice of e-commerce platform starts to matter, because the quality of its webhook and order API directly shapes how much code you’ll write.
Admin tooling. The first time a customer emails “I need to activate on my new laptop,” you’ll realize you need a way to look up their license, see their device bindings, release the old one, and confirm the new activation. That’s a dashboard. With search. With audit logs. Written by you.
Business logic growth. On day one you sell one SKU with perpetual licensing. Six months later you want to offer a trial, then a Pro tier, then a bundle that pairs two products under a single serial, then a multi-seat license, then a subscription option, then a discounted upgrade for v1 customers. Each addition interacts with every existing piece. The complexity is not linear.
Edge cases. Customers change hardware. They install on a second machine and expect it to work. They ask for a refund on day 29 of a 30-day window. They run your software inside a virtual machine that reports a different host ID every boot. Each case is a decision, a code path, and a potential support ticket.
Maintainability. None of this is your product. Under release pressure, the licensing bits get “just one more quick change” until they’re the part of the codebase you’re afraid to open. The shortcuts compound. Six months in, the developer who remembers exactly how a refund triggers a device release is only you — and you’re already starting to forget. Every small change to the licensing code carries more risk than it should, which slows future changes even further. This isn’t a failure of discipline; it’s what happens when a non-core concern has to share a schedule with a core one.
Opportunity cost. This is the cost developers notice last and regret most. The weeks you spend debugging webhook retries are weeks you didn’t spend improving your reverb algorithm, your mesh solver, your simulation core — the thing that actually makes customers choose your product over a competitor. Licensing doesn’t differentiate you. It just has to work.
None of these costs are deal-breakers. Developers have built their own licensing systems before and will again. But the honest estimate is months of focused work to reach a production-grade system, plus a steady percentage of your attention forever after. That’s the real number. Whether it’s worth it depends on what else you could be doing with that time.
When DIY Makes Sense, When It Doesn’t
There’s no universal answer. There’s a pattern, though.
DIY is a reasonable choice if:
- You sell a single SKU with a simple model — perpetual, one-time purchase
- You don’t sell through dealers or resellers, and don’t plan to
- You don’t need subscriptions, bundles, or version upgrades
- Your audience is small enough that support volume won’t take over your week
- Your licensing model is stable — you don’t expect it to grow
- You genuinely enjoy backend and operations work, and have time for it
DIY gets expensive if:
- You have or plan to have multiple SKUs, tiers, or bundles
- Dealers and resellers are part of your distribution
- You need both online and offline activation, across real-world customer setups
- You want trials, subscriptions, and upgrades that all compose cleanly
- Multi-seat licensing is on the table
- Your competitive edge lives in your product — the reverb algorithm, the mesh solver, the simulation core — and every hour away from it is an hour your competitors are spending on theirs
The list isn’t exhaustive, but the shape is clear. DIY scales with simplicity. As your licensing model grows, so does the cost of running your own.
An Alternative — Use a Dedicated Service
Full disclosure before this section: the list of work in everything above is exactly why KEYZY exists. We built it because we wanted to ship C++ desktop software without owning all of this ourselves — and because the licensing services we looked at weren’t designed for the way native C++ products actually work. So when we recommend the dedicated-service path, we recommend it as the people who took that path for their own reasons.
The other path, then, is to let a dedicated service own the pieces you don’t want to build. KEYZY is one such service, designed specifically for C++ desktop software. Most of the topics in this guide — key generation, activation, hardware binding, offline operation, trials, upgrades, bundles, dealers, revocation — are features you configure instead of features you build.
Integration into a C++ project is straightforward:
#include "KeyzyLicenseActivator.h"
Keyzy::ProductData productData(
"YOUR_APP_ID",
"YOUR_API_KEY",
"YOUR_PRODUCT_CODE",
"YOUR_CRYPTION_KEY"
);
auto pActivator = std::make_unique<Keyzy::KeyzyLicenseActivator>(productData);
auto pValidator = pActivator->getLicenseValidator();
// First run — user enters the serial they received at purchase.
Keyzy::LicenseStatus status = pActivator->activateSemiOnline(serialNumber);
if (status == Keyzy::LicenseStatus::VALID) {
// Unlock the application.
}
// Subsequent runs — no internet required, no serial prompt.
status = pValidator->validateOffline();
if (status == Keyzy::LicenseStatus::VALID) {
// Keep running.
}
A full working setup is in the C++ Quick Start tutorial, and the broader scope of the platform lives on the features page.
This is one path. It isn’t the only one, and it isn’t right for every product.
Bottom Line
The real question isn’t “can I build this.” You can. The question is whether you want to.
If your licensing model is simple and likely to stay that way, and running the infrastructure doesn’t bother you, building it yourself is a legitimate choice. If your model is growing — more SKUs, dealers, trials, upgrades, bundles, offline customers — the hours add up, and the cost is measured in time you’re not spending on the thing your customers actually buy you for.
Either way, the decision is yours. The goal of this guide was to give you a realistic picture of what “licensing” contains, so that decision is made against facts rather than an optimistic estimate.