solvapay-python 0.7.2__py3-none-any.whl → 0.9.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.
Files changed (46) hide show
  1. solvapay/__init__.py +31 -1
  2. solvapay/_async_client.py +126 -184
  3. solvapay/_http.py +16 -215
  4. solvapay/_stability.py +60 -0
  5. solvapay/_transport/__init__.py +106 -0
  6. solvapay/_transport/_recipe.py +63 -0
  7. solvapay/_transport/httpx_transport.py +401 -0
  8. solvapay/_transport/middleware.py +409 -0
  9. solvapay/adapters/__init__.py +3 -0
  10. solvapay/adapters/asgi.py +119 -0
  11. solvapay/adapters/langchain.py +88 -0
  12. solvapay/adapters/mcp.py +158 -0
  13. solvapay/client.py +107 -182
  14. solvapay/idempotency.py +25 -4
  15. solvapay/langchain.py +9 -61
  16. solvapay/operations/__init__.py +7 -0
  17. solvapay/operations/_registry.py +117 -0
  18. solvapay/operations/checkout.py +75 -0
  19. solvapay/operations/customers.py +256 -0
  20. solvapay/operations/limits.py +77 -0
  21. solvapay/operations/merchant.py +71 -0
  22. solvapay/operations/plans.py +167 -0
  23. solvapay/operations/products.py +141 -0
  24. solvapay/operations/purchases.py +103 -0
  25. solvapay/operations/usage.py +75 -0
  26. solvapay/paywall/__init__.py +32 -0
  27. solvapay/paywall/core.py +152 -0
  28. solvapay/paywall/decorators.py +126 -0
  29. solvapay/paywall/meta.py +18 -0
  30. solvapay/paywall/policy.py +14 -0
  31. solvapay/paywall/resolvers.py +60 -0
  32. solvapay/paywall/state.py +23 -0
  33. solvapay/webhooks/__init__.py +43 -0
  34. solvapay/webhooks/envelope.py +16 -0
  35. solvapay/webhooks/pipeline.py +77 -0
  36. solvapay/webhooks/replay.py +74 -0
  37. solvapay/webhooks/rotation.py +36 -0
  38. solvapay/webhooks/sign.py +37 -0
  39. solvapay/{webhooks.py → webhooks/verify.py} +4 -22
  40. solvapay_python-0.9.0.dist-info/METADATA +318 -0
  41. solvapay_python-0.9.0.dist-info/RECORD +50 -0
  42. solvapay/paywall.py +0 -153
  43. solvapay_python-0.7.2.dist-info/METADATA +0 -357
  44. solvapay_python-0.7.2.dist-info/RECORD +0 -19
  45. {solvapay_python-0.7.2.dist-info → solvapay_python-0.9.0.dist-info}/WHEEL +0 -0
  46. {solvapay_python-0.7.2.dist-info → solvapay_python-0.9.0.dist-info}/licenses/LICENSE +0 -0
solvapay/__init__.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from solvapay import paywall
6
6
  from solvapay._async_client import AsyncSolvaPay
7
+ from solvapay._stability import MANIFEST, deprecated, experimental, stable
7
8
  from solvapay.client import SolvaPay
