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.
- svc_infra/apf_payments/models.py +47 -108
- svc_infra/apf_payments/provider/__init__.py +2 -2
- svc_infra/apf_payments/provider/aiydan.py +42 -100
- svc_infra/apf_payments/provider/base.py +10 -26
- svc_infra/apf_payments/provider/registry.py +3 -5
- svc_infra/apf_payments/provider/stripe.py +63 -135
- svc_infra/apf_payments/schemas.py +82 -90
- svc_infra/apf_payments/service.py +40 -86
- svc_infra/apf_payments/settings.py +10 -13
- svc_infra/api/__init__.py +13 -13
- svc_infra/api/fastapi/__init__.py +19 -0
- svc_infra/api/fastapi/admin/add.py +13 -18
- svc_infra/api/fastapi/apf_payments/router.py +47 -84
- svc_infra/api/fastapi/apf_payments/setup.py +7 -13
- svc_infra/api/fastapi/auth/__init__.py +1 -1
- svc_infra/api/fastapi/auth/_cookies.py +3 -9
- svc_infra/api/fastapi/auth/add.py +4 -8
- svc_infra/api/fastapi/auth/gaurd.py +9 -26
- svc_infra/api/fastapi/auth/mfa/models.py +4 -7
- svc_infra/api/fastapi/auth/mfa/pre_auth.py +3 -3
- svc_infra/api/fastapi/auth/mfa/router.py +9 -15
- svc_infra/api/fastapi/auth/mfa/security.py +3 -5
- svc_infra/api/fastapi/auth/mfa/utils.py +3 -2
- svc_infra/api/fastapi/auth/mfa/verify.py +2 -9
- svc_infra/api/fastapi/auth/providers.py +4 -6
- svc_infra/api/fastapi/auth/routers/apikey_router.py +16 -18
- svc_infra/api/fastapi/auth/routers/oauth_router.py +37 -85
- svc_infra/api/fastapi/auth/routers/session_router.py +3 -6
- svc_infra/api/fastapi/auth/security.py +17 -28
- svc_infra/api/fastapi/auth/sender.py +1 -3
- svc_infra/api/fastapi/auth/settings.py +18 -19
- svc_infra/api/fastapi/auth/state.py +6 -7
- svc_infra/api/fastapi/auth/ws_security.py +2 -2
- svc_infra/api/fastapi/billing/router.py +6 -8
- svc_infra/api/fastapi/db/http.py +10 -11
- svc_infra/api/fastapi/db/nosql/mongo/add.py +5 -15
- svc_infra/api/fastapi/db/nosql/mongo/crud_router.py +14 -15
- svc_infra/api/fastapi/db/sql/add.py +6 -14
- svc_infra/api/fastapi/db/sql/crud_router.py +27 -40
- svc_infra/api/fastapi/db/sql/health.py +1 -3
- svc_infra/api/fastapi/db/sql/session.py +4 -5
- svc_infra/api/fastapi/db/sql/users.py +8 -11
- svc_infra/api/fastapi/dependencies/ratelimit.py +4 -6
- svc_infra/api/fastapi/docs/add.py +13 -23
- svc_infra/api/fastapi/docs/landing.py +6 -8
- svc_infra/api/fastapi/docs/scoped.py +34 -42
- svc_infra/api/fastapi/dual/dualize.py +1 -1
- svc_infra/api/fastapi/dual/protected.py +12 -21
- svc_infra/api/fastapi/dual/router.py +14 -31
- svc_infra/api/fastapi/ease.py +57 -13
- svc_infra/api/fastapi/http/conditional.py +3 -5
- svc_infra/api/fastapi/middleware/errors/catchall.py +2 -6
- svc_infra/api/fastapi/middleware/errors/exceptions.py +1 -4
- svc_infra/api/fastapi/middleware/errors/handlers.py +12 -18
- svc_infra/api/fastapi/middleware/graceful_shutdown.py +4 -13
- svc_infra/api/fastapi/middleware/idempotency.py +11 -16
- svc_infra/api/fastapi/middleware/idempotency_store.py +14 -14
- svc_infra/api/fastapi/middleware/optimistic_lock.py +5 -8
- svc_infra/api/fastapi/middleware/ratelimit.py +8 -8
- svc_infra/api/fastapi/middleware/ratelimit_store.py +7 -8
- svc_infra/api/fastapi/middleware/request_id.py +1 -3
- svc_infra/api/fastapi/middleware/timeout.py +9 -10
- svc_infra/api/fastapi/object_router.py +1060 -0
- svc_infra/api/fastapi/openapi/apply.py +5 -6
- svc_infra/api/fastapi/openapi/conventions.py +4 -4
- svc_infra/api/fastapi/openapi/mutators.py +13 -31
- 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 -3
- svc_infra/api/fastapi/ops/add.py +7 -9
- svc_infra/api/fastapi/pagination.py +25 -37
- svc_infra/api/fastapi/routers/__init__.py +16 -38
- svc_infra/api/fastapi/setup.py +13 -31
- svc_infra/api/fastapi/tenancy/add.py +3 -2
- svc_infra/api/fastapi/tenancy/context.py +8 -7
- svc_infra/api/fastapi/versioned.py +3 -2
- svc_infra/app/env.py +5 -7
- svc_infra/app/logging/add.py +2 -1
- svc_infra/app/logging/filter.py +1 -1
- svc_infra/app/logging/formats.py +3 -2
- svc_infra/app/root.py +3 -3
- svc_infra/billing/__init__.py +19 -2
- svc_infra/billing/async_service.py +27 -7
- svc_infra/billing/jobs.py +23 -33
- svc_infra/billing/models.py +21 -52
- svc_infra/billing/quotas.py +5 -7
- svc_infra/billing/schemas.py +4 -6
- svc_infra/cache/__init__.py +12 -5
- svc_infra/cache/add.py +6 -9
- svc_infra/cache/backend.py +6 -5
- svc_infra/cache/decorators.py +17 -28
- svc_infra/cache/keys.py +2 -2
- svc_infra/cache/recache.py +22 -35
- svc_infra/cache/resources.py +8 -16
- svc_infra/cache/ttl.py +2 -3
- svc_infra/cache/utils.py +5 -6
- svc_infra/cli/__init__.py +4 -12
- svc_infra/cli/cmds/db/nosql/mongo/mongo_cmds.py +11 -10
- svc_infra/cli/cmds/db/nosql/mongo/mongo_scaffold_cmds.py +6 -9
- svc_infra/cli/cmds/db/ops_cmds.py +3 -6
- svc_infra/cli/cmds/db/sql/alembic_cmds.py +24 -41
- svc_infra/cli/cmds/db/sql/sql_export_cmds.py +9 -17
- svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py +10 -10
- svc_infra/cli/cmds/docs/docs_cmds.py +7 -10
- svc_infra/cli/cmds/dx/dx_cmds.py +5 -11
- svc_infra/cli/cmds/jobs/jobs_cmds.py +2 -7
- svc_infra/cli/cmds/obs/obs_cmds.py +4 -7
- svc_infra/cli/cmds/sdk/sdk_cmds.py +5 -15
- svc_infra/cli/foundation/runner.py +6 -11
- svc_infra/cli/foundation/typer_bootstrap.py +1 -2
- svc_infra/data/__init__.py +83 -0
- svc_infra/data/add.py +5 -5
- svc_infra/data/backup.py +8 -10
- svc_infra/data/erasure.py +3 -2
- svc_infra/data/fixtures.py +3 -3
- svc_infra/data/retention.py +8 -13
- svc_infra/db/crud_schema.py +9 -8
- svc_infra/db/nosql/__init__.py +0 -1
- svc_infra/db/nosql/constants.py +1 -1
- svc_infra/db/nosql/core.py +7 -14
- svc_infra/db/nosql/indexes.py +11 -10
- svc_infra/db/nosql/management.py +3 -3
- svc_infra/db/nosql/mongo/client.py +3 -3
- svc_infra/db/nosql/mongo/settings.py +2 -6
- svc_infra/db/nosql/repository.py +27 -28
- svc_infra/db/nosql/resource.py +15 -20
- svc_infra/db/nosql/scaffold.py +13 -17
- svc_infra/db/nosql/service.py +3 -4
- svc_infra/db/nosql/service_with_hooks.py +4 -3
- svc_infra/db/nosql/types.py +2 -6
- svc_infra/db/nosql/utils.py +4 -4
- svc_infra/db/ops.py +14 -18
- svc_infra/db/outbox.py +15 -18
- svc_infra/db/sql/apikey.py +12 -21
- svc_infra/db/sql/authref.py +3 -7
- svc_infra/db/sql/constants.py +9 -9
- svc_infra/db/sql/core.py +11 -11
- svc_infra/db/sql/management.py +2 -6
- svc_infra/db/sql/repository.py +17 -24
- svc_infra/db/sql/resource.py +14 -13
- svc_infra/db/sql/scaffold.py +13 -17
- svc_infra/db/sql/service.py +7 -16
- svc_infra/db/sql/service_with_hooks.py +4 -3
- svc_infra/db/sql/tenant.py +6 -14
- svc_infra/db/sql/uniq.py +8 -7
- svc_infra/db/sql/uniq_hooks.py +14 -19
- svc_infra/db/sql/utils.py +24 -53
- svc_infra/db/utils.py +3 -3
- svc_infra/deploy/__init__.py +8 -15
- svc_infra/documents/add.py +7 -8
- svc_infra/documents/ease.py +8 -8
- svc_infra/documents/models.py +3 -3
- svc_infra/documents/storage.py +11 -13
- svc_infra/dx/__init__.py +58 -0
- svc_infra/dx/add.py +1 -3
- svc_infra/dx/changelog.py +2 -2
- svc_infra/dx/checks.py +1 -1
- svc_infra/health/__init__.py +15 -16
- svc_infra/http/client.py +10 -14
- svc_infra/jobs/__init__.py +79 -0
- svc_infra/jobs/builtins/outbox_processor.py +3 -5
- svc_infra/jobs/builtins/webhook_delivery.py +1 -3
- svc_infra/jobs/loader.py +4 -5
- svc_infra/jobs/queue.py +14 -24
- svc_infra/jobs/redis_queue.py +20 -34
- svc_infra/jobs/runner.py +7 -11
- svc_infra/jobs/scheduler.py +5 -5
- svc_infra/jobs/worker.py +1 -1
- svc_infra/loaders/base.py +5 -4
- svc_infra/loaders/github.py +1 -3
- svc_infra/loaders/url.py +3 -9
- svc_infra/logging/__init__.py +7 -6
- svc_infra/mcp/__init__.py +82 -0
- svc_infra/mcp/svc_infra_mcp.py +2 -2
- svc_infra/obs/add.py +4 -3
- svc_infra/obs/cloud_dash.py +1 -1
- svc_infra/obs/metrics/__init__.py +3 -3
- svc_infra/obs/metrics/asgi.py +9 -14
- svc_infra/obs/metrics/base.py +13 -13
- svc_infra/obs/metrics/http.py +5 -9
- svc_infra/obs/metrics/sqlalchemy.py +9 -12
- svc_infra/obs/metrics.py +3 -3
- svc_infra/obs/settings.py +2 -6
- 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 +5 -9
- svc_infra/security/audit.py +14 -17
- svc_infra/security/audit_service.py +9 -9
- svc_infra/security/hibp.py +3 -6
- svc_infra/security/jwt_rotation.py +7 -10
- svc_infra/security/lockout.py +12 -11
- svc_infra/security/models.py +37 -46
- svc_infra/security/oauth_models.py +8 -8
- svc_infra/security/org_invites.py +11 -13
- svc_infra/security/passwords.py +4 -6
- svc_infra/security/permissions.py +8 -7
- svc_infra/security/session.py +6 -7
- svc_infra/security/signed_cookies.py +9 -9
- svc_infra/storage/add.py +5 -8
- svc_infra/storage/backends/local.py +13 -21
- svc_infra/storage/backends/memory.py +4 -7
- svc_infra/storage/backends/s3.py +17 -36
- svc_infra/storage/base.py +2 -2
- svc_infra/storage/easy.py +4 -8
- svc_infra/storage/settings.py +16 -18
- svc_infra/testing/__init__.py +36 -39
- svc_infra/utils.py +169 -8
- svc_infra/webhooks/__init__.py +1 -1
- svc_infra/webhooks/add.py +17 -29
- svc_infra/webhooks/encryption.py +2 -2
- svc_infra/webhooks/fastapi.py +2 -4
- svc_infra/webhooks/router.py +3 -3
- svc_infra/webhooks/service.py +5 -6
- svc_infra/webhooks/signing.py +5 -5
- svc_infra/websocket/add.py +2 -3
- svc_infra/websocket/client.py +3 -2
- svc_infra/websocket/config.py +6 -18
- svc_infra/websocket/manager.py +9 -10
- {svc_infra-0.1.706.dist-info → svc_infra-1.1.0.dist-info}/METADATA +11 -5
- svc_infra-1.1.0.dist-info/RECORD +364 -0
- svc_infra/billing/service.py +0 -123
- svc_infra-0.1.706.dist-info/RECORD +0 -357
- {svc_infra-0.1.706.dist-info → svc_infra-1.1.0.dist-info}/LICENSE +0 -0
- {svc_infra-0.1.706.dist-info → svc_infra-1.1.0.dist-info}/WHEEL +0 -0
- {svc_infra-0.1.706.dist-info → svc_infra-1.1.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from pydantic import BaseModel, SecretStr
|
|
7
6
|
|
|
8
7
|
STRIPE_KEY = os.getenv("STRIPE_SECRET") or os.getenv("STRIPE_API_KEY")
|
|
9
8
|
STRIPE_WH = os.getenv("STRIPE_WH_SECRET")
|
|
10
9
|
PROVIDER = (
|
|
11
|
-
os.getenv("APF_PAYMENTS_PROVIDER")
|
|
12
|
-
or os.getenv("PAYMENTS_PROVIDER", "stripe")
|
|
13
|
-
or "stripe"
|
|
10
|
+
os.getenv("APF_PAYMENTS_PROVIDER") or os.getenv("PAYMENTS_PROVIDER", "stripe") or "stripe"
|
|
14
11
|
).lower()
|
|
15
12
|
|
|
16
13
|
AIYDAN_KEY = os.getenv("AIYDAN_API_KEY")
|
|
@@ -23,23 +20,23 @@ AIYDAN_WH = os.getenv("AIYDAN_WH_SECRET")
|
|
|
23
20
|
|
|
24
21
|
class StripeConfig(BaseModel):
|
|
25
22
|
secret_key: SecretStr
|
|
26
|
-
webhook_secret:
|
|
23
|
+
webhook_secret: SecretStr | None = None
|
|
27
24
|
|
|
28
25
|
|
|
29
26
|
class AiydanConfig(BaseModel):
|
|
30
27
|
api_key: SecretStr
|
|
31
|
-
client_key:
|
|
32
|
-
merchant_account:
|
|
33
|
-
hmac_key:
|
|
34
|
-
base_url:
|
|
35
|
-
webhook_secret:
|
|
28
|
+
client_key: SecretStr | None = None
|
|
29
|
+
merchant_account: str | None = None
|
|
30
|
+
hmac_key: SecretStr | None = None
|
|
31
|
+
base_url: str | None = None
|
|
32
|
+
webhook_secret: SecretStr | None = None
|
|
36
33
|
|
|
37
34
|
|
|
38
35
|
class PaymentsSettings(BaseModel):
|
|
39
36
|
default_provider: str = PROVIDER
|
|
40
37
|
|
|
41
38
|
# optional multi-tenant/provider map hook can be added later
|
|
42
|
-
stripe:
|
|
39
|
+
stripe: StripeConfig | None = (
|
|
43
40
|
StripeConfig(
|
|
44
41
|
secret_key=SecretStr(STRIPE_KEY),
|
|
45
42
|
webhook_secret=SecretStr(STRIPE_WH) if STRIPE_WH else None,
|
|
@@ -47,7 +44,7 @@ class PaymentsSettings(BaseModel):
|
|
|
47
44
|
if STRIPE_KEY
|
|
48
45
|
else None
|
|
49
46
|
)
|
|
50
|
-
aiydan:
|
|
47
|
+
aiydan: AiydanConfig | None = (
|
|
51
48
|
AiydanConfig(
|
|
52
49
|
api_key=SecretStr(AIYDAN_KEY),
|
|
53
50
|
client_key=SecretStr(AIYDAN_CLIENT_KEY) if AIYDAN_CLIENT_KEY else None,
|
|
@@ -61,7 +58,7 @@ class PaymentsSettings(BaseModel):
|
|
|
61
58
|
)
|
|
62
59
|
|
|
63
60
|
|
|
64
|
-
_SETTINGS:
|
|
61
|
+
_SETTINGS: PaymentsSettings | None = None
|
|
65
62
|
|
|
66
63
|
|
|
67
64
|
def get_payments_settings() -> PaymentsSettings:
|
svc_infra/api/__init__.py
CHANGED
|
@@ -7,30 +7,30 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
# Re-export from fastapi submodule
|
|
9
9
|
from svc_infra.api.fastapi import (
|
|
10
|
+
APIVersionSpec,
|
|
10
11
|
# Dual routers
|
|
11
12
|
DualAPIRouter,
|
|
12
|
-
dualize_protected,
|
|
13
|
-
dualize_public,
|
|
14
|
-
dualize_user,
|
|
15
13
|
# Service setup
|
|
16
14
|
ServiceInfo,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
easy_service_api,
|
|
20
|
-
easy_service_app,
|
|
21
|
-
setup_caching,
|
|
15
|
+
add_dependency_health,
|
|
16
|
+
add_health_routes,
|
|
22
17
|
# Health checks
|
|
23
18
|
add_startup_probe,
|
|
24
|
-
add_health_routes,
|
|
25
|
-
add_dependency_health,
|
|
26
19
|
check_database,
|
|
27
20
|
check_redis,
|
|
28
21
|
check_url,
|
|
22
|
+
cursor_window,
|
|
23
|
+
dualize_protected,
|
|
24
|
+
dualize_public,
|
|
25
|
+
dualize_user,
|
|
26
|
+
easy_service_api,
|
|
27
|
+
easy_service_app,
|
|
28
|
+
setup_caching,
|
|
29
|
+
setup_service_api,
|
|
30
|
+
sort_by,
|
|
31
|
+
text_filter,
|
|
29
32
|
# Pagination
|
|
30
33
|
use_pagination,
|
|
31
|
-
text_filter,
|
|
32
|
-
sort_by,
|
|
33
|
-
cursor_window,
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
__all__ = [
|
|
@@ -4,6 +4,16 @@ from svc_infra.api.fastapi.dual import (
|
|
|
4
4
|
dualize_public,
|
|
5
5
|
dualize_user,
|
|
6
6
|
)
|
|
7
|
+
from svc_infra.api.fastapi.object_router import (
|
|
8
|
+
DEFAULT_EXCEPTION_MAP,
|
|
9
|
+
STATUS_TITLES,
|
|
10
|
+
endpoint,
|
|
11
|
+
endpoint_exclude,
|
|
12
|
+
map_exception_to_http,
|
|
13
|
+
router_from_object,
|
|
14
|
+
router_from_object_with_websocket,
|
|
15
|
+
websocket_endpoint,
|
|
16
|
+
)
|
|
7
17
|
from svc_infra.api.fastapi.openapi.models import APIVersionSpec, ServiceInfo
|
|
8
18
|
from svc_infra.health import (
|
|
9
19
|
add_dependency_health,
|
|
@@ -43,4 +53,13 @@ __all__ = [
|
|
|
43
53
|
"text_filter",
|
|
44
54
|
"sort_by",
|
|
45
55
|
"cursor_window",
|
|
56
|
+
# Object Router
|
|
57
|
+
"router_from_object",
|
|
58
|
+
"router_from_object_with_websocket",
|
|
59
|
+
"endpoint",
|
|
60
|
+
"endpoint_exclude",
|
|
61
|
+
"websocket_endpoint",
|
|
62
|
+
"map_exception_to_http",
|
|
63
|
+
"DEFAULT_EXCEPTION_MAP",
|
|
64
|
+
"STATUS_TITLES",
|
|
46
65
|
]
|
|
@@ -7,9 +7,10 @@ import json
|
|
|
7
7
|
import logging
|
|
8
8
|
import os
|
|
9
9
|
import time
|
|
10
|
+
from collections.abc import Callable
|
|
10
11
|
from hashlib import sha256
|
|
11
12
|
from types import SimpleNamespace
|
|
12
|
-
from typing import Any,
|
|
13
|
+
from typing import Any, cast
|
|
13
14
|
|
|
14
15
|
from fastapi import APIRouter, Depends, HTTPException, Request, Response
|
|
15
16
|
|
|
@@ -49,18 +50,18 @@ def _verify(token: str, *, secret: str) -> dict:
|
|
|
49
50
|
payload = json.loads(body)
|
|
50
51
|
if int(payload.get("exp", 0)) < int(time.time()):
|
|
51
52
|
raise ValueError("expired")
|
|
52
|
-
return cast(dict[Any, Any], payload)
|
|
53
|
+
return cast("dict[Any, Any]", payload)
|
|
53
54
|
except Exception as e:
|
|
54
55
|
raise ValueError("invalid_token") from e
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
def admin_router(*, dependencies:
|
|
58
|
+
def admin_router(*, dependencies: list[Any] | None = None, **kwargs) -> APIRouter:
|
|
58
59
|
"""Role-gated admin router for coarse access control.
|
|
59
60
|
|
|
60
61
|
Use permission guards inside endpoints for fine-grained control.
|
|
61
62
|
"""
|
|
62
63
|
|
|
63
|
-
return cast(APIRouter, roles_router("admin", **kwargs))
|
|
64
|
+
return cast("APIRouter", roles_router("admin", **kwargs))
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
def add_admin(
|
|
@@ -68,10 +69,10 @@ def add_admin(
|
|
|
68
69
|
*,
|
|
69
70
|
base_path: str = "/admin",
|
|
70
71
|
enable_impersonation: bool = True,
|
|
71
|
-
secret:
|
|
72
|
+
secret: str | None = None,
|
|
72
73
|
ttl_seconds: int = 15 * 60,
|
|
73
74
|
cookie_name: str = "impersonation",
|
|
74
|
-
impersonation_user_getter:
|
|
75
|
+
impersonation_user_getter: Callable[[Any, str], Any] | None = None,
|
|
75
76
|
) -> None:
|
|
76
77
|
"""Wire admin surfaces with sensible defaults.
|
|
77
78
|
|
|
@@ -98,15 +99,13 @@ def add_admin(
|
|
|
98
99
|
|
|
99
100
|
r = admin_router(prefix=base_path, tags=["admin"]) # role-gated
|
|
100
101
|
|
|
101
|
-
async def _default_user_getter(
|
|
102
|
-
request: Request, user_id: str, session: SqlSessionDep
|
|
103
|
-
):
|
|
102
|
+
async def _default_user_getter(request: Request, user_id: str, session: SqlSessionDep):
|
|
104
103
|
try:
|
|
105
104
|
UserModel, _, _ = get_auth_state()
|
|
106
105
|
except Exception:
|
|
107
106
|
# Fallback: simple shim if auth state not configured
|
|
108
107
|
return SimpleNamespace(id=user_id)
|
|
109
|
-
obj = await cast(Any, session).get(UserModel, user_id)
|
|
108
|
+
obj = await cast("Any", session).get(UserModel, user_id)
|
|
110
109
|
if not obj:
|
|
111
110
|
raise HTTPException(404, "user_not_found")
|
|
112
111
|
return obj
|
|
@@ -182,9 +181,7 @@ def add_admin(
|
|
|
182
181
|
def _compose_override():
|
|
183
182
|
existing = app.dependency_overrides.get(_current_principal)
|
|
184
183
|
if existing and getattr(existing, "_is_admin_impersonation_override", False):
|
|
185
|
-
dep_provider = getattr(
|
|
186
|
-
existing, "_admin_impersonation_base", _current_principal
|
|
187
|
-
)
|
|
184
|
+
dep_provider = getattr(existing, "_admin_impersonation_base", _current_principal)
|
|
188
185
|
else:
|
|
189
186
|
dep_provider = existing or _current_principal
|
|
190
187
|
|
|
@@ -214,16 +211,14 @@ def add_admin(
|
|
|
214
211
|
)
|
|
215
212
|
target = await _res if inspect.isawaitable(_res) else _res
|
|
216
213
|
# Swap user but keep actor for audit if needed
|
|
217
|
-
|
|
214
|
+
base.actor = getattr(base, "user", None) # type: ignore[attr-defined]
|
|
218
215
|
# If target lacks roles, inherit actor roles to maintain permission checks
|
|
219
216
|
try:
|
|
220
217
|
if not getattr(target, "roles", None):
|
|
221
|
-
|
|
218
|
+
target.roles = actor_roles
|
|
222
219
|
except Exception:
|
|
223
220
|
# Best-effort; if target object is immutable, fallback by wrapping
|
|
224
|
-
target = SimpleNamespace(
|
|
225
|
-
id=getattr(target, "id", target_id), roles=actor_roles
|
|
226
|
-
)
|
|
221
|
+
target = SimpleNamespace(id=getattr(target, "id", target_id), roles=actor_roles)
|
|
227
222
|
base.user = target
|
|
228
223
|
base.via = "impersonated"
|
|
229
224
|
return base
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
-
from
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Literal, cast
|
|
5
6
|
|
|
6
7
|
from fastapi import Body, Depends, Header, HTTPException, Request, Response, status
|
|
7
8
|
from starlette.responses import JSONResponse
|
|
@@ -72,7 +73,7 @@ _TX_KINDS = {"payment", "refund", "fee", "payout", "capture"}
|
|
|
72
73
|
def _tx_kind(kind: str) -> Literal["payment", "refund", "fee", "payout", "capture"]:
|
|
73
74
|
if kind not in _TX_KINDS:
|
|
74
75
|
raise ValueError(f"Unknown ledger kind: {kind!r}")
|
|
75
|
-
return cast(Literal[
|
|
76
|
+
return cast("Literal['payment', 'refund', 'fee', 'payout', 'capture']", kind)
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
# --- tenant resolution ---
|
|
@@ -101,16 +102,16 @@ async def resolve_payments_tenant_id(
|
|
|
101
102
|
if inspect.isawaitable(val):
|
|
102
103
|
val = await val
|
|
103
104
|
if val:
|
|
104
|
-
return cast(str, val)
|
|
105
|
+
return cast("str", val)
|
|
105
106
|
# if None, continue default flow
|
|
106
107
|
|
|
107
108
|
# 2) Principal (user)
|
|
108
109
|
if identity and getattr(identity.user or object(), "tenant_id", None):
|
|
109
|
-
return cast(str,
|
|
110
|
+
return cast("str", identity.user.tenant_id)
|
|
110
111
|
|
|
111
112
|
# 3) Principal (api key)
|
|
112
113
|
if identity and getattr(identity.api_key or object(), "tenant_id", None):
|
|
113
|
-
return cast(str,
|
|
114
|
+
return cast("str", identity.api_key.tenant_id)
|
|
114
115
|
|
|
115
116
|
# 4) Explicit header argument (tests pass this)
|
|
116
117
|
if tenant_header:
|
|
@@ -119,7 +120,7 @@ async def resolve_payments_tenant_id(
|
|
|
119
120
|
# 5) Request state
|
|
120
121
|
state_tid = getattr(getattr(request, "state", object()), "tenant_id", None)
|
|
121
122
|
if state_tid:
|
|
122
|
-
return cast(str, state_tid)
|
|
123
|
+
return cast("str", state_tid)
|
|
123
124
|
|
|
124
125
|
raise HTTPException(status_code=400, detail="tenant_context_missing")
|
|
125
126
|
|
|
@@ -140,15 +141,11 @@ async def get_service(
|
|
|
140
141
|
else:
|
|
141
142
|
# allow tests to call without a Request; try identity or fallback
|
|
142
143
|
if identity and getattr(identity.user or object(), "tenant_id", None):
|
|
143
|
-
tid =
|
|
144
|
-
elif identity and getattr(
|
|
145
|
-
identity.api_key
|
|
146
|
-
):
|
|
147
|
-
tid = getattr(identity.api_key, "tenant_id")
|
|
144
|
+
tid = identity.user.tenant_id
|
|
145
|
+
elif identity and getattr(identity.api_key or object(), "tenant_id", None):
|
|
146
|
+
tid = identity.api_key.tenant_id
|
|
148
147
|
else:
|
|
149
|
-
raise HTTPException(
|
|
150
|
-
status_code=400, detail="tenant_context_missing"
|
|
151
|
-
)
|
|
148
|
+
raise HTTPException(status_code=400, detail="tenant_context_missing")
|
|
152
149
|
except HTTPException:
|
|
153
150
|
# fallback for routes/tests that don't set context; preserve prior default
|
|
154
151
|
tid = "test_tenant"
|
|
@@ -172,9 +169,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
172
169
|
dependencies=[Depends(require_idempotency_key)],
|
|
173
170
|
tags=["Customers"],
|
|
174
171
|
)
|
|
175
|
-
async def upsert_customer(
|
|
176
|
-
data: CustomerUpsertIn, svc: PaymentsService = Depends(get_service)
|
|
177
|
-
):
|
|
172
|
+
async def upsert_customer(data: CustomerUpsertIn, svc: PaymentsService = Depends(get_service)):
|
|
178
173
|
out = await svc.ensure_customer(data)
|
|
179
174
|
await svc.session.flush()
|
|
180
175
|
return out
|
|
@@ -197,9 +192,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
197
192
|
out = await svc.create_intent(user_id=None, data=data)
|
|
198
193
|
await svc.session.flush()
|
|
199
194
|
response.headers["Location"] = str(
|
|
200
|
-
request.url_for(
|
|
201
|
-
"payments_get_intent", provider_intent_id=out.provider_intent_id
|
|
202
|
-
)
|
|
195
|
+
request.url_for("payments_get_intent", provider_intent_id=out.provider_intent_id)
|
|
203
196
|
)
|
|
204
197
|
return out
|
|
205
198
|
|
|
@@ -213,9 +206,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
213
206
|
dependencies=[Depends(require_idempotency_key)],
|
|
214
207
|
tags=["Payment Intents"],
|
|
215
208
|
)
|
|
216
|
-
async def confirm_intent(
|
|
217
|
-
provider_intent_id: str, svc: PaymentsService = Depends(get_service)
|
|
218
|
-
):
|
|
209
|
+
async def confirm_intent(provider_intent_id: str, svc: PaymentsService = Depends(get_service)):
|
|
219
210
|
out = await svc.confirm_intent(provider_intent_id)
|
|
220
211
|
await svc.session.flush()
|
|
221
212
|
return out
|
|
@@ -227,9 +218,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
227
218
|
dependencies=[Depends(require_idempotency_key)],
|
|
228
219
|
tags=["Payment Intents"],
|
|
229
220
|
)
|
|
230
|
-
async def cancel_intent(
|
|
231
|
-
provider_intent_id: str, svc: PaymentsService = Depends(get_service)
|
|
232
|
-
):
|
|
221
|
+
async def cancel_intent(provider_intent_id: str, svc: PaymentsService = Depends(get_service)):
|
|
233
222
|
out = await svc.cancel_intent(provider_intent_id)
|
|
234
223
|
await svc.session.flush()
|
|
235
224
|
return out
|
|
@@ -302,7 +291,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
302
291
|
provider: str,
|
|
303
292
|
request: Request,
|
|
304
293
|
svc: PaymentsService = Depends(get_service),
|
|
305
|
-
signature:
|
|
294
|
+
signature: str | None = Header(None, alias="Stripe-Signature"),
|
|
306
295
|
):
|
|
307
296
|
payload = await request.body()
|
|
308
297
|
out = await svc.handle_webhook(provider.lower(), signature, payload)
|
|
@@ -359,9 +348,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
359
348
|
dependencies=[Depends(require_idempotency_key)],
|
|
360
349
|
tags=["Payment Methods"],
|
|
361
350
|
)
|
|
362
|
-
async def detach_method(
|
|
363
|
-
provider_method_id: str, svc: PaymentsService = Depends(get_service)
|
|
364
|
-
):
|
|
351
|
+
async def detach_method(provider_method_id: str, svc: PaymentsService = Depends(get_service)):
|
|
365
352
|
out = await svc.detach_payment_method(provider_method_id)
|
|
366
353
|
await svc.session.flush()
|
|
367
354
|
return out
|
|
@@ -378,9 +365,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
378
365
|
customer_provider_id: str,
|
|
379
366
|
svc: PaymentsService = Depends(get_service),
|
|
380
367
|
):
|
|
381
|
-
out = await svc.set_default_payment_method(
|
|
382
|
-
customer_provider_id, provider_method_id
|
|
383
|
-
)
|
|
368
|
+
out = await svc.set_default_payment_method(customer_provider_id, provider_method_id)
|
|
384
369
|
await svc.session.flush()
|
|
385
370
|
return out
|
|
386
371
|
|
|
@@ -393,9 +378,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
393
378
|
dependencies=[Depends(require_idempotency_key)],
|
|
394
379
|
tags=["Products"],
|
|
395
380
|
)
|
|
396
|
-
async def create_product(
|
|
397
|
-
data: ProductCreateIn, svc: PaymentsService = Depends(get_service)
|
|
398
|
-
):
|
|
381
|
+
async def create_product(data: ProductCreateIn, svc: PaymentsService = Depends(get_service)):
|
|
399
382
|
out = await svc.create_product(data)
|
|
400
383
|
await svc.session.flush()
|
|
401
384
|
return out
|
|
@@ -408,9 +391,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
408
391
|
dependencies=[Depends(require_idempotency_key)],
|
|
409
392
|
tags=["Prices"],
|
|
410
393
|
)
|
|
411
|
-
async def create_price(
|
|
412
|
-
data: PriceCreateIn, svc: PaymentsService = Depends(get_service)
|
|
413
|
-
):
|
|
394
|
+
async def create_price(data: PriceCreateIn, svc: PaymentsService = Depends(get_service)):
|
|
414
395
|
out = await svc.create_price(data)
|
|
415
396
|
await svc.session.flush()
|
|
416
397
|
return out
|
|
@@ -481,9 +462,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
481
462
|
out = await svc.create_invoice(data)
|
|
482
463
|
await svc.session.flush()
|
|
483
464
|
response.headers["Location"] = str(
|
|
484
|
-
request.url_for(
|
|
485
|
-
"payments_get_invoice", provider_invoice_id=out.provider_invoice_id
|
|
486
|
-
)
|
|
465
|
+
request.url_for("payments_get_invoice", provider_invoice_id=out.provider_invoice_id)
|
|
487
466
|
)
|
|
488
467
|
return out
|
|
489
468
|
|
|
@@ -508,9 +487,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
508
487
|
dependencies=[Depends(require_idempotency_key)],
|
|
509
488
|
tags=["Invoices"],
|
|
510
489
|
)
|
|
511
|
-
async def void_invoice(
|
|
512
|
-
provider_invoice_id: str, svc: PaymentsService = Depends(get_service)
|
|
513
|
-
):
|
|
490
|
+
async def void_invoice(provider_invoice_id: str, svc: PaymentsService = Depends(get_service)):
|
|
514
491
|
out = await svc.void_invoice(provider_invoice_id)
|
|
515
492
|
await svc.session.flush()
|
|
516
493
|
return out
|
|
@@ -522,9 +499,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
522
499
|
dependencies=[Depends(require_idempotency_key)],
|
|
523
500
|
tags=["Invoices"],
|
|
524
501
|
)
|
|
525
|
-
async def pay_invoice(
|
|
526
|
-
provider_invoice_id: str, svc: PaymentsService = Depends(get_service)
|
|
527
|
-
):
|
|
502
|
+
async def pay_invoice(provider_invoice_id: str, svc: PaymentsService = Depends(get_service)):
|
|
528
503
|
out = await svc.pay_invoice(provider_invoice_id)
|
|
529
504
|
await svc.session.flush()
|
|
530
505
|
return out
|
|
@@ -536,9 +511,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
536
511
|
name="payments_get_intent",
|
|
537
512
|
tags=["Payment Intents"],
|
|
538
513
|
)
|
|
539
|
-
async def get_intent(
|
|
540
|
-
provider_intent_id: str, svc: PaymentsService = Depends(get_service)
|
|
541
|
-
):
|
|
514
|
+
async def get_intent(provider_intent_id: str, svc: PaymentsService = Depends(get_service)):
|
|
542
515
|
return await svc.get_intent(provider_intent_id)
|
|
543
516
|
|
|
544
517
|
# STATEMENTS (rollup)
|
|
@@ -580,8 +553,8 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
580
553
|
tags=["Payment Intents"],
|
|
581
554
|
)
|
|
582
555
|
async def list_intents_endpoint(
|
|
583
|
-
customer_provider_id:
|
|
584
|
-
status:
|
|
556
|
+
customer_provider_id: str | None = None,
|
|
557
|
+
status: str | None = None,
|
|
585
558
|
svc: PaymentsService = Depends(get_service),
|
|
586
559
|
):
|
|
587
560
|
ctx = use_pagination()
|
|
@@ -621,8 +594,8 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
621
594
|
tags=["Invoices"],
|
|
622
595
|
)
|
|
623
596
|
async def list_invoices_endpoint(
|
|
624
|
-
customer_provider_id:
|
|
625
|
-
status:
|
|
597
|
+
customer_provider_id: str | None = None,
|
|
598
|
+
status: str | None = None,
|
|
626
599
|
svc: PaymentsService = Depends(get_service),
|
|
627
600
|
):
|
|
628
601
|
ctx = use_pagination()
|
|
@@ -656,7 +629,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
656
629
|
)
|
|
657
630
|
async def preview_invoice_endpoint(
|
|
658
631
|
customer_provider_id: str,
|
|
659
|
-
subscription_id:
|
|
632
|
+
subscription_id: str | None = None,
|
|
660
633
|
svc: PaymentsService = Depends(get_service),
|
|
661
634
|
):
|
|
662
635
|
return await svc.preview_invoice(customer_provider_id, subscription_id)
|
|
@@ -744,7 +717,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
744
717
|
tags=["Disputes"],
|
|
745
718
|
)
|
|
746
719
|
async def list_disputes(
|
|
747
|
-
status:
|
|
720
|
+
status: str | None = None,
|
|
748
721
|
svc: PaymentsService = Depends(get_service),
|
|
749
722
|
):
|
|
750
723
|
ctx = use_pagination()
|
|
@@ -759,9 +732,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
759
732
|
response_model=DisputeOut,
|
|
760
733
|
tags=["Disputes"],
|
|
761
734
|
)
|
|
762
|
-
async def get_dispute(
|
|
763
|
-
provider_dispute_id: str, svc: PaymentsService = Depends(get_service)
|
|
764
|
-
):
|
|
735
|
+
async def get_dispute(provider_dispute_id: str, svc: PaymentsService = Depends(get_service)):
|
|
765
736
|
return await svc.get_dispute(provider_dispute_id)
|
|
766
737
|
|
|
767
738
|
@prot.post(
|
|
@@ -773,9 +744,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
773
744
|
)
|
|
774
745
|
async def submit_dispute_evidence(
|
|
775
746
|
provider_dispute_id: str,
|
|
776
|
-
evidence: dict = Body(
|
|
777
|
-
..., embed=True
|
|
778
|
-
), # free-form evidence blob you validate internally
|
|
747
|
+
evidence: dict = Body(..., embed=True), # free-form evidence blob you validate internally
|
|
779
748
|
svc: PaymentsService = Depends(get_service),
|
|
780
749
|
):
|
|
781
750
|
out = await svc.submit_dispute_evidence(provider_dispute_id, evidence)
|
|
@@ -810,9 +779,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
810
779
|
response_model=PayoutOut,
|
|
811
780
|
tags=["Payouts"],
|
|
812
781
|
)
|
|
813
|
-
async def get_payout(
|
|
814
|
-
provider_payout_id: str, svc: PaymentsService = Depends(get_service)
|
|
815
|
-
):
|
|
782
|
+
async def get_payout(provider_payout_id: str, svc: PaymentsService = Depends(get_service)):
|
|
816
783
|
return await svc.get_payout(provider_payout_id)
|
|
817
784
|
|
|
818
785
|
# ===== Webhook replay (operational) =====
|
|
@@ -824,8 +791,8 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
824
791
|
tags=["Webhooks"],
|
|
825
792
|
)
|
|
826
793
|
async def replay_webhooks(
|
|
827
|
-
since:
|
|
828
|
-
until:
|
|
794
|
+
since: str | None = None,
|
|
795
|
+
until: str | None = None,
|
|
829
796
|
data: WebhookReplayIn = Body(default=WebhookReplayIn()),
|
|
830
797
|
svc: PaymentsService = Depends(get_service),
|
|
831
798
|
):
|
|
@@ -842,8 +809,8 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
842
809
|
tags=["Customers"],
|
|
843
810
|
)
|
|
844
811
|
async def list_customers_endpoint(
|
|
845
|
-
provider:
|
|
846
|
-
user_id:
|
|
812
|
+
provider: str | None = None,
|
|
813
|
+
user_id: str | None = None,
|
|
847
814
|
svc: PaymentsService = Depends(get_service),
|
|
848
815
|
):
|
|
849
816
|
ctx = use_pagination()
|
|
@@ -872,9 +839,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
872
839
|
name="payments_get_method",
|
|
873
840
|
tags=["Payment Methods"],
|
|
874
841
|
)
|
|
875
|
-
async def get_method(
|
|
876
|
-
provider_method_id: str, svc: PaymentsService = Depends(get_service)
|
|
877
|
-
):
|
|
842
|
+
async def get_method(provider_method_id: str, svc: PaymentsService = Depends(get_service)):
|
|
878
843
|
return await svc.get_payment_method(provider_method_id)
|
|
879
844
|
|
|
880
845
|
@prot.post(
|
|
@@ -913,7 +878,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
913
878
|
tags=["Products"],
|
|
914
879
|
)
|
|
915
880
|
async def list_products_endpoint(
|
|
916
|
-
active:
|
|
881
|
+
active: bool | None = None,
|
|
917
882
|
svc: PaymentsService = Depends(get_service),
|
|
918
883
|
):
|
|
919
884
|
ctx = use_pagination()
|
|
@@ -958,8 +923,8 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
958
923
|
tags=["Prices"],
|
|
959
924
|
)
|
|
960
925
|
async def list_prices_endpoint(
|
|
961
|
-
provider_product_id:
|
|
962
|
-
active:
|
|
926
|
+
provider_product_id: str | None = None,
|
|
927
|
+
active: bool | None = None,
|
|
963
928
|
svc: PaymentsService = Depends(get_service),
|
|
964
929
|
):
|
|
965
930
|
ctx = use_pagination()
|
|
@@ -1007,8 +972,8 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
1007
972
|
tags=["Subscriptions"],
|
|
1008
973
|
)
|
|
1009
974
|
async def list_subscriptions_endpoint(
|
|
1010
|
-
customer_provider_id:
|
|
1011
|
-
status:
|
|
975
|
+
customer_provider_id: str | None = None,
|
|
976
|
+
status: str | None = None,
|
|
1012
977
|
svc: PaymentsService = Depends(get_service),
|
|
1013
978
|
):
|
|
1014
979
|
ctx = use_pagination()
|
|
@@ -1047,7 +1012,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
1047
1012
|
tags=["Refunds"],
|
|
1048
1013
|
)
|
|
1049
1014
|
async def list_refunds_endpoint(
|
|
1050
|
-
provider_payment_intent_id:
|
|
1015
|
+
provider_payment_intent_id: str | None = None,
|
|
1051
1016
|
svc: PaymentsService = Depends(get_service),
|
|
1052
1017
|
):
|
|
1053
1018
|
ctx = use_pagination()
|
|
@@ -1078,8 +1043,8 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
1078
1043
|
tags=["Usage Records"],
|
|
1079
1044
|
)
|
|
1080
1045
|
async def list_usage_records_endpoint(
|
|
1081
|
-
subscription_item:
|
|
1082
|
-
provider_price_id:
|
|
1046
|
+
subscription_item: str | None = None,
|
|
1047
|
+
provider_price_id: str | None = None,
|
|
1083
1048
|
svc: PaymentsService = Depends(get_service),
|
|
1084
1049
|
):
|
|
1085
1050
|
ctx = use_pagination()
|
|
@@ -1113,9 +1078,7 @@ def build_payments_routers(prefix: str = "/payments") -> list[DualAPIRouter]:
|
|
|
1113
1078
|
dependencies=[Depends(require_idempotency_key)],
|
|
1114
1079
|
tags=["Payment Methods"],
|
|
1115
1080
|
)
|
|
1116
|
-
async def delete_method_alias(
|
|
1117
|
-
alias_id: str, svc: PaymentsService = Depends(get_service)
|
|
1118
|
-
):
|
|
1081
|
+
async def delete_method_alias(alias_id: str, svc: PaymentsService = Depends(get_service)):
|
|
1119
1082
|
"""
|
|
1120
1083
|
Removes the local alias/association to a payment method.
|
|
1121
1084
|
This does **not** delete the underlying payment method at the provider.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from typing import TYPE_CHECKING, cast
|
|
5
6
|
|
|
6
7
|
from fastapi import FastAPI
|
|
7
8
|
|
|
@@ -14,9 +15,7 @@ if TYPE_CHECKING:
|
|
|
14
15
|
logger = logging.getLogger(__name__)
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def _maybe_register_default_providers(
|
|
18
|
-
register_defaults: bool, adapters: Optional[Iterable[object]]
|
|
19
|
-
):
|
|
18
|
+
def _maybe_register_default_providers(register_defaults: bool, adapters: Iterable[object] | None):
|
|
20
19
|
reg = get_provider_registry()
|
|
21
20
|
if register_defaults:
|
|
22
21
|
# Try Stripe by default; silently skip if not configured
|
|
@@ -34,9 +33,7 @@ def _maybe_register_default_providers(
|
|
|
34
33
|
pass
|
|
35
34
|
if adapters:
|
|
36
35
|
for a in adapters:
|
|
37
|
-
reg.register(
|
|
38
|
-
cast("ProviderAdapter", a)
|
|
39
|
-
) # must implement ProviderAdapter protocol
|
|
36
|
+
reg.register(cast("ProviderAdapter", a)) # must implement ProviderAdapter protocol
|
|
40
37
|
|
|
41
38
|
|
|
42
39
|
def add_payments(
|
|
@@ -44,9 +41,8 @@ def add_payments(
|
|
|
44
41
|
*,
|
|
45
42
|
prefix: str = "/payments",
|
|
46
43
|
register_default_providers: bool = True,
|
|
47
|
-
adapters:
|
|
48
|
-
include_in_docs: bool
|
|
49
|
-
| None = None, # None = keep your env-based default visibility
|
|
44
|
+
adapters: Iterable[object] | None = None,
|
|
45
|
+
include_in_docs: bool | None = None, # None = keep your env-based default visibility
|
|
50
46
|
) -> None:
|
|
51
47
|
"""
|
|
52
48
|
One-call payments installer.
|
|
@@ -60,9 +56,7 @@ def add_payments(
|
|
|
60
56
|
for r in build_payments_routers(prefix=prefix):
|
|
61
57
|
app.include_router(
|
|
62
58
|
r,
|
|
63
|
-
include_in_schema=True
|
|
64
|
-
if include_in_docs is None
|
|
65
|
-
else bool(include_in_docs),
|
|
59
|
+
include_in_schema=True if include_in_docs is None else bool(include_in_docs),
|
|
66
60
|
)
|
|
67
61
|
|
|
68
62
|
# Store the startup function to be called by lifespan if needed
|
|
@@ -27,7 +27,7 @@ from .security import (
|
|
|
27
27
|
RequireService,
|
|
28
28
|
RequireUser,
|
|
29
29
|
)
|
|
30
|
-
from .settings import AuthSettings,
|
|
30
|
+
from .settings import AuthSettings, JWTSettings, OIDCProvider, get_auth_settings
|
|
31
31
|
|
|
32
32
|
if TYPE_CHECKING:
|
|
33
33
|
from .add import add_auth_users as add_auth_users
|