svc-infra 0.1.595__py3-none-any.whl → 1.1.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.
Potentially problematic release.
This version of svc-infra might be problematic. Click here for more details.
- svc_infra/__init__.py +58 -2
- svc_infra/apf_payments/models.py +68 -38
- svc_infra/apf_payments/provider/__init__.py +2 -2
- svc_infra/apf_payments/provider/aiydan.py +39 -23
- svc_infra/apf_payments/provider/base.py +8 -3
- svc_infra/apf_payments/provider/registry.py +3 -5
- svc_infra/apf_payments/provider/stripe.py +74 -52
- svc_infra/apf_payments/schemas.py +84 -83
- svc_infra/apf_payments/service.py +27 -16
- svc_infra/apf_payments/settings.py +12 -11
- svc_infra/api/__init__.py +61 -0
- svc_infra/api/fastapi/__init__.py +34 -0
- svc_infra/api/fastapi/admin/__init__.py +3 -0
- svc_infra/api/fastapi/admin/add.py +240 -0
- svc_infra/api/fastapi/apf_payments/router.py +94 -73
- svc_infra/api/fastapi/apf_payments/setup.py +10 -9
- svc_infra/api/fastapi/auth/__init__.py +65 -0
- svc_infra/api/fastapi/auth/_cookies.py +1 -3
- svc_infra/api/fastapi/auth/add.py +14 -15
- svc_infra/api/fastapi/auth/gaurd.py +32 -20
- svc_infra/api/fastapi/auth/mfa/models.py +3 -4
- svc_infra/api/fastapi/auth/mfa/pre_auth.py +13 -9
- svc_infra/api/fastapi/auth/mfa/router.py +9 -8
- svc_infra/api/fastapi/auth/mfa/security.py +4 -7
- svc_infra/api/fastapi/auth/mfa/utils.py +5 -3
- svc_infra/api/fastapi/auth/policy.py +0 -1
- svc_infra/api/fastapi/auth/providers.py +3 -3
- svc_infra/api/fastapi/auth/routers/apikey_router.py +19 -21
- svc_infra/api/fastapi/auth/routers/oauth_router.py +98 -52
- svc_infra/api/fastapi/auth/routers/session_router.py +6 -5
- svc_infra/api/fastapi/auth/security.py +25 -15
- svc_infra/api/fastapi/auth/sender.py +5 -0
- svc_infra/api/fastapi/auth/settings.py +18 -19
- svc_infra/api/fastapi/auth/state.py +5 -4
- svc_infra/api/fastapi/auth/ws_security.py +275 -0
- svc_infra/api/fastapi/billing/router.py +71 -0
- svc_infra/api/fastapi/billing/setup.py +19 -0
- svc_infra/api/fastapi/cache/add.py +9 -5
- svc_infra/api/fastapi/db/__init__.py +5 -1
- svc_infra/api/fastapi/db/http.py +10 -9
- svc_infra/api/fastapi/db/nosql/__init__.py +39 -1
- svc_infra/api/fastapi/db/nosql/mongo/add.py +35 -30
- svc_infra/api/fastapi/db/nosql/mongo/crud_router.py +39 -21
- svc_infra/api/fastapi/db/sql/__init__.py +5 -1
- svc_infra/api/fastapi/db/sql/add.py +62 -25
- svc_infra/api/fastapi/db/sql/crud_router.py +205 -30
- svc_infra/api/fastapi/db/sql/session.py +19 -2
- svc_infra/api/fastapi/db/sql/users.py +18 -9
- svc_infra/api/fastapi/dependencies/ratelimit.py +76 -14
- svc_infra/api/fastapi/docs/add.py +163 -0
- svc_infra/api/fastapi/docs/landing.py +6 -6
- svc_infra/api/fastapi/docs/scoped.py +75 -36
- svc_infra/api/fastapi/dual/__init__.py +12 -2
- svc_infra/api/fastapi/dual/dualize.py +2 -2
- svc_infra/api/fastapi/dual/protected.py +123 -10
- svc_infra/api/fastapi/dual/public.py +25 -0
- svc_infra/api/fastapi/dual/router.py +18 -8
- svc_infra/api/fastapi/dx.py +33 -2
- svc_infra/api/fastapi/ease.py +59 -7
- svc_infra/api/fastapi/http/concurrency.py +2 -1
- svc_infra/api/fastapi/http/conditional.py +2 -2
- svc_infra/api/fastapi/middleware/debug.py +4 -1
- svc_infra/api/fastapi/middleware/errors/exceptions.py +2 -5
- svc_infra/api/fastapi/middleware/errors/handlers.py +50 -10
- svc_infra/api/fastapi/middleware/graceful_shutdown.py +95 -0
- svc_infra/api/fastapi/middleware/idempotency.py +190 -68
- svc_infra/api/fastapi/middleware/idempotency_store.py +187 -0
- svc_infra/api/fastapi/middleware/optimistic_lock.py +39 -0
- svc_infra/api/fastapi/middleware/ratelimit.py +125 -28
- svc_infra/api/fastapi/middleware/ratelimit_store.py +45 -13
- svc_infra/api/fastapi/middleware/request_id.py +24 -10
- svc_infra/api/fastapi/middleware/request_size_limit.py +3 -3
- svc_infra/api/fastapi/middleware/timeout.py +176 -0
- svc_infra/api/fastapi/object_router.py +1060 -0
- svc_infra/api/fastapi/openapi/apply.py +4 -3
- svc_infra/api/fastapi/openapi/conventions.py +13 -6
- svc_infra/api/fastapi/openapi/mutators.py +144 -17
- svc_infra/api/fastapi/openapi/pipeline.py +2 -2
- svc_infra/api/fastapi/openapi/responses.py +4 -6
- svc_infra/api/fastapi/openapi/security.py +1 -1
- svc_infra/api/fastapi/ops/add.py +73 -0
- svc_infra/api/fastapi/pagination.py +47 -32
- svc_infra/api/fastapi/routers/__init__.py +16 -10
- svc_infra/api/fastapi/routers/ping.py +1 -0
- svc_infra/api/fastapi/setup.py +167 -54
- svc_infra/api/fastapi/tenancy/add.py +20 -0
- svc_infra/api/fastapi/tenancy/context.py +113 -0
- svc_infra/api/fastapi/versioned.py +102 -0
- svc_infra/app/README.md +5 -5
- svc_infra/app/__init__.py +3 -1
- svc_infra/app/env.py +70 -4
- svc_infra/app/logging/add.py +10 -2
- svc_infra/app/logging/filter.py +1 -1
- svc_infra/app/logging/formats.py +13 -5
- svc_infra/app/root.py +3 -3
- svc_infra/billing/__init__.py +40 -0
- svc_infra/billing/async_service.py +167 -0
- svc_infra/billing/jobs.py +231 -0
- svc_infra/billing/models.py +146 -0
- svc_infra/billing/quotas.py +101 -0
- svc_infra/billing/schemas.py +34 -0
- svc_infra/bundled_docs/README.md +5 -0
- svc_infra/bundled_docs/__init__.py +1 -0
- svc_infra/bundled_docs/getting-started.md +6 -0
- svc_infra/cache/__init__.py +21 -5
- svc_infra/cache/add.py +167 -0
- svc_infra/cache/backend.py +9 -7
- svc_infra/cache/decorators.py +75 -20
- svc_infra/cache/demo.py +2 -2
- svc_infra/cache/keys.py +26 -6
- svc_infra/cache/recache.py +26 -27
- svc_infra/cache/resources.py +6 -5
- svc_infra/cache/tags.py +19 -44
- svc_infra/cache/ttl.py +2 -3
- svc_infra/cache/utils.py +4 -3
- svc_infra/cli/__init__.py +44 -8
- svc_infra/cli/__main__.py +4 -0
- svc_infra/cli/cmds/__init__.py +39 -2
- svc_infra/cli/cmds/db/nosql/mongo/mongo_cmds.py +18 -14
- svc_infra/cli/cmds/db/nosql/mongo/mongo_scaffold_cmds.py +9 -10
- svc_infra/cli/cmds/db/ops_cmds.py +267 -0
- svc_infra/cli/cmds/db/sql/alembic_cmds.py +97 -29
- svc_infra/cli/cmds/db/sql/sql_export_cmds.py +80 -0
- svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py +13 -13
- svc_infra/cli/cmds/docs/docs_cmds.py +139 -0
- svc_infra/cli/cmds/dx/__init__.py +12 -0
- svc_infra/cli/cmds/dx/dx_cmds.py +110 -0
- svc_infra/cli/cmds/health/__init__.py +179 -0
- svc_infra/cli/cmds/health/health_cmds.py +8 -0
- svc_infra/cli/cmds/help.py +4 -0
- svc_infra/cli/cmds/jobs/__init__.py +1 -0
- svc_infra/cli/cmds/jobs/jobs_cmds.py +42 -0
- svc_infra/cli/cmds/obs/obs_cmds.py +31 -13
- svc_infra/cli/cmds/sdk/__init__.py +0 -0
- svc_infra/cli/cmds/sdk/sdk_cmds.py +102 -0
- svc_infra/cli/foundation/runner.py +4 -5
- svc_infra/cli/foundation/typer_bootstrap.py +1 -2
- svc_infra/data/__init__.py +83 -0
- svc_infra/data/add.py +61 -0
- svc_infra/data/backup.py +56 -0
- svc_infra/data/erasure.py +46 -0
- svc_infra/data/fixtures.py +42 -0
- svc_infra/data/retention.py +56 -0
- svc_infra/db/__init__.py +15 -0
- svc_infra/db/crud_schema.py +14 -13
- svc_infra/db/inbox.py +67 -0
- svc_infra/db/nosql/__init__.py +2 -0
- svc_infra/db/nosql/constants.py +1 -1
- svc_infra/db/nosql/core.py +19 -5
- svc_infra/db/nosql/indexes.py +12 -9
- svc_infra/db/nosql/management.py +4 -4
- svc_infra/db/nosql/mongo/README.md +13 -13
- svc_infra/db/nosql/mongo/client.py +21 -4
- svc_infra/db/nosql/mongo/settings.py +1 -1
- svc_infra/db/nosql/repository.py +46 -27
- svc_infra/db/nosql/resource.py +28 -16
- svc_infra/db/nosql/scaffold.py +14 -12
- svc_infra/db/nosql/service.py +2 -1
- svc_infra/db/nosql/service_with_hooks.py +4 -3
- svc_infra/db/nosql/utils.py +4 -4
- svc_infra/db/ops.py +380 -0
- svc_infra/db/outbox.py +105 -0
- svc_infra/db/sql/apikey.py +34 -15
- svc_infra/db/sql/authref.py +8 -6
- svc_infra/db/sql/constants.py +5 -1
- svc_infra/db/sql/core.py +13 -13
- svc_infra/db/sql/management.py +5 -6
- svc_infra/db/sql/repository.py +92 -26
- svc_infra/db/sql/resource.py +18 -12
- svc_infra/db/sql/scaffold.py +11 -11
- svc_infra/db/sql/service.py +2 -1
- svc_infra/db/sql/service_with_hooks.py +4 -3
- svc_infra/db/sql/templates/models_schemas/auth/models.py.tmpl +7 -56
- svc_infra/db/sql/templates/setup/env_async.py.tmpl +34 -12
- svc_infra/db/sql/templates/setup/env_sync.py.tmpl +29 -7
- svc_infra/db/sql/tenant.py +80 -0
- svc_infra/db/sql/uniq.py +8 -7
- svc_infra/db/sql/uniq_hooks.py +12 -11
- svc_infra/db/sql/utils.py +105 -47
- svc_infra/db/sql/versioning.py +14 -0
- svc_infra/db/utils.py +3 -3
- svc_infra/deploy/__init__.py +531 -0
- svc_infra/documents/__init__.py +100 -0
- svc_infra/documents/add.py +263 -0
- svc_infra/documents/ease.py +233 -0
- svc_infra/documents/models.py +114 -0
- svc_infra/documents/storage.py +262 -0
- svc_infra/dx/__init__.py +58 -0
- svc_infra/dx/add.py +63 -0
- svc_infra/dx/changelog.py +74 -0
- svc_infra/dx/checks.py +68 -0
- svc_infra/exceptions.py +141 -0
- svc_infra/health/__init__.py +863 -0
- svc_infra/http/__init__.py +13 -0
- svc_infra/http/client.py +101 -0
- svc_infra/jobs/__init__.py +79 -0
- svc_infra/jobs/builtins/outbox_processor.py +38 -0
- svc_infra/jobs/builtins/webhook_delivery.py +93 -0
- svc_infra/jobs/easy.py +33 -0
- svc_infra/jobs/loader.py +49 -0
- svc_infra/jobs/queue.py +106 -0
- svc_infra/jobs/redis_queue.py +242 -0
- svc_infra/jobs/runner.py +75 -0
- svc_infra/jobs/scheduler.py +53 -0
- svc_infra/jobs/worker.py +40 -0
- svc_infra/loaders/__init__.py +186 -0
- svc_infra/loaders/base.py +143 -0
- svc_infra/loaders/github.py +309 -0
- svc_infra/loaders/models.py +147 -0
- svc_infra/loaders/url.py +229 -0
- svc_infra/logging/__init__.py +375 -0
- svc_infra/mcp/__init__.py +82 -0
- svc_infra/mcp/svc_infra_mcp.py +91 -33
- svc_infra/obs/README.md +2 -0
- svc_infra/obs/add.py +68 -11
- svc_infra/obs/cloud_dash.py +2 -1
- svc_infra/obs/grafana/dashboards/http-overview.json +45 -0
- svc_infra/obs/metrics/__init__.py +6 -7
- svc_infra/obs/metrics/asgi.py +8 -7
- svc_infra/obs/metrics/base.py +13 -13
- svc_infra/obs/metrics/http.py +3 -3
- svc_infra/obs/metrics/sqlalchemy.py +14 -13
- svc_infra/obs/metrics.py +9 -8
- svc_infra/resilience/__init__.py +44 -0
- svc_infra/resilience/circuit_breaker.py +328 -0
- svc_infra/resilience/retry.py +289 -0
- svc_infra/security/__init__.py +167 -0
- svc_infra/security/add.py +213 -0
- svc_infra/security/audit.py +97 -18
- svc_infra/security/audit_service.py +10 -9
- svc_infra/security/headers.py +15 -2
- svc_infra/security/hibp.py +14 -7
- svc_infra/security/jwt_rotation.py +78 -29
- svc_infra/security/lockout.py +23 -16
- svc_infra/security/models.py +77 -44
- svc_infra/security/oauth_models.py +73 -0
- svc_infra/security/org_invites.py +12 -12
- svc_infra/security/passwords.py +3 -3
- svc_infra/security/permissions.py +31 -7
- svc_infra/security/session.py +7 -8
- svc_infra/security/signed_cookies.py +26 -6
- svc_infra/storage/__init__.py +93 -0
- svc_infra/storage/add.py +250 -0
- svc_infra/storage/backends/__init__.py +11 -0
- svc_infra/storage/backends/local.py +331 -0
- svc_infra/storage/backends/memory.py +213 -0
- svc_infra/storage/backends/s3.py +334 -0
- svc_infra/storage/base.py +239 -0
- svc_infra/storage/easy.py +181 -0
- svc_infra/storage/settings.py +193 -0
- svc_infra/testing/__init__.py +682 -0
- svc_infra/utils.py +170 -5
- svc_infra/webhooks/__init__.py +69 -0
- svc_infra/webhooks/add.py +327 -0
- svc_infra/webhooks/encryption.py +115 -0
- svc_infra/webhooks/fastapi.py +37 -0
- svc_infra/webhooks/router.py +55 -0
- svc_infra/webhooks/service.py +69 -0
- svc_infra/webhooks/signing.py +34 -0
- svc_infra/websocket/__init__.py +79 -0
- svc_infra/websocket/add.py +139 -0
- svc_infra/websocket/client.py +283 -0
- svc_infra/websocket/config.py +57 -0
- svc_infra/websocket/easy.py +76 -0
- svc_infra/websocket/exceptions.py +61 -0
- svc_infra/websocket/manager.py +343 -0
- svc_infra/websocket/models.py +49 -0
- svc_infra-1.1.0.dist-info/LICENSE +21 -0
- svc_infra-1.1.0.dist-info/METADATA +362 -0
- svc_infra-1.1.0.dist-info/RECORD +364 -0
- svc_infra-0.1.595.dist-info/METADATA +0 -80
- svc_infra-0.1.595.dist-info/RECORD +0 -253
- {svc_infra-0.1.595.dist-info → svc_infra-1.1.0.dist-info}/WHEEL +0 -0
- {svc_infra-0.1.595.dist-info → svc_infra-1.1.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from functools import partial
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
import anyio
|
|
7
7
|
|
|
@@ -44,7 +44,7 @@ from .base import ProviderAdapter
|
|
|
44
44
|
try:
|
|
45
45
|
import stripe
|
|
46
46
|
except Exception: # pragma: no cover
|
|
47
|
-
stripe = None # type: ignore
|
|
47
|
+
stripe = None # type: ignore[assignment]
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
async def _acall(fn, /, *args, **kwargs):
|
|
@@ -224,7 +224,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
224
224
|
name=c.get("name"),
|
|
225
225
|
)
|
|
226
226
|
|
|
227
|
-
async def get_customer(self, provider_customer_id: str) ->
|
|
227
|
+
async def get_customer(self, provider_customer_id: str) -> CustomerOut | None:
|
|
228
228
|
c = await _acall(stripe.Customer.retrieve, provider_customer_id)
|
|
229
229
|
return CustomerOut(
|
|
230
230
|
id=c.id,
|
|
@@ -235,9 +235,14 @@ class StripeAdapter(ProviderAdapter):
|
|
|
235
235
|
)
|
|
236
236
|
|
|
237
237
|
async def list_customers(
|
|
238
|
-
self,
|
|
238
|
+
self,
|
|
239
|
+
*,
|
|
240
|
+
provider: str | None,
|
|
241
|
+
user_id: str | None,
|
|
242
|
+
limit: int,
|
|
243
|
+
cursor: str | None,
|
|
239
244
|
) -> tuple[list[CustomerOut], str | None]:
|
|
240
|
-
params = {"limit": int(limit)}
|
|
245
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
241
246
|
if cursor:
|
|
242
247
|
params["starting_after"] = cursor
|
|
243
248
|
# Stripe has no direct filter for our custom user_id; many teams store mapping in DB.
|
|
@@ -271,13 +276,21 @@ class StripeAdapter(ProviderAdapter):
|
|
|
271
276
|
invoice_settings={"default_payment_method": pm.id},
|
|
272
277
|
)
|
|
273
278
|
is_default = (
|
|
274
|
-
getattr(
|
|
279
|
+
getattr(
|
|
280
|
+
getattr(cust, "invoice_settings", None),
|
|
281
|
+
"default_payment_method",
|
|
282
|
+
None,
|
|
283
|
+
)
|
|
275
284
|
== pm.id
|
|
276
285
|
)
|
|
277
286
|
else:
|
|
278
287
|
cust = await _acall(stripe.Customer.retrieve, data.customer_provider_id)
|
|
279
288
|
is_default = (
|
|
280
|
-
getattr(
|
|
289
|
+
getattr(
|
|
290
|
+
getattr(cust, "invoice_settings", None),
|
|
291
|
+
"default_payment_method",
|
|
292
|
+
None,
|
|
293
|
+
)
|
|
281
294
|
== pm.id
|
|
282
295
|
)
|
|
283
296
|
return _pm_to_out(pm, is_default=is_default)
|
|
@@ -366,7 +379,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
366
379
|
async def list_products(
|
|
367
380
|
self, *, active: bool | None, limit: int, cursor: str | None
|
|
368
381
|
) -> tuple[list[ProductOut], str | None]:
|
|
369
|
-
params = {"limit": int(limit)}
|
|
382
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
370
383
|
if active is not None:
|
|
371
384
|
params["active"] = bool(active)
|
|
372
385
|
if cursor:
|
|
@@ -390,12 +403,12 @@ class StripeAdapter(ProviderAdapter):
|
|
|
390
403
|
return _product_to_out(p)
|
|
391
404
|
|
|
392
405
|
async def create_price(self, data: PriceCreateIn) -> PriceOut:
|
|
393
|
-
kwargs: dict[str, Any] =
|
|
394
|
-
product
|
|
395
|
-
currency
|
|
396
|
-
unit_amount
|
|
397
|
-
active
|
|
398
|
-
|
|
406
|
+
kwargs: dict[str, Any] = {
|
|
407
|
+
"product": data.provider_product_id,
|
|
408
|
+
"currency": data.currency.lower(),
|
|
409
|
+
"unit_amount": int(data.unit_amount),
|
|
410
|
+
"active": bool(data.active),
|
|
411
|
+
}
|
|
399
412
|
if data.interval:
|
|
400
413
|
kwargs["recurring"] = {"interval": data.interval}
|
|
401
414
|
if data.trial_days is not None:
|
|
@@ -415,7 +428,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
415
428
|
limit: int,
|
|
416
429
|
cursor: str | None,
|
|
417
430
|
) -> tuple[list[PriceOut], str | None]:
|
|
418
|
-
params = {"limit": int(limit)}
|
|
431
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
419
432
|
if provider_product_id:
|
|
420
433
|
params["product"] = provider_product_id
|
|
421
434
|
if active is not None:
|
|
@@ -441,11 +454,11 @@ class StripeAdapter(ProviderAdapter):
|
|
|
441
454
|
|
|
442
455
|
# -------- Subscriptions --------
|
|
443
456
|
async def create_subscription(self, data: SubscriptionCreateIn) -> SubscriptionOut:
|
|
444
|
-
kwargs: dict[str, Any] =
|
|
445
|
-
customer
|
|
446
|
-
items
|
|
447
|
-
proration_behavior
|
|
448
|
-
|
|
457
|
+
kwargs: dict[str, Any] = {
|
|
458
|
+
"customer": data.customer_provider_id,
|
|
459
|
+
"items": [{"price": data.price_provider_id, "quantity": int(data.quantity)}],
|
|
460
|
+
"proration_behavior": data.proration_behavior,
|
|
461
|
+
}
|
|
449
462
|
if data.trial_days is not None:
|
|
450
463
|
kwargs["trial_period_days"] = int(data.trial_days)
|
|
451
464
|
s = await _acall(stripe.Subscription.create, **kwargs)
|
|
@@ -493,7 +506,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
493
506
|
limit: int,
|
|
494
507
|
cursor: str | None,
|
|
495
508
|
) -> tuple[list[SubscriptionOut], str | None]:
|
|
496
|
-
params = {"limit": int(limit)}
|
|
509
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
497
510
|
if customer_provider_id:
|
|
498
511
|
params["customer"] = customer_provider_id
|
|
499
512
|
if status:
|
|
@@ -530,19 +543,20 @@ class StripeAdapter(ProviderAdapter):
|
|
|
530
543
|
self, provider_invoice_id: str, data: InvoiceLineItemIn
|
|
531
544
|
) -> InvoiceOut:
|
|
532
545
|
# attach an item to a DRAFT invoice
|
|
533
|
-
kwargs: dict[str, Any] =
|
|
534
|
-
invoice
|
|
535
|
-
customer
|
|
536
|
-
quantity
|
|
537
|
-
currency
|
|
538
|
-
description
|
|
539
|
-
|
|
546
|
+
kwargs: dict[str, Any] = {
|
|
547
|
+
"invoice": provider_invoice_id,
|
|
548
|
+
"customer": data.customer_provider_id,
|
|
549
|
+
"quantity": int(data.quantity or 1),
|
|
550
|
+
"currency": data.currency.lower(),
|
|
551
|
+
"description": data.description or None,
|
|
552
|
+
}
|
|
540
553
|
if data.provider_price_id:
|
|
541
554
|
kwargs["price"] = data.provider_price_id
|
|
542
555
|
else:
|
|
543
556
|
kwargs["unit_amount"] = int(data.unit_amount)
|
|
544
557
|
await _acall(
|
|
545
|
-
stripe.InvoiceItem.create,
|
|
558
|
+
stripe.InvoiceItem.create,
|
|
559
|
+
**{k: v for k, v in kwargs.items() if v is not None},
|
|
546
560
|
)
|
|
547
561
|
inv = await _acall(stripe.Invoice.retrieve, provider_invoice_id)
|
|
548
562
|
return _inv_to_out(inv)
|
|
@@ -555,7 +569,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
555
569
|
limit: int,
|
|
556
570
|
cursor: str | None,
|
|
557
571
|
) -> tuple[list[InvoiceOut], str | None]:
|
|
558
|
-
params = {"limit": int(limit)}
|
|
572
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
559
573
|
if customer_provider_id:
|
|
560
574
|
params["customer"] = customer_provider_id
|
|
561
575
|
if status:
|
|
@@ -577,13 +591,13 @@ class StripeAdapter(ProviderAdapter):
|
|
|
577
591
|
params = {"customer": customer_provider_id}
|
|
578
592
|
if subscription_id:
|
|
579
593
|
params["subscription"] = subscription_id
|
|
580
|
-
inv = await _acall(stripe.Invoice.upcoming, **params)
|
|
594
|
+
inv = await _acall(stripe.Invoice.upcoming, **params) # type: ignore[attr-defined]
|
|
581
595
|
return _inv_to_out(inv)
|
|
582
596
|
|
|
583
597
|
async def list_invoice_line_items(
|
|
584
598
|
self, provider_invoice_id: str, *, limit: int, cursor: str | None
|
|
585
599
|
) -> tuple[list[InvoiceLineItemOut], str | None]:
|
|
586
|
-
params = {"limit": int(limit)}
|
|
600
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
587
601
|
if cursor:
|
|
588
602
|
params["starting_after"] = cursor
|
|
589
603
|
res = await _acall(stripe.Invoice.list_lines, provider_invoice_id, **params)
|
|
@@ -607,17 +621,20 @@ class StripeAdapter(ProviderAdapter):
|
|
|
607
621
|
|
|
608
622
|
# -------- Intents --------
|
|
609
623
|
async def create_intent(self, data: IntentCreateIn, *, user_id: str | None) -> IntentOut:
|
|
610
|
-
kwargs: dict[str, Any] =
|
|
611
|
-
amount
|
|
612
|
-
currency
|
|
613
|
-
description
|
|
614
|
-
capture_method
|
|
615
|
-
automatic_payment_methods
|
|
616
|
-
|
|
624
|
+
kwargs: dict[str, Any] = {
|
|
625
|
+
"amount": int(data.amount),
|
|
626
|
+
"currency": data.currency.lower(),
|
|
627
|
+
"description": data.description or None,
|
|
628
|
+
"capture_method": "manual" if data.capture_method == "manual" else "automatic",
|
|
629
|
+
"automatic_payment_methods": {"enabled": True}
|
|
630
|
+
if not data.payment_method_types
|
|
631
|
+
else None,
|
|
632
|
+
}
|
|
617
633
|
if data.payment_method_types:
|
|
618
634
|
kwargs["payment_method_types"] = data.payment_method_types
|
|
619
635
|
pi = await _acall(
|
|
620
|
-
stripe.PaymentIntent.create,
|
|
636
|
+
stripe.PaymentIntent.create,
|
|
637
|
+
**{k: v for k, v in kwargs.items() if v is not None},
|
|
621
638
|
)
|
|
622
639
|
return _pi_to_out(pi)
|
|
623
640
|
|
|
@@ -663,7 +680,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
663
680
|
limit: int,
|
|
664
681
|
cursor: str | None,
|
|
665
682
|
) -> tuple[list[IntentOut], str | None]:
|
|
666
|
-
params = {"limit": int(limit)}
|
|
683
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
667
684
|
if customer_provider_id:
|
|
668
685
|
params["customer"] = customer_provider_id
|
|
669
686
|
if status:
|
|
@@ -678,7 +695,8 @@ class StripeAdapter(ProviderAdapter):
|
|
|
678
695
|
# ---- Setup Intents (off-session readiness) ----
|
|
679
696
|
async def create_setup_intent(self, data: SetupIntentCreateIn) -> SetupIntentOut:
|
|
680
697
|
si = await _acall(
|
|
681
|
-
stripe.SetupIntent.create,
|
|
698
|
+
stripe.SetupIntent.create,
|
|
699
|
+
payment_method_types=data.payment_method_types or ["card"],
|
|
682
700
|
)
|
|
683
701
|
return SetupIntentOut(
|
|
684
702
|
id=si.id,
|
|
@@ -720,7 +738,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
720
738
|
async def list_disputes(
|
|
721
739
|
self, *, status: str | None, limit: int, cursor: str | None
|
|
722
740
|
) -> tuple[list[DisputeOut], str | None]:
|
|
723
|
-
params = {"limit": int(limit)}
|
|
741
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
724
742
|
if status:
|
|
725
743
|
params["status"] = status
|
|
726
744
|
if cursor:
|
|
@@ -738,7 +756,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
738
756
|
d = await _acall(stripe.Dispute.modify, provider_dispute_id, evidence=evidence)
|
|
739
757
|
# Some disputes require explicit submit call:
|
|
740
758
|
try:
|
|
741
|
-
d = await _acall(stripe.Dispute.submit, provider_dispute_id)
|
|
759
|
+
d = await _acall(stripe.Dispute.submit, provider_dispute_id) # type: ignore[attr-defined]
|
|
742
760
|
except Exception:
|
|
743
761
|
pass
|
|
744
762
|
return _dispute_to_out(d)
|
|
@@ -761,7 +779,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
761
779
|
async def list_payouts(
|
|
762
780
|
self, *, limit: int, cursor: str | None
|
|
763
781
|
) -> tuple[list[PayoutOut], str | None]:
|
|
764
|
-
params = {"limit": int(limit)}
|
|
782
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
765
783
|
if cursor:
|
|
766
784
|
params["starting_after"] = cursor
|
|
767
785
|
res = await _acall(stripe.Payout.list, **params)
|
|
@@ -777,7 +795,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
777
795
|
async def list_refunds(
|
|
778
796
|
self, *, provider_payment_intent_id: str | None, limit: int, cursor: str | None
|
|
779
797
|
) -> tuple[list[RefundOut], str | None]:
|
|
780
|
-
params = {"limit": int(limit)}
|
|
798
|
+
params: dict[str, Any] = {"limit": int(limit)}
|
|
781
799
|
if provider_payment_intent_id:
|
|
782
800
|
params["payment_intent"] = provider_payment_intent_id
|
|
783
801
|
if cursor:
|
|
@@ -810,7 +828,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
810
828
|
}
|
|
811
829
|
if data.timestamp:
|
|
812
830
|
body["timestamp"] = int(data.timestamp)
|
|
813
|
-
rec = await _acall(stripe.UsageRecord.create, **body)
|
|
831
|
+
rec = await _acall(stripe.UsageRecord.create, **body) # type: ignore[attr-defined]
|
|
814
832
|
return UsageRecordOut(
|
|
815
833
|
id=rec.id,
|
|
816
834
|
quantity=int(rec.quantity),
|
|
@@ -829,15 +847,19 @@ class StripeAdapter(ProviderAdapter):
|
|
|
829
847
|
sub_item = items.data[0].id if items.data else None
|
|
830
848
|
if not sub_item:
|
|
831
849
|
return [], None
|
|
832
|
-
params = {"limit": int(f.limit or 50)}
|
|
850
|
+
params: dict[str, Any] = {"limit": int(f.limit or 50)}
|
|
833
851
|
if f.cursor:
|
|
834
852
|
params["starting_after"] = f.cursor
|
|
835
|
-
res = await _acall(
|
|
836
|
-
|
|
853
|
+
res = await _acall(
|
|
854
|
+
stripe.SubscriptionItem.list_usage_record_summaries, # type: ignore[attr-defined]
|
|
855
|
+
sub_item,
|
|
856
|
+
**params,
|
|
857
|
+
)
|
|
858
|
+
usage_records: list[UsageRecordOut] = []
|
|
837
859
|
for s in res.data:
|
|
838
860
|
# No record id in summaries—synthesize a stable id from period start.
|
|
839
861
|
synthesized_id = f"{sub_item}:{getattr(s, 'period', {}).get('start')}"
|
|
840
|
-
|
|
862
|
+
usage_records.append(
|
|
841
863
|
UsageRecordOut(
|
|
842
864
|
id=synthesized_id,
|
|
843
865
|
quantity=int(getattr(s, "total_usage", 0)),
|
|
@@ -851,7 +873,7 @@ class StripeAdapter(ProviderAdapter):
|
|
|
851
873
|
if getattr(res, "has_more", False) and res.data and hasattr(res.data[-1], "id")
|
|
852
874
|
else None
|
|
853
875
|
)
|
|
854
|
-
return
|
|
876
|
+
return usage_records, next_cursor
|
|
855
877
|
|
|
856
878
|
async def get_usage_record(self, usage_record_id: str) -> UsageRecordOut:
|
|
857
879
|
# Stripe has no direct "retrieve usage record by id" API.
|
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any, Literal
|
|
3
|
+
from typing import Annotated, Any, Literal
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel, Field,
|
|
5
|
+
from pydantic import BaseModel, Field, StringConstraints
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
# Type aliases for payment fields using Annotated with proper type hints
|
|
8
|
+
Currency = Annotated[str, StringConstraints(pattern=r"^[A-Z]{3}$")]
|
|
9
|
+
AmountMinor = Annotated[int, Field(ge=0)] # minor units (cents)
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class CustomerUpsertIn(BaseModel):
|
|
12
|
-
user_id:
|
|
13
|
-
email:
|
|
14
|
-
name:
|
|
13
|
+
user_id: str | None = None
|
|
14
|
+
email: str | None = None
|
|
15
|
+
name: str | None = None
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class CustomerOut(BaseModel):
|
|
18
19
|
id: str
|
|
19
20
|
provider: str
|
|
20
21
|
provider_customer_id: str
|
|
21
|
-
email:
|
|
22
|
-
name:
|
|
22
|
+
email: str | None = None
|
|
23
|
+
name: str | None = None
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class IntentCreateIn(BaseModel):
|
|
26
27
|
amount: AmountMinor = Field(..., description="Minor units (e.g., cents)")
|
|
27
28
|
currency: Currency = Field(..., json_schema_extra={"example": "USD"})
|
|
28
|
-
description:
|
|
29
|
+
description: str | None = None
|
|
29
30
|
capture_method: Literal["automatic", "manual"] = "automatic"
|
|
30
31
|
payment_method_types: list[str] = Field(default_factory=list) # let provider default
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class NextAction(BaseModel):
|
|
34
|
-
type:
|
|
35
|
-
data:
|
|
35
|
+
type: str | None = None
|
|
36
|
+
data: dict[str, Any] | None = None
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
class IntentOut(BaseModel):
|
|
@@ -42,13 +43,13 @@ class IntentOut(BaseModel):
|
|
|
42
43
|
status: str
|
|
43
44
|
amount: AmountMinor
|
|
44
45
|
currency: Currency
|
|
45
|
-
client_secret:
|
|
46
|
-
next_action:
|
|
46
|
+
client_secret: str | None = None
|
|
47
|
+
next_action: NextAction | None = None
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
class RefundIn(BaseModel):
|
|
50
|
-
amount:
|
|
51
|
-
reason:
|
|
51
|
+
amount: AmountMinor | None = None
|
|
52
|
+
reason: str | None = None
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
class TransactionRow(BaseModel):
|
|
@@ -60,9 +61,9 @@ class TransactionRow(BaseModel):
|
|
|
60
61
|
status: str
|
|
61
62
|
provider: str
|
|
62
63
|
provider_ref: str
|
|
63
|
-
user_id:
|
|
64
|
-
net:
|
|
65
|
-
fee:
|
|
64
|
+
user_id: str | None = None
|
|
65
|
+
net: int | None = None
|
|
66
|
+
fee: int | None = None
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
class StatementRow(BaseModel):
|
|
@@ -87,10 +88,10 @@ class PaymentMethodOut(BaseModel):
|
|
|
87
88
|
provider: str
|
|
88
89
|
provider_customer_id: str
|
|
89
90
|
provider_method_id: str
|
|
90
|
-
brand:
|
|
91
|
-
last4:
|
|
92
|
-
exp_month:
|
|
93
|
-
exp_year:
|
|
91
|
+
brand: str | None = None
|
|
92
|
+
last4: str | None = None
|
|
93
|
+
exp_month: int | None = None
|
|
94
|
+
exp_year: int | None = None
|
|
94
95
|
is_default: bool = False
|
|
95
96
|
|
|
96
97
|
|
|
@@ -111,8 +112,8 @@ class PriceCreateIn(BaseModel):
|
|
|
111
112
|
provider_product_id: str
|
|
112
113
|
currency: Currency
|
|
113
114
|
unit_amount: AmountMinor
|
|
114
|
-
interval:
|
|
115
|
-
trial_days:
|
|
115
|
+
interval: Literal["day", "week", "month", "year"] | None = None
|
|
116
|
+
trial_days: int | None = None
|
|
116
117
|
active: bool = True
|
|
117
118
|
|
|
118
119
|
|
|
@@ -123,8 +124,8 @@ class PriceOut(BaseModel):
|
|
|
123
124
|
provider_product_id: str
|
|
124
125
|
currency: Currency
|
|
125
126
|
unit_amount: AmountMinor
|
|
126
|
-
interval:
|
|
127
|
-
trial_days:
|
|
127
|
+
interval: str | None = None
|
|
128
|
+
trial_days: int | None = None
|
|
128
129
|
active: bool = True
|
|
129
130
|
|
|
130
131
|
|
|
@@ -132,14 +133,14 @@ class SubscriptionCreateIn(BaseModel):
|
|
|
132
133
|
customer_provider_id: str
|
|
133
134
|
price_provider_id: str
|
|
134
135
|
quantity: int = 1
|
|
135
|
-
trial_days:
|
|
136
|
+
trial_days: int | None = None
|
|
136
137
|
proration_behavior: Literal["create_prorations", "none", "always_invoice"] = "create_prorations"
|
|
137
138
|
|
|
138
139
|
|
|
139
140
|
class SubscriptionUpdateIn(BaseModel):
|
|
140
|
-
price_provider_id:
|
|
141
|
-
quantity:
|
|
142
|
-
cancel_at_period_end:
|
|
141
|
+
price_provider_id: str | None = None
|
|
142
|
+
quantity: int | None = None
|
|
143
|
+
cancel_at_period_end: bool | None = None
|
|
143
144
|
proration_behavior: Literal["create_prorations", "none", "always_invoice"] = "create_prorations"
|
|
144
145
|
|
|
145
146
|
|
|
@@ -151,7 +152,7 @@ class SubscriptionOut(BaseModel):
|
|
|
151
152
|
status: str
|
|
152
153
|
quantity: int
|
|
153
154
|
cancel_at_period_end: bool
|
|
154
|
-
current_period_end:
|
|
155
|
+
current_period_end: str | None = None
|
|
155
156
|
|
|
156
157
|
|
|
157
158
|
class InvoiceCreateIn(BaseModel):
|
|
@@ -167,45 +168,45 @@ class InvoiceOut(BaseModel):
|
|
|
167
168
|
status: str
|
|
168
169
|
amount_due: AmountMinor
|
|
169
170
|
currency: Currency
|
|
170
|
-
hosted_invoice_url:
|
|
171
|
-
pdf_url:
|
|
171
|
+
hosted_invoice_url: str | None = None
|
|
172
|
+
pdf_url: str | None = None
|
|
172
173
|
|
|
173
174
|
|
|
174
175
|
class CaptureIn(BaseModel):
|
|
175
|
-
amount:
|
|
176
|
+
amount: AmountMinor | None = None # partial capture supported
|
|
176
177
|
|
|
177
178
|
|
|
178
179
|
class IntentListFilter(BaseModel):
|
|
179
|
-
customer_provider_id:
|
|
180
|
-
status:
|
|
181
|
-
limit:
|
|
182
|
-
cursor:
|
|
180
|
+
customer_provider_id: str | None = None
|
|
181
|
+
status: str | None = None
|
|
182
|
+
limit: int | None = Field(default=50, ge=1, le=200)
|
|
183
|
+
cursor: str | None = None # opaque provider cursor when supported
|
|
183
184
|
|
|
184
185
|
|
|
185
186
|
class UsageRecordIn(BaseModel):
|
|
186
187
|
# Stripe: subscription_item is the target for metered billing.
|
|
187
188
|
# If provider doesn't use subscription_item, allow provider_price_id as fallback.
|
|
188
|
-
subscription_item:
|
|
189
|
-
provider_price_id:
|
|
190
|
-
quantity:
|
|
191
|
-
timestamp:
|
|
192
|
-
action:
|
|
189
|
+
subscription_item: str | None = None
|
|
190
|
+
provider_price_id: str | None = None
|
|
191
|
+
quantity: Annotated[int, Field(ge=0)]
|
|
192
|
+
timestamp: int | None = None # Unix seconds; provider defaults to "now"
|
|
193
|
+
action: Literal["increment", "set"] | None = "increment"
|
|
193
194
|
|
|
194
195
|
|
|
195
196
|
class InvoiceLineItemIn(BaseModel):
|
|
196
197
|
customer_provider_id: str
|
|
197
|
-
description:
|
|
198
|
+
description: str | None = None
|
|
198
199
|
unit_amount: AmountMinor
|
|
199
200
|
currency: Currency
|
|
200
|
-
quantity:
|
|
201
|
-
provider_price_id:
|
|
201
|
+
quantity: int | None = 1
|
|
202
|
+
provider_price_id: str | None = None # if linked to a price, unit_amount may be ignored
|
|
202
203
|
|
|
203
204
|
|
|
204
205
|
class InvoicesListFilter(BaseModel):
|
|
205
|
-
customer_provider_id:
|
|
206
|
-
status:
|
|
207
|
-
limit:
|
|
208
|
-
cursor:
|
|
206
|
+
customer_provider_id: str | None = None
|
|
207
|
+
status: str | None = None
|
|
208
|
+
limit: int | None = Field(default=50, ge=1, le=200)
|
|
209
|
+
cursor: str | None = None
|
|
209
210
|
|
|
210
211
|
|
|
211
212
|
class SetupIntentOut(BaseModel):
|
|
@@ -213,8 +214,8 @@ class SetupIntentOut(BaseModel):
|
|
|
213
214
|
provider: str
|
|
214
215
|
provider_setup_intent_id: str
|
|
215
216
|
status: str
|
|
216
|
-
client_secret:
|
|
217
|
-
next_action:
|
|
217
|
+
client_secret: str | None = None
|
|
218
|
+
next_action: NextAction | None = None
|
|
218
219
|
|
|
219
220
|
|
|
220
221
|
class DisputeOut(BaseModel):
|
|
@@ -223,10 +224,10 @@ class DisputeOut(BaseModel):
|
|
|
223
224
|
provider_dispute_id: str
|
|
224
225
|
amount: AmountMinor
|
|
225
226
|
currency: Currency
|
|
226
|
-
reason:
|
|
227
|
+
reason: str | None = None
|
|
227
228
|
status: str
|
|
228
|
-
evidence_due_by:
|
|
229
|
-
created_at:
|
|
229
|
+
evidence_due_by: str | None = None
|
|
230
|
+
created_at: str | None = None
|
|
230
231
|
|
|
231
232
|
|
|
232
233
|
class PayoutOut(BaseModel):
|
|
@@ -236,8 +237,8 @@ class PayoutOut(BaseModel):
|
|
|
236
237
|
amount: AmountMinor
|
|
237
238
|
currency: Currency
|
|
238
239
|
status: str
|
|
239
|
-
arrival_date:
|
|
240
|
-
type:
|
|
240
|
+
arrival_date: str | None = None
|
|
241
|
+
type: str | None = None
|
|
241
242
|
|
|
242
243
|
|
|
243
244
|
class BalanceAmount(BaseModel):
|
|
@@ -255,7 +256,7 @@ class SetupIntentCreateIn(BaseModel):
|
|
|
255
256
|
|
|
256
257
|
|
|
257
258
|
class WebhookReplayIn(BaseModel):
|
|
258
|
-
event_ids:
|
|
259
|
+
event_ids: list[str] | None = None
|
|
259
260
|
|
|
260
261
|
|
|
261
262
|
class WebhookReplayOut(BaseModel):
|
|
@@ -269,36 +270,36 @@ class WebhookAckOut(BaseModel):
|
|
|
269
270
|
class UsageRecordOut(BaseModel):
|
|
270
271
|
id: str
|
|
271
272
|
quantity: int
|
|
272
|
-
timestamp:
|
|
273
|
-
subscription_item:
|
|
274
|
-
provider_price_id:
|
|
275
|
-
action:
|
|
273
|
+
timestamp: int | None = None
|
|
274
|
+
subscription_item: str | None = None
|
|
275
|
+
provider_price_id: str | None = None
|
|
276
|
+
action: Literal["increment", "set"] | None = None
|
|
276
277
|
|
|
277
278
|
|
|
278
279
|
# -------- Customers list filter ----------
|
|
279
280
|
class CustomersListFilter(BaseModel):
|
|
280
|
-
provider:
|
|
281
|
-
user_id:
|
|
282
|
-
limit:
|
|
283
|
-
cursor:
|
|
281
|
+
provider: str | None = None
|
|
282
|
+
user_id: str | None = None
|
|
283
|
+
limit: int | None = Field(default=50, ge=1, le=200)
|
|
284
|
+
cursor: str | None = None # we’ll paginate on provider_customer_id asc
|
|
284
285
|
|
|
285
286
|
|
|
286
287
|
# -------- Products / Prices updates ----------
|
|
287
288
|
class ProductUpdateIn(BaseModel):
|
|
288
|
-
name:
|
|
289
|
-
active:
|
|
289
|
+
name: str | None = None
|
|
290
|
+
active: bool | None = None
|
|
290
291
|
|
|
291
292
|
|
|
292
293
|
class PriceUpdateIn(BaseModel):
|
|
293
|
-
active:
|
|
294
|
+
active: bool | None = None
|
|
294
295
|
|
|
295
296
|
|
|
296
297
|
# -------- Payment Method update ----------
|
|
297
298
|
class PaymentMethodUpdateIn(BaseModel):
|
|
298
299
|
# keep minimal + commonly supported card fields
|
|
299
|
-
name:
|
|
300
|
-
exp_month:
|
|
301
|
-
exp_year:
|
|
300
|
+
name: str | None = None
|
|
301
|
+
exp_month: int | None = None
|
|
302
|
+
exp_year: int | None = None
|
|
302
303
|
# extend here later with address fields (line1, city, etc.)
|
|
303
304
|
|
|
304
305
|
|
|
@@ -307,27 +308,27 @@ class RefundOut(BaseModel):
|
|
|
307
308
|
id: str
|
|
308
309
|
provider: str
|
|
309
310
|
provider_refund_id: str
|
|
310
|
-
provider_payment_intent_id:
|
|
311
|
+
provider_payment_intent_id: str | None = None
|
|
311
312
|
amount: AmountMinor
|
|
312
313
|
currency: Currency
|
|
313
314
|
status: str
|
|
314
|
-
reason:
|
|
315
|
-
created_at:
|
|
315
|
+
reason: str | None = None
|
|
316
|
+
created_at: str | None = None
|
|
316
317
|
|
|
317
318
|
|
|
318
319
|
# -------- Invoice line items (list) ----------
|
|
319
320
|
class InvoiceLineItemOut(BaseModel):
|
|
320
321
|
id: str
|
|
321
|
-
description:
|
|
322
|
+
description: str | None = None
|
|
322
323
|
amount: AmountMinor
|
|
323
324
|
currency: Currency
|
|
324
|
-
quantity:
|
|
325
|
-
provider_price_id:
|
|
325
|
+
quantity: int | None = 1
|
|
326
|
+
provider_price_id: str | None = None
|
|
326
327
|
|
|
327
328
|
|
|
328
329
|
# -------- Usage records list/get ----------
|
|
329
330
|
class UsageRecordListFilter(BaseModel):
|
|
330
|
-
subscription_item:
|
|
331
|
-
provider_price_id:
|
|
332
|
-
limit:
|
|
333
|
-
cursor:
|
|
331
|
+
subscription_item: str | None = None
|
|
332
|
+
provider_price_id: str | None = None
|
|
333
|
+
limit: int | None = Field(default=50, ge=1, le=200)
|
|
334
|
+
cursor: str | None = None
|