svc-infra 0.1.706__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.

Files changed (227) hide show
  1. svc_infra/apf_payments/models.py +47 -108
  2. svc_infra/apf_payments/provider/__init__.py +2 -2
  3. svc_infra/apf_payments/provider/aiydan.py +42 -100
  4. svc_infra/apf_payments/provider/base.py +10 -26
  5. svc_infra/apf_payments/provider/registry.py +3 -5
  6. svc_infra/apf_payments/provider/stripe.py +63 -135
  7. svc_infra/apf_payments/schemas.py +82 -90
  8. svc_infra/apf_payments/service.py +40 -86
  9. svc_infra/apf_payments/settings.py +10 -13
  10. svc_infra/api/__init__.py +13 -13
  11. svc_infra/api/fastapi/__init__.py +19 -0
  12. svc_infra/api/fastapi/admin/add.py +13 -18
  13. svc_infra/api/fastapi/apf_payments/router.py +47 -84
  14. svc_infra/api/fastapi/apf_payments/setup.py +7 -13
  15. svc_infra/api/fastapi/auth/__init__.py +1 -1
  16. svc_infra/api/fastapi/auth/_cookies.py +3 -9
  17. svc_infra/api/fastapi/auth/add.py +4 -8
  18. svc_infra/api/fastapi/auth/gaurd.py +9 -26
  19. svc_infra/api/fastapi/auth/mfa/models.py +4 -7
  20. svc_infra/api/fastapi/auth/mfa/pre_auth.py +3 -3
  21. svc_infra/api/fastapi/auth/mfa/router.py +9 -15
  22. svc_infra/api/fastapi/auth/mfa/security.py +3 -5
  23. svc_infra/api/fastapi/auth/mfa/utils.py +3 -2
  24. svc_infra/api/fastapi/auth/mfa/verify.py +2 -9
  25. svc_infra/api/fastapi/auth/providers.py +4 -6
  26. svc_infra/api/fastapi/auth/routers/apikey_router.py +16 -18
  27. svc_infra/api/fastapi/auth/routers/oauth_router.py +37 -85
  28. svc_infra/api/fastapi/auth/routers/session_router.py +3 -6
  29. svc_infra/api/fastapi/auth/security.py +17 -28
  30. svc_infra/api/fastapi/auth/sender.py +1 -3
  31. svc_infra/api/fastapi/auth/settings.py +18 -19
  32. svc_infra/api/fastapi/auth/state.py +6 -7
  33. svc_infra/api/fastapi/auth/ws_security.py +2 -2
  34. svc_infra/api/fastapi/billing/router.py +6 -8
  35. svc_infra/api/fastapi/db/http.py +10 -11
  36. svc_infra/api/fastapi/db/nosql/mongo/add.py +5 -15
  37. svc_infra/api/fastapi/db/nosql/mongo/crud_router.py +14 -15
  38. svc_infra/api/fastapi/db/sql/add.py +6 -14
  39. svc_infra/api/fastapi/db/sql/crud_router.py +27 -40
  40. svc_infra/api/fastapi/db/sql/health.py +1 -3
  41. svc_infra/api/fastapi/db/sql/session.py +4 -5
  42. svc_infra/api/fastapi/db/sql/users.py +8 -11
  43. svc_infra/api/fastapi/dependencies/ratelimit.py +4 -6
  44. svc_infra/api/fastapi/docs/add.py +13 -23
  45. svc_infra/api/fastapi/docs/landing.py +6 -8
  46. svc_infra/api/fastapi/docs/scoped.py +34 -42
  47. svc_infra/api/fastapi/dual/dualize.py +1 -1
  48. svc_infra/api/fastapi/dual/protected.py +12 -21
  49. svc_infra/api/fastapi/dual/router.py +14 -31
  50. svc_infra/api/fastapi/ease.py +57 -13
  51. svc_infra/api/fastapi/http/conditional.py +3 -5
  52. svc_infra/api/fastapi/middleware/errors/catchall.py +2 -6
  53. svc_infra/api/fastapi/middleware/errors/exceptions.py +1 -4
  54. svc_infra/api/fastapi/middleware/errors/handlers.py +12 -18
  55. svc_infra/api/fastapi/middleware/graceful_shutdown.py +4 -13
  56. svc_infra/api/fastapi/middleware/idempotency.py +11 -16
  57. svc_infra/api/fastapi/middleware/idempotency_store.py +14 -14
  58. svc_infra/api/fastapi/middleware/optimistic_lock.py +5 -8
  59. svc_infra/api/fastapi/middleware/ratelimit.py +8 -8
  60. svc_infra/api/fastapi/middleware/ratelimit_store.py +7 -8
  61. svc_infra/api/fastapi/middleware/request_id.py +1 -3
  62. svc_infra/api/fastapi/middleware/timeout.py +9 -10
  63. svc_infra/api/fastapi/object_router.py +1060 -0
  64. svc_infra/api/fastapi/openapi/apply.py +5 -6
  65. svc_infra/api/fastapi/openapi/conventions.py +4 -4
  66. svc_infra/api/fastapi/openapi/mutators.py +13 -31
  67. svc_infra/api/fastapi/openapi/pipeline.py +2 -2
  68. svc_infra/api/fastapi/openapi/responses.py +4 -6
  69. svc_infra/api/fastapi/openapi/security.py +1 -3
  70. svc_infra/api/fastapi/ops/add.py +7 -9
  71. svc_infra/api/fastapi/pagination.py +25 -37
  72. svc_infra/api/fastapi/routers/__init__.py +16 -38
  73. svc_infra/api/fastapi/setup.py +13 -31
  74. svc_infra/api/fastapi/tenancy/add.py +3 -2
  75. svc_infra/api/fastapi/tenancy/context.py +8 -7
  76. svc_infra/api/fastapi/versioned.py +3 -2
  77. svc_infra/app/env.py +5 -7
  78. svc_infra/app/logging/add.py +2 -1
  79. svc_infra/app/logging/filter.py +1 -1
  80. svc_infra/app/logging/formats.py +3 -2
  81. svc_infra/app/root.py +3 -3
  82. svc_infra/billing/__init__.py +19 -2
  83. svc_infra/billing/async_service.py +27 -7
  84. svc_infra/billing/jobs.py +23 -33
  85. svc_infra/billing/models.py +21 -52
  86. svc_infra/billing/quotas.py +5 -7
  87. svc_infra/billing/schemas.py +4 -6
  88. svc_infra/cache/__init__.py +12 -5
  89. svc_infra/cache/add.py +6 -9
  90. svc_infra/cache/backend.py +6 -5
  91. svc_infra/cache/decorators.py +17 -28
  92. svc_infra/cache/keys.py +2 -2
  93. svc_infra/cache/recache.py +22 -35
  94. svc_infra/cache/resources.py +8 -16
  95. svc_infra/cache/ttl.py +2 -3
  96. svc_infra/cache/utils.py +5 -6
  97. svc_infra/cli/__init__.py +4 -12
  98. svc_infra/cli/cmds/db/nosql/mongo/mongo_cmds.py +11 -10
  99. svc_infra/cli/cmds/db/nosql/mongo/mongo_scaffold_cmds.py +6 -9
  100. svc_infra/cli/cmds/db/ops_cmds.py +3 -6
  101. svc_infra/cli/cmds/db/sql/alembic_cmds.py +24 -41
  102. svc_infra/cli/cmds/db/sql/sql_export_cmds.py +9 -17
  103. svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py +10 -10
  104. svc_infra/cli/cmds/docs/docs_cmds.py +7 -10
  105. svc_infra/cli/cmds/dx/dx_cmds.py +5 -11
  106. svc_infra/cli/cmds/jobs/jobs_cmds.py +2 -7
  107. svc_infra/cli/cmds/obs/obs_cmds.py +4 -7
  108. svc_infra/cli/cmds/sdk/sdk_cmds.py +5 -15
  109. svc_infra/cli/foundation/runner.py +6 -11
  110. svc_infra/cli/foundation/typer_bootstrap.py +1 -2
  111. svc_infra/data/__init__.py +83 -0
  112. svc_infra/data/add.py +5 -5
  113. svc_infra/data/backup.py +8 -10
  114. svc_infra/data/erasure.py +3 -2
  115. svc_infra/data/fixtures.py +3 -3
  116. svc_infra/data/retention.py +8 -13
  117. svc_infra/db/crud_schema.py +9 -8
  118. svc_infra/db/nosql/__init__.py +0 -1
  119. svc_infra/db/nosql/constants.py +1 -1
  120. svc_infra/db/nosql/core.py +7 -14
  121. svc_infra/db/nosql/indexes.py +11 -10
  122. svc_infra/db/nosql/management.py +3 -3
  123. svc_infra/db/nosql/mongo/client.py +3 -3
  124. svc_infra/db/nosql/mongo/settings.py +2 -6
  125. svc_infra/db/nosql/repository.py +27 -28
  126. svc_infra/db/nosql/resource.py +15 -20
  127. svc_infra/db/nosql/scaffold.py +13 -17
  128. svc_infra/db/nosql/service.py +3 -4
  129. svc_infra/db/nosql/service_with_hooks.py +4 -3
  130. svc_infra/db/nosql/types.py +2 -6
  131. svc_infra/db/nosql/utils.py +4 -4
  132. svc_infra/db/ops.py +14 -18
  133. svc_infra/db/outbox.py +15 -18
  134. svc_infra/db/sql/apikey.py +12 -21
  135. svc_infra/db/sql/authref.py +3 -7
  136. svc_infra/db/sql/constants.py +9 -9
  137. svc_infra/db/sql/core.py +11 -11
  138. svc_infra/db/sql/management.py +2 -6
  139. svc_infra/db/sql/repository.py +17 -24
  140. svc_infra/db/sql/resource.py +14 -13
  141. svc_infra/db/sql/scaffold.py +13 -17
  142. svc_infra/db/sql/service.py +7 -16
  143. svc_infra/db/sql/service_with_hooks.py +4 -3
  144. svc_infra/db/sql/tenant.py +6 -14
  145. svc_infra/db/sql/uniq.py +8 -7
  146. svc_infra/db/sql/uniq_hooks.py +14 -19
  147. svc_infra/db/sql/utils.py +24 -53
  148. svc_infra/db/utils.py +3 -3
  149. svc_infra/deploy/__init__.py +8 -15
  150. svc_infra/documents/add.py +7 -8
  151. svc_infra/documents/ease.py +8 -8
  152. svc_infra/documents/models.py +3 -3
  153. svc_infra/documents/storage.py +11 -13
  154. svc_infra/dx/__init__.py +58 -0
  155. svc_infra/dx/add.py +1 -3
  156. svc_infra/dx/changelog.py +2 -2
  157. svc_infra/dx/checks.py +1 -1
  158. svc_infra/health/__init__.py +15 -16
  159. svc_infra/http/client.py +10 -14
  160. svc_infra/jobs/__init__.py +79 -0
  161. svc_infra/jobs/builtins/outbox_processor.py +3 -5
  162. svc_infra/jobs/builtins/webhook_delivery.py +1 -3
  163. svc_infra/jobs/loader.py +4 -5
  164. svc_infra/jobs/queue.py +14 -24
  165. svc_infra/jobs/redis_queue.py +20 -34
  166. svc_infra/jobs/runner.py +7 -11
  167. svc_infra/jobs/scheduler.py +5 -5
  168. svc_infra/jobs/worker.py +1 -1
  169. svc_infra/loaders/base.py +5 -4
  170. svc_infra/loaders/github.py +1 -3
  171. svc_infra/loaders/url.py +3 -9
  172. svc_infra/logging/__init__.py +7 -6
  173. svc_infra/mcp/__init__.py +82 -0
  174. svc_infra/mcp/svc_infra_mcp.py +2 -2
  175. svc_infra/obs/add.py +4 -3
  176. svc_infra/obs/cloud_dash.py +1 -1
  177. svc_infra/obs/metrics/__init__.py +3 -3
  178. svc_infra/obs/metrics/asgi.py +9 -14
  179. svc_infra/obs/metrics/base.py +13 -13
  180. svc_infra/obs/metrics/http.py +5 -9
  181. svc_infra/obs/metrics/sqlalchemy.py +9 -12
  182. svc_infra/obs/metrics.py +3 -3
  183. svc_infra/obs/settings.py +2 -6
  184. svc_infra/resilience/__init__.py +44 -0
  185. svc_infra/resilience/circuit_breaker.py +328 -0
  186. svc_infra/resilience/retry.py +289 -0
  187. svc_infra/security/__init__.py +167 -0
  188. svc_infra/security/add.py +5 -9
  189. svc_infra/security/audit.py +14 -17
  190. svc_infra/security/audit_service.py +9 -9
  191. svc_infra/security/hibp.py +3 -6
  192. svc_infra/security/jwt_rotation.py +7 -10
  193. svc_infra/security/lockout.py +12 -11
  194. svc_infra/security/models.py +37 -46
  195. svc_infra/security/oauth_models.py +8 -8
  196. svc_infra/security/org_invites.py +11 -13
  197. svc_infra/security/passwords.py +4 -6
  198. svc_infra/security/permissions.py +8 -7
  199. svc_infra/security/session.py +6 -7
  200. svc_infra/security/signed_cookies.py +9 -9
  201. svc_infra/storage/add.py +5 -8
  202. svc_infra/storage/backends/local.py +13 -21
  203. svc_infra/storage/backends/memory.py +4 -7
  204. svc_infra/storage/backends/s3.py +17 -36
  205. svc_infra/storage/base.py +2 -2
  206. svc_infra/storage/easy.py +4 -8
  207. svc_infra/storage/settings.py +16 -18
  208. svc_infra/testing/__init__.py +36 -39
  209. svc_infra/utils.py +169 -8
  210. svc_infra/webhooks/__init__.py +1 -1
  211. svc_infra/webhooks/add.py +17 -29
  212. svc_infra/webhooks/encryption.py +2 -2
  213. svc_infra/webhooks/fastapi.py +2 -4
  214. svc_infra/webhooks/router.py +3 -3
  215. svc_infra/webhooks/service.py +5 -6
  216. svc_infra/webhooks/signing.py +5 -5
  217. svc_infra/websocket/add.py +2 -3
  218. svc_infra/websocket/client.py +3 -2
  219. svc_infra/websocket/config.py +6 -18
  220. svc_infra/websocket/manager.py +9 -10
  221. {svc_infra-0.1.706.dist-info → svc_infra-1.1.0.dist-info}/METADATA +11 -5
  222. svc_infra-1.1.0.dist-info/RECORD +364 -0
  223. svc_infra/billing/service.py +0 -123
  224. svc_infra-0.1.706.dist-info/RECORD +0 -357
  225. {svc_infra-0.1.706.dist-info → svc_infra-1.1.0.dist-info}/LICENSE +0 -0
  226. {svc_infra-0.1.706.dist-info → svc_infra-1.1.0.dist-info}/WHEEL +0 -0
  227. {svc_infra-0.1.706.dist-info → svc_infra-1.1.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Optional, Protocol
3
+ from typing import Any, Protocol
4
4
 
5
5
  from ..schemas import (
6
6
  BalanceSnapshotOut,
@@ -42,14 +42,10 @@ class ProviderAdapter(Protocol):
42
42
  async def ensure_customer(self, data: CustomerUpsertIn) -> CustomerOut:
43
43
  pass
44
44
 
45
- async def attach_payment_method(
46
- self, data: PaymentMethodAttachIn
47
- ) -> PaymentMethodOut:
45
+ async def attach_payment_method(self, data: PaymentMethodAttachIn) -> PaymentMethodOut:
48
46
  pass
49
47
 
50
- async def list_payment_methods(
51
- self, provider_customer_id: str
52
- ) -> list[PaymentMethodOut]:
48
+ async def list_payment_methods(self, provider_customer_id: str) -> list[PaymentMethodOut]:
53
49
  pass
54
50
 
55
51
  async def detach_payment_method(self, provider_method_id: str) -> PaymentMethodOut:
@@ -91,9 +87,7 @@ class ProviderAdapter(Protocol):
91
87
  async def pay_invoice(self, provider_invoice_id: str) -> InvoiceOut:
92
88
  pass
93
89
 
94
- async def create_intent(
95
- self, data: IntentCreateIn, *, user_id: str | None
96
- ) -> IntentOut:
90
+ async def create_intent(self, data: IntentCreateIn, *, user_id: str | None) -> IntentOut:
97
91
  pass
98
92
 
99
93
  async def confirm_intent(self, provider_intent_id: str) -> IntentOut:
@@ -113,9 +107,7 @@ class ProviderAdapter(Protocol):
113
107
  ) -> dict[str, Any]:
114
108
  pass
115
109
 
116
- async def capture_intent(
117
- self, provider_intent_id: str, *, amount: int | None
118
- ) -> IntentOut:
110
+ async def capture_intent(self, provider_intent_id: str, *, amount: int | None) -> IntentOut:
119
111
  pass
120
112
 
121
113
  async def list_intents(
@@ -158,9 +150,7 @@ class ProviderAdapter(Protocol):
158
150
  async def create_setup_intent(self, data: SetupIntentCreateIn) -> SetupIntentOut:
159
151
  pass
160
152
 
161
- async def confirm_setup_intent(
162
- self, provider_setup_intent_id: str
163
- ) -> SetupIntentOut:
153
+ async def confirm_setup_intent(self, provider_setup_intent_id: str) -> SetupIntentOut:
164
154
  pass
165
155
 
166
156
  async def get_setup_intent(self, provider_setup_intent_id: str) -> SetupIntentOut:
@@ -179,9 +169,7 @@ class ProviderAdapter(Protocol):
179
169
  async def get_dispute(self, provider_dispute_id: str) -> DisputeOut:
180
170
  pass
181
171
 
182
- async def submit_dispute_evidence(
183
- self, provider_dispute_id: str, evidence: dict
184
- ) -> DisputeOut:
172
+ async def submit_dispute_evidence(self, provider_dispute_id: str, evidence: dict) -> DisputeOut:
185
173
  pass
186
174
 
187
175
  # --- Balance & Payouts ---
@@ -208,7 +196,7 @@ class ProviderAdapter(Protocol):
208
196
  """Optional: if not implemented, the service will list from local DB."""
209
197
  pass
210
198
 
211
- async def get_customer(self, provider_customer_id: str) -> Optional[CustomerOut]:
199
+ async def get_customer(self, provider_customer_id: str) -> CustomerOut | None:
212
200
  pass
213
201
 
214
202
  # --- Products / Prices ---
@@ -220,9 +208,7 @@ class ProviderAdapter(Protocol):
220
208
  ) -> tuple[list[ProductOut], str | None]:
221
209
  pass
222
210
 
223
- async def update_product(
224
- self, provider_product_id: str, data: ProductUpdateIn
225
- ) -> ProductOut:
211
+ async def update_product(self, provider_product_id: str, data: ProductUpdateIn) -> ProductOut:
226
212
  pass
227
213
 
228
214
  async def get_price(self, provider_price_id: str) -> PriceOut:
@@ -238,9 +224,7 @@ class ProviderAdapter(Protocol):
238
224
  ) -> tuple[list[PriceOut], str | None]:
