AppCotton は、**公開(public)と認証必須(private)**の 2 系統の REST API を提供します。
基本思想は「プロダクト/プランの公開参照」と「ライセンスの発行・検証・ドメイン紐づけ」を分離することです。
基本情報
- ベース URL:
https://{your-site}/wp-json/appcotton/v1/ - 文字コード:UTF-8 / JSON
- タイムスタンプ:UTC(ISO 8601)
- 安定性:バージョン付き(
v1)。後方互換が壊れる変更はv2以降で提供。
認証方式
- 公開系(/products, /plans 等):原則認証不要(読み取り)
- ライセンス系(/licenses/):
- 検証(validate)は、サーバ間問い合わせ用途で認証不要(レート制限あり)
- 発行・アクティベーション・解除は管理者/購入者のログイン済み(Cookie/Nonce)またはサーバ側キーによる保護
- CSRF 対策:管理画面/フロント UI 経由で叩く場合は
X-WP-Nonceを必須化 - CORS:
GET公開エンドポイントのみ許可(既定)。POSTは same-origin 前提(必要に応じて allowlist)
共通ヘッダ
Content-Type: application/json
Accept: application/json
X-WP-Nonce: {nonce} // 認証を要する操作時
エラー形式(共通)
{
"success": false,
"error": "license_not_found",
"message": "ライセンスが見つかりません。",
"details": { "hint": "キーを確認してください" }
}
- 主な HTTP ステータス
- 200 / 201: 成功
- 400: バリデーション失敗/不正リクエスト
- 401: 未認証
- 403: 権限不足/Nonce 不正
- 404: 対象が存在しない
- 409: 競合(すでにアクティブ等)
- 429: レート制限
- 500: 予期せぬエラー
エンドポイント一覧
1) Products / Plans(公開)
GET /products
プロダクトの一覧を返します。
レスポンス例
{
"success": true,
"items": [
{
"id": 1,
"name": "CombPass Premium",
"product_slug": "combpass_premium",
"description": "予約管理の上位機能",
"active": true
}
]
}
GET /products/{id}
特定プロダクトの詳細。
{
"success": true,
"product": {
"id": 1,
"product_slug": "combpass_premium",
"name": "CombPass Premium",
"description": "…"
}
}
GET /products/{id}/plans
該当プロダクトに紐づく 販売プランを返します(例:1サイト/3サイト/5サイト、one_time/subscription 等)。
{
"success": true,
"plans": [
{
"plan_id": 101,
"plan_name": "1サイト",
"activation_limit": 1,
"billing_type": "one_time",
"price": 9800,
"currency": "JPY",
"sort_order": 10
},
{
"plan_id": 102,
"plan_name": "3サイト",
"activation_limit": 3,
"billing_type": "one_time",
"price": 19800,
"currency": "JPY",
"sort_order": 20
}
]
}
GET /products/{id}/price
単発購入に使う 現在価格(税/通貨処理後)を返します。
無料配布の可否など UI 分岐に使用。
{ "success": true, "price": 19800, "currency": "JPY" }
2) Licenses(検証・発行・ドメイン紐づけ)
GET /licenses/validate?license_key={key}&product_id={id}&domain={origin}
ライセンスの存在・有効性・残枠を検証し、指定ドメインがアクティブかも返します。
クライアント SDK の is_premium() が内部で叩く想定。
成功例
{
"valid": true,
"message": "OK",
"expires_at": null,
"is_activated": true,
"usage": {
"used": 2,
"limit": 3,
"remaining": 1,
"activations": [
{ "domain": "https://site-a.com", "status": "active" },
{ "domain": "https://site-b.com", "status": "active" }
]
}
}
エラー例
{ "valid": false, "error": "limit_exceeded", "message": "上限に達しています" }
POST /licenses/activate
ドメインをアクティベーション(紐づけ)します。
リクエスト
{
"license_key": "AC-XXXX-YYYY",
"domain": "https://example.com",
"site_url": "https://example.com",
"instance_id": "optional-guid"
}
レスポンス
{
"success": true,
"message": "Activated",
"usage": { "used": 1, "limit": 3, "remaining": 2 }
}
POST /licenses/deactivate
ドメインの解除(枠を戻す)。
{
"license_key": "AC-XXXX-YYYY",
"domain": "https://example.com"
}
成功時:
{
"success": true,
"message": "Deactivated",
"usage": { "used": 0, "limit": 3, "remaining": 3 }
}
GET /licenses/usage?license_key={key}
使用状況のみ(管理画面やポータルでの表示用)。
{
"success": true,
"usage": {
"used": 2,
"limit": 3,
"remaining": 1,
"activations": [ ... ]
}
}
POST /licenses/request-free
無料プランのライセンス発行(メール送付)。
※ いたずら対策として reCAPTCHA・レート制限・メールドメイン制限を推奨。
{
"product_id": 1,
"customer_email": "user@example.com"
}
3) Checkout / Upgrade(差額課金の開始)
POST /checkout/create-session
新規購入(Stripe Checkout セッション作成)。
バックエンドでプラン ID・金額・セッション URL を生成し、redirect_url を返す。
リクエスト
{ "product_id": 1, "plan_id": 102 }
レスポンス
{ "success": true, "redirect_url": "https://checkout.stripe.com/..." }
POST /licenses/upgrade/start
上位プランへの差額課金(現在のライセンスからターゲットプランへ)。
バックエンドで差額(現在価格 − 既払い相当)を計算し Checkout を生成。
リクエスト
{
"license_key": "AC-XXXX-YYYY",
"target_plan_id": 103,
"success_url": "https://your-site/thanks",
"cancel_url": "https://your-site/cancel"
}
レスポンス
{
"success": true,
"redirect_url": "https://checkout.stripe.com/...",
"diff_cents": 5000,
"currency": "JPY"
}
ページング・並び順・検索
- 一覧は
?page=1&per_page=20&orderby=sort_order&order=ascなどを受け付け - レスポンスヘッダに
X-Total,X-Total-Pagesを付与(可能なら)
レート制限(推奨)
- 公開 GET:IP 単位で1 分 60 回
/licenses/validate:1 分 20 回(ドメイン/ライセンスキー単位のバースト抑制)- 429 時の再試行は
Retry-After秒数を返却
べき等性(idempotency)
- /licenses/activate/deactivate:
同一license_key + domainに対する短時間の重複 POST は無害(同じ結果)で返す - Checkout 作成:クライアントが重送する場合は Idempotency-Key(UUID)を任意ヘッダで受け取り、短期間は同一セッションを返す設計が望ましい
セキュリティ注意
- ドメイン正規化(
https://example.com/ 末尾スラッシュ除去 / サブドメイン一致方針を統一) - IP・UAの保存はプライバシーポリシー記載
- Nonce 検証(管理画面/同一オリジン UI)
- 不正アクセスログの監査(activation の連打や違反パターン)
具体例
cURL:ライセンス検証
curl -G "https://your-site/wp-json/appcotton/v1/licenses/validate" \
--data-urlencode "license_key=AC-XXXX-YYYY" \
--data-urlencode "product_id=1" \
--data-urlencode "domain=https://client-site.com"
PHP(WordPress)での検証
$response = wp_remote_get( add_query_arg([
'license_key' => $license_key,
'product_id' => $product_id,
'domain' => home_url()
], rest_url('appcotton/v1/licenses/validate') ) );
if ( is_wp_error($response) ) { /* ネットワークエラー処理 */ }
$body = json_decode( wp_remote_retrieve_body($response), true );
if ( ! empty($body['valid']) ) {
// is_premium = true
}
JS(フロント)でのアクティベーション
const res = await fetch('/wp-json/appcotton/v1/licenses/activate', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': window.appcottonNonce },
body: JSON.stringify({
license_key: form.license.value,
domain: window.location.origin,
site_url: window.location.origin
})
});
const json = await res.json();
if (json.success) { /* usage 表示 */ }
バージョニングと互換性
- 非互換変更は
v2で提供 v1ではレスポンスにフィールド追加は行うが、既存キーの意味と型は維持
よくあるエラーコード(抜粋)
| error | 意味 |
|---|---|
missing_param | 必須パラメータ不足 |
license_not_found | キーが存在しない |
product_mismatch | プロダクト不一致 |
limit_exceeded | 有効化上限超過 |
already_activated | 既に同ドメインで有効 |
not_activated | 解除対象が見つからない |
forbidden | 権限不足/Nonce 不正 |
rate_limited | レート制限 |
checkout_failed | 決済セッション生成失敗 |
この章をベースに、**SDK(is_premium などのラッパー)とUI自動生成(プラン一覧/購入・アップグレードボタン)**を組み合わせれば、プラグイン開発者は最小実装で組み込み可能になります。