paiment 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
paiment/__init__.py ADDED
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from ._errors import PaimentAuthError, PaimentError, PaimentNotFoundError, PaimentValidationError
6
+ from ._http import AsyncHttpClient, HttpClient
7
+ from ._models import FixedPrice, Page, Plan, Pagination, Subscription, Usage, UsageBasedPrice, User
8
+ from .resources.plans import AsyncPlansResource, PlansResource
9
+ from .resources.subscriptions import AsyncSubscriptionsResource, SubscriptionsResource
10
+ from .resources.usage import AsyncUsageResource, UsageResource
11
+ from .resources.users import AsyncUsersResource, UsersResource
12
+
13
+ __all__ = [
14
+ "PaimentClient",
15
+ "AsyncPaimentClient",
16
+ "PaimentError",
17
+ "PaimentAuthError",
18
+ "PaimentNotFoundError",
19
+ "PaimentValidationError",
20
+ "User",
21
+ "Subscription",
22
+ "Plan",
23
+ "UsageBasedPrice",
24
+ "FixedPrice",
25
+ "Usage",
26
+ "Page",
27
+ "Pagination",
28
+ ]
29
+
30
+ _DEFAULT_BASE_URL = "https://api.paiment.tech"
31
+
32
+
33
+ class PaimentClient:
34
+ def __init__(
35
+ self,
36
+ *,
37
+ api_key: str,
38
+ base_url: str = _DEFAULT_BASE_URL,
39
+ workspace_id: str | None = None,
40
+ ) -> None:
41
+ self._http = HttpClient(api_key=api_key, base_url=base_url, workspace_id=workspace_id)
42
+ self.users = UsersResource(self._http)
43
+ self.subscriptions = SubscriptionsResource(self._http)
44
+ self.plans = PlansResource(self._http)
45
+ self.usage = UsageResource(self._http)
46
+
47
+ def close(self) -> None:
48
+ self._http.close()
49
+
50
+ def __enter__(self) -> "PaimentClient":
51
+ return self
52
+
53
+ def __exit__(self, *_: Any) -> None:
54
+ self.close()
55
+
56
+
57
+ class AsyncPaimentClient:
58
+ def __init__(
59
+ self,
60
+ *,
61
+ api_key: str,
62
+ base_url: str = _DEFAULT_BASE_URL,
63
+ workspace_id: str | None = None,
64
+ ) -> None:
65
+ self._http = AsyncHttpClient(api_key=api_key, base_url=base_url, workspace_id=workspace_id)
66
+ self.users = AsyncUsersResource(self._http)
67
+ self.subscriptions = AsyncSubscriptionsResource(self._http)
68
+ self.plans = AsyncPlansResource(self._http)
69
+ self.usage = AsyncUsageResource(self._http)
70
+
71
+ async def aclose(self) -> None:
72
+ await self._http.aclose()
73
+
74
+ async def __aenter__(self) -> "AsyncPaimentClient":
75
+ return self
76
+
77
+ async def __aexit__(self, *_: Any) -> None:
78
+ await self.aclose()
paiment/_errors.py ADDED
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ class PaimentError(Exception):
7
+ def __init__(self, message: str, status_code: int | None = None, body: Any = None) -> None:
8
+ super().__init__(message)
9
+ self.status_code = status_code
10
+ self.body = body
11
+
12
+
13
+ class PaimentAuthError(PaimentError):
14
+ def __init__(self, body: Any = None) -> None:
15
+ super().__init__("Authentication failed. Check your API key.", 401, body)
16
+
17
+
18
+ class PaimentNotFoundError(PaimentError):
19
+ def __init__(self, resource: str = "Resource") -> None:
20
+ super().__init__(f"{resource} not found.", 404)
21
+
22
+
23
+ class PaimentValidationError(PaimentError):
24
+ def __init__(self, body: Any = None) -> None:
25
+ super().__init__("Validation failed.", 400, body)
paiment/_http.py ADDED
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import httpx
6
+
7
+ from ._errors import PaimentAuthError, PaimentError, PaimentNotFoundError, PaimentValidationError
8
+
9
+ _DEFAULT_TIMEOUT = 30.0
10
+
11
+
12
+ def _raise_for_status(response: httpx.Response) -> None:
13
+ if response.is_success:
14
+ return
15
+ try:
16
+ body: Any = response.json()
17
+ except Exception:
18
+ body = response.text
19
+
20
+ status = response.status_code
21
+ if status == 401:
22
+ raise PaimentAuthError(body)
23
+ if status == 404:
24
+ raise PaimentNotFoundError()
25
+ if status == 400:
26
+ raise PaimentValidationError(body)
27
+ raise PaimentError(f"Request failed with status {status}", status, body)
28
+
29
+
30
+ class HttpClient:
31
+ def __init__(self, api_key: str, base_url: str, workspace_id: str | None = None) -> None:
32
+ headers: dict[str, str] = {"X-Api-Key": api_key, "Content-Type": "application/json"}
33
+ if workspace_id:
34
+ headers["X-Workspace-Id"] = workspace_id
35
+ self.workspace_id = workspace_id
36
+ self._client = httpx.Client(
37
+ base_url=base_url.rstrip("/"),
38
+ headers=headers,
39
+ timeout=_DEFAULT_TIMEOUT,
40
+ )
41
+
42
+ def close(self) -> None:
43
+ self._client.close()
44
+
45
+ def __enter__(self) -> "HttpClient":
46
+ return self
47
+
48
+ def __exit__(self, *_: Any) -> None:
49
+ self.close()
50
+
51
+ def get(self, path: str, params: dict[str, Any] | None = None) -> Any:
52
+ r = self._client.get(path, params={k: v for k, v in (params or {}).items() if v is not None})
53
+ _raise_for_status(r)
54
+ return r.json() if r.status_code != 204 else None
55
+
56
+ def post(self, path: str, body: Any = None) -> Any:
57
+ r = self._client.post(path, json=body)
58
+ _raise_for_status(r)
59
+ return r.json() if r.status_code != 204 else None
60
+
61
+ def patch(self, path: str, body: Any = None) -> Any:
62
+ r = self._client.patch(path, json=body)
63
+ _raise_for_status(r)
64
+ return r.json() if r.status_code != 204 else None
65
+
66
+ def put(self, path: str, body: Any = None) -> Any:
67
+ r = self._client.put(path, json=body)
68
+ _raise_for_status(r)
69
+ return r.json() if r.status_code != 204 else None
70
+
71
+ def delete(self, path: str) -> None:
72
+ r = self._client.delete(path)
73
+ _raise_for_status(r)
74
+
75
+
76
+ class AsyncHttpClient:
77
+ def __init__(self, api_key: str, base_url: str, workspace_id: str | None = None) -> None:
78
+ headers: dict[str, str] = {"X-Api-Key": api_key, "Content-Type": "application/json"}
79
+ if workspace_id:
80
+ headers["X-Workspace-Id"] = workspace_id
81
+ self.workspace_id = workspace_id
82
+ self._client = httpx.AsyncClient(
83
+ base_url=base_url.rstrip("/"),
84
+ headers=headers,
85
+ timeout=_DEFAULT_TIMEOUT,
86
+ )
87
+
88
+ async def aclose(self) -> None:
89
+ await self._client.aclose()
90
+
91
+ async def __aenter__(self) -> "AsyncHttpClient":
92
+ return self
93
+
94
+ async def __aexit__(self, *_: Any) -> None:
95
+ await self.aclose()
96
+
97
+ async def get(self, path: str, params: dict[str, Any] | None = None) -> Any:
98
+ r = await self._client.get(path, params={k: v for k, v in (params or {}).items() if v is not None})
99
+ _raise_for_status(r)
100
+ return r.json() if r.status_code != 204 else None
101
+
102
+ async def post(self, path: str, body: Any = None) -> Any:
103
+ r = await self._client.post(path, json=body)
104
+ _raise_for_status(r)
105
+ return r.json() if r.status_code != 204 else None
106
+
107
+ async def patch(self, path: str, body: Any = None) -> Any:
108
+ r = await self._client.patch(path, json=body)
109
+ _raise_for_status(r)
110
+ return r.json() if r.status_code != 204 else None
111
+
112
+ async def put(self, path: str, body: Any = None) -> Any:
113
+ r = await self._client.put(path, json=body)
114
+ _raise_for_status(r)
115
+ return r.json() if r.status_code != 204 else None
116
+
117
+ async def delete(self, path: str) -> None:
118
+ r = await self._client.delete(path)
119
+ _raise_for_status(r)
paiment/_models.py ADDED
@@ -0,0 +1,221 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Generic, TypeVar
5
+
6
+ T = TypeVar("T")
7
+
8
+
9
+ def _get(d: dict[str, Any], key: str, default: Any = None) -> Any:
10
+ return d.get(key, default)
11
+
12
+
13
+ @dataclass
14
+ class Subscription:
15
+ id: int
16
+ tenant_id: str
17
+ workspace_id: str
18
+ user_id: str
19
+ plan_id: int | None
20
+ plan_name: str
21
+ status: str
22
+ subscription_start: str
23
+ subscription_end: str | None
24
+ billing_period_start: str | None
25
+ billing_period_end: str | None
26
+ trial_start: str | None
27
+ trial_end: str | None
28
+ price: int
29
+ currency: str
30
+ interval: str
31
+ source: str
32
+ created_at: str
33
+
34
+ @classmethod
35
+ def from_dict(cls, d: dict[str, Any]) -> "Subscription":
36
+ return cls(
37
+ id=d["id"],
38
+ tenant_id=d.get("tenantId", ""),
39
+ workspace_id=str(d.get("workspaceId", "")),
40
+ user_id=d.get("userId", ""),
41
+ plan_id=_get(d, "planId"),
42
+ plan_name=d.get("planName", ""),
43
+ status=d.get("status", "Active"),
44
+ subscription_start=d.get("subscriptionStart", ""),
45
+ subscription_end=_get(d, "subscriptionEnd"),
46
+ billing_period_start=_get(d, "billingPeriodStart"),
47
+ billing_period_end=_get(d, "billingPeriodEnd"),
48
+ trial_start=_get(d, "trialStart"),
49
+ trial_end=_get(d, "trialEnd"),
50
+ price=d.get("price", 0),
51
+ currency=d.get("currency", ""),
52
+ interval=d.get("interval", ""),
53
+ source=d.get("source", ""),
54
+ created_at=d.get("createdAt", ""),
55
+ )
56
+
57
+
58
+ @dataclass
59
+ class User:
60
+ id: int
61
+ user_id: str | None
62
+ tenant_id: str | None
63
+ user_name: str | None
64
+ user_email: str | None
65
+ workspace_id: str | None
66
+ created_at: str
67
+ subscriptions: list[Subscription]
68
+ plan_name: str
69
+ price: int
70
+ currency: str
71
+ interval: str
72
+ subscription_date: str
73
+ subscription_end_date: str | None
74
+ source: str
75
+
76
+ @classmethod
77
+ def from_dict(cls, d: dict[str, Any]) -> "User":
78
+ return cls(
79
+ id=d["id"],
80
+ user_id=_get(d, "userId"),
81
+ tenant_id=_get(d, "tenantId"),
82
+ user_name=_get(d, "userName"),
83
+ user_email=_get(d, "userEmail"),
84
+ workspace_id=str(_get(d, "workspaceId")) if _get(d, "workspaceId") is not None else None,
85
+ created_at=d.get("createdAt", ""),
86
+ subscriptions=[Subscription.from_dict(s) for s in d.get("subscriptions", [])],
87
+ plan_name=d.get("planName", ""),
88
+ price=d.get("price", 0),
89
+ currency=d.get("currency", ""),
90
+ interval=d.get("interval", ""),
91
+ subscription_date=d.get("subscriptionDate", ""),
92
+ subscription_end_date=_get(d, "subscriptionEndDate"),
93
+ source=d.get("source", ""),
94
+ )
95
+
96
+
97
+ @dataclass
98
+ class UsageBasedPrice:
99
+ billable_metric: str
100
+ billing_cycle: str
101
+ recurring_period: str | None
102
+ price_model: str
103
+ per_unit: float
104
+ price_adjustments: dict[str, Any] | None = None
105
+
106
+ @classmethod
107
+ def from_dict(cls, d: dict[str, Any]) -> "UsageBasedPrice":
108
+ return cls(
109
+ billable_metric=d.get("billableMetric", ""),
110
+ billing_cycle=d.get("billingCycle", ""),
111
+ recurring_period=_get(d, "recurringPeriod"),
112
+ price_model=d.get("priceModel", "unit-pricing"),
113
+ per_unit=float(d.get("perUnit", 0)),
114
+ price_adjustments=_get(d, "priceAdjustments"),
115
+ )
116
+
117
+
118
+ @dataclass
119
+ class FixedPrice:
120
+ item: str
121
+ price_model: str
122
+ quantity: int
123
+ per_unit: float
124
+
125
+ @classmethod
126
+ def from_dict(cls, d: dict[str, Any]) -> "FixedPrice":
127
+ return cls(
128
+ item=d.get("item", ""),
129
+ price_model=d.get("priceModel", "unit-pricing"),
130
+ quantity=d.get("quantity", 1),
131
+ per_unit=float(d.get("perUnit", 0)),
132
+ )
133
+
134
+
135
+ @dataclass
136
+ class Plan:
137
+ id: int
138
+ name: str
139
+ tenant_id: str
140
+ external_plan_id: str | None
141
+ description: str | None
142
+ currency: str
143
+ list_price_cents: int
144
+ usage_based_prices: list[UsageBasedPrice] = field(default_factory=list)
145
+ fixed_prices: list[FixedPrice] = field(default_factory=list)
146
+
147
+ @classmethod
148
+ def from_dict(cls, d: dict[str, Any]) -> "Plan":
149
+ return cls(
150
+ id=d["id"],
151
+ name=d.get("name", ""),
152
+ tenant_id=d.get("tenantId", ""),
153
+ external_plan_id=_get(d, "externalPlanId"),
154
+ description=_get(d, "description"),
155
+ currency=d.get("currency", ""),
156
+ list_price_cents=d.get("listPriceCents", 0),
157
+ usage_based_prices=[
158
+ UsageBasedPrice.from_dict(p) for p in d.get("usageBasedPrices", [])
159
+ ],
160
+ fixed_prices=[FixedPrice.from_dict(p) for p in d.get("fixedPrices", [])],
161
+ )
162
+
163
+
164
+ @dataclass
165
+ class Usage:
166
+ id: int
167
+ user_id: str
168
+ tenant_id: str | None
169
+ plan_id: int | None
170
+ workspace_id: str | None
171
+ input_tokens: int
172
+ output_tokens: int
173
+ llm_provider: str
174
+ llm_model: str
175
+ feature: str | None
176
+ feature_session: str | None
177
+ ts: int | None
178
+ total_cost: float | None
179
+ formatted_cost: str | None
180
+
181
+ @classmethod
182
+ def from_dict(cls, d: dict[str, Any]) -> "Usage":
183
+ return cls(
184
+ id=d["id"],
185
+ user_id=d.get("userId", ""),
186
+ tenant_id=_get(d, "tenantId"),
187
+ plan_id=_get(d, "planId"),
188
+ workspace_id=str(_get(d, "workspaceId")) if _get(d, "workspaceId") is not None else None,
189
+ input_tokens=d.get("inputTokens", 0),
190
+ output_tokens=d.get("outputTokens", 0),
191
+ llm_provider=d.get("llmProvider", ""),
192
+ llm_model=d.get("llmModel", ""),
193
+ feature=_get(d, "feature"),
194
+ feature_session=_get(d, "featureSession"),
195
+ ts=_get(d, "ts"),
196
+ total_cost=_get(d, "totalCost"),
197
+ formatted_cost=_get(d, "formattedCost"),
198
+ )
199
+
200
+
201
+ @dataclass
202
+ class Pagination:
203
+ current_page: int
204
+ items_per_page: int
205
+ total_items: int
206
+ total_pages: int
207
+
208
+ @classmethod
209
+ def from_dict(cls, d: dict[str, Any]) -> "Pagination":
210
+ return cls(
211
+ current_page=d.get("currentPage", 1),
212
+ items_per_page=d.get("itemsPerPage", 20),
213
+ total_items=d.get("totalItems", 0),
214
+ total_pages=d.get("totalPages", 0),
215
+ )
216
+
217
+
218
+ @dataclass
219
+ class Page(Generic[T]):
220
+ data: list[T]
221
+ pagination: Pagination
File without changes
@@ -0,0 +1,163 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from .._http import AsyncHttpClient, HttpClient
6
+ from .._models import Plan
7
+
8
+
9
+ def _build_plan_body(
10
+ *,
11
+ name: str | None = None,
12
+ currency: str | None = None,
13
+ external_plan_id: str | None = None,
14
+ description: str | None = None,
15
+ list_price_cents: int | None = None,
16
+ tokens: int | None = None,
17
+ usage_based_prices: list[dict[str, Any]] | None = None,
18
+ fixed_prices: list[dict[str, Any]] | None = None,
19
+ ) -> dict[str, Any]:
20
+ body: dict[str, Any] = {}
21
+ if name is not None:
22
+ body["name"] = name
23
+ if currency is not None:
24
+ body["currency"] = currency
25
+ if external_plan_id is not None:
26
+ body["externalPlanId"] = external_plan_id
27
+ if description is not None:
28
+ body["description"] = description
29
+ if list_price_cents is not None:
30
+ body["listPriceCents"] = list_price_cents
31
+ if tokens is not None:
32
+ body["tokens"] = tokens
33
+ if usage_based_prices is not None:
34
+ body["usageBasedPrices"] = usage_based_prices
35
+ if fixed_prices is not None:
36
+ body["fixedPrices"] = fixed_prices
37
+ return body
38
+
39
+
40
+ class PlansResource:
41
+ def __init__(self, http: HttpClient) -> None:
42
+ self._http = http
43
+
44
+ def create(
45
+ self,
46
+ *,
47
+ name: str,
48
+ currency: str,
49
+ external_plan_id: str | None = None,
50
+ description: str | None = None,
51
+ list_price_cents: int | None = None,
52
+ tokens: int | None = None,
53
+ usage_based_prices: list[dict[str, Any]] | None = None,
54
+ fixed_prices: list[dict[str, Any]] | None = None,
55
+ ) -> Plan:
56
+ body = _build_plan_body(
57
+ name=name,
58
+ currency=currency,
59
+ external_plan_id=external_plan_id,
60
+ description=description,
61
+ list_price_cents=list_price_cents,
62
+ tokens=tokens,
63
+ usage_based_prices=usage_based_prices,
64
+ fixed_prices=fixed_prices,
65
+ )
66
+ return Plan.from_dict(self._http.post("/api/plans", body))
67
+
68
+ def list(self) -> list[Plan]:
69
+ return [Plan.from_dict(p) for p in self._http.get("/api/plans")]
70
+
71
+ def get(self, plan_id: int) -> Plan:
72
+ return Plan.from_dict(self._http.get(f"/api/plans/{plan_id}"))
73
+
74
+ def update(
75
+ self,
76
+ plan_id: int,
77
+ *,
78
+ name: str,
79
+ currency: str,
80
+ external_plan_id: str | None = None,
81
+ description: str | None = None,
82
+ list_price_cents: int | None = None,
83
+ tokens: int | None = None,
84
+ usage_based_prices: list[dict[str, Any]] | None = None,
85
+ fixed_prices: list[dict[str, Any]] | None = None,
86
+ ) -> Plan:
87
+ body = _build_plan_body(
88
+ name=name,
89
+ currency=currency,
90
+ external_plan_id=external_plan_id,
91
+ description=description,
92
+ list_price_cents=list_price_cents,
93
+ tokens=tokens,
94
+ usage_based_prices=usage_based_prices,
95
+ fixed_prices=fixed_prices,
96
+ )
97
+ return Plan.from_dict(self._http.put(f"/api/plans/{plan_id}", body))
98
+
99
+ def delete(self, plan_id: int) -> None:
100
+ self._http.delete(f"/api/plans/{plan_id}")
101
+
102
+
103
+ class AsyncPlansResource:
104
+ def __init__(self, http: AsyncHttpClient) -> None:
105
+ self._http = http
106
+
107
+ async def create(
108
+ self,
109
+ *,
110
+ name: str,
111
+ currency: str,
112
+ external_plan_id: str | None = None,
113
+ description: str | None = None,
114
+ list_price_cents: int | None = None,
115
+ tokens: int | None = None,
116
+ usage_based_prices: list[dict[str, Any]] | None = None,
117
+ fixed_prices: list[dict[str, Any]] | None = None,
118
+ ) -> Plan:
119
+ body = _build_plan_body(
120
+ name=name,
121
+ currency=currency,
122
+ external_plan_id=external_plan_id,
123
+ description=description,
124
+ list_price_cents=list_price_cents,
125
+ tokens=tokens,
126
+ usage_based_prices=usage_based_prices,
127
+ fixed_prices=fixed_prices,
128
+ )
129
+ return Plan.from_dict(await self._http.post("/api/plans", body))
130
+
131
+ async def list(self) -> list[Plan]:
132
+ return [Plan.from_dict(p) for p in await self._http.get("/api/plans")]
133
+
134
+ async def get(self, plan_id: int) -> Plan:
135
+ return Plan.from_dict(await self._http.get(f"/api/plans/{plan_id}"))
136
+
137
+ async def update(
138
+ self,
139
+ plan_id: int,
140
+ *,
141
+ name: str,
142
+ currency: str,
143
+ external_plan_id: str | None = None,
144
+ description: str | None = None,
145
+ list_price_cents: int | None = None,
146
+ tokens: int | None = None,
147
+ usage_based_prices: list[dict[str, Any]] | None = None,
148
+ fixed_prices: list[dict[str, Any]] | None = None,
149
+ ) -> Plan:
150
+ body = _build_plan_body(
151
+ name=name,
152
+ currency=currency,
153
+ external_plan_id=external_plan_id,
154
+ description=description,
155
+ list_price_cents=list_price_cents,
156
+ tokens=tokens,
157
+ usage_based_prices=usage_based_prices,
158
+ fixed_prices=fixed_prices,
159
+ )
160
+ return Plan.from_dict(await self._http.put(f"/api/plans/{plan_id}", body))
161
+
162
+ async def delete(self, plan_id: int) -> None:
163
+ await self._http.delete(f"/api/plans/{plan_id}")
@@ -0,0 +1,216 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import Any
5
+ from urllib.parse import urlencode
6
+
7
+ from .._http import AsyncHttpClient, HttpClient
8
+ from .._models import Page, Pagination, Subscription
9
+
10
+
11
+ def _with_workspace(path: str, workspace_id: str | None) -> str:
12
+ if not workspace_id:
13
+ return path
14
+ sep = "&" if "?" in path else "?"
15
+ return f"{path}{sep}{urlencode({'workspaceId': workspace_id})}"
16
+
17
+
18
+ def _resolve_workspace(http: HttpClient | AsyncHttpClient, workspace_id: str | None) -> str | None:
19
+ return workspace_id or http.workspace_id
20
+
21
+
22
+ def _parse_subscription_list(data: Any) -> Page[Subscription]:
23
+ if isinstance(data, dict) and "data" in data:
24
+ return Page(
25
+ data=[Subscription.from_dict(s) for s in data["data"]],
26
+ pagination=Pagination.from_dict(data["pagination"]),
27
+ )
28
+ items = data if isinstance(data, list) else [data]
29
+ return Page(
30
+ data=[Subscription.from_dict(s) for s in items],
31
+ pagination=Pagination(1, len(items), len(items), 1),
32
+ )
33
+
34
+
35
+ class SubscriptionsResource:
36
+ def __init__(self, http: HttpClient) -> None:
37
+ self._http = http
38
+
39
+ def list(
40
+ self,
41
+ *,
42
+ user_id: str | None = None,
43
+ workspace_id: str | None = None,
44
+ status: str | None = None,
45
+ page: int | None = None,
46
+ limit: int | None = None,
47
+ ) -> Page[Subscription]:
48
+ ws = _resolve_workspace(self._http, workspace_id)
49
+ data = self._http.get(
50
+ "/api/subscriptions",
51
+ {"userId": user_id, "workspaceId": ws, "status": status, "page": page, "limit": limit},
52
+ )
53
+ return _parse_subscription_list(data)
54
+
55
+ def get(self, subscription_id: int, *, workspace_id: str | None = None) -> Subscription:
56
+ ws = _resolve_workspace(self._http, workspace_id)
57
+ return Subscription.from_dict(
58
+ self._http.get(_with_workspace(f"/api/subscriptions/{subscription_id}", ws))
59
+ )
60
+
61
+ def create(
62
+ self,
63
+ *,
64
+ user_id: str,
65
+ price: int,
66
+ currency: str,
67
+ interval: str,
68
+ plan_id: int | None = None,
69
+ plan_name: str | None = None,
70
+ user_name: str | None = None,
71
+ user_email: str | None = None,
72
+ status: str = "Active",
73
+ subscription_start: str | None = None,
74
+ subscription_end: str | None = None,
75
+ source: str = "sdk",
76
+ workspace_id: str | None = None,
77
+ ) -> Subscription:
78
+ ws = _resolve_workspace(self._http, workspace_id)
79
+ body = {k: v for k, v in {
80
+ "userId": user_id,
81
+ "userName": user_name,
82
+ "userEmail": user_email,
83
+ "planId": plan_id,
84
+ "planName": plan_name,
85
+ "status": status,
86
+ "subscriptionStart": subscription_start,
87
+ "subscriptionEnd": subscription_end,
88
+ "price": price,
89
+ "currency": currency,
90
+ "interval": interval,
91
+ "source": source,
92
+ }.items() if v is not None}
93
+ return Subscription.from_dict(
94
+ self._http.post(_with_workspace("/api/subscriptions", ws), body)
95
+ )
96
+
97
+ def end(
98
+ self,
99
+ subscription_id: int,
100
+ *,
101
+ subscription_end: str | None = None,
102
+ workspace_id: str | None = None,
103
+ ) -> Subscription:
104
+ ws = _resolve_workspace(self._http, workspace_id)
105
+ body = {"subscriptionEnd": subscription_end} if subscription_end else {}
106
+ return Subscription.from_dict(
107
+ self._http.post(_with_workspace(f"/api/subscriptions/{subscription_id}/end", ws), body)
108
+ )
109
+
110
+ def update(
111
+ self,
112
+ subscription_id: int,
113
+ data: dict[str, Any],
114
+ *,
115
+ workspace_id: str | None = None,
116
+ ) -> Subscription:
117
+ ws = _resolve_workspace(self._http, workspace_id)
118
+ return Subscription.from_dict(
119
+ self._http.patch(_with_workspace(f"/api/subscriptions/{subscription_id}", ws), data)
120
+ )
121
+
122
+ def cancel(self, subscription_id: int, *, workspace_id: str | None = None) -> Subscription:
123
+ now = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
124
+ return self.end(subscription_id, subscription_end=now, workspace_id=workspace_id)
125
+
126
+
127
+ class AsyncSubscriptionsResource:
128
+ def __init__(self, http: AsyncHttpClient) -> None:
129
+ self._http = http
130
+
131
+ async def list(
132
+ self,
133
+ *,
134
+ user_id: str | None = None,
135
+ workspace_id: str | None = None,
136
+ status: str | None = None,
137
+ page: int | None = None,
138
+ limit: int | None = None,
139
+ ) -> Page[Subscription]:
140
+ ws = _resolve_workspace(self._http, workspace_id)
141
+ data = await self._http.get(
142
+ "/api/subscriptions",
143
+ {"userId": user_id, "workspaceId": ws, "status": status, "page": page, "limit": limit},
144
+ )
145
+ return _parse_subscription_list(data)
146
+
147
+ async def get(self, subscription_id: int, *, workspace_id: str | None = None) -> Subscription:
148
+ ws = _resolve_workspace(self._http, workspace_id)
149
+ return Subscription.from_dict(
150
+ await self._http.get(_with_workspace(f"/api/subscriptions/{subscription_id}", ws))
151
+ )
152
+
153
+ async def create(
154
+ self,
155
+ *,
156
+ user_id: str,
157
+ price: int,
158
+ currency: str,
159
+ interval: str,
160
+ plan_id: int | None = None,
161
+ plan_name: str | None = None,
162
+ user_name: str | None = None,
163
+ user_email: str | None = None,
164
+ status: str = "Active",
165
+ subscription_start: str | None = None,
166
+ subscription_end: str | None = None,
167
+ source: str = "sdk",
168
+ workspace_id: str | None = None,
169
+ ) -> Subscription:
170
+ ws = _resolve_workspace(self._http, workspace_id)
171
+ body = {k: v for k, v in {
172
+ "userId": user_id,
173
+ "userName": user_name,
174
+ "userEmail": user_email,
175
+ "planId": plan_id,
176
+ "planName": plan_name,
177
+ "status": status,
178
+ "subscriptionStart": subscription_start,
179
+ "subscriptionEnd": subscription_end,
180
+ "price": price,
181
+ "currency": currency,
182
+ "interval": interval,
183
+ "source": source,
184
+ }.items() if v is not None}
185
+ return Subscription.from_dict(
186
+ await self._http.post(_with_workspace("/api/subscriptions", ws), body)
187
+ )
188
+
189
+ async def end(
190
+ self,
191
+ subscription_id: int,
192
+ *,
193
+ subscription_end: str | None = None,
194
+ workspace_id: str | None = None,
195
+ ) -> Subscription:
196
+ ws = _resolve_workspace(self._http, workspace_id)
197
+ body = {"subscriptionEnd": subscription_end} if subscription_end else {}
198
+ return Subscription.from_dict(
199
+ await self._http.post(_with_workspace(f"/api/subscriptions/{subscription_id}/end", ws), body)
200
+ )
201
+
202
+ async def update(
203
+ self,
204
+ subscription_id: int,
205
+ data: dict[str, Any],
206
+ *,
207
+ workspace_id: str | None = None,
208
+ ) -> Subscription:
209
+ ws = _resolve_workspace(self._http, workspace_id)
210
+ return Subscription.from_dict(
211
+ await self._http.patch(_with_workspace(f"/api/subscriptions/{subscription_id}", ws), data)
212
+ )
213
+
214
+ async def cancel(self, subscription_id: int, *, workspace_id: str | None = None) -> Subscription:
215
+ now = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
216
+ return await self.end(subscription_id, subscription_end=now, workspace_id=workspace_id)
@@ -0,0 +1,130 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import Any
5
+ from urllib.parse import urlencode
6
+
7
+ from .._http import AsyncHttpClient, HttpClient
8
+ from .._models import Page, Pagination, Usage
9
+
10
+
11
+ def _resolve_workspace(http: HttpClient | AsyncHttpClient, workspace_id: str | None) -> str | None:
12
+ return workspace_id or http.workspace_id
13
+
14
+
15
+ def _parse_usage_list(data: Any) -> Page[Usage]:
16
+ if isinstance(data, dict) and "data" in data:
17
+ return Page(
18
+ data=[Usage.from_dict(u) for u in data["data"]],
19
+ pagination=Pagination.from_dict(data["pagination"]),
20
+ )
21
+ items = data if isinstance(data, list) else [data]
22
+ return Page(data=[Usage.from_dict(u) for u in items], pagination=Pagination(1, len(items), len(items), 1))
23
+
24
+
25
+ class UsageResource:
26
+ def __init__(self, http: HttpClient) -> None:
27
+ self._http = http
28
+
29
+ def report(
30
+ self,
31
+ *,
32
+ user_id: str,
33
+ input_tokens: int,
34
+ output_tokens: int,
35
+ provider: str,
36
+ model: str,
37
+ plan_id: int | None = None,
38
+ feature: str | None = None,
39
+ feature_session: str | None = None,
40
+ ts: int | None = None,
41
+ workspace_id: str | None = None,
42
+ ) -> Usage:
43
+ ws = _resolve_workspace(self._http, workspace_id)
44
+ path = "/api/usages"
45
+ if ws:
46
+ path = f"{path}?{urlencode({'workspaceId': ws})}"
47
+
48
+ body: dict[str, Any] = {
49
+ "userId": user_id,
50
+ "inputTokens": input_tokens,
51
+ "outputTokens": output_tokens,
52
+ "llmProvider": provider,
53
+ "llmModel": model,
54
+ "ts": ts if ts is not None else int(time.time() * 1000),
55
+ }
56
+ if plan_id is not None:
57
+ body["planId"] = plan_id
58
+ if feature is not None:
59
+ body["feature"] = feature
60
+ if feature_session is not None:
61
+ body["featureSession"] = feature_session
62
+
63
+ return Usage.from_dict(self._http.post(path, body))
64
+
65
+ def list(
66
+ self,
67
+ *,
68
+ user_id: str | None = None,
69
+ workspace_id: str | None = None,
70
+ page: int | None = None,
71
+ limit: int | None = None,
72
+ ) -> Page[Usage]:
73
+ ws = _resolve_workspace(self._http, workspace_id)
74
+ return _parse_usage_list(
75
+ self._http.get("/api/usages", {"userId": user_id, "workspaceId": ws, "page": page, "limit": limit})
76
+ )
77
+
78
+
79
+ class AsyncUsageResource:
80
+ def __init__(self, http: AsyncHttpClient) -> None:
81
+ self._http = http
82
+
83
+ async def report(
84
+ self,
85
+ *,
86
+ user_id: str,
87
+ input_tokens: int,
88
+ output_tokens: int,
89
+ provider: str,
90
+ model: str,
91
+ plan_id: int | None = None,
92
+ feature: str | None = None,
93
+ feature_session: str | None = None,
94
+ ts: int | None = None,
95
+ workspace_id: str | None = None,
96
+ ) -> Usage:
97
+ ws = _resolve_workspace(self._http, workspace_id)
98
+ path = "/api/usages"
99
+ if ws:
100
+ path = f"{path}?{urlencode({'workspaceId': ws})}"
101
+
102
+ body: dict[str, Any] = {
103
+ "userId": user_id,
104
+ "inputTokens": input_tokens,
105
+ "outputTokens": output_tokens,
106
+ "llmProvider": provider,
107
+ "llmModel": model,
108
+ "ts": ts if ts is not None else int(time.time() * 1000),
109
+ }
110
+ if plan_id is not None:
111
+ body["planId"] = plan_id
112
+ if feature is not None:
113
+ body["feature"] = feature
114
+ if feature_session is not None:
115
+ body["featureSession"] = feature_session
116
+
117
+ return Usage.from_dict(await self._http.post(path, body))
118
+
119
+ async def list(
120
+ self,
121
+ *,
122
+ user_id: str | None = None,
123
+ workspace_id: str | None = None,
124
+ page: int | None = None,
125
+ limit: int | None = None,
126
+ ) -> Page[Usage]:
127
+ ws = _resolve_workspace(self._http, workspace_id)
128
+ return _parse_usage_list(
129
+ await self._http.get("/api/usages", {"userId": user_id, "workspaceId": ws, "page": page, "limit": limit})
130
+ )
@@ -0,0 +1,219 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import Any
5
+ from urllib.parse import quote
6
+
7
+ from .._http import AsyncHttpClient, HttpClient
8
+ from .._models import Page, Pagination, User
9
+
10
+
11
+ def _build_user_body(
12
+ *,
13
+ user_id: str | None = None,
14
+ user_name: str | None = None,
15
+ user_email: str | None = None,
16
+ plan_name: str | None = None,
17
+ plan_id: int | None = None,
18
+ price: int | None = None,
19
+ currency: str | None = None,
20
+ interval: str | None = None,
21
+ subscription_date: str | None = None,
22
+ subscription_end_date: str | None = None,
23
+ source: str | None = None,
24
+ ) -> dict[str, Any]:
25
+ body: dict[str, Any] = {}
26
+ if user_id is not None:
27
+ body["userId"] = user_id
28
+ if user_name is not None:
29
+ body["userName"] = user_name
30
+ if user_email is not None:
31
+ body["userEmail"] = user_email
32
+ if plan_name is not None:
33
+ body["planName"] = plan_name
34
+ if plan_id is not None:
35
+ body["planId"] = plan_id
36
+ if price is not None:
37
+ body["price"] = price
38
+ if currency is not None:
39
+ body["currency"] = currency
40
+ if interval is not None:
41
+ body["interval"] = interval
42
+ if subscription_date is not None:
43
+ body["subscriptionDate"] = subscription_date
44
+ if subscription_end_date is not None:
45
+ body["subscriptionEndDate"] = subscription_end_date
46
+ if source is not None:
47
+ body["source"] = source
48
+ return body
49
+
50
+
51
+ def _parse_user_response(data: Any) -> User | Page[User]:
52
+ if isinstance(data, dict) and "data" in data and "pagination" in data:
53
+ return Page(
54
+ data=[User.from_dict(u) for u in data["data"]],
55
+ pagination=Pagination.from_dict(data["pagination"]),
56
+ )
57
+ if isinstance(data, list):
58
+ return Page(data=[User.from_dict(u) for u in data], pagination=Pagination(1, len(data), len(data), 1))
59
+ return User.from_dict(data)
60
+
61
+
62
+ class UsersResource:
63
+ def __init__(self, http: HttpClient) -> None:
64
+ self._http = http
65
+
66
+ def create(
67
+ self,
68
+ *,
69
+ user_id: str,
70
+ plan_name: str,
71
+ price: int,
72
+ currency: str,
73
+ interval: str,
74
+ user_name: str | None = None,
75
+ user_email: str | None = None,
76
+ plan_id: int | None = None,
77
+ subscription_date: str | None = None,
78
+ source: str = "sdk",
79
+ ) -> User:
80
+ body = _build_user_body(
81
+ user_id=user_id,
82
+ user_name=user_name,
83
+ user_email=user_email,
84
+ plan_name=plan_name,
85
+ plan_id=plan_id,
86
+ price=price,
87
+ currency=currency,
88
+ interval=interval,
89
+ subscription_date=subscription_date,
90
+ source=source,
91
+ )
92
+ body.setdefault("subscriptionDate", time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()))
93
+ return User.from_dict(self._http.post("/api/users", body))
94
+
95
+ def list(
96
+ self,
97
+ *,
98
+ page: int | None = None,
99
+ limit: int | None = None,
100
+ plan_name: str | None = None,
101
+ ) -> Page[User]:
102
+ data = self._http.get("/api/users", {"page": page, "limit": limit, "planName": plan_name})
103
+ result = _parse_user_response(data)
104
+ return result if isinstance(result, Page) else Page(data=[result], pagination=Pagination(1, 1, 1, 1))
105
+
106
+ def get(self, user_id: str) -> User:
107
+ return User.from_dict(self._http.get(f"/api/users/external/{quote(user_id, safe='')}"))
108
+
109
+ def update(
110
+ self,
111
+ user_id: str,
112
+ *,
113
+ user_name: str | None = None,
114
+ user_email: str | None = None,
115
+ plan_name: str | None = None,
116
+ plan_id: int | None = None,
117
+ price: int | None = None,
118
+ currency: str | None = None,
119
+ interval: str | None = None,
120
+ subscription_date: str | None = None,
121
+ subscription_end_date: str | None = None,
122
+ source: str | None = None,
123
+ ) -> User:
124
+ body = _build_user_body(
125
+ user_name=user_name,
126
+ user_email=user_email,
127
+ plan_name=plan_name,
128
+ plan_id=plan_id,
129
+ price=price,
130
+ currency=currency,
131
+ interval=interval,
132
+ subscription_date=subscription_date,
133
+ subscription_end_date=subscription_end_date,
134
+ source=source,
135
+ )
136
+ return User.from_dict(self._http.put(f"/api/users/external/{quote(user_id, safe='')}", body))
137
+
138
+ def delete(self, user_id: str) -> None:
139
+ self._http.delete(f"/api/users/external/{quote(user_id, safe='')}")
140
+
141
+
142
+ class AsyncUsersResource:
143
+ def __init__(self, http: AsyncHttpClient) -> None:
144
+ self._http = http
145
+
146
+ async def create(
147
+ self,
148
+ *,
149
+ user_id: str,
150
+ plan_name: str,
151
+ price: int,
152
+ currency: str,
153
+ interval: str,
154
+ user_name: str | None = None,
155
+ user_email: str | None = None,
156
+ plan_id: int | None = None,
157
+ subscription_date: str | None = None,
158
+ source: str = "sdk",
159
+ ) -> User:
160
+ body = _build_user_body(
161
+ user_id=user_id,
162
+ user_name=user_name,
163
+ user_email=user_email,
164
+ plan_name=plan_name,
165
+ plan_id=plan_id,
166
+ price=price,
167
+ currency=currency,
168
+ interval=interval,
169
+ subscription_date=subscription_date,
170
+ source=source,
171
+ )
172
+ body.setdefault("subscriptionDate", time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()))
173
+ return User.from_dict(await self._http.post("/api/users", body))
174
+
175
+ async def list(
176
+ self,
177
+ *,
178
+ page: int | None = None,
179
+ limit: int | None = None,
180
+ plan_name: str | None = None,
181
+ ) -> Page[User]:
182
+ data = await self._http.get("/api/users", {"page": page, "limit": limit, "planName": plan_name})
183
+ result = _parse_user_response(data)
184
+ return result if isinstance(result, Page) else Page(data=[result], pagination=Pagination(1, 1, 1, 1))
185
+
186
+ async def get(self, user_id: str) -> User:
187
+ return User.from_dict(await self._http.get(f"/api/users/external/{quote(user_id, safe='')}"))
188
+
189
+ async def update(
190
+ self,
191
+ user_id: str,
192
+ *,
193
+ user_name: str | None = None,
194
+ user_email: str | None = None,
195
+ plan_name: str | None = None,
196
+ plan_id: int | None = None,
197
+ price: int | None = None,
198
+ currency: str | None = None,
199
+ interval: str | None = None,
200
+ subscription_date: str | None = None,
201
+ subscription_end_date: str | None = None,
202
+ source: str | None = None,
203
+ ) -> User:
204
+ body = _build_user_body(
205
+ user_name=user_name,
206
+ user_email=user_email,
207
+ plan_name=plan_name,
208
+ plan_id=plan_id,
209
+ price=price,
210
+ currency=currency,
211
+ interval=interval,
212
+ subscription_date=subscription_date,
213
+ subscription_end_date=subscription_end_date,
214
+ source=source,
215
+ )
216
+ return User.from_dict(await self._http.put(f"/api/users/external/{quote(user_id, safe='')}", body))
217
+
218
+ async def delete(self, user_id: str) -> None:
219
+ await self._http.delete(f"/api/users/external/{quote(user_id, safe='')}")
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.4
2
+ Name: paiment
3
+ Version: 0.2.0
4
+ Summary: Official Python SDK for the Paiment billing API
5
+ Author: Paiment
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://paiment.tech
8
+ Project-URL: Documentation, https://paiment.tech
9
+ Keywords: paiment,billing,sdk,llm,usage
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: httpx>=0.27.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8.0; extra == "dev"
24
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
25
+ Requires-Dist: mypy>=1.9; extra == "dev"
26
+ Requires-Dist: build; extra == "dev"
27
+ Requires-Dist: twine; extra == "dev"
28
+
29
+ # paiment (Python SDK)
30
+
31
+ Official Python SDK for the [Paiment](https://paiment.tech) billing API.
32
+
33
+ > **npm users:** install `@paiment/sdk` instead. This package is the Python equivalent.
34
+
35
+ Use it in your **backend** to register subscribers, report LLM usage, and manage plans.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install paiment
41
+ ```
42
+
43
+ Python 3.9+.
44
+
45
+ ## Setup
46
+
47
+ Get an API key from **Settings → API Keys**.
48
+
49
+ | Key format | Notes |
50
+ |------------|--------|
51
+ | `tenantId:workspaceId:secret` | **Recommended** |
52
+ | `tenantId:secret` | Pass `workspace_id` on the client |
53
+
54
+ ```python
55
+ from paiment import PaimentClient
56
+
57
+ client = PaimentClient(api_key="tenantId:workspaceId:secret")
58
+ # client = PaimentClient(api_key="...", base_url="http://localhost:5246")
59
+ ```
60
+
61
+ ## Quick start
62
+
63
+ ```python
64
+ from paiment import PaimentClient
65
+
66
+ with PaimentClient(api_key="tenantId:workspaceId:secret") as client:
67
+ sub = client.subscriptions.create(
68
+ user_id="user_123",
69
+ plan_name="Pro",
70
+ price=2999,
71
+ currency="USD",
72
+ interval="monthly",
73
+ )
74
+
75
+ client.usage.report(
76
+ user_id="user_123",
77
+ input_tokens=1250,
78
+ output_tokens=340,
79
+ provider="openai",
80
+ model="gpt-4o",
81
+ plan_id=sub.plan_id,
82
+ )
83
+
84
+ client.subscriptions.end(sub.id)
85
+ ```
86
+
87
+ ### Async
88
+
89
+ ```python
90
+ from paiment import AsyncPaimentClient
91
+
92
+ async with AsyncPaimentClient(api_key="tenantId:workspaceId:secret") as client:
93
+ sub = await client.subscriptions.create(
94
+ user_id="user_123",
95
+ plan_name="Pro",
96
+ price=2999,
97
+ currency="USD",
98
+ interval="monthly",
99
+ )
100
+ await client.usage.report(
101
+ user_id="user_123",
102
+ input_tokens=1250,
103
+ output_tokens=340,
104
+ provider="openai",
105
+ model="gpt-4o",
106
+ )
107
+ ```
108
+
109
+ ## API reference
110
+
111
+ ### Subscriptions
112
+
113
+ | Method | Description |
114
+ |--------|-------------|
115
+ | `create(user_id, plan_name, price, currency, interval, ...)` | New subscription |
116
+ | `list(user_id=..., status=...)` | List subscriptions |
117
+ | `get(subscription_id)` | Get by id |
118
+ | `update(subscription_id, data)` | Patch subscription |
119
+ | `end(subscription_id)` | Close subscription |
120
+ | `cancel(subscription_id)` | End immediately |
121
+
122
+ ### Users
123
+
124
+ | Method | Description |
125
+ |--------|-------------|
126
+ | `create(user_id, plan_name, price, currency, interval, ...)` | User + subscription shortcut |
127
+ | `get(user_id)` | Get by your user id |
128
+ | `list(plan_name=..., page=..., limit=...)` | List users |
129
+ | `update(user_id, ...)` | Partial update |
130
+ | `delete(user_id)` | Delete user |
131
+
132
+ ### Usage
133
+
134
+ | Method | Description |
135
+ |--------|-------------|
136
+ | `report(user_id, input_tokens, output_tokens, provider, model, ...)` | Record usage |
137
+ | `list(user_id=..., page=..., limit=...)` | List usage rows |
138
+
139
+ ### Plans
140
+
141
+ | Method | Description |
142
+ |--------|-------------|
143
+ | `create(name, currency, list_price_cents=..., ...)` | Create plan |
144
+ | `list()` / `get(id)` / `update(id, ...)` / `delete(id)` | CRUD |
145
+
146
+ ## Fields
147
+
148
+ | Field | Meaning |
149
+ |-------|---------|
150
+ | `user_id` | Your identifier for the end-user |
151
+ | `plan_name` | Plan catalog name |
152
+ | `price` | Subscription price in **cents** |
153
+ | `list_price_cents` | Catalog list price on plans only |
154
+
155
+ ## Errors
156
+
157
+ ```python
158
+ from paiment import PaimentAuthError, PaimentNotFoundError, PaimentValidationError, PaimentError
159
+ ```
160
+
161
+ | Class | HTTP status |
162
+ |-------|-------------|
163
+ | `PaimentAuthError` | 401 |
164
+ | `PaimentNotFoundError` | 404 |
165
+ | `PaimentValidationError` | 400 |
166
+ | `PaimentError` | other |
@@ -0,0 +1,13 @@
1
+ paiment/__init__.py,sha256=CFxl3p38owsvaL1Np57Pp5bdMm6VF4QsV_NH6EVqauw,2333
2
+ paiment/_errors.py,sha256=CU0JTGvp1odMPdXed9FSSalwCJyV6SEFYOhc5nnc4Ec,772
3
+ paiment/_http.py,sha256=VVFJKvLHGkndiH0zBxoJLn76DT0gYv8UY1j4Owho40U,4028
4
+ paiment/_models.py,sha256=TNl4Xo0cRxecWUW7fHvyCTdkbTKe1KjKF1sjxz2MMwk,6511
5
+ paiment/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ paiment/resources/plans.py,sha256=d0NXso9Q_11MJXwe0yxuibNGL_ha3mjE0eFPmmO9Ep8,5310
7
+ paiment/resources/subscriptions.py,sha256=CCknfCZCIv_TssMjJG1JF6mJ-HZol5v2wB0bkyi90h4,7514
8
+ paiment/resources/usage.py,sha256=u2Ucb02gkJykEFYkTi-ntG63nGPEolnzhDKQNAOV-ak,4082
9
+ paiment/resources/users.py,sha256=uc3dhIrK-tGTueLghpf5R8aNZdogUeHn8GJN52i1kkE,7227
10
+ paiment-0.2.0.dist-info/METADATA,sha256=gvVRcXuu6EtEwx7F2lkugcQo1Rz3n7P44Ib-FV2QuQY,4540
11
+ paiment-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
12
+ paiment-0.2.0.dist-info/top_level.txt,sha256=GtbfxyFcWDyErOY1gORz2Tz1us6Gi8AUKOP5LlZ4uRk,8
13
+ paiment-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ paiment