統合ガイド

各言語・プラットフォームでの Lookup API の統合例です。

Node.js / TypeScript

サーバーサイドで Lookup API を呼び出す例です。Express ミドルウェアとしても利用できます。

typescript
const MEMGATE_API_KEY = process.env.MEMGATE_API_KEY;
const MEMGATE_BASE_URL = "https://www.memgate.io/api/v1";

interface LookupResult {
  member: {
    note_id: string;
    email: string;
    plan: string;
    valid_from: string;
    valid_until: string;
    status: "active" | "expired";
  } | null;
  status?: "not_found";
}

async function lookupMember(
  noteId: string,
  email: string
): Promise<LookupResult> {
  const params = new URLSearchParams({ note_id: noteId, email });
  const res = await fetch(
    `${MEMGATE_BASE_URL}/lookup?${params}`,
    {
      headers: { "X-API-Key": MEMGATE_API_KEY! },
    }
  );

  if (!res.ok) {
    const err = await res.json();
    throw new Error(err.error?.message ?? "Lookup failed");
  }

  return res.json();
}

// Usage
const result = await lookupMember("yamada_taro", "yamada@example.com");

if (result.member && result.member.status === "active") {
  console.log(`Active plan: ${result.member.plan}`);
} else {
  console.log("No active membership");
}

Express ミドルウェア例

typescript
import type { Request, Response, NextFunction } from "express";

async function requireMembership(
  req: Request,
  res: Response,
  next: NextFunction
) {
  const { note_id, email } = req.body;

  try {
    const result = await lookupMember(note_id, email);

    if (result.member?.status === "active") {
      req.memberPlan = result.member.plan;
      return next();
    }

    return res.status(403).json({
      error: "Active membership required",
    });
  } catch {
    return res.status(502).json({
      error: "Membership verification failed",
    });
  }
}

Python

Python の requests ライブラリを使った例です。

python
import os
import requests

MEMGATE_API_KEY = os.environ["MEMGATE_API_KEY"]
MEMGATE_BASE_URL = "https://www.memgate.io/api/v1"


def lookup_member(note_id: str, email: str) -> dict:
    """MemGate Lookup API でメンバー情報を照会する"""
    res = requests.get(
        f"{MEMGATE_BASE_URL}/lookup",
        params={"note_id": note_id, "email": email},
        headers={"X-API-Key": MEMGATE_API_KEY},
        timeout=10,
    )
    res.raise_for_status()
    return res.json()


# Usage
result = lookup_member("yamada_taro", "yamada@example.com")

if result.get("member") and result["member"]["status"] == "active":
    print(f"Active plan: {result['member']['plan']}")
else:
    print("No active membership")

Flask デコレータ例

python
from functools import wraps
from flask import request, jsonify


def require_membership(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        note_id = request.json.get("note_id")
        email = request.json.get("email")

        if not note_id or not email:
            return jsonify({"error": "note_id and email required"}), 400

        result = lookup_member(note_id, email)
        member = result.get("member")

        if not member or member["status"] != "active":
            return jsonify({"error": "Active membership required"}), 403

        request.member_plan = member["plan"]
        return f(*args, **kwargs)

    return decorated

Chrome 拡張機能

クライアントキー(mck_ プレフィックス)を使うと、 自前のバックエンドサーバーなしで Chrome 拡張機能から直接 MemGate API を呼び出せます。

セットアップ

手順

  1. 拡張機能の ID を確認(chrome://extensions/ → デベロッパーモード ON)
  2. MemGate 管理画面の「APIキー → クライアントキー」タブでキーを作成
  3. 許可オリジンに chrome-extension://YOUR_EXTENSION_ID を設定
  4. manifest.json host_permissions "https://www.memgate.io/*" を追加
  5. Service Worker(background script)から Lookup API を呼び出し

manifest.json の設定

json
{
  "manifest_version": 3,
  "host_permissions": [
    "https://www.memgate.io/*"
  ],
  "background": {
    "service_worker": "background.js"
  }
}

Service Worker からの呼び出し

Popup(UI層)から直接 API を呼ばず、Service Worker 経由で通信することを推奨します。 これにより、クライアントキーが popup.js のスコープに露出せず、 レスポンスキャッシュも Service Worker レイヤーで管理できます。

typescript
// background.js (Service Worker)

const MEMGATE_CLIENT_KEY = "mck_your_client_key_here";
const MEMGATE_API_URL = "https://www.memgate.io/api/v1/lookup";

async function checkMembership(
  noteId: string,
  email: string
): Promise<{ active: boolean; plan?: string }> {
  const params = new URLSearchParams({ note_id: noteId, email });
  const res = await fetch(
    `${MEMGATE_API_URL}?${params}`,
    {
      headers: { "X-API-Key": MEMGATE_CLIENT_KEY },
    }
  );

  if (!res.ok) return { active: false };

  const data = await res.json();
  if (data.member?.status === "active") {
    return { active: true, plan: data.member.plan };
  }
  return { active: false };
}

// Popup / Content script からのメッセージを受信
chrome.runtime.onMessage.addListener(
  (message, _sender, sendResponse) => {
    if (message.type === "CHECK_MEMBERSHIP") {
      checkMembership(message.noteId, message.email)
        .then((result) => sendResponse(result));
      return true; // async response
    }
  }
);

Origin ヘッダーについて

Chrome MV3 の挙動

Chrome MV3 の Service Worker は、host_permissions に含まれるドメインへのリクエストで Origin ヘッダーを送信しないケースがあります。 MemGate はこのケースに対応しており、Origin が未送信の場合はクライアントキー認証のみで リクエストを許可します。Origin が送信された場合は、登録済みの許可オリジンと照合します。

Chrome Web Store 公開時の注意

開発者モード(パッケージ解凍読み込み)で使用している拡張機能の ID は、 Chrome Web Store に公開すると変更されます。 公開後に新しい拡張 ID を管理画面の「許可オリジン」に追加してください。 開発用の ID は不要になったら削除を推奨します。

ベストプラクティス

キーの種類と使い分け

  • サーバーキーmgk_): バックエンドサーバーで使用。環境変数に格納し、クライアントに公開しない
  • クライアントキーmck_): Chrome 拡張機能やフロントエンドで使用。許可オリジンで保護される
  • 不要になったキーは管理画面から即座に無効化する

429 レートリミットへの対応

429 レスポンスを受けた場合は、指数バックオフでリトライしてください。 クライアントキーはサーバーキーより低いレートリミット(30回/分)が適用されます。

typescript
async function lookupWithRetry(
  noteId: string,
  email: string,
  maxRetries = 3
): Promise<LookupResult> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(/* ... */);

    if (res.status === 429 && attempt < maxRetries) {
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise((r) => setTimeout(r, delay));
      continue;
    }

    if (!res.ok) throw new Error("Lookup failed");
    return res.json();
  }

  throw new Error("Max retries exceeded");
}

レスポンスのキャッシュ

同じユーザーに対する短時間の連続リクエストを避けるため、 サーバーサイドでレスポンスを短時間キャッシュすることを推奨します。 ただし、キャッシュ期間はメンバーシップの更新頻度に応じて調整してください。

エラーハンドリング

  • MemGate API が一時的に利用できない場合のフォールバックを検討する
  • タイムアウトを設定する(推奨: 10秒)
  • member: null のケースを必ずハンドリングする