freemius-core について
freemius-core は、JavaScript と TypeScript のバックエンドに Freemius の課金を組み込むスキルです。サーバー側のチェックアウト、権利確認、Webhook によるライセンス同期を案内します。
主な機能
- 01既存設定と不足項目の検出
- 02サーバー側チェックアウトルートの作成
- 03Webhook による購入とライセンス状態の同期
- 04ユーザー権利の保存と確認
- 05課金サービスとルート処理の分離
活用シーン
- 01サブスクリプションや単発購入のチェックアウト追加
- 02有効な権利に基づく機能制限
- 03ローカルの権利レコードの同期
- 04既存の Freemius 連携の継続
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ freemius/ freemius-ai --skill freemius-coreインストール後、Claude Code で/freemius-coreを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/AGENTS.sample.mdreferences/checkout.mddashboard-and-secrets.mdentitlement-logic.mdentitlement-table.mdpurchase-processing.mdvite-hono-notes.mdwebhooks.md詳細
Freemius Core
Freemius の課金機能を JS/TS バックエンドに組み込み、ログイン中のユーザーがプランを購入できるようにします。アプリが確実に答えられるようにするのは、次の質問です。「このユーザーは現在、有効な利用権を持っていますか?」
この Skill は、Freemius の公式 JS SDK 統合ガイドに沿っています: https://freemius.com/help/documentation/saas-sdk/js-sdk/integration/
フレームワークに関する注記。 この Skill の例は Hono フレームワークで記述されていますが、JS SDK は Web Standard の Request/Response ベースのルーティングに対応するあらゆるフレームワークをサポートします。例は、アプリで実際に使われている HTTP フレームワークに合わせて調整してください。
上流のドキュメントが必要な場合は、アプリのスタックに応じて選んでください。
- Next.js アプリ → https://freemius.com/help/documentation/saas-sdk/framework/nextjs.md
- その他すべての JS/TS ランタイム → https://freemius.com/help/documentation/saas-sdk/js-sdk/integration.md
基本ルール(必ず守ること)
- Freemius SDK/API の呼び出しはすべてサーバー側で行います。 製品の
secretKeyとapiKeyをブラウザーに渡してはいけません。フロントエンドに渡すのは、公開用の Checkout リンク/オプションのみです。 - 統合ロジックは、ルート/コントローラーとは分けた通常の TS サービスに配置します。
src/services/freemius/*.tsのようなファイルに配置してください。 ルートではリクエストを解析し、サービスを呼び出して、レスポンスを整形するだけにします。 - ローカル DB はミラーであり、信頼できる唯一の情報源ではありません。 課金状態を管理するのは Freemius です。
user_fs_entitlementテーブルは、購入後のリダイレクトと Webhook を通じて同期されるキャッシュです。判断に迷った場合は、freemius.purchase.retrievePurchase(licenseId)を使って Freemius から再取得してください。 - Webhook は必須です。 Webhook がないと、解約/更新/有効期限切れが DB に反映されず、ユーザーのアクセス権が誤って維持されたり失われたりします。
- シークレットは環境変数からのみ取得します。 キー、API トークン、URL をハードコードしてはいけません。
ステップ 0 — 初期確認(最初に実行)
何かを構築する前に、作成者の製品から Freemius の設定を推測し、本当に推測できない情報だけを尋ねてください。まず検出し、あとから質問するのであり、質問票ではありません。状況に応じた判断も必要です。
最初に 2 つのモードを提示してください(作成者はいつでもガイド付きに切り替えられます)。
- 自律モード — 「推測できることは検出し、必要なときだけ確認します。」
- ガイド付きモード — 「手順を案内してください。進めながら質問してください。」
また、どこで実行するかも尋ねてください。インストールするものとデータベースの配置場所が変わります。 決めつけてはいけません。作成者ごとに環境は異なり、作業マシンをクリーンに保ちたい人もいます。
- ローカル優先 — アプリとローカルの Postgres をインストール/実行し、
localhostで検証してから、必要に応じてデプロイします。データベースをローカルにインストールしてもよいことを作成者が確認した場合にのみ選択してください。 - リモートのみ — データベースをローカルにインストールせず、ローカル DB に対してスキーママイグレーションを適用せず、ローカルの開発サーバーも起動しないでください。ビルド/型チェックはローカルで行ってかまいません(DB には触れません)。アプリとデータベースはデプロイ先(例:Railway/Render/Fly)でのみ実行し、検証はすべてデプロイ済み URL に対して行います。
次のように尋ねてください。「ローカル優先(ローカルの Postgres をインストールして localhost で実行)と、リモートのみ(ローカルにはインストールせず、Railway などのホスト上でプロビジョニングしてすべて実行)のどちらで設定しますか?」 インストール/実行/マイグレーションのすべての手順で、回答に従ってください。
また、サンドボックスとライブのどちらを使うかも最初に尋ねてください。 「まずサンドボックスで構築しますか(デフォルト。チェックアウトはサンドボックスモードで開き、テスト用クレジットカードを利用できるため、エンドツーエンドの実行は無料です)、それとも最初からライブにしますか?」 検証中は、ほとんどの場合サンドボックスを使います。サンドボックスはチェックアウトモードであり、割引ではありません。クーポンは不要で、関係もありません。専用の FREEMIUS_SANDBOX 環境変数で制御し、NODE_ENV には決して連動させないでください。リモートのみでデプロイするときは、サンドボックスのまま NODE_ENV=production に設定するため、両者を連動させると誤って LIVE チェックアウトが開いてしまいます。本番運用を開始するときに限り、FREEMIUS_SANDBOX=false を設定してください。
a. 検出(質問する前にスキャンする)
対象リポジトリを調査し、必要な入力をそれぞれ KNOWN / MISSING /
UNVERIFIED に分類します。すでに KNOWN の情報は、決して再度尋ねないでください。まず既存の設定を読み(.env、.env.example、README、既存の
services/freemius/client.ts または環境変数モジュール)、作成者に再度貼り付けを依頼するのではなく、そこにすでに記載されている ID/キーを使用します。
| 確認する項目 | わかること |
|---|---|
.env / .env.example 内の FREEMIUS_PRODUCT_ID、FREEMIUS_PUBLIC_KEY、FREEMIUS_SECRET_KEY、FREEMIUS_API_KEY、サンドボックスフラグ | 設定済みと未設定のシークレット |
user_fs_entitlement テーブル/Prisma モデル/マイグレーション | 構成要素 1 がすでに存在するかどうか |
src/services/freemius/*、/api/checkout、/api/webhooks/freemius ルート | 足場ができている構成要素 |
package.json の依存関係/サーバーエントリーポイント(Hono、Express、Next、Fastify) | フレームワークとルートのスタイル |
PUBLIC_APP_URL/デプロイ設定/公開中のドメイン | 公開 URL がすでにあるかどうか |
設定に FREEMIUS_API_KEY と FREEMIUS_PRODUCT_ID の両方がある場合、対象のプロダクトはすでに特定されています。プロダクト ID を再度尋ねないでください。(SDK クライアントは productId を指定して構築されるため、設定に含まれています。API キーから導出されるものではありません。)アプリで必要な各 プラン ID の役割(例:
PRO_PLAN_ID、LIFETIME_PLAN_ID、CREDITS_PLAN_ID)について、空欄を埋めるために ID を貼り付けるよう作成者に求めるのではなく、次の順序で解決します。
- 名前による API 検索 — アプリの README には、各プランの 名前 → 役割 が記載されています(「Plans configured」テーブルの規約。詳しくは references/dashboard-and-secrets.md → 「Plan roles」を参照)。API キーを使ってプロダクトのプラン一覧を取得し、宣言された各名前を該当する役割に対応付けて、現在のプラン ID を確認します。
- README の ID を代替として使用 — 名前が曖昧、解決できない、または API 検索が利用できない場合は、そのテーブルの「Plan ID (fallback)」列に固定されている ID(または既存の
.env)を使用します。 - 質問 — どちらの方法でも解決できない役割についてのみ尋ねます。
構築を始める前に、解決した役割 → ID の対応表を確認します。本当に MISSING の入力についてのみ、.env の記述例を添えて「Dashboard → API & Keys」を案内します。
b. プロダクトから導き出す
Freemius の設定は固定の手順ではなく、作成者の収益化の意図に応じて決まります。コードベースの内容と、要点を絞ったいくつかの質問から推測します。
- ゲート対象とするプランと、その形態:サブスクリプション(月間/年間)、一回限りの購入/追加購入、またはクレジット。
- ペイウォールの背後に置く機能/エンドポイント。
- Webhook URL =デプロイ済みのオリジン +
/api/webhooks/freemius。 - チェックアウトのリダイレクト URL =デプロイ済みのオリジン + リダイレクトハンドラーのパス。
c. 不足している項目だけを確認する(1つのコピペ用ブロック)
まだ不足している項目はすべて、作成者がまとめて一度に入力できるよう、簡潔なブロック1つにまとめて提示します(通常、これらの値はすでに把握しています)。未確認の項目については、何も確認せずに再質問するのではなく、「これは今も正しいですか?」と尋ねます。依存関係の順に確認します。
Run mode: local-first (install local Postgres) | remote-only (no local install; run on host)
Freemius Product ID: e.g. <your_product_id>
Plan ID(s) to sell/gate: e.g. <your_plan_id>
Public key (pk_...): SDK public key
Secret key (sk_...): SERVER-ONLY — goes in .env, never committed
API key (token): SERVER-ONLY — goes in .env, never committed
Sandbox or live?: sandbox while validating (sets FREEMIUS_SANDBOX, independent of NODE_ENV)
Monetization shape: subscription monthly/annual | one-off/top-ups | credits
Feature/endpoint to gate: e.g. POST /api/ocr
Deploy target + access: e.g. Railway project token — for remote-only, this is where it runs
Public/deployed URL: for webhook + redirect (the deployed origin; only localhost if local-first)
Freemius の ID/キーについては、作成者にFreemius Developer Dashboard → Products → Settings → API & Keysを開き、用意されているJS SDK の .env スニペットをコピーするよう伝えてください。ここに製品 ID とすべてのキーがまとめて含まれています。
スクリーンショットと手順:
Developer Dashboard からキーを取得する。
シークレット/API キーは**Git 管理対象外の .env**に保存し、コミットせず、クライアント側で使用しないでください(Golden rules #1 + #5)。環境変数の一覧と、各値が Dashboard のどこにあるかについては、
references/dashboard-and-secrets.mdを参照してください。ここに重複して記載しないでください。
d. 計画を確認してから構築する
検出した状態、今回提供された値、構築順序(以下の4つのブロックのうち、すでに存在するものを除く)をあらためて提示します。作成者の承認を待ってから進めてください。
e. 再開しやすくする
ブロックがすでに存在する場合(entitlement テーブルがある、サービス/ルートが存在するなど)は、拡張するか、スキップしてください。作り直してはいけません。前回の続きから構築を再開してください。
f. チェックポイント(一時停止して確認する。推測しない)
- 適用前にスキーマを確認します。スタック固有のマイグレーションツールで適用してください(例:Prisma の場合は
prisma db push/prisma migrate、raw SQL の場合は提供された DDL を実行、その他の ORM の場合は同等の方法を使用)。 - 購入処理と Webhook を接続する前に公開 URLを確認します。
- Dashboard の手順(Webhook URL、チェックアウトのリダイレクト)は、明示的に「今すぐ実行し、完了したら知らせてください」と伝えて人間に依頼し、その間は処理を一時停止します。以下のDashboard の設定(人間に依頼)を参照してください。
4つの構成要素
以下の順に構築します。それぞれに専用のリファレンスファイルがあります。必要なものを参照してください。
| ステップ | 構築するもの | リファレンス |
|---|---|---|
| 1 | user_fs_entitlement テーブル(Prisma/SQL/Drizzle) | references/entitlement-table.md |
| 2 | サーバー側のチェックアウト作成(POST /api/checkout) | references/checkout.md |
| 3 | 購入処理(リダイレクトハンドラー + オーバーレイエンドポイント) | references/purchase-processing.md |
| 4 | ライセンスのライフサイクルを同期する Webhook リスナー(POST /api/webhooks/freemius) | references/webhooks.md |
クイックスタート
1. SDK をインストールして構築する(サーバーのみ)
npm install @freemius/sdk
// src/services/freemius/client.ts
import { Freemius } from '@freemius/sdk';
export const freemius = new Freemius({
productId: process.env.FREEMIUS_PRODUCT_ID!,
apiKey: process.env.FREEMIUS_API_KEY!,
secretKey: process.env.FREEMIUS_SECRET_KEY!,
publicKey: process.env.FREEMIUS_PUBLIC_KEY!,
});
// Sandbox is its OWN flag — decouple it from NODE_ENV. You will deploy to a
// remote host with NODE_ENV=production while STILL validating in sandbox, so
// `NODE_ENV !== 'production'` would wrongly send checkout to the LIVE URL.
// Default to sandbox unless FREEMIUS_SANDBOX is explicitly "false".
export const IS_SANDBOX = process.env.FREEMIUS_SANDBOX !== 'false';
⚠️ サンドボックスを
NODE_ENVに連動させないでください。 よくある初期のミスは、デプロイ済みのホスト(NODE_ENV=productionの環境)で検証するとサンドボックスが無効になり、チェックアウトでサンドボックスではなく本番ストアが開いてしまうことです。FREEMIUS_SANDBOXは専用の環境変数として管理してください。テスト中はtrueにするか未設定にし、本番公開時にのみfalseに設定します。
2. ログイン中のユーザー向けにチェックアウトを作成する
const checkout = await freemius.checkout.create({
user: { email: session.user.email, firstName, lastName },
planId,
isSandbox: IS_SANDBOX,
});
const { options, link } = checkout.serialize();
// Send `link` (hosted) or `options` (overlay) to the frontend.
3. 購入時と Webhook で Entitlement を同期する
freemius.purchase.retrievePurchase(licenseId) →
purchase.toEntitlementRecord() →
db.userFsEntitlement.upsert({ where: { fsLicenseId }, ... })。チェックアウトのリダイレクト、オーバーレイの success コールバック、すべての Webhook イベントで同じコードパスを使用します。references/purchase-processing.mdを参照してください。
4. 有料機能を制限する
const entitlement = await getUserEntitlement(userId); // active subscription OR valid one-off (Lifetime)
if (!entitlement) return new Response('Payment required', { status: 402 });
// ...run the premium feature...
getUserEntitlement()は、単にfreemius.entitlement.getActive(rows)を呼び出すだけではありません。 この SDK ヘルパーはtype: 'subscription'の行のみを返すため、一度限りの Lifetime ライセンスは見落とされます。一度限りのライセンスは自分で解決してください。詳しくは references/entitlement-logic.md を参照してください。
references/entitlement-logic.md のエンタイトルメントヘルパーを使って 機能を制限します。
基本的な考え方
Frontend (pricing page) Backend services (plain TS) Freemius
│ POST /api/checkout ───────────▶ freemius.checkout.create() ──────────▶ checkout
│ ◀── { link, options } ────────── checkout.serialize()
│ user pays on Freemius checkout ───────────────────────────────────────▶ license created
│ redirect ?… ──────────────────▶ checkout.processRedirect()
│ purchase.retrievePurchase(licenseId)
│ upsert user_fs_entitlement ◀────────── PurchaseInfo
│ later: cancel/renew/expire ───────────────────────────────────────────▶ webhook event
│ POST /api/webhooks/freemius
│ re-sync user_fs_entitlement
│ GET premium feature ───────────▶ getUserEntitlement() → 402 or run
データマッピングに関する注意点
SDK では camelCase(fsLicenseId、fsPlanId、isCanceled)を使いますが、SQL
のカラム名は通常 snake_case(fs_license_id など)です。マッピングには、
お使いのスタックが提供する方法を利用してください。Prisma を使う場合は、@map/@@map を
優先してください(そうすれば purchase.toEntitlementRecord() をモデルにそのまま渡せます)。別の ORM/クエリレイヤーを使う場合は、
同等のカラムマッピング機能を適用してください。生の SQL を使う場合は、カラム名に snake_case を直接指定し、
両方向の変換を明示的に行ってください。詳しくは
references/entitlement-table.md を参照してください。
ランタイムと想定されるインターフェース
Freemius の各要素がアプリのスタックに必要とするものは次のとおりです。
- JS SDK(
@freemius/sdk) — Node.js、Deno、または Bun で動作します。API は Web 標準のRequest/Responseベースのルーティングに依存しているため、これをサポートするフレームワークなら直接利用できます(Hono、Next.js のルートハンドラー、Fetch アダプターを使う Fastify、サーバーレスの Fetch ハンドラーなど)。サポートしていないフレームワークの場合は、Request/Responseとの変換を行う小さなアダプターを作成してください。 - React Starter Kit — React 19 以降と shadcn UI + Tailwind が必要です。公式の shadcn CLI からインストールしてください。
npx shadcn@latest add https://shadcn.freemius.com/all.json。アプリで shadcn/React を使っていない場合は、Starter Kit を参考資料として使い、 アプリ独自のフレームワークで UI を実装してください。
Next.js 向けのドキュメントを別のスタック(Vite + Hono)に移植した実例については、 references/vite-hono-notes.md を参照してください。
ダッシュボードの設定(担当者に依頼)
これは、Step 0 — Intake でチェックポイントとして示されている ダッシュボード引き継ぎの具体的なチェックリストです。公開 URL でデプロイした後、Freemius Developer Dashboard (https://dashboard.freemius.com/)で設定してください。
- Product → Webhooks →
POST /api/webhooks/freemiusを追加し、次のイベントを登録します。license.created、license.updated、license.extended、license.shortened、license.cancelled、license.expired、license.plan.changed、license.quota.changed、license.deleted。 - Product → Plans → Customization → チェックアウトのリダイレクト URL に、購入リダイレクトハンドラーを設定します。
環境変数の一覧とダッシュボードのチェックリストについては、 references/dashboard-and-secrets.md を参照してください。
アプリのAGENTS.mdを生成/更新する
ペイウォールの組み込み後、リポジトリのルートに簡潔な**AGENTS.md**を残し、次のエージェント(または人間)がプレミアム機能を見つけ、Freemiusでどのようにペイウォールを設けているかを理解し、ペイウォールライブラリのファイルを参照できるようにしてください。1画面程度に収め、ベンダーに依存しない内容にします。アプリ固有の説明は含めず、概要だけを記載してください。
ビルドの最後に実行してください。AGENTS.mdがなければ作成し、すでに存在する場合は、ファイルの作成者の内容を書き換えず、フェンスで囲んだ## Freemius paywallセクションだけを挿入/更新してください。構築した実際の連携内容から、すべての角括弧内を埋めてください。実際にゲートされるルートと実際のサービスファイルのパスを記載し、プレースホルダーを残さないでください。
すぐにコピーできるひな形が、このSkillに含まれています: assets/AGENTS.sample.md。これをコピーして、角括弧内を置き換えてください。3つのセクションは、作成者の依頼内容に対応しています。
- プレミアム機能 — 識別方法 — ゲートされる機能→エントリーポイント(ルート)→適用されるガード(
requireEntitlement/requireCredits)を示す表です。ルール:ペイウォールミドルウェアでラップされたルートはプレミアム機能です。無料ルートにはガードがありません。 - Freemiusによるペイウォール — 販売→同期→ゲートの流れを3行で示し、「エンタイトルメントがない場合は402を返す」「すべてのSDK呼び出しはサーバー側で行い、シークレットは環境変数に保存する」と記載します。
- ペイウォールライブラリ関数の場所 — SDKクライアント、チェックアウト、エンタイトルメントの同期とゲート用ヘルパー、ペイウォールミドルウェア、および(使用している場合)クレジット計測ファイルの実際のファイルを示す表に加え、サーバー専用の環境変数一覧を記載します。
コピー&ペースト用テンプレート(AGENTS.mdに貼り付け、すべての[...]を置き換えてください):
## Freemius paywall
### 1. Premium features — how to identify them
Premium features are the routes/actions gated behind a Freemius entitlement —
find them by the paywall guard they carry (§3), not by guesswork.
| Premium feature | Entry point | Gated by |
| --------------- | ----------------- | ---------------------- |
| `[feature]` | `[METHOD /route]` | `[requireEntitlement]` |
| `[feature]` | `[METHOD /route]` | `[requireCredits(n)]` |
### 2. Paywalling premium features with Freemius
1. **Sell** — the frontend opens the Freemius Checkout for a plan (sandbox mode
accepts test cards while validating).
2. **Sync** — the purchase redirect + Freemius webhooks upsert the local
entitlement mirror (Freemius stays the source of truth).
3. **Gate** — before a premium feature runs, resolve the active entitlement (or
credit balance for top-up/metered features). No entitlement → **HTTP 402**;
the frontend branches on 402 to show the upgrade UI.
All Freemius SDK calls are **server-side**; `secretKey`/`apiKey` never reach the
browser; secrets come from env vars only.
### 3. Where the paywall library functions live
| Concern | File |
| ---------------------------------------- | --------------------------------- |
| Freemius SDK client (keys, sandbox flag) | `[src/…/freemius/client.ts]` |
| Checkout creation + pricing | `[src/…/freemius/checkout.ts]` |
| Entitlement sync (redirect + webhooks) | `[src/…/freemius/entitlement.ts]` |
| Entitlement resolve + gating helpers | `[src/…/freemius/entitlement.ts]` |
| Paywall guard (402 middleware) | `[src/…/middleware/paywall.ts]` |
| Credit balance / metering (if used) | `[src/…/credits.ts]` |
Env (server-only, git-ignored `.env`): `FREEMIUS_PRODUCT_ID`,
`FREEMIUS_PUBLIC_KEY`, `FREEMIUS_SECRET_KEY`, `FREEMIUS_API_KEY`,
`FREEMIUS_SANDBOX`.