239
225
  pass
240
226
 
241
- async def update_price(
242
- self, provider_price_id: str, data: PriceUpdateIn
243
- ) -> PriceOut:
227
+ async def update_price(self, provider_price_id: str, data: PriceUpdateIn) -> PriceOut:
244
228
  pass
245
229
 
246
230
  # --- Subscriptions ---
@@ -1,19 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Dict, Optional
4
-
5
3
  from ..settings import get_payments_settings
6
4
  from .base import ProviderAdapter
7
5
 
8
6
 
9
7
  class ProviderRegistry:
10
8
  def __init__(self):
11
- self._adapters: Dict[str, ProviderAdapter] = {}
9
+ self._adapters: dict[str, ProviderAdapter] = {}
12
10
 
13
11
  def register(self, adapter: ProviderAdapter):
14
12
  self._adapters[adapter.name] = adapter
15
13
 
16
- def get(self, name: Optional[str] = None) -> ProviderAdapter:
14
+ def get(self, name: str | None = None) -> ProviderAdapter:
17
15
  settings = get_payments_settings()
18
16
  key = (name or settings.default_provider).lower()
19
17
  if key not in self._adapters:
@@ -21,7 +19,7 @@ class ProviderRegistry:
21
19
  return self._adapters[key]
22
20
 