8
9
  from solvapay.events import (
9
10
  CheckoutSessionCreated,
@@ -38,7 +39,33 @@ from solvapay.models import BalanceResponse, Merchant, Plan, PlatformConfig, Pro
38
39
  from solvapay.paywall import PaywallRequired
39
40
  from solvapay.webhooks import verify_webhook
40
41
 
42
+ # Register stable exports in MANIFEST (HLD V1.2).
43
+ # stable(X) returns X unchanged — isinstance() continues to work (HLD SM1).
44
+ stable(SolvaPay)
45
+ stable(AsyncSolvaPay)
46
+ stable(SolvaPayError)
47
+ stable(APIError)
48
+ stable(AuthenticationError)
49
+ stable(PermissionError)
50
+ stable(NotFoundError)
51
+ stable(RateLimitError)
52
+ stable(InvalidRequestError)
53
+ stable(APIServerError)
54
+ stable(APIConnectionError)
55
+ stable(APITimeoutError)
56
+ stable(PaywallRequired)
57
+ stable(verify_webhook)
58
+ stable(BalanceResponse)
59
+ stable(Product)
60
+ stable(Plan)
61
+ stable(Merchant)
62
+ stable(PlatformConfig)
63
+ stable(WebhookEvent)
64
+ # SolvaPayAPIError is a back-compat alias — deprecated in v1.0, removed v2.0
65
+ deprecated(removed_in="2.0")(SolvaPayAPIError)
66
+
41
67
  __all__ = [
68
+ "MANIFEST",
42
69
  "APIConnectionError",
43
70
  "APIError",
44
71
  "APIServerError",
@@ -72,7 +99,10 @@ __all__ = [
72
99
  "SolvaPayAPIError",
73
100
  "SolvaPayError",
74
101
  "WebhookEvent",
102
+ "deprecated",
103
+ "experimental",
75
104
  "paywall",
105
+ "stable",
76
106
  "verify_webhook",
77
107
  ]
78
- __version__ = "0.7.2"
108
+ __version__ = "0.9.0"
solvapay/_async_client.py CHANGED
@@ -7,37 +7,52 @@ Use `async with AsyncSolvaPay() as sv: ...` for proper teardown.
7
7
  from __future__ import annotations
8
8
 
9
9
  import logging
10
- import time
10
+ import warnings
11
11
  from typing import Any
12
12
 
13
13
  from solvapay._config import resolve_api_key, resolve_base_url
14
- from solvapay._http import AsyncHttpClient, _RequestSpec
15
- from solvapay.exceptions import SolvaPayAPIError
14
+ from solvapay._http import AsyncHttpClient
16
15
  from solvapay.models import (
17
16
  BalanceResponse,
18
- CancelPurchaseRequest,
19
- CheckLimitsRequest,
20
17
  CheckoutSession,
21
- CheckoutSessionRequest,
22
- CloneProductRequest,
23
- CreateCustomerRequest,
24
- CreatePlanRequest,
25
- CreateProductRequest,
26
18
  Customer,
27
19
  LimitResponse,
28
20
  Merchant,
29
21
  Plan,
30
22
  PlatformConfig,
31
23
  Product,
32
- TrackUsageRequest,
33
- UpdateCustomerRequest,
34
- UpdatePlanRequest,
35
24
  )
25
+ from solvapay.operations.checkout import CheckoutOperations
26
+ from solvapay.operations.customers import CustomersOperations
27
+ from solvapay.operations.limits import LimitsOperations
28
+ from solvapay.operations.merchant import MerchantOperations
29
+ from solvapay.operations.plans import PlansOperations
30
+ from solvapay.operations.products import ProductsOperations
31
+ from solvapay.operations.purchases import PurchasesOperations
32
+ from solvapay.operations.usage import UsageOperations
33
+
34
+
35
+ def _shim_warn(new: str) -> None:
36
+ warnings.warn(
37
+ f"Flat method deprecated; use {new} instead",
38
+ DeprecationWarning,
39
+ stacklevel=3,
40
+ )
36
41
 
37
42
 
38
43
  class AsyncSolvaPay:
39
44
  """Async SolvaPay API client.
40
45
 
46
+ Resource namespaces (v0.8+):
47
+ sv.customers.aensure / aget / aupdate / abalance
48
+ sv.checkout.acreate_session
49
+ sv.limits.acheck
50
+ sv.purchases.acancel / areactivate
51
+ sv.usage.atrack
52
+ sv.products.alist / aget / acreate / adelete / aclone
53
+ sv.plans.alist / acreate / aupdate / adelete
54
+ sv.merchant.aget / aget_platform_config
55
+
41
56
  Args:
42
57
  api_key: SolvaPay secret key. Falls back to SOLVAPAY_SECRET_KEY env var.
43
58
  base_url: API base URL. Falls back to SOLVAPAY_API_BASE_URL env var,
@@ -46,7 +61,7 @@ class AsyncSolvaPay:
46
61
 
47
62
  Example:
48
63
  >>> async with AsyncSolvaPay() as sv:
49
- ... session = await sv.create_checkout_session(
64
+ ... session = await sv.checkout.acreate_session(
50
65
  ... customer_ref="cus_123", product_ref="prd_0QKI8NHF"
51
66
  ... )
52
67
  """
@@ -58,23 +73,56 @@ class AsyncSolvaPay:
58
73
  base_url: str | None = None,
59
74
  timeout: float = 30.0,
60
75
  logger: logging.Logger | None = None,
76
+ api_version: str | None = "2026-05-22",
61
77
  ) -> None:
62
78
  self._http = AsyncHttpClient(
63
79
  api_key=resolve_api_key(api_key),
64
80
  base_url=resolve_base_url(base_url),
65
81
  timeout=timeout,
66
82
  logger=logger,
83
+ api_version=api_version,
67
84
  )
85
+ # Eager namespace construction (HLD RN1).
86
+ _t = self._http._transport
87
+ self.customers = CustomersOperations(sync_transport=None, async_transport=_t)
88
+ self.checkout = CheckoutOperations(sync_transport=None, async_transport=_t)
89
+ self.limits = LimitsOperations(sync_transport=None, async_transport=_t)
90
+ self.purchases = PurchasesOperations(sync_transport=None, async_transport=_t)
91
+ self.usage = UsageOperations(sync_transport=None, async_transport=_t)
92
+ self.products = ProductsOperations(sync_transport=None, async_transport=_t)
93
+ self.plans = PlansOperations(sync_transport=None, async_transport=_t)
94
+ self.merchant = MerchantOperations(sync_transport=None, async_transport=_t)
95
+ self._closed = False
68
96
 
69
97
  async def aclose(self) -> None:
98
+ self._closed = True
70
99
  await self._http.aclose()
71
100
 
101
+ def __del__(self) -> None:
102
+ if not self._closed:
103
+ import asyncio
104
+ import warnings
105
+
106
+ try:
107
+ loop = asyncio.get_event_loop()
108
+ except RuntimeError:
109
+ return
110
+ if loop.is_running():
111
+ warnings.warn(
112
+ "AsyncSolvaPay was not closed. "
113
+ "Use `async with AsyncSolvaPay(...)` or call `await sv.aclose()`.",
114
+ ResourceWarning,
115
+ stacklevel=2,
116
+ )
117
+
72
118
  async def __aenter__(self) -> AsyncSolvaPay:
73
119
  return self
74
120
 
75
121
  async def __aexit__(self, *_: object) -> None:
76
122
  await self.aclose()
77
123
 
124
+ # ── Deprecated flat shims — removed in v2.0 ──
125
+
78
126
  async def create_checkout_session(
79
127
  self,
80
128
  *,
@@ -84,21 +132,14 @@ class AsyncSolvaPay:
84
132
  return_url: str | None = None,
85
133
  idempotency_key: str | None = None,
86
134
  ) -> CheckoutSession:
87
- req = CheckoutSessionRequest(
135
+ _shim_warn("sv.checkout.acreate_session()")
136
+ return await self.checkout.acreate_session(
88
137
  customer_ref=customer_ref,
89
138
  product_ref=product_ref,
90
139
  plan_ref=plan_ref,
91
140
  return_url=return_url,
141
+ idempotency_key=idempotency_key,
92
142
  )
93
- data = await self._http.send(
94
- _RequestSpec(
95
- "POST",
96
- "/v1/sdk/checkout-sessions",
97
- json=req.model_dump(by_alias=True, exclude_none=True),
98
- idempotency_key=idempotency_key,
99
- )
100
- )
101
- return CheckoutSession.model_validate(data)
102
143
 
103
144
  async def ensure_customer(
104
145
  self,
@@ -109,35 +150,14 @@ class AsyncSolvaPay:
109
150
  name: str | None = None,
110
151
  idempotency_key: str | None = None,
111
152
  ) -> str:
112
- lookup_ref = external_ref or customer_ref
113
- try:
114
- existing = await self._http.send(
115
- _RequestSpec("GET", "/v1/sdk/customers", params={"externalRef": lookup_ref})
116
- )
117
- ref = existing.get("reference") or existing.get("customerRef")
118
- if ref:
119
- return str(ref)
120
- except SolvaPayAPIError as exc:
121
- if exc.status_code != 404:
122
- raise
123
-
124
- req = CreateCustomerRequest(
125
- email=email or f"{customer_ref}-{int(time.time())}@auto-created.local",
126
- external_ref=lookup_ref,
153
+ _shim_warn("sv.customers.aensure()")
154
+ return await self.customers.aensure(
155
+ customer_ref,
156
+ external_ref,
157
+ email=email,
127
158
  name=name,
159
+ idempotency_key=idempotency_key,
128
160
  )
129
- created = await self._http.send(
130
- _RequestSpec(
131
- "POST",
132
- "/v1/sdk/customers",
133
- json=req.model_dump(by_alias=True, exclude_none=True),
134
- idempotency_key=idempotency_key,
135
- )
136
- )
137
- ref = created.get("reference") or created.get("customerRef")
138
- if not ref:
139
- raise SolvaPayAPIError(200, f"customer create returned no reference: {created!r}")
140
- return str(ref)
141
161
 
142
162
  async def get_customer(
143
163
  self,
@@ -146,19 +166,8 @@ class AsyncSolvaPay:
146
166
  external_ref: str | None = None,
147
167
  email: str | None = None,
148
168
  ) -> Customer:
149
- if customer_ref:
150
- data = await self._http.send(_RequestSpec("GET", f"/v1/sdk/customers/{customer_ref}"))
151
- elif external_ref:
152
- data = await self._http.send(
153
- _RequestSpec("GET", "/v1/sdk/customers", params={"externalRef": external_ref})
154
- )
155
- elif email:
156
- data = await self._http.send(
157
- _RequestSpec("GET", "/v1/sdk/customers", params={"email": email})
158
- )
159
- else:
160
- raise ValueError("Must provide customer_ref, external_ref, or email")
161
- return Customer.model_validate(data)
169
+ _shim_warn("sv.customers.aget()")
170
+ return await self.customers.aget(customer_ref, external_ref=external_ref, email=email)
162
171
 
163
172
  async def check_limits(
164
173
  self,
@@ -169,19 +178,14 @@ class AsyncSolvaPay:
169
178
  meter_name: str | None = None,
170
179
  usage_type: str | None = None,
171
180
  ) -> LimitResponse:
172
- req = CheckLimitsRequest(
181
+ _shim_warn("sv.limits.acheck()")
182
+ return await self.limits.acheck(
173
183
  customer_ref=customer_ref,
174
184
  product_ref=product_ref,
175
185
  plan_ref=plan_ref,
176
186
  meter_name=meter_name,
177
187
  usage_type=usage_type,
178
188
  )
179
- data = await self._http.send(
180
- _RequestSpec(
181
- "POST", "/v1/sdk/limits", json=req.model_dump(by_alias=True, exclude_none=True)
182
- )
183
- )
184
- return LimitResponse.model_validate(data)
185
189
 
186
190
  async def track_usage(
187
191
  self,
@@ -192,20 +196,13 @@ class AsyncSolvaPay:
192
196
  units: float,
193
197
  idempotency_key: str | None = None,
194
198
  ) -> dict[str, Any]:
195
- """Record usage against a meter. Maps to POST /v1/sdk/usages."""
196
- req = TrackUsageRequest(
199
+ _shim_warn("sv.usage.atrack()")
200
+ return await self.usage.atrack(
197
201
  customer_ref=customer_ref,
198
202
  product_ref=product_ref,
199
203
  meter_name=meter_name,
200
204
  units=units,
201
- )
202
- return await self._http.send(
203
- _RequestSpec(
204
- "POST",
205
- "/v1/sdk/usages",
206
- json=req.model_dump(by_alias=True, exclude_none=True),
207
- idempotency_key=idempotency_key,
208
- )
205
+ idempotency_key=idempotency_key,
209
206
  )
210
207
 
211
208
  async def update_customer(
@@ -216,23 +213,14 @@ class AsyncSolvaPay:
216
213
  name: str | None = None,
217
214
  external_ref: str | None = None,
218
215
  ) -> Customer:
219
- """Update customer fields. Maps to PATCH /v1/sdk/customers/{ref}."""
220
- req = UpdateCustomerRequest(email=email, name=name, external_ref=external_ref)
221
- data = await self._http.send(
222
- _RequestSpec(
223
- "PATCH",
224
- f"/v1/sdk/customers/{customer_ref}",
225
- json=req.model_dump(by_alias=True, exclude_none=True),
226
- )
216
+ _shim_warn("sv.customers.aupdate()")
217
+ return await self.customers.aupdate(
218
+ customer_ref, email=email, name=name, external_ref=external_ref
227
219
  )
228
- return Customer.model_validate(data)
229
220
 
230
221
  async def get_customer_balance(self, customer_ref: str) -> BalanceResponse:
231
- """Get credit balance for a customer. Maps to GET /v1/sdk/customers/{ref}/balance."""
232
- data = await self._http.send(
233
- _RequestSpec("GET", f"/v1/sdk/customers/{customer_ref}/balance")
234
- )
235
- return BalanceResponse.model_validate(data)
222
+ _shim_warn("sv.customers.abalance()")
223
+ return await self.customers.abalance(customer_ref)
236
224
 
237
225
  async def cancel_purchase(
238
226
  self,
@@ -241,83 +229,48 @@ class AsyncSolvaPay:
241
229
  reason: str | None = None,
242
230
  idempotency_key: str | None = None,
243
231
  ) -> dict[str, Any]:
244
- """Cancel a purchase. Maps to POST /v1/sdk/purchases/{ref}/cancel."""
245
- req = CancelPurchaseRequest(reason=reason)
246
- return await self._http.send(
247
- _RequestSpec(
248
- "POST",
249
- f"/v1/sdk/purchases/{purchase_ref}/cancel",
250
- json=req.model_dump(by_alias=True, exclude_none=True),
251
- idempotency_key=idempotency_key,
252
- )
232
+ _shim_warn("sv.purchases.acancel()")
233
+ return await self.purchases.acancel(
234
+ purchase_ref, reason=reason, idempotency_key=idempotency_key
253
235
  )
254
236
 
255
237
  async def reactivate_purchase(
256
238
  self, purchase_ref: str, *, idempotency_key: str | None = None
257
239
  ) -> dict[str, Any]:
258
- """Reactivate a cancelled purchase. Maps to POST /v1/sdk/purchases/{ref}/reactivate."""
259
- return await self._http.send(
260
- _RequestSpec(
261
- "POST",
262
- f"/v1/sdk/purchases/{purchase_ref}/reactivate",
263
- idempotency_key=idempotency_key,
264
- )
265
- )
266
-
267
- # --- Admin: Products ---
240
+ _shim_warn("sv.purchases.areactivate()")
241
+ return await self.purchases.areactivate(purchase_ref, idempotency_key=idempotency_key)
268
242
 
269
243
  async def list_products(self) -> list[Product]:
270
- """List all products. Maps to GET /v1/sdk/products."""
271
- data = await self._http.send(_RequestSpec("GET", "/v1/sdk/products"))
272
- items: list[Any] = data if isinstance(data, list) else data.get("products", [])
273
- return [Product.model_validate(p) for p in items]
244
+ _shim_warn("sv.products.alist()")
245
+ return await self.products.alist()
274
246
 
275
247
  async def get_product(self, product_ref: str) -> Product:
276
- """Get a product by ref. Maps to GET /v1/sdk/products/{ref}."""
277
- data = await self._http.send(_RequestSpec("GET", f"/v1/sdk/products/{product_ref}"))
278
- return Product.model_validate(data)
248
+ _shim_warn("sv.products.aget()")
249
+ return await self.products.aget(product_ref)
279
250
 
280
251
  async def create_product(
281
252
  self, *, name: str, type: str, default_currency: str, idempotency_key: str | None = None
282
253
  ) -> Product:
283
- """Create a product. Maps to POST /v1/sdk/products."""
284
- req = CreateProductRequest(name=name, type=type, default_currency=default_currency)
285
- data = await self._http.send(
286
- _RequestSpec(
287
- "POST",
288
- "/v1/sdk/products",
289
- json=req.model_dump(by_alias=True, exclude_none=True),
290
- idempotency_key=idempotency_key,
291
- )
254
+ _shim_warn("sv.products.acreate()")
255
+ return await self.products.acreate(
256
+ name=name, type=type, default_currency=default_currency, idempotency_key=idempotency_key
292
257
  )
293
- return Product.model_validate(data)
294
258
 
295
259
  async def delete_product(self, product_ref: str) -> dict[str, Any]:
296
- """Delete a product. Maps to DELETE /v1/sdk/products/{ref}."""
297
- return await self._http.send(_RequestSpec("DELETE", f"/v1/sdk/products/{product_ref}"))
260
+ _shim_warn("sv.products.adelete()")
261
+ return await self.products.adelete(product_ref)
298
262
 
299
263
  async def clone_product(
300
264
  self, product_ref: str, *, new_name: str, idempotency_key: str | None = None
301
265
  ) -> Product:
302
- """Clone a product with a new name. Maps to POST /v1/sdk/products/{ref}/clone."""
303
- req = CloneProductRequest(new_name=new_name)
304
- data = await self._http.send(
305
- _RequestSpec(
306
- "POST",
307
- f"/v1/sdk/products/{product_ref}/clone",
308
- json=req.model_dump(by_alias=True, exclude_none=True),
309
- idempotency_key=idempotency_key,
310
- )
266
+ _shim_warn("sv.products.aclone()")
267
+ return await self.products.aclone(
268
+ product_ref, new_name=new_name, idempotency_key=idempotency_key
311
269
  )
312
- return Product.model_validate(data)
313
-
314
- # --- Admin: Plans ---
315
270
 
316
271
  async def list_plans(self, product_ref: str) -> list[Plan]:
317
- """List plans for a product. Maps to GET /v1/sdk/products/{ref}/plans."""
318
- data = await self._http.send(_RequestSpec("GET", f"/v1/sdk/products/{product_ref}/plans"))
319
- items: list[Any] = data if isinstance(data, list) else data.get("plans", [])
320
- return [Plan.model_validate(p) for p in items]
272
+ _shim_warn("sv.plans.alist()")
273
+ return await self.plans.alist(product_ref)
321
274
 
322
275
  async def create_plan(
323
276
  self,
@@ -330,19 +283,16 @@ class AsyncSolvaPay:
330
283
  interval: str | None = None,
331
284
  idempotency_key: str | None = None,
332
285
  ) -> Plan:
333
- """Create a plan for a product. Maps to POST /v1/sdk/products/{ref}/plans."""
334
- req = CreatePlanRequest(
335
- name=name, type=type, price=price, currency=currency, interval=interval
336
- )
337
- data = await self._http.send(
338
- _RequestSpec(
339
- "POST",
340
- f"/v1/sdk/products/{product_ref}/plans",
341
- json=req.model_dump(by_alias=True, exclude_none=True),
342
- idempotency_key=idempotency_key,
343
- )
286
+ _shim_warn("sv.plans.acreate()")
287
+ return await self.plans.acreate(
288
+ product_ref,
289
+ name=name,
290
+ type=type,
291
+ price=price,
292
+ currency=currency,
293
+ interval=interval,
294
+ idempotency_key=idempotency_key,
344
295
  )
345
- return Plan.model_validate(data)
346
296
 
347
297
  async def update_plan(
348
298
  self,
@@ -355,33 +305,25 @@ class AsyncSolvaPay:
355
305
  currency: str | None = None,
356
306
  interval: str | None = None,
357
307
  ) -> Plan:
358
- """Update a plan. Maps to PUT /v1/sdk/products/{ref}/plans/{ref}."""
359
- req = UpdatePlanRequest(
360
- name=name, type=type, price=price, currency=currency, interval=interval
361
- )
362
- data = await self._http.send(
363
- _RequestSpec(
364
- "PUT",
365
- f"/v1/sdk/products/{product_ref}/plans/{plan_ref}",
366
- json=req.model_dump(by_alias=True, exclude_none=True),
367
- )
308
+ _shim_warn("sv.plans.aupdate()")
309
+ return await self.plans.aupdate(
310
+ product_ref,
311
+ plan_ref,
312
+ name=name,
313
+ type=type,
314
+ price=price,
315
+ currency=currency,
316
+ interval=interval,
368
317
  )
369
- return Plan.model_validate(data)
370
318
 
371
319
  async def delete_plan(self, product_ref: str, plan_ref: str) -> dict[str, Any]:
372
- """Delete a plan. Maps to DELETE /v1/sdk/products/{ref}/plans/{ref}."""
373
- return await self._http.send(
374
- _RequestSpec("DELETE", f"/v1/sdk/products/{product_ref}/plans/{plan_ref}")
375
- )
376
-
377
- # --- Admin: Merchant + Platform ---
320
+ _shim_warn("sv.plans.adelete()")
321
+ return await self.plans.adelete(product_ref, plan_ref)
378
322
 
379
323
  async def get_merchant(self) -> Merchant:
380
- """Get merchant account details. Maps to GET /v1/sdk/merchant."""
381
- data = await self._http.send(_RequestSpec("GET", "/v1/sdk/merchant"))
382
- return Merchant.model_validate(data)
324
+ _shim_warn("sv.merchant.aget()")
325
+ return await self.merchant.aget()
383
326
 
384
327
  async def get_platform_config(self) -> PlatformConfig:
385
- """Get platform-level configuration. Maps to GET /v1/sdk/platform-config."""
386
- data = await self._http.send(_RequestSpec("GET", "/v1/sdk/platform-config"))
387
- return PlatformConfig.model_validate(data)
328
+ _shim_warn("sv.merchant.aget_platform_config()")
329
+ return await self.merchant.aget_platform_config()