svc-infra 0.1.506__py3-none-any.whl → 0.1.654__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 (202) hide show
  1. svc_infra/apf_payments/README.md +732 -0
  2. svc_infra/apf_payments/alembic.py +11 -0
  3. svc_infra/apf_payments/models.py +339 -0
  4. svc_infra/apf_payments/provider/__init__.py +4 -0
  5. svc_infra/apf_payments/provider/aiydan.py +797 -0
  6. svc_infra/apf_payments/provider/base.py +270 -0
  7. svc_infra/apf_payments/provider/registry.py +31 -0
  8. svc_infra/apf_payments/provider/stripe.py +873 -0
  9. svc_infra/apf_payments/schemas.py +333 -0
  10. svc_infra/apf_payments/service.py +892 -0
  11. svc_infra/apf_payments/settings.py +67 -0
  12. svc_infra/api/fastapi/__init__.py +6 -0
  13. svc_infra/api/fastapi/admin/__init__.py +3 -0
  14. svc_infra/api/fastapi/admin/add.py +231 -0
  15. svc_infra/api/fastapi/apf_payments/__init__.py +0 -0
  16. svc_infra/api/fastapi/apf_payments/router.py +1082 -0
  17. svc_infra/api/fastapi/apf_payments/setup.py +73 -0
  18. svc_infra/api/fastapi/auth/add.py +15 -6
  19. svc_infra/api/fastapi/auth/gaurd.py +67 -5
  20. svc_infra/api/fastapi/auth/mfa/router.py +18 -9
  21. svc_infra/api/fastapi/auth/routers/account.py +3 -2
  22. svc_infra/api/fastapi/auth/routers/apikey_router.py +11 -5
  23. svc_infra/api/fastapi/auth/routers/oauth_router.py +82 -37
  24. svc_infra/api/fastapi/auth/routers/session_router.py +63 -0
  25. svc_infra/api/fastapi/auth/security.py +3 -1
  26. svc_infra/api/fastapi/auth/settings.py +2 -0
  27. svc_infra/api/fastapi/auth/state.py +1 -1
  28. svc_infra/api/fastapi/billing/router.py +64 -0
  29. svc_infra/api/fastapi/billing/setup.py +19 -0
  30. svc_infra/api/fastapi/cache/add.py +9 -5
  31. svc_infra/api/fastapi/db/nosql/mongo/add.py +33 -27
  32. svc_infra/api/fastapi/db/sql/add.py +40 -18
  33. svc_infra/api/fastapi/db/sql/crud_router.py +176 -14
  34. svc_infra/api/fastapi/db/sql/session.py +16 -0
  35. svc_infra/api/fastapi/db/sql/users.py +14 -2
  36. svc_infra/api/fastapi/dependencies/ratelimit.py +116 -0
  37. svc_infra/api/fastapi/docs/add.py +160 -0
  38. svc_infra/api/fastapi/docs/landing.py +1 -1
  39. svc_infra/api/fastapi/docs/scoped.py +254 -0
  40. svc_infra/api/fastapi/dual/dualize.py +38 -33
  41. svc_infra/api/fastapi/dual/router.py +48 -1
  42. svc_infra/api/fastapi/dx.py +3 -3
  43. svc_infra/api/fastapi/http/__init__.py +0 -0
  44. svc_infra/api/fastapi/http/concurrency.py +14 -0
  45. svc_infra/api/fastapi/http/conditional.py +33 -0
  46. svc_infra/api/fastapi/http/deprecation.py +21 -0
  47. svc_infra/api/fastapi/middleware/errors/handlers.py +45 -7
  48. svc_infra/api/fastapi/middleware/graceful_shutdown.py +87 -0
  49. svc_infra/api/fastapi/middleware/idempotency.py +116 -0
  50. svc_infra/api/fastapi/middleware/idempotency_store.py +187 -0
  51. svc_infra/api/fastapi/middleware/optimistic_lock.py +37 -0
  52. svc_infra/api/fastapi/middleware/ratelimit.py +119 -0
  53. svc_infra/api/fastapi/middleware/ratelimit_store.py +84 -0
  54. svc_infra/api/fastapi/middleware/request_id.py +23 -0
  55. svc_infra/api/fastapi/middleware/request_size_limit.py +36 -0
  56. svc_infra/api/fastapi/middleware/timeout.py +148 -0
  57. svc_infra/api/fastapi/openapi/mutators.py +768 -55
  58. svc_infra/api/fastapi/ops/add.py +73 -0
  59. svc_infra/api/fastapi/pagination.py +363 -0
  60. svc_infra/api/fastapi/paths/auth.py +14 -14
  61. svc_infra/api/fastapi/paths/prefix.py +0 -1
  62. svc_infra/api/fastapi/paths/user.py +1 -1
  63. svc_infra/api/fastapi/routers/ping.py +1 -0
  64. svc_infra/api/fastapi/setup.py +48 -15
  65. svc_infra/api/fastapi/tenancy/add.py +19 -0
  66. svc_infra/api/fastapi/tenancy/context.py +112 -0
  67. svc_infra/api/fastapi/versioned.py +101 -0
  68. svc_infra/app/README.md +5 -5
  69. svc_infra/billing/__init__.py +23 -0
  70. svc_infra/billing/async_service.py +147 -0
  71. svc_infra/billing/jobs.py +230 -0
  72. svc_infra/billing/models.py +131 -0
  73. svc_infra/billing/quotas.py +101 -0
  74. svc_infra/billing/schemas.py +33 -0
  75. svc_infra/billing/service.py +115 -0
  76. svc_infra/bundled_docs/README.md +5 -0
  77. svc_infra/bundled_docs/__init__.py +1 -0
  78. svc_infra/bundled_docs/getting-started.md +6 -0
  79. svc_infra/cache/__init__.py +4 -0
  80. svc_infra/cache/add.py +158 -0
  81. svc_infra/cache/backend.py +5 -2
  82. svc_infra/cache/decorators.py +19 -1
  83. svc_infra/cache/keys.py +24 -4
  84. svc_infra/cli/__init__.py +32 -8
  85. svc_infra/cli/__main__.py +4 -0
  86. svc_infra/cli/cmds/__init__.py +10 -0
  87. svc_infra/cli/cmds/db/nosql/mongo/mongo_cmds.py +4 -3
  88. svc_infra/cli/cmds/db/nosql/mongo/mongo_scaffold_cmds.py +4 -4
  89. svc_infra/cli/cmds/db/sql/alembic_cmds.py +120 -14
  90. svc_infra/cli/cmds/db/sql/sql_export_cmds.py +80 -0
  91. svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py +5 -4
  92. svc_infra/cli/cmds/docs/docs_cmds.py +140 -0
  93. svc_infra/cli/cmds/dx/__init__.py +12 -0
  94. svc_infra/cli/cmds/dx/dx_cmds.py +99 -0
  95. svc_infra/cli/cmds/help.py +4 -0
  96. svc_infra/cli/cmds/jobs/__init__.py +1 -0
  97. svc_infra/cli/cmds/jobs/jobs_cmds.py +43 -0
  98. svc_infra/cli/cmds/obs/obs_cmds.py +4 -3
  99. svc_infra/cli/cmds/sdk/__init__.py +0 -0
  100. svc_infra/cli/cmds/sdk/sdk_cmds.py +102 -0
  101. svc_infra/data/add.py +61 -0
  102. svc_infra/data/backup.py +53 -0
  103. svc_infra/data/erasure.py +45 -0
  104. svc_infra/data/fixtures.py +40 -0
  105. svc_infra/data/retention.py +55 -0
  106. svc_infra/db/inbox.py +67 -0
  107. svc_infra/db/nosql/mongo/README.md +13 -13
  108. svc_infra/db/outbox.py +104 -0
  109. svc_infra/db/sql/apikey.py +1 -1
  110. svc_infra/db/sql/authref.py +61 -0
  111. svc_infra/db/sql/core.py +2 -2
  112. svc_infra/db/sql/repository.py +52 -12
  113. svc_infra/db/sql/resource.py +5 -0
  114. svc_infra/db/sql/scaffold.py +16 -4
  115. svc_infra/db/sql/templates/models_schemas/auth/schemas.py.tmpl +1 -1
  116. svc_infra/db/sql/templates/setup/env_async.py.tmpl +199 -76
  117. svc_infra/db/sql/templates/setup/env_sync.py.tmpl +231 -79
  118. svc_infra/db/sql/tenant.py +79 -0
  119. svc_infra/db/sql/utils.py +18 -4
  120. svc_infra/db/sql/versioning.py +14 -0
  121. svc_infra/docs/acceptance-matrix.md +71 -0
  122. svc_infra/docs/acceptance.md +44 -0
  123. svc_infra/docs/admin.md +425 -0
  124. svc_infra/docs/adr/0002-background-jobs-and-scheduling.md +40 -0
  125. svc_infra/docs/adr/0003-webhooks-framework.md +24 -0
  126. svc_infra/docs/adr/0004-tenancy-model.md +42 -0
  127. svc_infra/docs/adr/0005-data-lifecycle.md +86 -0
  128. svc_infra/docs/adr/0006-ops-slos-and-metrics.md +47 -0
  129. svc_infra/docs/adr/0007-docs-and-sdks.md +83 -0
  130. svc_infra/docs/adr/0008-billing-primitives.md +143 -0
  131. svc_infra/docs/adr/0009-acceptance-harness.md +40 -0
  132. svc_infra/docs/adr/0010-timeouts-and-resource-limits.md +54 -0
  133. svc_infra/docs/adr/0011-admin-scope-and-impersonation.md +73 -0
  134. svc_infra/docs/api.md +59 -0
  135. svc_infra/docs/auth.md +11 -0
  136. svc_infra/docs/billing.md +190 -0
  137. svc_infra/docs/cache.md +76 -0
  138. svc_infra/docs/cli.md +74 -0
  139. svc_infra/docs/contributing.md +34 -0
  140. svc_infra/docs/data-lifecycle.md +52 -0
  141. svc_infra/docs/database.md +14 -0
  142. svc_infra/docs/docs-and-sdks.md +62 -0
  143. svc_infra/docs/environment.md +114 -0
  144. svc_infra/docs/getting-started.md +63 -0
  145. svc_infra/docs/idempotency.md +111 -0
  146. svc_infra/docs/jobs.md +67 -0
  147. svc_infra/docs/observability.md +16 -0
  148. svc_infra/docs/ops.md +37 -0
  149. svc_infra/docs/rate-limiting.md +125 -0
  150. svc_infra/docs/repo-review.md +48 -0
  151. svc_infra/docs/security.md +176 -0
  152. svc_infra/docs/tenancy.md +35 -0
  153. svc_infra/docs/timeouts-and-resource-limits.md +147 -0
  154. svc_infra/docs/versioned-integrations.md +146 -0
  155. svc_infra/docs/webhooks.md +112 -0
  156. svc_infra/dx/add.py +63 -0
  157. svc_infra/dx/changelog.py +74 -0
  158. svc_infra/dx/checks.py +67 -0
  159. svc_infra/http/__init__.py +13 -0
  160. svc_infra/http/client.py +72 -0
  161. svc_infra/jobs/builtins/outbox_processor.py +38 -0
  162. svc_infra/jobs/builtins/webhook_delivery.py +90 -0
  163. svc_infra/jobs/easy.py +32 -0
  164. svc_infra/jobs/loader.py +45 -0
  165. svc_infra/jobs/queue.py +81 -0
  166. svc_infra/jobs/redis_queue.py +191 -0
  167. svc_infra/jobs/runner.py +75 -0
  168. svc_infra/jobs/scheduler.py +41 -0
  169. svc_infra/jobs/worker.py +40 -0
  170. svc_infra/mcp/svc_infra_mcp.py +85 -28
  171. svc_infra/obs/README.md +2 -0
  172. svc_infra/obs/add.py +54 -7
  173. svc_infra/obs/grafana/dashboards/http-overview.json +45 -0
  174. svc_infra/obs/metrics/__init__.py +53 -0
  175. svc_infra/obs/metrics.py +52 -0
  176. svc_infra/security/add.py +201 -0
  177. svc_infra/security/audit.py +130 -0
  178. svc_infra/security/audit_service.py +73 -0
  179. svc_infra/security/headers.py +52 -0
  180. svc_infra/security/hibp.py +95 -0
  181. svc_infra/security/jwt_rotation.py +53 -0
  182. svc_infra/security/lockout.py +96 -0
  183. svc_infra/security/models.py +255 -0
  184. svc_infra/security/org_invites.py +128 -0
  185. svc_infra/security/passwords.py +77 -0
  186. svc_infra/security/permissions.py +149 -0
  187. svc_infra/security/session.py +98 -0
  188. svc_infra/security/signed_cookies.py +80 -0
  189. svc_infra/webhooks/__init__.py +16 -0
  190. svc_infra/webhooks/add.py +322 -0
  191. svc_infra/webhooks/fastapi.py +37 -0
  192. svc_infra/webhooks/router.py +55 -0
  193. svc_infra/webhooks/service.py +67 -0
  194. svc_infra/webhooks/signing.py +30 -0
  195. svc_infra-0.1.654.dist-info/METADATA +154 -0
  196. svc_infra-0.1.654.dist-info/RECORD +352 -0
  197. svc_infra/api/fastapi/deps.py +0 -3
  198. svc_infra-0.1.506.dist-info/METADATA +0 -78
  199. svc_infra-0.1.506.dist-info/RECORD +0 -213
  200. /svc_infra/{api/fastapi/schemas → apf_payments}/__init__.py +0 -0
  201. {svc_infra-0.1.506.dist-info → svc_infra-0.1.654.dist-info}/WHEEL +0 -0
  202. {svc_infra-0.1.506.dist-info → svc_infra-0.1.654.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,270 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional, Protocol
4
+
5
+ from ..schemas import (
6
+ BalanceSnapshotOut,
7
+ CustomerOut,
8
+ CustomerUpsertIn,
9
+ DisputeOut,
10
+ IntentCreateIn,
11
+ IntentOut,
12
+ InvoiceCreateIn,
13
+ InvoiceLineItemIn,
14
+ InvoiceLineItemOut,
15
+ InvoiceOut,
16
+ PaymentMethodAttachIn,
17
+ PaymentMethodOut,
18
+ PaymentMethodUpdateIn,
19
+ PayoutOut,
20
+ PriceCreateIn,
21
+ PriceOut,
22
+ PriceUpdateIn,
23
+ ProductCreateIn,
24
+ ProductOut,
25
+ ProductUpdateIn,
26
+ RefundIn,
27
+ RefundOut,
28
+ SetupIntentCreateIn,
29
+ SetupIntentOut,
30
+ SubscriptionCreateIn,
31
+ SubscriptionOut,
32
+ SubscriptionUpdateIn,
33
+ UsageRecordIn,
34
+ UsageRecordListFilter,
35
+ UsageRecordOut,
36
+ )
37
+
38
+
39
+ class ProviderAdapter(Protocol):
40
+ name: str
41
+
42
+ async def ensure_customer(self, data: CustomerUpsertIn) -> CustomerOut:
43
+ pass
44
+
45
+ async def attach_payment_method(self, data: PaymentMethodAttachIn) -> PaymentMethodOut:
46
+ pass
47
+
48
+ async def list_payment_methods(self, provider_customer_id: str) -> list[PaymentMethodOut]:
49
+ pass
50
+
51
+ async def detach_payment_method(self, provider_method_id: str) -> PaymentMethodOut:
52
+ pass
53
+
54
+ async def set_default_payment_method(
55
+ self, provider_customer_id: str, provider_method_id: str
56
+ ) -> PaymentMethodOut:
57
+ pass
58
+
59
+ async def create_product(self, data: ProductCreateIn) -> ProductOut:
60
+ pass
61
+
62
+ async def create_price(self, data: PriceCreateIn) -> PriceOut:
63
+ pass
64
+
65
+ async def create_subscription(self, data: SubscriptionCreateIn) -> SubscriptionOut:
66
+ pass
67
+
68
+ async def update_subscription(
69
+ self, provider_subscription_id: str, data: SubscriptionUpdateIn
70
+ ) -> SubscriptionOut:
71
+ pass
72
+
73
+ async def cancel_subscription(
74
+ self, provider_subscription_id: str, at_period_end: bool = True
75
+ ) -> SubscriptionOut:
76
+ pass
77
+
78
+ async def create_invoice(self, data: InvoiceCreateIn) -> InvoiceOut:
79
+ pass
80
+
81
+ async def finalize_invoice(self, provider_invoice_id: str) -> InvoiceOut:
82
+ pass
83
+
84
+ async def void_invoice(self, provider_invoice_id: str) -> InvoiceOut:
85
+ pass
86
+
87
+ async def pay_invoice(self, provider_invoice_id: str) -> InvoiceOut:
88
+ pass
89
+
90
+ async def create_intent(self, data: IntentCreateIn, *, user_id: str | None) -> IntentOut:
91
+ pass
92
+
93
+ async def confirm_intent(self, provider_intent_id: str) -> IntentOut:
94
+ pass
95
+
96
+ async def cancel_intent(self, provider_intent_id: str) -> IntentOut:
97
+ pass
98
+
99
+ async def refund(self, provider_intent_id: str, data: RefundIn) -> IntentOut:
100
+ pass
101
+
102
+ async def hydrate_intent(self, provider_intent_id: str) -> IntentOut:
103
+ pass
104
+
105
+ async def verify_and_parse_webhook(
106
+ self, signature: str | None, payload: bytes
107
+ ) -> dict[str, Any]:
108
+ pass
109
+
110
+ async def capture_intent(self, provider_intent_id: str, *, amount: int | None) -> IntentOut:
111
+ pass
112
+
113
+ async def list_intents(
114
+ self,
115
+ *,
116
+ customer_provider_id: str | None,
117
+ status: str | None,
118
+ limit: int,
119
+ cursor: str | None,
120
+ ) -> tuple[list[IntentOut], str | None]:
121
+ pass
122
+
123
+ async def add_invoice_line_item(
124
+ self, provider_invoice_id: str, data: InvoiceLineItemIn
125
+ ) -> InvoiceOut:
126
+ pass
127
+
128
+ async def list_invoices(
129
+ self,
130
+ *,
131
+ customer_provider_id: str | None,
132
+ status: str | None,
133
+ limit: int,
134
+ cursor: str | None,
135
+ ) -> tuple[list[InvoiceOut], str | None]:
136
+ pass
137
+
138
+ async def get_invoice(self, provider_invoice_id: str) -> InvoiceOut:
139
+ pass
140
+
141
+ async def preview_invoice(
142
+ self, *, customer_provider_id: str, subscription_id: str | None = None
143
+ ) -> InvoiceOut:
144
+ pass
145
+
146
+ async def create_usage_record(self, data: UsageRecordIn) -> UsageRecordOut:
147
+ pass
148
+
149
+ # --- Setup Intents ---
150
+ async def create_setup_intent(self, data: SetupIntentCreateIn) -> SetupIntentOut:
151
+ pass
152
+
153
+ async def confirm_setup_intent(self, provider_setup_intent_id: str) -> SetupIntentOut:
154
+ pass
155
+
156
+ async def get_setup_intent(self, provider_setup_intent_id: str) -> SetupIntentOut:
157
+ pass
158
+
159
+ # --- SCA / 3DS resume ---
160
+ async def resume_intent_after_action(self, provider_intent_id: str) -> IntentOut:
161
+ pass
162
+
163
+ # --- Disputes ---
164
+ async def list_disputes(
165
+ self, *, status: str | None, limit: int, cursor: str | None
166
+ ) -> tuple[list[DisputeOut], str | None]:
167
+ pass
168
+
169
+ async def get_dispute(self, provider_dispute_id: str) -> DisputeOut:
170
+ pass
171
+
172
+ async def submit_dispute_evidence(self, provider_dispute_id: str, evidence: dict) -> DisputeOut:
173
+ pass
174
+
175
+ # --- Balance & Payouts ---
176
+ async def get_balance_snapshot(self) -> BalanceSnapshotOut:
177
+ pass
178
+
179
+ async def list_payouts(
180
+ self, *, limit: int, cursor: str | None
181
+ ) -> tuple[list[PayoutOut], str | None]:
182
+ pass
183
+
184
+ async def get_payout(self, provider_payout_id: str) -> PayoutOut:
185
+ pass
186
+
187
+ # --- Customers ---
188
+ async def list_customers(
189
+ self, *, provider: str | None, user_id: str | None, limit: int, cursor: str | None
190
+ ) -> tuple[list[CustomerOut], str | None]:
191
+ """Optional: if not implemented, the service will list from local DB."""
192
+ pass
193
+
194
+ async def get_customer(self, provider_customer_id: str) -> Optional[CustomerOut]:
195
+ pass
196
+
197
+ # --- Products / Prices ---
198
+ async def get_product(self, provider_product_id: str) -> ProductOut:
199
+ pass
200
+
201
+ async def list_products(
202
+ self, *, active: bool | None, limit: int, cursor: str | None
203
+ ) -> tuple[list[ProductOut], str | None]:
204
+ pass
205
+
206
+ async def update_product(self, provider_product_id: str, data: ProductUpdateIn) -> ProductOut:
207
+ pass
208
+
209
+ async def get_price(self, provider_price_id: str) -> PriceOut:
210
+ pass
211
+
212
+ async def list_prices(
213
+ self,
214
+ *,
215
+ provider_product_id: str | None,
216
+ active: bool | None,
217
+ limit: int,
218
+ cursor: str | None,
219
+ ) -> tuple[list[PriceOut], str | None]:
220
+ pass
221
+
222
+ async def update_price(self, provider_price_id: str, data: PriceUpdateIn) -> PriceOut:
223
+ pass
224
+
225
+ # --- Subscriptions ---
226
+ async def get_subscription(self, provider_subscription_id: str) -> SubscriptionOut:
227
+ pass
228
+
229
+ async def list_subscriptions(
230
+ self,
231
+ *,
232
+ customer_provider_id: str | None,
233
+ status: str | None,
234
+ limit: int,
235
+ cursor: str | None,
236
+ ) -> tuple[list[SubscriptionOut], str | None]:
237
+ pass
238
+
239
+ # --- Payment Method (single + update) ---
240
+ async def get_payment_method(self, provider_method_id: str) -> PaymentMethodOut:
241
+ pass
242
+
243
+ async def update_payment_method(
244
+ self, provider_method_id: str, data: PaymentMethodUpdateIn
245
+ ) -> PaymentMethodOut:
246
+ pass
247
+
248
+ # --- Refunds list/get ---
249
+ async def list_refunds(
250
+ self, *, provider_payment_intent_id: str | None, limit: int, cursor: str | None
251
+ ) -> tuple[list[RefundOut], str | None]:
252
+ pass
253
+
254
+ async def get_refund(self, provider_refund_id: str) -> RefundOut:
255
+ pass
256
+
257
+ # --- Invoice line items list ---
258
+ async def list_invoice_line_items(
259
+ self, provider_invoice_id: str, *, limit: int, cursor: str | None
260
+ ) -> tuple[list[InvoiceLineItemOut], str | None]:
261
+ pass
262
+
263
+ # --- Usage records list/get ---
264
+ async def list_usage_records(
265
+ self, f: UsageRecordListFilter
266
+ ) -> tuple[list[UsageRecordOut], str | None]:
267
+ pass
268
+
269
+ async def get_usage_record(self, usage_record_id: str) -> UsageRecordOut:
270
+ pass
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Dict, Optional
4
+
5
+ from ..settings import get_payments_settings
6
+ from .base import ProviderAdapter
7
+
8
+
9
+ class ProviderRegistry:
10
+ def __init__(self):
11
+ self._adapters: Dict[str, ProviderAdapter] = {}
12
+
13
+ def register(self, adapter: ProviderAdapter):
14
+ self._adapters[adapter.name] = adapter
15
+
16
+ def get(self, name: Optional[str] = None) -> ProviderAdapter:
17
+ settings = get_payments_settings()
18
+ key = (name or settings.default_provider).lower()
19
+ if key not in self._adapters:
20
+ raise RuntimeError(f"No payments adapter registered for '{key}'")
21
+ return self._adapters[key]
22
+
23
+
24
+ _REGISTRY: Optional[ProviderRegistry] = None
25
+
26
+
27
+ def get_provider_registry() -> ProviderRegistry:
28
+ global _REGISTRY
29
+ if _REGISTRY is None:
30
+ _REGISTRY = ProviderRegistry()
31
+ return _REGISTRY