23
21
 
24
- _REGISTRY: Optional[ProviderRegistry] = None
22
+ _REGISTRY: ProviderRegistry | None = None
25
23
 
26
24
 
27
25
  def get_provider_registry() -> ProviderRegistry:
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from functools import partial
4
- from typing import Any, Optional
4
+ from typing import Any
5
5
 
6
6
  import anyio
7
7
 
@@ -60,9 +60,7 @@ def _pi_to_out(pi) -> IntentOut:
60
60
  amount=int(pi.amount),
61
61
  currency=str(pi.currency).upper(),
62
62
  client_secret=getattr(pi, "client_secret", None),
63
- next_action=NextAction(
64
- type=getattr(getattr(pi, "next_action", None), "type", None)
65
- ),
63
+ next_action=NextAction(type=getattr(getattr(pi, "next_action", None), "type", None)),
66
64
  )
67
65
 
68
66
 
@@ -136,9 +134,7 @@ def _sub_to_out(s) -> SubscriptionOut:
136
134
  quantity=int(qty or 0),
137
135
  cancel_at_period_end=bool(s.cancel_at_period_end),
138
136
  current_period_end=(
139
- str(s.current_period_end)
140
- if getattr(s, "current_period_end", None)
141
- else None
137
+ str(s.current_period_end) if getattr(s, "current_period_end", None) else None
142
138
  ),
