メインコンテンツへスキップ
見出し画像

Chrome拡張機能で「クレジットカード登録後3日間無料」有料サブスクリプションを実装する手

    このnoteでは、書籍紹介など幅広いテーマを扱いつつ、ビジネスをメインに発信しています。
    ビジネスに興味のある方は、メンバーシップ「謎ビジネス実験室」の案内もぜひご覧ください。

    本記事の目的と内容の概要

    「Chrome拡張にサブスクリプション課金を導入したいけど、公式の課金手段が使えないってホント!?
    はい、残念ながらChrome Web Storeの公式決済は停止中。ですが、Stripeなどの外部ツールを駆使すれば、実はしっかりサブスク実装が可能なんです。
    このノートの無料部分では、以下のような点にフォーカスします。

    • サブスク導入の大まかな仕組み

    • 導入の“良いところ”と“注意したいところ”

    • どんなメリットが得られるのか

    そして有料部分では、実際に機能を作るための具体的なステップや、設定ファイルなどを余すことなく解説。
    「無料部分で全体像を把握 → 有料部分で詳細を実装」という流れになっています。


    Chrome拡張機能を 有料のサブスクリプション制 で提供し、最初の3日間は無料で利用できる仕組みを導入する方法をまとめました。

    2022年以降、Chrome ウェブストアの公式課金機能(Chrome Web Store Payments)は新規/更新共に停止しているため、Stripeなどの外部決済サービスを使用するのが必須となります。
    本記事では Firebase + Stripe を組み合わせ、クレジットカード登録後3日間無料トライアル、その後自動課金する仕組みを解説します。

    ポイント
    - Chrome Web Store公式の課金機能は利用不可。
    - Stripeの「Trial period(トライアル期間)」を使うと、3日間無料 → 自動課金が容易。
    - Webhook + Firestoreでユーザーのサブスク状態を管理し、拡張機能側はその状態を参照して機能をロック/アンロックする。
    - Chrome拡張のCSP制約により、Stripe Checkoutを拡張機能内で直接動かすには工夫が必要。
     → 外部Webページ(例: Firebase Hostingなど)に誘導して決済させる構成がおすすめ。


    全体的な流れ

    1. Firebaseプロジェクトを作成し、Firestore と Firebase Authentication を有効化。

    2. Stripeアカウントを作成し、3日間の無料トライアル付きのサブスクリプションプランを登録。

    3. Firebase FunctionsでWebhookエンドポイントを作成し、Stripeのイベント通知(決済成功/失敗)を受け取る。

    4. Chrome拡張のユーザーが料金プランを選び、決済情報入力 → 3日無料トライアル開始。

    5. Webhookで「サブスク有効ユーザー」をFirestoreに保存し、拡張機能はユーザーごとにサブスク状態を参照して機能を制限。


    A. Firebase + Stripeのバックエンドを作る

    1. Firebaseプロジェクト作成 & 初期設定

    まずはFirebase Consoleで新しいプロジェクトを作成してください。
    その後、「Authentication」と「Cloud Firestore」を有効化します。

    Authentication

    • Firebaseコンソールの「Build > Authentication」から「始める」をクリック。

    • Googleログインやメール・パスワード認証など、使いたいログイン方法を有効にする。

    Cloud Firestore

    • Firebaseコンソールの「Build > Firestore Database」から「データベースを作成」。

    • 「本番モード(Production mode)」を選択してリージョンを設定し、有効化。

    2. Firebase CLI導入

    npm install -g firebase-tools
    firebase login
    
    

    3. Functionsの初期化

    mkdir my-extension-backend
    cd my-extension-backend
    firebase init functions
    
    

    ここで言語選択を求められたら JavaScript または TypeScript を選択します。
    初期設定が完了すると functions ディレクトリが生成されます。

    B. Stripeアカウント作成・サブスクリプション商品設定

    1. Stripe公式サイトでアカウントを作成し、事業情報などを登録。

    2. Stripeダッシュボードでテスト用「Publishable key」「Secret key」を取得。
      本番運用時は「本番キー」を使用しますが、最初はテストキーで進めます。

    3. Stripeダッシュボードの「商品(Product)」で新規商品を作成。
      続いて「価格(Price)」を追加し、定期購読(月額など)に設定。
      この時、試用期間(Trial period)を3日に指定しておきましょう。

    C. Firebase FunctionsでWebhook & Checkoutセッション作成

    1. Stripeパッケージ導入

    cd functions
    npm install stripe
    
    

    2. index.js (または index.ts) のサンプル

    以下は最小限のサンプルコードです。コピペして利用可能ですが、実際の運用ではエラーハンドリングやセキュリティ強化をしてください。

    
    // functions/index.js
    
    const functions = require("firebase-functions");
    const admin = require("firebase-admin");
    const Stripe = require("stripe");
    
    // Firebase初期化
    admin.initializeApp();
    
    // 環境変数からStripeのシークレットキーを読み込む
    // (デプロイ時に firebase functions:config:set で設定)
    const stripe = new Stripe(functions.config().stripe.secret_key);
    
    // Checkoutセッション作成API (Callable Functions)
    exports.createStripeCheckout = functions.https.onCall(async (data, context) => {
      // ログインしていない場合はエラー
      if (!context.auth) {
        throw new functions.https.HttpsError("unauthenticated", "ログインが必要です");
      }
    
      // Stripe Checkout セッションを作成
      const session = await stripe.checkout.sessions.create({
        payment_method_types: ["card"],
        mode: "subscription",
        line_items: [
          {
            price: data.priceId, // Stripeダッシュボードで作成した定期課金用のPrice ID
            quantity: 1,
          },
        ],
        success_url: data.successUrl,
        cancel_url: data.cancelUrl,
        customer_email: data.email, // Authのメールアドレスを利用
      });
    
      // セッションIDを返す
      return { sessionId: session.id };
    });
    
    // Stripe Webhookで支払いイベントを受け取る
    exports.stripeWebhook = functions.https.onRequest(async (req, res) => {
      let event;
      const endpointSecret = functions.config().stripe.webhook_secret;
    
      try {
        const sig = req.headers["stripe-signature"];
        event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);
      } catch (err) {
        console.error("Webhook signature verification failed.", err);
        return res.sendStatus(400);
      }
    
      switch (event.type) {
        case "checkout.session.completed": {
          // 3日無料トライアルを含むサブスク開始
          const session = event.data.object;
          const customerEmail = session.customer_email;
    
          // Firestoreでユーザーを探し、activeSubscriptionをtrueに更新
          const userSnapshot = await admin.firestore()
            .collection("users")
            .where("email", "==", customerEmail)
            .limit(1)
            .get();
    
          if (!userSnapshot.empty) {
            const userDoc = userSnapshot.docs[0];
            await userDoc.ref.update({
              activeSubscription: true,
              subscriptionStart: new Date()
            });
          }
    
          break;
        }
    
        case "invoice.payment_failed": {
          // トライアル後の引き落とし失敗など
          const invoice = event.data.object;
          // customerIdを使ってFirestore上の該当ユーザーを探して処理
          break;
        }
    
        case "customer.subscription.deleted": {
          // サブスク解約
          const subscription = event.data.object;
          // customerIdからFirestoreユーザーを探して activeSubscription = false にする等
          break;
        }
    
        default:
          console.log(`Unhandled event type ${event.type}`);
      }
    
      res.json({ received: true });
    });
    
    

    3. 環境変数設定 & デプロイ

    
    // 環境変数にStripeのシークレットキーやWebhook Secretを設定
    firebase functions:config:set stripe.secret_key="YOUR_STRIPE_SECRET_KEY" \
    stripe.webhook_secret="YOUR_STRIPE_WEBHOOK_SECRET"
    
    // デプロイ
    firebase deploy --only functions
    
    

    「YOUR_STRIPE_SECRET_KEY」はStripeダッシュボードから取得したシークレットキーを入れてください。
    同様に「YOUR_STRIPE_WEBHOOK_SECRET」はWebhook設定時にStripeから取得します。


    D. StripeダッシュボードでWebhook設定

    1. Stripeダッシュボードの「Developers > Webhooks」で「Add endpoint」をクリック。

    2. URLに https://[YOUR_CLOUD_FUNCTIONS_DOMAIN]/stripeWebhook を入力。

    3. 「Event to send」は checkout.session.completed, invoice.payment_failed, customer.subscription.deleted あたりを送信しておけばOKです(運用に応じて追加)。

    4. 作成後、Webhook Secretが発行されます。これを使って firebase functions:config:set ... で設定します。


    E. Chrome拡張側の実装

    1. manifest.json (Manifest V3例)

    
    {
      "name": "My Paid Extension",
      "version": "1.0.0",
      "manifest_version": 3,
      "description": "最初の3日間が無料の有料サブスクリプション拡張機能",
      "permissions": ["storage", "identity"],
      "background": {
        "service_worker": "background.js"
      },
      "action": {
        "default_popup": "popup.html"
      }
    }
    
    

    2. popup.html の例

    
    
    
      
        
        
        
        
        
      
      
        

    My Paid Extension

    Googleでログイン

    3. popup.js の例

    以下では Callable Function (createStripeCheckout) を呼び出し、Stripe Checkout セッションを作成してブラウザを遷移させる流れを示しています。
    実際のCSP対策としては、外部サイトに一度飛ばして決済を完了させるほうが安全・簡単です。

    
    // popup.js
    import { initializeApp } from "https://www.gstatic.com/firebasejs/9.17.2/firebase-app.js";
    import {
      getAuth,
      signInWithPopup,
      GoogleAuthProvider,
      onAuthStateChanged
    } from "https://www.gstatic.com/firebasejs/9.17.2/firebase-auth.js";
    
    import { getFunctions, httpsCallable } from "https://www.gstatic.com/firebasejs/9.17.2/firebase-functions.js";
    
    // 1. Firebase 初期化 (プロジェクトの設定情報を入力)
    const firebaseConfig = {
      apiKey: "YOUR_API_KEY",
      authDomain: "YOUR_APP.firebaseapp.com",
      projectId: "YOUR_APP",
      storageBucket: "YOUR_APP.appspot.com",
      messagingSenderId: "SENDER_ID",
      appId: "APP_ID",
    };
    const app = initializeApp(firebaseConfig);
    const auth = getAuth(app);
    const functions = getFunctions(app);
    
    // DOM取得
    const loginBtn = document.getElementById("login-btn");
    const subscriptionSection = document.getElementById("subscription-section");
    const subscriptionStatus = document.getElementById("subscription-status");
    const subscribeBtn = document.getElementById("subscribe-btn");
    
    // Googleログイン
    loginBtn.addEventListener("click", async () => {
      const provider = new GoogleAuthProvider();
      try {
        await signInWithPopup(auth, provider);
      } catch (error) {
        console.error(error);
        alert("ログインに失敗しました");
      }
    });
    
    // ログイン状態監視
    onAuthStateChanged(auth, (user) => {
      if (user) {
        // ログイン済み → サブスク登録画面を表示
        document.getElementById("login-section").style.display = "none";
        subscriptionSection.style.display = "block";
    
        // FirestoreやFunctions経由でサブスク状態を確認して表示
        checkSubscriptionStatus(user).then(isActive => {
          subscriptionStatus.innerText = isActive
            ? "サブスク有効です"
            : "サブスク未加入です";
        });
      } else {
        // 未ログイン → ログインボタン表示
        document.getElementById("login-section").style.display = "block";
        subscriptionSection.style.display = "none";
      }
    });
    
    // サブスク登録ボタン
    subscribeBtn.addEventListener("click", async () => {
      const createStripeCheckout = httpsCallable(functions, "createStripeCheckout");
      try {
        // 事前にStripeダッシュボードで作成したPrice IDを設定
        const priceId = "price_xxx";
        const successUrl = "https://example.com/success"; // 決済完了後のリダイレクト先
        const cancelUrl = "https://example.com/cancel";   // キャンセル時の戻り先
    
        const result = await createStripeCheckout({
          priceId,
          successUrl,
          cancelUrl,
          email: auth.currentUser.email
        });
    
        // 受け取った sessionId を使い、Stripe Checkoutへリダイレクト
        if (result.data && result.data.sessionId) {
          const stripePublicKey = "YOUR_STRIPE_PUBLISHABLE_KEY";
          const stripe = Stripe(stripePublicKey); // 事前に  が必要
          await stripe.redirectToCheckout({ sessionId: result.data.sessionId });
        }
      } catch (error) {
        console.error(error);
        alert("サブスク登録ページの作成に失敗しました");
      }
    });
    
    // サブスク状態をチェックする仮関数 (FirestoreやカスタムClaimsを用いて実装)
    async function checkSubscriptionStatus(user) {
      // カスタムClaimsを利用する場合の例
      const tokenResult = await user.getIdTokenResult(true);
      return tokenResult.claims.activeSubscription === true;
    }
    
    

    F. 拡張機能で機能制限する方法

    本質的には「ユーザーがサブスクを持っているかどうか」をFirestoreやカスタムClaimsから確認して、サブスクが無効なら機能をロックする仕組みを作ればOKです。
    例えば background.js や popup.js でユーザーがログインするたびにサブスク状態をチェックし、activeSubscription が true なら全機能解放、false なら制限する流れを実装します。


    G. 全手順のまとめ (コピペ手順)

    1. Firebaseプロジェクトを用意し、Authentication(ログイン)とFirestore(データベース)を有効化。

    2. Stripeアカウントを作成し、商品(定期購読プラン)を登録。トライアル期間を3日に設定。

    3. mkdir my-extension-backend cd my-extension-backend firebase init functions cd functions npm install stripe

    4. functions/index.js を編集し、createStripeCheckout 関数と stripeWebhook 関数を定義。

    5. firebase functions:config:set stripe.secret_key="YOUR_STRIPE_SECRET_KEY" ^ stripe.webhook_secret="YOUR_STRIPE_WEBHOOK_SECRET" firebase deploy --only functions

    6. Stripeダッシュボードで Webhook を作成し、stripeWebhook のURLに通知を送信。
      Webhook Secretを取得して firebase functions:config:set で設定。

    7. Chrome拡張 (manifest.json, popup.html, popup.js など) を作成し、Firebase SDK & Stripe Checkoutを呼び出す。

    8. サブスク登録が完了すると Firestoreのユーザーデータに activeSubscription: true を記録し、拡張機能はそのフラグをチェックして機能のON/OFFを切り替える。


    H. 注意事項 & ヒント

    • Chrome Web Storeの公式課金機能は廃止されたため、サブスク機能は自前で実装する必要があります。

    • 拡張機能内で Stripe.js を直接読み込む際には CSP(Content Security Policy) の制限に注意。外部CDNからのスクリプトを読み込めない可能性があります。

    • 安全策としては、拡張機能外のWebページ(Firebase Hostingや独自サイト)に移動させてStripe Checkoutを利用する構成が望ましいです。

    • Stripeの3日間無料トライアル設定を使うと、自動的にトライアル終了 → 課金が行われます。

    • Webhookで確実に課金状態を検知し、Firestore上でサブスクステータスを更新。拡張機能は常にFirestoreの状態を見て制御するようにしてください。

    • 決済周りはユーザーのクレジットカード情報を扱うため、セキュリティやエラーハンドリングを十分考慮する必要があります。ここでのサンプルはあくまで「最小限の動作例」です。

    • 本番運用に入る前に必ずテストモードでの動作確認を徹底しましょう。


    まとめ

    本記事の手順を踏めば、「クレジットカード登録後3日間無料 → 以降は自動課金」というサブスクリプション課金モデルをChrome拡張で実装できます。
    Stripe を使えば、トライアル期間や月額・年額プランなどを柔軟に設定可能です。
    Chrome拡張側はサブスク状態をチェックして、支払いが有効な間だけ機能をフル解放する仕組みを作るだけです。

    ぜひ本ガイドを参考に、あなたのChrome拡張に有料サブスクを導入してみてください。
    セキュリティやCSP対策など、実運用ではさらにブラッシュアップが必要ですが、まずは本記事の流れで「動く形」を構築してみることをおすすめします。

    ここまでお読みいただき、ありがとうございます。

    このnoteでは、書籍紹介をはじめ、さまざまなテーマで記事を書いていますが、メインで取り上げているのはビジネスの話です。

    メンバーシップ「謎ビジネス実験室」では、ネットや海外で見つけた気になるビジネスを調べ、実際に試す過程や、そこで分かったことを共有しています。

    ビジネスの仕組みや実践・検証に興味のある方は、ぜひこちらの案内をご覧ください。普段の発信内容や、メンバーシップのプランについてまとめています。

     
     
    会社員・ひとり事業者向け。海外・SNSで見つけた「本当に成立する?」を小さく再現。AI、自動化、複数端末、ネット施策を、原価・作業時間・規約・再現率・失敗まで公開します。噂を、手順と数字に変える「再現ラボ」。

    あなたへのおすすめ