v0.3.0 — entitlements catalog in PublicPoliciesResponse
Mirrors keysat 0014 + TS/Rust SDK 0.3.0. PublicPoliciesProduct gains entitlements_catalog: list[EntitlementDef] with slug + name + description. SDK consumers' in-app tier pickers can render display names + tooltip descriptions instead of raw slugs. Empty list on legacy products without a catalog. No breaking change.
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "keysat-licensing-client"
|
name = "keysat-licensing-client"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
description = "Python client for Keysat — a self-hosted Bitcoin-paid software licensing server that runs on Start9. Verifies signed license keys offline and wraps the HTTP API for purchase, redemption, and revocation checks."
|
description = "Python client for Keysat — a self-hosted Bitcoin-paid software licensing server that runs on Start9. Verifies signed license keys offline and wraps the HTTP API for purchase, redemption, and revocation checks."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ try:
|
|||||||
ValidateResponse,
|
ValidateResponse,
|
||||||
ValidateOptions,
|
ValidateOptions,
|
||||||
StartPurchaseOptions,
|
StartPurchaseOptions,
|
||||||
|
EntitlementDef,
|
||||||
PublicPolicy,
|
PublicPolicy,
|
||||||
PublicPoliciesProduct,
|
PublicPoliciesProduct,
|
||||||
PublicPoliciesResponse,
|
PublicPoliciesResponse,
|
||||||
@@ -81,6 +82,7 @@ try:
|
|||||||
"ValidateResponse",
|
"ValidateResponse",
|
||||||
"ValidateOptions",
|
"ValidateOptions",
|
||||||
"StartPurchaseOptions",
|
"StartPurchaseOptions",
|
||||||
|
"EntitlementDef",
|
||||||
"PublicPolicy",
|
"PublicPolicy",
|
||||||
"PublicPoliciesProduct",
|
"PublicPoliciesProduct",
|
||||||
"PublicPoliciesResponse",
|
"PublicPoliciesResponse",
|
||||||
@@ -93,4 +95,4 @@ except ImportError:
|
|||||||
# httpx not installed — that's fine, online client is optional.
|
# httpx not installed — that's fine, online client is optional.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
__version__ = "0.2.0"
|
__version__ = "0.3.0"
|
||||||
|
|||||||
@@ -89,12 +89,27 @@ class PublicPolicy:
|
|||||||
trial_days: int
|
trial_days: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EntitlementDef:
|
||||||
|
"""One entry in a product's entitlements catalog (Keysat
|
||||||
|
migration 0014). Operator declares the closed list once per
|
||||||
|
product; policies pick from this list. Use ``name`` as the
|
||||||
|
human-readable label when rendering an in-app tier picker
|
||||||
|
(e.g. "AI summaries" instead of the raw ``ai_summaries`` slug).
|
||||||
|
"""
|
||||||
|
|
||||||
|
slug: str
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PublicPoliciesProduct:
|
class PublicPoliciesProduct:
|
||||||
slug: str
|
slug: str
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
base_price_sats: int
|
base_price_sats: int
|
||||||
|
entitlements_catalog: list[EntitlementDef]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -277,12 +292,21 @@ class Client:
|
|||||||
raw = self._get(f"/v1/products/{product_slug}/policies")
|
raw = self._get(f"/v1/products/{product_slug}/policies")
|
||||||
product = raw.get("product", {}) or {}
|
product = raw.get("product", {}) or {}
|
||||||
policies_raw = raw.get("policies") or []
|
policies_raw = raw.get("policies") or []
|
||||||
|
catalog_raw = product.get("entitlements_catalog") or []
|
||||||
return PublicPoliciesResponse(
|
return PublicPoliciesResponse(
|
||||||
product=PublicPoliciesProduct(
|
product=PublicPoliciesProduct(
|
||||||
slug=product.get("slug", ""),
|
slug=product.get("slug", ""),
|
||||||
name=product.get("name", ""),
|
name=product.get("name", ""),
|
||||||
description=product.get("description", "") or "",
|
description=product.get("description", "") or "",
|
||||||
base_price_sats=int(product.get("base_price_sats", 0)),
|
base_price_sats=int(product.get("base_price_sats", 0)),
|
||||||
|
entitlements_catalog=[
|
||||||
|
EntitlementDef(
|
||||||
|
slug=c.get("slug", ""),
|
||||||
|
name=c.get("name", "") or c.get("slug", ""),
|
||||||
|
description=c.get("description", "") or "",
|
||||||
|
)
|
||||||
|
for c in catalog_raw
|
||||||
|
],
|
||||||
),
|
),
|
||||||
policies=[
|
policies=[
|
||||||
PublicPolicy(
|
PublicPolicy(
|
||||||
|
|||||||
Reference in New Issue
Block a user