fix: handle Polar activation limit gracefully + switch checkout to command pattern

- Server: wrap licenseKeys.activate() in try/catch — if activation limit
  reached, treat as already-activated and proceed to store the key
- Web: switch activateFromCheckout from form() to command() pattern for
  programmatic invocation with proper error handling
- Activate page: auto-fires on mount, shows spinner/error states, retry button
This commit is contained in:
Sanju Sivalingam
2026-02-18 23:28:19 +05:30
parent e9d1c863e1
commit ce6d1e320b
3 changed files with 104 additions and 80 deletions

View File

@@ -55,12 +55,18 @@ license.post("/activate", async (c) => {
// Determine plan from benefit ID or default to "ltd"
const plan = "ltd";
// Activate the key (tracks activation count on Polar's side)
await polar.licenseKeys.activate({
key,
organizationId: env.POLAR_ORGANIZATION_ID,
label: `${currentUser.email}`,
});
// Activate the key (may fail if already activated from previous attempt)
try {
await polar.licenseKeys.activate({
key,
organizationId: env.POLAR_ORGANIZATION_ID,
label: `${currentUser.email}`,
});
} catch (activateErr) {
const msg = activateErr instanceof Error ? activateErr.message : String(activateErr);
if (!msg.includes("limit")) throw activateErr;
console.log(`[License] Key already activated for ${currentUser.email}, storing anyway`);
}
// Store on user record
await db
@@ -140,12 +146,19 @@ license.post("/activate-checkout", async (c) => {
return c.json({ error: "No license key found for this purchase" }, 400);
}
// 3. Activate the key
await polar.licenseKeys.activate({
key: customerKey.key,
organizationId: env.POLAR_ORGANIZATION_ID,
label: `${currentUser.email}`,
});
// 3. Activate the key (may fail if already activated from previous attempt)
try {
await polar.licenseKeys.activate({
key: customerKey.key,
organizationId: env.POLAR_ORGANIZATION_ID,
label: `${currentUser.email}`,
});
} catch (activateErr) {
const msg = activateErr instanceof Error ? activateErr.message : String(activateErr);
if (!msg.includes("limit")) throw activateErr;
// Limit reached = key was already activated, that's fine — proceed to store
console.log(`[License] Key already activated for ${currentUser.email}, storing anyway`);
}
// 4. Store on user record
await db
@@ -161,6 +174,14 @@ license.post("/activate-checkout", async (c) => {
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error(`[License] Checkout activation failed for ${currentUser.email}:`, message);
if (message.includes("limit")) {
return c.json({ error: "License key activation limit reached" }, 400);
}
if (message.includes("not found") || message.includes("invalid")) {
return c.json({ error: "Invalid or expired checkout" }, 400);
}
return c.json({ error: "Failed to activate from checkout" }, 500);
}
});