WordPress プラグインに Pro 版ライセンス管理機能を実装する必要がありました。Lemon Squeezy を使ったライセンス販売・管理システムを構築し、その実装パターンとベストプラクティスをまとめます。

要件

  • ライセンスキーの検証・認証・無効化
  • Lemon Squeezy API との統合
  • セキュリティ検証(フォーマット、署名、レート制限)
  • 開発環境での自動バイパス
  • キャッシング戦略(12 時間キャッシュ + 7 日グレースピリオド)
  • Pro 機能の条件付きゲート実装
  • エンドユーザー向けの簡潔な設定 UI

実装方針

1. アーキテクチャ: ハイブリッドアプローチ

┌─────────────────────────────────────┐
│  WordPress Admin UI                 │
│  (ライセンスキー入力)                │
└──────────────┬──────────────────────┘
               │
       ┌───────▼────────┐
       │License Manager │ ← Singleton パターン
       │ (中央管理)      │
       └───────┬────────┘
               │
      ┌────────┴─────────┐
      │                  │
   ┌──▼──┐         ┌────▼─────┐
   │Cache│         │Lemon      │
   │(12h)│         │Squeezy API│
   └─────┘         └──────────┘
      ▲                  │
      │    ┌─────────────┘
      └────┤ (7日グレースピリオド)
           │
    ┌──────▼─────────┐
    │Pro Features    │ ← Registry
    │Registry        │
    └────────────────┘
           ▲
           │
    ┌──────┴──────────────┐
    │                     │
┌───┴──┐          ┌──────┴──┐
│Class │          │Trait    │
│A     │          │Gateway  │
└──────┘          └─────────┘
(Pro機能の実装)   (条件付き実行)

2. コンポーネント分割

GFMR_License_Manager

  • ライセンスの検証・認証・無効化
  • API 呼び出しと結果のキャッシング
  • グレースピリオド実装
  • Multisite 対応

GFMR_License_Security

  • UUID v4 フォーマット検証
  • HMAC-SHA256 署名検証
  • レート制限(10 試行/時間)
  • 開発環境判定

GFMR_Pro_Features(Registry)

  • Pro 機能の一元管理
  • 機能の登録・メタデータ管理
  • ライセンス状態との連携

GFMR_Premium_Feature_Gate(Trait)

  • 任意のクラスで利用可能
  • 条件付き実行の簡潔な記述

3. セキュリティレイヤー

多層防御戦略:

  1. フォーマット検証(UUID v4)
  2. API 署名検証(HMAC-SHA256)
  3. レート制限(DoS 対策)
  4. ライセンス有効期限の確認
  5. サイト URL 紐付け(ライセンス転売防止)
  6. 開発環境の自動バイパス(ローカル開発効率向上)

実装例:

// フォーマット検証
private function validate_license_format(string $license_key): bool {
    return preg_match(
        '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i',
        $license_key
    ) === 1;
}

// 署名検証
private function validate_webhook_signature(string $payload, string $signature): bool {
    $computed = hash_hmac('sha256', $payload, $this->webhook_secret, true);
    return hash_equals(base64_encode($computed), $signature);
}

ポイント

キャッシング戦略の重要性

12時間キャッシュ + 7日グレースピリオド = 信頼性と可用性のバランス

[Day 1]
└─ API検証 ✓
   └─ 12時間キャッシュ有効
      └─ API通信なし(効率的)

[Day 1 後24時間]
└─ キャッシュ期限切れ
   └─ API検証(ネットワーク障害の場合)
      └─ 7日グレースピリオド有効
         └─ ユーザーに影響なく継続利用可

[Day 8以降]
└─ グレースピリオド期限切れ
   └─ ライセンス検証失敗
      └─ Pro機能が無効化

Pro 機能ゲートの簡潔な実装

トレイトを使うことで、どのクラスでも条件付き機能実装が簡単になります。

class MyRenderer {
    use GFMR_Premium_Feature_Gate;

    public function render_with_features() {
        return $this->execute_if_pro(
            fn() => $this->render_advanced_features(),
            fn() => $this->render_free_version()
        );
    }
}

開発環境の判定

private function is_development_environment(): bool {
    return (
        strpos($this->site_url, 'localhost') !== false
        || preg_match('/\.(local|dev|test|staging)$/', $this->site_url)
        || (defined('WP_DEBUG') && WP_DEBUG)
    );
}

開発環境ではライセンス検証をバイパスし、Pro 機能を自動的に有効化します。

学び

  1. アーキテクチャ設計の重要性: 単一責務原則を厳守することで、テストしやすさ・保守性・拡張容易性が得られた。
  2. グレースピリオドの価値: API 通信障害時も 7 日間は動作継続できるため、ユーザーの利便性が向上し、サーバー障害に強い設計になる。
  3. セキュリティの多層防御: 単一の検証ではなく複数レイヤーを組み合わせることで、ライセンス盗用・転売のリスクを下げ、API キー漏洩の影響を限定できる。
  4. 開発環境と本番環境の分離: 定数上書きにより、ローカルで Pro 機能を確認しつつ本番に影響を与えない。

実装上の注意事項

Webhook の扱い

当初 Webhook を実装しましたが、エンドユーザーにとって設定が複雑、ファイアウォール越しの通信がセキュリティリスク、API ベースの定期検証で十分対応可能、という理由から削除する方針にしました。

Store ID / Product ID のハードコード化

Lemon Squeezy 固有の ID をプラグインコードに埋め込むことで、エンドユーザーは設定不要になり UI が簡潔になります。変更時はプラグイン更新が必要ですが、定数の上書きで開発・テストに対応できます。

参考情報


これは Markdown Renderer for GitHub に実装されています。