SuperAPI AI Directory

freemius-troubleshooting

Coding & development

View repository

Updated 10-15 21:47

About this skill

Freemius Troubleshooting

When a Freemius integration breaks, the failure is almost always in one of five places: webhooks/redirect plumbing, SDK types/events, the stack/porting layer, checkout/portal wiring, or secrets/dashboard config. This Skill is a diagnostic index — find the symptom, jump to the reference, apply the fix.

Source FAQ: https://freemius.com/help/documentation/ai/lovable/#troubleshooting

How to use this Skill

  1. Match your symptom in the table below.
  2. Open the linked reference for the root cause + fix.
  3. If it is a freemius-core / freemius-checkout / freemius-customer-portal concern, the reference points you back to the exact section of those Skills.

Symptom → fix index

Symptom Likely cause Reference
Checkout completes but the user never gets access Redirect URL or webhook not configured / not public references/webhooks-and-redirect.md
Webhook signature verification fails Raw body was parsed before handing to the listener references/webhooks-and-redirect.md
TS error: Type '"lifetime"' is not assignable on an entitlement type Entitlement type is subscription | oneoff, not lifetime references/sdk-types-and-events.md
Subscribing to license.quota.changed does nothing / type error That event does not exist in @freemius/sdk@0.3.0 references/sdk-types-and-events.md
TS error: Object is possibly 'false' on license in a webhook handler Array-subscription license is a union incl. the deleted shape references/sdk-types-and-events.md
prisma generate / client fails wanting a driver adapter; url rejected Prisma 7 dropped classic datasource url — pin Prisma 6 references/stack-and-porting.md
process.env.NEXT_PUBLIC_* is undefined in the browser Vite uses import.meta.env.VITE_*, not process.env references/stack-and-porting.md
@/... or @shared/... import cannot be resolved Missing alias in vite.config.ts + tsconfig paths references/stack-and-porting.md
Installed skills via npx skills add but Claude Code (CLI) doesn't see them / slash commands missing Claude Code isn't a default install target; skills went to ~/.agents/skills/ with no ~/.claude/skills/ symlink references/stack-and-porting.md
Tried the npx skills add … -a claude-code CLI route on Claude Desktop (Claude.ai app) and it doesn't apply The CLI / -a claude-code flag is a Claude Code concept only; Desktop installs each Skill as a per-Skill .zip via Customize → Skills → "+" → Upload a skill (core first; Code execution enabled under Settings → Capabilities) references/stack-and-porting.md
Secret key / API token leaks to the browser, or 401 from Freemius A Freemius call was made client-side instead of server-side references/checkout-and-portal.md
Checkout overlay opens empty / unconfigured Overlay not primed from a server-created CheckoutSerialized references/checkout-and-portal.md
Upgrade route 500s: "Payment method update is not allowed for lifetime licenses" checkout.create({ licenseId }) calls a payment-method-update endpoint that rejects renewing licenses (expiration: null) — use setLicenseUpgradeByKey references/checkout-and-portal.md
User "upgraded" but now has two subscriptions / double-billed Upgrade offered as a plain subscribe checkout to an already-subscribed user — use a license-upgrade checkout (setLicenseUpgradeByKey) + entitlement-aware surfaces references/checkout-and-portal.md
Portal actions 404 / token invalid One /api/portal endpoint must serve data and signed actions references/checkout-and-portal.md
One-off / lifetime purchase doesn't behave like the subscription demo Different modality — use billing_cycle: 'lifetime', type oneoff references/checkout-and-portal.md
Upgraded to Lifetime but still gated / credits still charged / not shown as active (invoice is correct) freemius.entitlement.getActive() filters to type: 'subscription' — a one-off Lifetime row is invisible, so getUserEntitlement() returns null references/sdk-types-and-events.md
Coupon'd subscription "renews" ~100 years out (USD 0 · renews 2126) 100%-off coupon scoped to "First payment and renewals" references/checkout-and-portal.md
Re-subscribe blocked ("already on an active subscription") after a cancel Admin cancelled the license, leaving the subscription orphaned-active references/checkout-and-portal.md
Subscribed user still sees the paywall / 402 despite an active subscription; user_fs_entitlement table is empty Checkout used the bare hosted redirect with no overlay purchaseCompleted sync and no Dashboard checkout-redirect URL — nothing ever wrote the entitlement row references/checkout-and-portal.md
user_fs_entitlement empty for every user; sync POST returns 400 "License ID is required in the request body" (frontend .catch() hides it) The overlay purchaseCompleted POST was routed through checkout.request.createProcessor / processRedirect (or syncEndpoint pointed at /api/checkout/redirect?action=process_purchase). That path expects a signed redirect / license_id in the body the overlay payload lacks. Overlay sync must be a plain POST /api/purchase that reads the license from the payload → syncEntitlementByLicenseId — separate endpoint from the hosted GET /api/checkout/redirect freemius-checkout → references/checkout-provider.md
A gated feature (e.g. image OCR on Premium) shows a generic "OCR failed. Please try another file." The server's tier/entitlement gate (403/402 with a reason like tier_upgrade_required/image_requires_pro) was swallowed into the catch-all error. A gate is not a failure — branch on status+reason and open the paywall with an upgrade path, not the error toast freemius-checkout → references/paywall.md
Purchase succeeds but there's no pricing page / no way to browse or buy plans without hitting a paywall first Build shipped the paywall but skipped the standalone pricing route — a dedicated /pricing page (Subscribe + Topup tables from GET /api/checkout/pricing) + nav link is a required deliverable freemius-checkout → references/pricing-tables.md
Success toast says "credits added to your account" after buying a subscription (or wrong copy for Lifetime) Post-purchase copy didn't branch on the synced purchase type — use the re-fetched entitlement tier: lifetime → "Lifetime access unlocked", subscription → "<tier> unlocked", credits pack → "page credits added" freemius-checkout → references/checkout-provider.md
Credit balance shows the old value after an OCR (metered) run until a manual page refresh The server debits credits correctly (credit -= pages), but the metered action's success handler never refreshes the displayed balance — the badge is stale until a full reload. Refresh the display without a reload: either read the new balance from the action's response, or await the same refreshEntitlement() (GET /api/entitlement → credit) the purchase flow uses. This is the consumption twin of the post-purchase entitlement refresh freemius-checkout → references/paywall.md
/pricing shows nothing for a Lifetime / top-tier holder ("nothing is shown anymore") Every buy surface is correctly hidden (they own the top tier), but hiding them all leaves a blank page. Render a friendly "you own everything" empty-state instead — never a blank page. (A credits-only viewer must still see Subscribe + Topup.) freemius-checkout → references/pricing-tables.md
Account-page "Change plan" renders as a bare underlined link with no padding, misaligned next to Cancel "Change plan" is a router <Link> given a button class (e.g. btn-outline) that only inherits padding / radius / text-decoration:none from a base .btn class — applied alone to an <a> it loses all of it. Make the button/outline class self-contained, or put both base+modifier classes on the <Link> freemius-customer-portal → references/subscription-and-upgrade.md
Retention coupon: clicking "Apply discount & keep my plan" just closes the wizard, nothing happens, Dashboard shows 0 redemptions The apply POST sent an empty {} body — the SDK's renewal-coupon action requires { couponId: <coupons[0].coupon_id> } and 400s otherwise; AND the wizard ignored res.ok, so the failed call closed as if it succeeded. Send the real couponId and check res.ok (show error + keep open on failure) freemius-customer-portal → references/cancellation-and-coupon.md
After a hosted checkout/upgrade the browser lands on /checkout-result?success=true showing a blank page with only the header The server GET redirect handler bounces to /checkout-result, but the SPA router (App.tsx <Routes>) has no matching client route — the redirect target is a real page the build forgot to ship. Add a /checkout-result route that reads success, refreshes entitlement once on mount, shows the confirmation, and routes back into the app freemius-core → references/purchase-processing.md
Entitlement doesn't update after a cancel/renew; webhook seems dead — but handler, URL and signature all check out Lifecycle event fired before the webhook URL was active/propagated in the Dashboard — never delivered (not retro-sent) references/webhooks-and-redirect.md

First-resort checks (the Lovable FAQ baseline)

The official FAQ's top recommendations, applied to this stack:

  1. Ask the agent to re-run the failing step. These Skills are modular — re-invoke the specific reference (e.g. freemius-core → purchase-processing.md) rather than re-doing the whole integration.
  2. Webhooks + checkout redirect are the two critical paths. Verify both end to end first; most "no access after paying" reports are one of these misconfigured. Freemius emails you when webhook processing fails — paste that email content into the agent to pinpoint the failure.
  3. After publishing/deploying, update all URLs. A redirect or webhook URL still pointing at localhost (or an old domain) silently breaks the flow. PUBLIC_APP_URL must be the live domain.
  4. One-off purchases need a different approach than the subscription path — see the checkout/portal reference for the lifetime modality.
  5. Still stuck? Contact Freemius support (support@freemius.com) with the product id, the failing step, and screenshots.

Capturing new entries

This Skill is meant to grow. When a Skill defect is found during a validation gate, log it in the gate's "Skill defects found" table, fix the offending Skill, then add a troubleshooting entry here (symptom → cause → fix) so the next integrator skips the stumble.