143
139
  )
144
140
 
@@ -167,9 +163,7 @@ def _dispute_to_out(d) -> DisputeOut:
167
163
  reason=getattr(d, "reason", None),
168
164
  status=d.status,
169
165
  evidence_due_by=(
170
- str(d.evidence_details.get("due_by"))
171
- if getattr(d, "evidence_details", None)
172
- else None
166
+ str(d.evidence_details.get("due_by")) if getattr(d, "evidence_details", None) else None
173
167
  ),
174
168
  created_at=str(d.created) if getattr(d, "created", None) else None,
175
169
  )
@@ -199,9 +193,7 @@ class StripeAdapter(ProviderAdapter):
199
193
  raise RuntimeError("stripe SDK is not installed. pip install stripe")
200
194
  stripe.api_key = st.stripe.secret_key.get_secret_value()
201
195
  self._wh_secret = (
202
- st.stripe.webhook_secret.get_secret_value()
203
- if st.stripe.webhook_secret
204
- else None
196
+ st.stripe.webhook_secret.get_secret_value() if st.stripe.webhook_secret else None
205
197
  )
206
198
 
207
199
  # -------- Customers --------
@@ -232,7 +224,7 @@ class StripeAdapter(ProviderAdapter):
232
224
  name=c.get("name"),
