code-heaven/license-sdk
Composer 安装命令:
composer require code-heaven/license-sdk
包简介
Vendor SDK for the Code Heaven Developer License API (validation, domain activation, updates, downloads).
README 文档
README
Vendor SDK for the Code Heaven Developer License API. Use it inside your plugin/theme/app to validate licenses, activate and free domain seats, check for updates, and fetch signed download URLs.
This is the vendor server-to-server flow. Authentication is a single vendor
API key (X-CH-Vendor-Key), not the buyer OAuth flow.
- Base URL:
https://api.code-heaven.com/v1 - PHP 8.0+
- No hard runtime dependencies (cURL or
file_get_contents); pluggable HTTP so it works happily inside WordPress viawp_remote_request.
Install
composer require code-heaven/license-sdk
Quick start: validate and gate
use CodeHeaven\License\Client; use CodeHeaven\License\LicenseException; use CodeHeaven\License\TransportException; $client = new Client('YOUR_VENDOR_KEY', [ 'cacheTtl' => 12 * 3600, // serve a fresh answer for 12h 'offlineGrace' => 14 * 24 * 3600, // keep working up to 14 days if the API is down ]); try { $res = $client->validate('LICENSE-KEY', 'shop.example.com', 'booknetic-pro'); } catch (LicenseException $e) { // 403: invalid / expired / domain_not_activated ($e->apiCode tells you which) bail("License problem: {$e->apiCode}"); } catch (TransportException $e) { // API unreachable AND no cached valid result within grace. bail('License server unreachable.'); } if ($res['valid']) { enable_premium_features(); } else { // $res['status'] is one of: valid | invalid | expired | domain_not_activated | revoked disable_premium_features($res['status']); }
validate() returns the decoded API body plus two SDK markers:
| key | meaning |
|---|---|
valid |
bool |
status |
valid | invalid | expired | domain_not_activated | revoked |
product |
product slug (or null) |
expiresAt |
ISO 8601 string or null |
activations |
[{domain, activatedAt}, ...] |
seatLimit |
int |
_cached |
present & true when served from cache |
_offline |
present & true when served from cache during an outage |
Activate a seat on first run
When validate() returns domain_not_activated (a LicenseException with
apiCode === 'domain_not_activated'), claim a seat:
use CodeHeaven\License\SeatLimitException; try { $client->activateDomain('LICENSE-KEY', 'shop.example.com'); } catch (SeatLimitException $e) { // 409 — buyer has used every seat; prompt them to deactivate another site. }
Check for and apply an update
$update = $client->checkUpdate('LICENSE-KEY', 'booknetic-pro', '4.1.0', 'shop.example.com'); if ($update['hasUpdate']) { // latestVersion + changelog[{version,date,notes}] $dl = $client->download('LICENSE-KEY', 'booknetic-pro', 'shop.example.com', $update['latestVersion']); // $dl['url'] is a short-lived signed package URL; $dl['expiresAt'] tells you when it dies. download_and_install_zip($dl['url']); }
Free the seat on uninstall
$client->deactivateDomain('LICENSE-KEY', 'shop.example.com');
Caching and offline grace
validate() is cached so repeated calls in one request never hit the wire
twice, and the result persists across requests when you supply a persistent
cache:
cacheTtl— how long a cached answer is considered fresh. Within this windowvalidate()returns the cached body without any network call.offlineGrace— how long a cachedvalidanswer may be served after it goes stale, but only when the API is unreachable. This stops a transient outage from locking out paying customers. Served results carry_offline => true.
Persistent caches:
use CodeHeaven\License\FileCache; use CodeHeaven\License\TransientCache; // Outside WordPress: new Client($key, ['cache' => new FileCache('/var/cache/myplugin')]); // Inside WordPress (rides transients / the object cache): new Client($key, ['cache' => new TransientCache('myplugin_')]);
The default is an in-process ArrayCache (no cross-request persistence, so no
offline grace across requests — supply FileCache/TransientCache in
production).
Custom HTTP transport (WordPress, Guzzle, …)
Inject any callable as http. It receives (method, url, headers, ?body) and
must return ['status' => int, 'headers' => array, 'body' => string], or throw
TransportException on a connection failure (which triggers offline grace).
new Client($key, [ 'http' => function (string $method, string $url, array $headers, ?string $body): array { $res = wp_remote_request($url, compact('method', 'headers', 'body') + ['timeout' => 15]); if (is_wp_error($res)) { throw new \CodeHeaven\License\TransportException($res->get_error_message()); } return [ 'status' => (int) wp_remote_retrieve_response_code($res), 'headers' => wp_remote_retrieve_headers($res)->getAll(), 'body' => (string) wp_remote_retrieve_body($res), ]; }, ]);
See examples/gate-plugin.php for a complete,
fail-safe WordPress gate (validate → activate-on-first-run → admin nag →
deactivate-on-uninstall).
Exceptions
All extend CodeHeaven\License\CodeHeavenException and expose
->statusCode, ->apiCode, ->response.
| Exception | HTTP | When |
|---|---|---|
AuthException |
401 | Vendor key missing/invalid (your packaging bug) |
LicenseException |
403 | license_invalid / expired / domain_not_activated |
SeatLimitException |
409 | seat_limit_exceeded on activateDomain() |
RateLimitException |
429 | Rate limited (->retryAfter holds the hint, if any) |
TransportException |
— | API unreachable and no usable cached result |
CodeHeavenException |
4xx/5xx | Any other non-2xx response |
Note: an invalid or expired license that the API reports with HTTP 200 and
valid:falseis returned as data, not thrown. The API only throwsLicenseExceptionfor the 403 cases above.
Development
composer install composer test # phpunit
License
MIT.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-08