freemius-checkout について
freemius-checkoutは、ReactフロントエンドにFreemiusの決済を組み込むスキルです。サブスクリプションと単発購入の選択肢を表示して決済オーバーレイを開き、価格情報の取得と購入確認・権利同期はバックエンドで行います。
主な機能
- 01月額・年額のサブスクリプション価格表
- 02単発購入とクレジット追加の価格表
- 03決済オーバーレイとカスタムボタン
- 04機能に応じた購入選択肢を示すペイウォール
- 05購入後のバックエンド権利同期
活用シーン
- 01Reactアプリでサブスクリプションプランを表示
- 02単発購入やクレジット追加を提供
- 03機能制限に応じたペイウォールを表示
- 04バックエンドで購入を確認し権利を更新
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ freemius/ freemius-ai --skill freemius-checkoutインストール後、Claude Code で/freemius-checkoutを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/checkout-provider.mdcustom-buttons.mdpaywall.mdpricing-tables.mdvite-hono-porting.md詳細
Freemius Checkout
React フロントエンドで料金を表示し、Freemius Checkout オーバーレイを開いて、ユーザーがあらゆる購入形態、つまり定期的なサブスクリプションまたは**単発/トップアップ(クレジット)**購入を行えるようにし、結果をサーバー側と同期します。
出典:React Starter Kit の Checkout コンポーネント https://freemius.com/help/documentation/saas-sdk/react-starter/components/#checkout-components
フレームワークに関する注意。 この Skill の例は Vite/Hono 向けですが、このスタックは必須ではありません。サーバー側(
freemius-coreが所有)は、Web 標準のRequest/Responseルーティングをサポートするあらゆるフレームワークで動作します。また、React Starter Kit のコンポーネントは shadcn UI + Tailwind を使用する React 19 以降を対象としています。npx shadcn@latest add https://shadcn.freemius.com/all.jsonでインストールしてください。アプリで shadcn/React を使用していない場合は、Starter Kit をダウンロードし、アプリ独自のフレームワークで同じ UI を実装するための参考資料として使用してください。プリミティブを作り直すよりも、アプリケーションに既存のコンポーネント(ボタン、入力欄、カード、ダイアログ)を再利用してください。新しいプリミティブを導入するのではなく、Starter Kit の構造と動作をアプリ独自のデザインシステムに移植してください。
サンドボックス。 テスト用クレジットカードを受け付けるチェックアウトモードであるサンドボックスモードで構築し、検証してください(割引ではなく、クーポンは不要であり、関与しません)。専用の
FREEMIUS_SANDBOX環境変数(NODE_ENVは使用しないでください)に接続され、freemius-coreが所有します。サーバーで作成されたチェックアウトにはすでにサンドボックスフラグが設定されており、この Skill はそれを継承します。
黄金律(違反しないでください)
- ブラウザー内で実行されるのはオーバーレイだけです。 オーバーレイには、サーバー側で作成されたシリアライズ済みチェックアウト(
freemius-coreのcheckout.serialize())を事前に設定します。secretKey/apiKeyがクライアントに渡ることはありません。 - ブラウザーが自分自身を信頼することはありません。 オーバーレイで購入が成功するとコールバックが実行され、ペイロードがバックエンドに POST されます。バックエンドは Freemius から正式な購入情報を再取得し、エンタイトルメントをアップサートします。ペイウォールはサーバー側で適用され(HTTP 402)、UI のゲートは UX のためだけのものです。
- ⛔️ オーバーレイの同期とホスト型リダイレクトは、ペイロードの契約が異なる別々のエンドポイントです。決して統合しないでください。 オーバーレイの
purchaseCompletedコールバックは、専用のPOST /api/purchaseに JSON 本文を POST します。このハンドラーはオーバーレイのペイロードからlicense_idを読み取り、syncEntitlementByLicenseId(licenseId)を呼び出します。ホスト型の戻り先は別のGET /api/checkout/redirectであり、署名付きクエリ文字列に対してfreemius.checkout.processRedirect(url, PUBLIC_URL)を実行します(freemius-core→ 購入処理を参照)。オーバーレイの POST をfreemius.checkout.request.createProcessor(...)/processRedirect(...)経由で処理してはいけません。また、syncEndpointを/api/checkout/redirect?action=process_purchaseに指定してはいけません。これらの SDK パスは、オーバーレイのペイロードにはない形式の署名付きリダイレクト/license_idを想定しています。そのため POST は400 "License ID is required in the request body"を返し、フロントエンドの.catch()がそのエラーを握りつぶします。その結果、UI は正常に見える一方で、すべてのユーザーのuser_fs_entitlementが空のままになります。(createProcessorはカスタマーポータルのヘッドレスエンドポイント用であり、オーバーレイ購入の同期には使いません。ここで流用しないでください。)
- ⛔️ オーバーレイの同期とホスト型リダイレクトは、ペイロードの契約が異なる別々のエンドポイントです。決して統合しないでください。 オーバーレイの
- 料金データは1つのサーバーエンドポイントから取得します。 フロントエンドはバックエンド(
GET /api/checkout/pricing)からプラン、価格、プランごとのチェックアウトコンテキストを取得します。Freemius API を直接呼び出すことはありません。 - すべての購入形態に対応してください。 サブスクリプションでは課金サイクルの切り替え(
monthly/annual)を使います。買い切り/トップアップのプランでは、billing_cycle: 'lifetime'を指定してオーバーレイを開きます。両方を表示してください。デモのサブスクリプション専用のフローをハードコードしてはいけません。 - 作成者にサブスクリプションモデルを再度説明させないでください。 料金表は、Freemius Dashboard を情報源とする
GET /api/checkout/pricingからプランと価格を自動取得します。プランと料金の唯一の信頼できる情報源は Dashboard です。そのデータから購入形態を判定してください(lifetime のみのプラン →Topup、monthly/annual のプラン →Subscribe)。ビルド用プロンプトや受付時の質問で、購入形態をあらかじめ指定してはいけません。 - 購入後の確認 UX はアプリ側で管理します。 オーバーレイは
show_confirmation_dialog: falseを指定して開き、Freemius の確認モーダルをスキップし、代わりにアプリ内で成功状態を表示します。アプリ内成功メッセージを参照してください。同じonPurchaseハンドラー内でペイウォールを隠し、ゲートされたアクションの 402 による古いゲートエラー/結果を消去してから、エンタイトルメントを再確認してください。そうしないと、再読み込みするまで、ゲートされた画面に「サブスクリプションが必要です」/「アップグレードしてください」というメッセージが残ります。 - 購入画面ではエンタイトルメントを考慮します。 すでにサブスクリプションを契約しているユーザーに、通常の Subscribe ボタンを表示してはいけません(通常のサブスクリプションチェックアウトでは並行する別のサブスクリプションが作成され、二重請求になります)。その場合は、サーバー側で構築したライセンスアップグレード用チェックアウトを使用します(ライセンスを取得し、
setLicenseUpgradeByKeyを使用します。freemius-customer-portalを参照してください。更新中のライセンスでは 500 エラーになるcheckout.create({ licenseId })は使いません)。アクティブな無制限サブスクリプションの契約者にはクレジットのトップアップを表示せず、所有中のプランに印を付け、サブスクリプション契約者には Lifetime に関する注記を表示するか、非表示にしてください。references/pricing-tables.md の「エンタイトルメントを考慮した表示」を参照してください。
コンポーネント
編集可能な shadcn/ui スタイルの React ソースとして移植してください(ブラックボックスのパッケージにはしないでください)。 各コンポーネントには用途に絞ったリファレンスがあります。必要なものをお読みください。
| コンポーネント | 機能 | リファレンス |
|---|---|---|
CheckoutProvider | オーバーレイのインスタンスを 1 つ保持し、useCheckout() を公開します | references/checkout-provider.md |
Subscribe | サブスクリプション料金表(月間/年間の切り替え) | references/pricing-tables.md |
Topup | 単発購入/クレジットの料金表(lifetime 課金) | references/pricing-tables.md |
Paywall | 機能が制限されている場合に適切な料金表を表示するオーバーレイ | references/paywall.md |
| カスタムボタン | useCheckout().open(...) を呼び出す任意の要素 | references/custom-buttons.md |
Next.js から Vite/Hono への移植範囲(環境変数、@/@shared エイリアス、2 つのサーバーエンドポイント)については、
references/vite-hono-porting.md を参照してください。
クイックスタート
1. フロントエンド SDK をインストールする
npm install @freemius/checkout
2. サーバーで作成したチェックアウトでプロバイダーを初期化する
// The base checkout is created server-side (freemius-core) and exposed at
// GET /api/checkout → { options, link, baseUrl }. Load it, then wrap the UI.
const checkout = await getBaseCheckout(); // CheckoutSerialized
<CheckoutProvider
checkout={checkout}
syncEndpoint="/api/purchase" // POST target for the success callback
fetcher={portalFetch} // attaches auth headers
onPurchase={refreshEntitlement} // re-fetch entitlement after success
>
<Subscribe />
<Topup />
</CheckoutProvider>;
3. 選択したプランのオーバーレイを開く
const checkout = useCheckout();
// Subscription (with billing cycle):
checkout.open({
plan_id: planId,
billing_cycle: 'annual',
show_confirmation_dialog: false, // default — the app shows its own success state
});
// One-off / top-up (consumable credits):
checkout.open({
plan_id: planId,
billing_cycle: 'lifetime',
show_confirmation_dialog: false,
});
open() は、ベースオプションにパラメーターをマージし、purchaseCompleted コールバックを設定します。このコールバックは syncEndpoint に POST します。
すべての open() 呼び出しで show_confirmation_dialog: false をデフォルトに設定する(またはプロバイダーに組み込む)と、Freemius 独自の購入後確認モーダルがスキップされ、アプリの成功状態が表示されます。UX が向上し、ダイアログが 2 つではなく 1 つになります。Freemius のダイアログが無効になるため、アプリは必ず独自の確認を表示する必要があります(次のセクションを参照してください)。
アプリ内の成功メッセージ(購入後)
show_confirmation_dialog: false の場合、どちらの購入経路でもアプリが確認 UX を担当します。
- オーバーレイ経路 —
purchaseCompleted→ プロバイダーが/api/purchaseに POST する(権利の同期)→onPurchaseが呼び出されます。これを使って、(1)権利/残高を再取得し、(2)目に見える成功状態を表示します。たとえば、トースト/バナーに**「購入が完了しました — プレミアム機能が利用可能になりました」(サブスクリプション)または「クレジットがアカウントに追加されました」**(チャージ)と表示します。購入を完了しても何も表示しない状態にはしないでください。 - ホスト型リダイレクト経路 — Freemius が購入リダイレクトハンドラー(
freemius-core→ 購入処理)にリダイレクトします。結果ページには同じ成功メッセージを表示する必要があります。
設定方法については、references/checkout-provider.md →「アプリ内の成功メッセージ」を参照してください。
基本的な考え方
Server (freemius-core) Frontend (this Skill) Freemius
GET /api/checkout ───────────────▶ getBaseCheckout()
{ options, link, baseUrl } ──────▶ new Checkout(options,…) (one instance)
GET /api/checkout/pricing ───────▶ <Subscribe/> + <Topup/> tables
user clicks "Subscribe"/"Buy"
useCheckout().open({plan_id, billing_cycle}) ─▶ overlay
user pays ───────────────────────────────────▶ license/payment
POST /api/purchase ◀────────────── purchaseCompleted(data) callback
(re-fetch + upsert entitlement)
402 on gated feature ────────────▶ <Paywall/> shows the right table
ホスト型チェックアウトの代替手段
@freemius/checkout を読み込めない場合(または最も簡単な方法を使いたい場合)、料金レスポンス内の各プランにはホスト型の checkoutLink も含まれています。ブラウザーでそのリンクに移動してください。Freemius は購入リダイレクトハンドラー(freemius-core → 購入処理)にリダイレクトします。UX を優先するならオーバーレイを使い、フォールバックとしてリンクを残してください。
ダッシュボードの設定(人間が対応)
Freemius Developer Dashboard(https://dashboard.freemius.com/)で設定します。
- Product → Plans → 各プランと価格を定義します。lifetime 価格のみ(月額・年額なし)のプランは
Topupテーブルに表示され、月額・年額価格のあるプランはSubscribeに表示されます。 - トップアップの前提条件: 一括払い/トップアップ方式は、製品に lifetime のみのプランが存在する場合に限り表示されます。作成者がトップアップを希望する場合(または、その方式のデモや検証が必要な場合)は、まずプランを作成するよう依頼してください。これは受付時の確認事項/前提条件として扱い、
oneoffが空で返された場合はTopupテーブルを適切に非表示にしてください。 - Checkout → appearance / behaviour → オプションでオーバーレイのブランディングを設定します。
- Webhook とリダイレクト URL の設定は
freemius-coreが担当します。
購入によってユーザーに付与される権利(機能のロック解除、クレジット残高)の付与はサーバー側で行い、freemius-core が担当します。トップアップのクレジットについては、freemius-core → entitlement-logic.md →「Usage-based / credit metering
(top-up model)」を参照してください(残高はユーザーをキーとする別テーブルに保存し、user_fs_entitlement を拡張する形にはしないでください)。