233
225
  )
234
226
 
235
- async def get_customer(self, provider_customer_id: str) -> Optional[CustomerOut]:
227
+ async def get_customer(self, provider_customer_id: str) -> CustomerOut | None:
236
228
  c = await _acall(stripe.Customer.retrieve, provider_customer_id)
237
229
  return CustomerOut(
238
230
  id=c.id,
@@ -266,15 +258,11 @@ class StripeAdapter(ProviderAdapter):
266
258
  )
267
259
  for c in res.data
268
260
  ]
269
- next_cursor = (
270
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
271
- )
261
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
272
262
  return items, next_cursor
273
263
 
274
264
  # -------- Payment Methods --------
275
- async def attach_payment_method(
276
- self, data: PaymentMethodAttachIn
277
- ) -> PaymentMethodOut:
265
+ async def attach_payment_method(self, data: PaymentMethodAttachIn) -> PaymentMethodOut:
278
266
  pm = await _acall(
279
267
  stripe.PaymentMethod.attach,
280
268
  data.payment_method_token,
@@ -307,16 +295,12 @@ class StripeAdapter(ProviderAdapter):
307
295
  )
308
296
  return _pm_to_out(pm, is_default=is_default)
309
297
 
310
- async def list_payment_methods(
311
- self, provider_customer_id: str
312
- ) -> list[PaymentMethodOut]:
298
+ async def list_payment_methods(self, provider_customer_id: str) -> list[PaymentMethodOut]:
313
299
  cust = await _acall(stripe.Customer.retrieve, provider_customer_id)
314
300
  default_pm = getattr(
315
301
  getattr(cust, "invoice_settings", None), "default_payment_method", None
316
302
  )
317
- res = await _acall(
318
- stripe.PaymentMethod.list, customer=provider_customer_id, type="card"
319
- )
303
+ res = await _acall(stripe.PaymentMethod.list, customer=provider_customer_id, type="card")
320
304
  return [_pm_to_out(pm, is_default=(pm.id == default_pm)) for pm in res.data]
321
305
 
322
306
  async def detach_payment_method(self, provider_method_id: str) -> PaymentMethodOut:
@@ -341,9 +325,7 @@ class StripeAdapter(ProviderAdapter):
341
325
  )
342
326
  pm = await _acall(stripe.PaymentMethod.retrieve, provider_method_id)
343
327
  is_default = (
344
- getattr(
345
- getattr(cust, "invoice_settings", None), "default_payment_method", None
346
- )
328
+ getattr(getattr(cust, "invoice_settings", None), "default_payment_method", None)
347
329
  == pm.id
348
330
  )
