Lemon Squeezyのlicense activateとvalidateは同じレスポンス形状ではない
ライセンスキーが正しくても、アプリ側のparserが間違っていると拒否されます。
Lemon Squeezy License APIには、activation、validation、deactivationがあります。すべて「license key」に関するAPIだからといって、同じJSON形状を返すとは限りません。
症状: valid keyがinvalid扱いになる
よくあるミスはこうです。
const data = await response.json();
const status = data.license_key?.data?.attributes?.status;
if (status !== "active") {
return { ok: false, reason: "invalid_key" };
}
このparserは、あるendpointでは正しくても別のendpointでは間違いです。activation responseがflatな形なら、data.license_key.data.attributes.status は undefined になります。
結果として、provider側では成功したキーをアプリが拒否します。
activateとvalidateを別契約として扱う
endpointごとに型を分けます。
type ActivateResponse = {
activated: boolean;
error: string | null;
license_key?: {
status?: string;
activation_limit?: number;
activation_usage?: number;
};
instance?: {
id?: string;
name?: string;
};
};
validationは別にします。
type ValidateResponse = {
valid: boolean;
error: string | null;
license_key?: unknown;
instance?: unknown;
};
正確なfieldは最新の公式docsと実際のtest responseで確認します。大事なのは、activation用parserとvalidation用parserを使い回さないことです。
provider responseをアプリ用の状態に変換する
providerのJSONをUI全体に広げないほうが安全です。
type AppLicenseResult =
| { ok: true; status: "active"; instanceId: string | null }
| { ok: false; reason: "invalid_key" | "limit_reached" | "network_error" | "provider_error" };
function mapActivateResponse(data: ActivateResponse): AppLicenseResult {
if (!data.activated) {
return { ok: false, reason: "invalid_key" };
}
return {
ok: true,
status: "active",
instanceId: data.instance?.id ?? null,
};
}
Tauri command、React store、settings UIは、このアプリ用resultを使います。provider payloadはintegration detailです。
failureを分けてテストする
全部を「invalid key」にすると、ユーザーにも開発者にも原因が分かりません。
最低限、mock responseで次を確認します。
- activation success
- activation limit reached
- key not found
- validation invalid
- malformed JSON
- network timeout
有料アプリでは network_error と revoked は別です。短時間のネットワーク障害ならfail-open、明確な失効ならfail-closedにする、といった判断が必要になります。
offline validationとは分ける
activationとvalidationはonline checkです。
desktop appでは、起動時にlocal stateをHMACなどで検証することもあります。
activate online
license key, instance id, activation time, HMACを保存
startup時にdevice idとHMACを再計算
periodic refreshだけproviderへ問い合わせる
offline validationはprovider response parserを使いません。provider parsing、local storage、device binding、network failureを分けることで、問題の切り分けが簡単になります。