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. セキュリティレイヤー
多層防御戦略:
- フォーマット検証(UUID v4)
- API 署名検証(HMAC-SHA256)
- レート制限(DoS 対策)
- ライセンス有効期限の確認
- サイト URL 紐付け(ライセンス転売防止)
- 開発環境の自動バイパス(ローカル開発効率向上)
実装例:
// フォーマット検証
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 機能を自動的に有効化します。
学び
- アーキテクチャ設計の重要性: 単一責務原則を厳守することで、テストしやすさ・保守性・拡張容易性が得られた。
- グレースピリオドの価値: API 通信障害時も 7 日間は動作継続できるため、ユーザーの利便性が向上し、サーバー障害に強い設計になる。
- セキュリティの多層防御: 単一の検証ではなく複数レイヤーを組み合わせることで、ライセンス盗用・転売のリスクを下げ、API キー漏洩の影響を限定できる。
- 開発環境と本番環境の分離: 定数上書きにより、ローカルで Pro 機能を確認しつつ本番に影響を与えない。
実装上の注意事項
Webhook の扱い
当初 Webhook を実装しましたが、エンドユーザーにとって設定が複雑、ファイアウォール越しの通信がセキュリティリスク、API ベースの定期検証で十分対応可能、という理由から削除する方針にしました。
Store ID / Product ID のハードコード化
Lemon Squeezy 固有の ID をプラグインコードに埋め込むことで、エンドユーザーは設定不要になり UI が簡潔になります。変更時はプラグイン更新が必要ですが、定数の上書きで開発・テストに対応できます。
参考情報
これは Markdown Renderer for GitHub に実装されています。