349
331
  return _pm_to_out(pm, is_default=is_default)
@@ -387,9 +369,7 @@ class StripeAdapter(ProviderAdapter):
387
369
 
388
370
  # -------- Products / Prices --------
389
371
  async def create_product(self, data: ProductCreateIn) -> ProductOut:
390
- p = await _acall(
391
- stripe.Product.create, name=data.name, active=bool(data.active)
392
- )
372
+ p = await _acall(stripe.Product.create, name=data.name, active=bool(data.active))
393
373
  return _product_to_out(p)
394
374
 
395
375
  async def get_product(self, provider_product_id: str) -> ProductOut:
@@ -406,14 +386,10 @@ class StripeAdapter(ProviderAdapter):
406
386
  params["starting_after"] = cursor
407
387
  res = await _acall(stripe.Product.list, **params)
408
388
  items = [_product_to_out(p) for p in res.data]
409
- next_cursor = (
410
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
411
- )
389
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
412
390
  return items, next_cursor
413
391
 
414
- async def update_product(
415
- self, provider_product_id: str, data: ProductUpdateIn
416
- ) -> ProductOut:
392
+ async def update_product(self, provider_product_id: str, data: ProductUpdateIn) -> ProductOut:
417
393
  update: dict[str, Any] = {}
418
394
  if data.name is not None:
419
395
  update["name"] = data.name
@@ -427,12 +403,12 @@ class StripeAdapter(ProviderAdapter):
427
403
  return _product_to_out(p)
428
404
 
429
405
  async def create_price(self, data: PriceCreateIn) -> PriceOut:
430
- kwargs: dict[str, Any] = dict(
431
- product=data.provider_product_id,
432
- currency=data.currency.lower(),
433
- unit_amount=int(data.unit_amount),
434
- active=bool(data.active),
435
- )
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
+ }
436
412
  if data.interval:
437
413
  kwargs["recurring"] = {"interval": data.interval}
438
414
  if data.trial_days is not None:
@@ -461,14 +437,10 @@ class StripeAdapter(ProviderAdapter):
461
437
  params["starting_after"] = cursor
462
438
  res = await _acall(stripe.Price.list, **params)
463
439
  items = [_price_to_out(p) for p in res.data]
464
- next_cursor = (
465
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
466
- )
440
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
467
441
  return items, next_cursor
468
442
 
469
- async def update_price(
470
- self, provider_price_id: str, data: PriceUpdateIn
471
- ) -> PriceOut:
443
+ async def update_price(self, provider_price_id: str, data: PriceUpdateIn) -> PriceOut:
472
444
  # Stripe allows toggling `active` and updating metadata, but not amount/currency/product.
473
445
  update: dict[str, Any] = {}
474
446
  if data.active is not None:
@@ -482,11 +454,11 @@ class StripeAdapter(ProviderAdapter):
482
454
 
483
455
  # -------- Subscriptions --------
484
456
  async def create_subscription(self, data: SubscriptionCreateIn) -> SubscriptionOut:
485
- kwargs: dict[str, Any] = dict(
486
- customer=data.customer_provider_id,
487
- items=[{"price": data.price_provider_id, "quantity": int(data.quantity)}],
488
- proration_behavior=data.proration_behavior,
489
- )
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
+ }
490
462
  if data.trial_days is not None:
491
463
  kwargs["trial_period_days"] = int(data.trial_days)
492
464
  s = await _acall(stripe.Subscription.create, **kwargs)
@@ -495,9 +467,7 @@ class StripeAdapter(ProviderAdapter):
495
467
  async def update_subscription(
496
468
  self, provider_subscription_id: str, data: SubscriptionUpdateIn
497
469
  ) -> SubscriptionOut:
498
- s = await _acall(
499
- stripe.Subscription.retrieve, provider_subscription_id, expand=["items"]
500
- )
470
+ s = await _acall(stripe.Subscription.retrieve, provider_subscription_id, expand=["items"])
501
471
  items = s.items.data
502
472
  update_kwargs: dict[str, Any] = {"proration_behavior": data.proration_behavior}
503
473
  # update first item (simple plan model)
@@ -511,27 +481,21 @@ class StripeAdapter(ProviderAdapter):
511
481
  update_kwargs["items"] = [item_update]
512
482
  if data.cancel_at_period_end is not None:
513
483
  update_kwargs["cancel_at_period_end"] = bool(data.cancel_at_period_end)
514
- s2 = await _acall(
515
- stripe.Subscription.modify, provider_subscription_id, **update_kwargs
516
- )
484
+ s2 = await _acall(stripe.Subscription.modify, provider_subscription_id, **update_kwargs)
517
485
  return _sub_to_out(s2)
518
486
 
519
487
  async def cancel_subscription(
520
488
  self, provider_subscription_id: str, at_period_end: bool = True
521
489
  ) -> SubscriptionOut:
522
490
  s = await _acall(
523
- stripe.Subscription.cancel
524
- if not at_period_end
525
- else stripe.Subscription.modify,
491
+ stripe.Subscription.cancel if not at_period_end else stripe.Subscription.modify,
526
492
  provider_subscription_id,
527
493
  **({} if not at_period_end else {"cancel_at_period_end": True}),
528
494
  )
529
495
  return _sub_to_out(s)
530
496
 
531
497
  async def get_subscription(self, provider_subscription_id: str) -> SubscriptionOut:
532
- s = await _acall(
533
- stripe.Subscription.retrieve, provider_subscription_id, expand=["items"]
534
- )
498
+ s = await _acall(stripe.Subscription.retrieve, provider_subscription_id, expand=["items"])
535
499
  return _sub_to_out(s)
536
500
 
537
501
  async def list_subscriptions(
@@ -551,9 +515,7 @@ class StripeAdapter(ProviderAdapter):
551
515
  params["starting_after"] = cursor
552
516
  res = await _acall(stripe.Subscription.list, **params)
553
517
  items = [_sub_to_out(s) for s in res.data]
554
- next_cursor = (
555
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
556
- )
518
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
557
519
  return items, next_cursor
558
520
 
559
521
  # -------- Invoices --------
@@ -581,13 +543,13 @@ class StripeAdapter(ProviderAdapter):
581
543
  self, provider_invoice_id: str, data: InvoiceLineItemIn
582
544
  ) -> InvoiceOut:
583
545
  # attach an item to a DRAFT invoice
584
- kwargs: dict[str, Any] = dict(
585
- invoice=provider_invoice_id,
586
- customer=data.customer_provider_id,
587
- quantity=int(data.quantity or 1),
588
- currency=data.currency.lower(),
589
- description=data.description or None,
590
- )
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
+ }
591
553
  if data.provider_price_id:
592
554
  kwargs["price"] = data.provider_price_id
593
555
  else:
@@ -616,9 +578,7 @@ class StripeAdapter(ProviderAdapter):
616
578
  params["starting_after"] = cursor
617
579
  res = await _acall(stripe.Invoice.list, **params)
618
580
  items = [_inv_to_out(inv) for inv in res.data]
619
- next_cursor = (
620
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
621
- )
581
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
622
582
  return items, next_cursor
623
583
 
624
584
  async def get_invoice(self, provider_invoice_id: str) -> InvoiceOut:
@@ -656,24 +616,20 @@ class StripeAdapter(ProviderAdapter):
656
616
  provider_price_id=price_id,
657
617
  )
658
618
  )
659
- next_cursor = (
660
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
661
- )
619
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
662
620
  return items, next_cursor
663
621
 
664
622
  # -------- Intents --------
665
- async def create_intent(
666
- self, data: IntentCreateIn, *, user_id: str | None
667
- ) -> IntentOut:
668
- kwargs: dict[str, Any] = dict(
669
- amount=int(data.amount),
670
- currency=data.currency.lower(),
671
- description=data.description or None,
672
- capture_method="manual" if data.capture_method == "manual" else "automatic",
673
- automatic_payment_methods={"enabled": True}
623
+ async def create_intent(self, data: IntentCreateIn, *, user_id: str | None) -> IntentOut:
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}
674
630
  if not data.payment_method_types
675
631
  else None,
676
- )
632
+ }
677
633
  if data.payment_method_types:
678
634
  kwargs["payment_method_types"] = data.payment_method_types
679
635
  pi = await _acall(
@@ -709,9 +665,7 @@ class StripeAdapter(ProviderAdapter):
709
665
  pi = await _acall(stripe.PaymentIntent.retrieve, provider_intent_id)
710
666
  return _pi_to_out(pi)
711
667
 
712
- async def capture_intent(
713
- self, provider_intent_id: str, *, amount: int | None
714
- ) -> IntentOut:
668
+ async def capture_intent(self, provider_intent_id: str, *, amount: int | None) -> IntentOut:
715
669
  kwargs = {}
716
670
  if amount is not None:
717
671
  kwargs["amount_to_capture"] = int(amount)
@@ -735,9 +689,7 @@ class StripeAdapter(ProviderAdapter):
735
689
  params["starting_after"] = cursor
736
690
  res = await _acall(stripe.PaymentIntent.list, **params)
737
691
  items = [_pi_to_out(pi) for pi in res.data]
738
- next_cursor = (
739
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
740
- )
692
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
741
693
  return items, next_cursor
742
694
 
743
695
  # ---- Setup Intents (off-session readiness) ----
@@ -752,14 +704,10 @@ class StripeAdapter(ProviderAdapter):
752
704
  provider_setup_intent_id=si.id,
753
705
  status=si.status,
754
706
  client_secret=getattr(si, "client_secret", None),
755
- next_action=NextAction(
756
- type=getattr(getattr(si, "next_action", None), "type", None)
757
- ),
707
+ next_action=NextAction(type=getattr(getattr(si, "next_action", None), "type", None)),
758
708
  )
759
709
 
760
- async def confirm_setup_intent(
761
- self, provider_setup_intent_id: str
762
- ) -> SetupIntentOut:
710
+ async def confirm_setup_intent(self, provider_setup_intent_id: str) -> SetupIntentOut:
763
711
  si = await _acall(stripe.SetupIntent.confirm, provider_setup_intent_id)
764
712
  return SetupIntentOut(
765
713
  id=si.id,
@@ -767,9 +715,7 @@ class StripeAdapter(ProviderAdapter):
767
715
  provider_setup_intent_id=si.id,
768
716
  status=si.status,
769
717
  client_secret=getattr(si, "client_secret", None),
770
- next_action=NextAction(
771
- type=getattr(getattr(si, "next_action", None), "type", None)
772
- ),
718
+ next_action=NextAction(type=getattr(getattr(si, "next_action", None), "type", None)),
773
719
  )
774
720
 
775
721
  async def get_setup_intent(self, provider_setup_intent_id: str) -> SetupIntentOut:
@@ -780,9 +726,7 @@ class StripeAdapter(ProviderAdapter):
780
726
  provider_setup_intent_id=si.id,
781
727
  status=si.status,
782
728
  client_secret=getattr(si, "client_secret", None),
783
- next_action=NextAction(
784
- type=getattr(getattr(si, "next_action", None), "type", None)
785
- ),
729
+ next_action=NextAction(type=getattr(getattr(si, "next_action", None), "type", None)),
786
730
  )
787
731
 
788
732
  # ---- 3DS/SCA resume ----
@@ -801,18 +745,14 @@ class StripeAdapter(ProviderAdapter):
801
745
  params["starting_after"] = cursor
802
746
  res = await _acall(stripe.Dispute.list, **params)
803
747
  items = [_dispute_to_out(d) for d in res.data]
804
- next_cursor = (
805
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
806
- )
748
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
807
749
  return items, next_cursor
808
750
 
809
751
  async def get_dispute(self, provider_dispute_id: str) -> DisputeOut:
810
752
  d = await _acall(stripe.Dispute.retrieve, provider_dispute_id)
811
753
  return _dispute_to_out(d)
812
754
 
813
- async def submit_dispute_evidence(
814
- self, provider_dispute_id: str, evidence: dict
815
- ) -> DisputeOut:
755
+ async def submit_dispute_evidence(self, provider_dispute_id: str, evidence: dict) -> DisputeOut:
816
756
  d = await _acall(stripe.Dispute.modify, provider_dispute_id, evidence=evidence)
817
757
  # Some disputes require explicit submit call:
818
758
  try:
@@ -828,9 +768,7 @@ class StripeAdapter(ProviderAdapter):
828
768
  def _bucket(entries):
829
769
  out = []
830
770
  for b in entries or []:
831
- out.append(
832
- {"currency": str(b.currency).upper(), "amount": int(b.amount)}
833
- )
771
+ out.append({"currency": str(b.currency).upper(), "amount": int(b.amount)})
834
772
  return out
835
773
 
836
774
  return BalanceSnapshotOut(
@@ -846,9 +784,7 @@ class StripeAdapter(ProviderAdapter):
846
784
  params["starting_after"] = cursor
847
785
  res = await _acall(stripe.Payout.list, **params)
848
786
  items = [_payout_to_out(p) for p in res.data]
849
- next_cursor = (
850
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
851
- )
787
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
852
788
  return items, next_cursor
853
789
 
854
790
  async def get_payout(self, provider_payout_id: str) -> PayoutOut:
@@ -866,9 +802,7 @@ class StripeAdapter(ProviderAdapter):
866
802
  params["starting_after"] = cursor
867
803
  res = await _acall(stripe.Refund.list, **params)
868
804
  items = [_refund_to_out(r) for r in res.data]
869
- next_cursor = (
870
- res.data[-1].id if getattr(res, "has_more", False) and res.data else None
871
- )
805
+ next_cursor = res.data[-1].id if getattr(res, "has_more", False) and res.data else None
872
806
  return items, next_cursor
873
807
 
874
808
  async def get_refund(self, provider_refund_id: str) -> RefundOut:
@@ -909,9 +843,7 @@ class StripeAdapter(ProviderAdapter):
909
843
  # Stripe exposes *summaries* per period. We surface them as list results.
910
844
  sub_item = f.subscription_item
911
845
  if not sub_item and f.provider_price_id:
912
- items = await _acall(
913
- stripe.SubscriptionItem.list, price=f.provider_price_id, limit=1
914
- )
846
+ items = await _acall(stripe.SubscriptionItem.list, price=f.provider_price_id, limit=1)
915
847
  sub_item = items.data[0].id if items.data else None
916
848
  if not sub_item:
917
849
  return [], None
@@ -938,9 +870,7 @@ class StripeAdapter(ProviderAdapter):
938
870
  )
939
871
  next_cursor = (
940
872
  res.data[-1].id
941
- if getattr(res, "has_more", False)
942
- and res.data
943
- and hasattr(res.data[-1], "id")
873
+ if getattr(res, "has_more", False) and res.data and hasattr(res.data[-1], "id")
944
874
  else None
945
875
  )
946
876
  return usage_records, next_cursor
@@ -948,9 +878,7 @@ class StripeAdapter(ProviderAdapter):
948
878
  async def get_usage_record(self, usage_record_id: str) -> UsageRecordOut:
949
879
  # Stripe has no direct "retrieve usage record by id" API.
950
880
  # You can reconstruct via list summaries or store records locally when creating.
951
- raise NotImplementedError(
952
- "Stripe does not support retrieving a single usage record by id"
953
- )
881
+ raise NotImplementedError("Stripe does not support retrieving a single usage record by id")
954
882
 
955
883
  # -------- Webhooks --------
956
884
  async def verify_and_parse_webhook(