foldset-fastapi 0.1.0__tar.gz

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.
@@ -0,0 +1,40 @@
1
+ # Dependencies
2
+ node_modules
3
+ .pnpm-store
4
+
5
+ # Build outputs
6
+ dist
7
+ # TODO: Remove these exceptions once packages are published to npm.
8
+ # Currently committing dist/ because Vercel doesn't run prepare scripts
9
+ # for git dependencies properly.
10
+ !packages/typescript/nextjs/dist
11
+ !packages/typescript/core/dist
12
+ build
13
+ .next
14
+ out
15
+
16
+ # Environment files
17
+ .env
18
+ .env.local
19
+ .env.*.local
20
+
21
+ # IDE
22
+ .idea
23
+ .vscode
24
+ *.swp
25
+ *.swo
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
30
+
31
+ # Debug logs
32
+ npm-debug.log*
33
+ pnpm-debug.log*
34
+
35
+ # TypeScript
36
+ *.tsbuildinfo
37
+
38
+ # Misc
39
+ *.log
40
+ .cache
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.4
2
+ Name: foldset-fastapi
3
+ Version: 0.1.0
4
+ Summary: FastAPI middleware for Foldset payment gating
5
+ Project-URL: Homepage, https://foldset.com
6
+ Project-URL: Documentation, https://docs.foldset.com
7
+ Project-URL: Repository, https://github.com/foldset/sdks
8
+ Author-email: Foldset <team@foldset.com>
9
+ License-Expression: MIT
10
+ Keywords: agents,ai,fastapi,foldset,micropayments,middleware
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Framework :: FastAPI
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: fastapi>=0.100.0
21
+ Requires-Dist: foldset>=0.1.0
22
+ Requires-Dist: starlette>=0.27.0
@@ -0,0 +1,4 @@
1
+ from .middleware import FoldsetMiddleware
2
+ from foldset.types import FoldsetOptions
3
+
4
+ __all__ = ["FoldsetMiddleware", "FoldsetOptions"]
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from foldset.types import RequestAdapter
6
+ from starlette.requests import Request
7
+
8
+
9
+ class FastAPIAdapter(RequestAdapter):
10
+ def __init__(self, request: Request) -> None:
11
+ self._request = request
12
+ self._body: Any | None = None
13
+
14
+ def get_ip_address(self) -> str | None:
15
+ forwarded = self.get_header("x-forwarded-for")
16
+ if forwarded:
17
+ return forwarded.split(",")[0].strip()
18
+ if self._request.client:
19
+ return self._request.client.host
20
+ return None
21
+
22
+ def get_header(self, name: str) -> str | None:
23
+ return self._request.headers.get(name)
24
+
25
+ def get_method(self) -> str:
26
+ return self._request.method
27
+
28
+ def get_path(self) -> str:
29
+ return self._request.url.path
30
+
31
+ def get_url(self) -> str:
32
+ return str(self._request.url)
33
+
34
+ def get_host(self) -> str:
35
+ return self._request.url.hostname or ""
36
+
37
+ def get_accept_header(self) -> str:
38
+ return self.get_header("accept") or ""
39
+
40
+ def get_user_agent(self) -> str:
41
+ return self.get_header("user-agent") or ""
42
+
43
+ def get_query_params(self) -> dict[str, str | list[str]] | None:
44
+ result: dict[str, str | list[str]] = {}
45
+ for key, value in self._request.query_params.multi_items():
46
+ existing = result.get(key)
47
+ if existing is None:
48
+ result[key] = value
49
+ elif isinstance(existing, list):
50
+ existing.append(value)
51
+ else:
52
+ result[key] = [existing, value]
53
+ return result
54
+
55
+ def get_query_param(self, name: str) -> str | list[str] | None:
56
+ params = self.get_query_params()
57
+ if params:
58
+ return params.get(name)
59
+ return None
60
+
61
+ async def get_body(self) -> Any:
62
+ if self._body is None:
63
+ try:
64
+ self._body = await self._request.json()
65
+ except Exception:
66
+ self._body = None
67
+ return self._body
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import Any
5
+
6
+ from foldset import WorkerCore, report_error
7
+ from foldset.types import FoldsetOptions
8
+ from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
9
+ from starlette.requests import Request
10
+ from starlette.responses import Response
11
+
12
+ from .adapter import FastAPIAdapter
13
+
14
+ from importlib.metadata import version as _pkg_version
15
+
16
+ PACKAGE_VERSION = _pkg_version("foldset-fastapi")
17
+
18
+
19
+ def _set_headers(response: Response, headers: dict[str, str]) -> None:
20
+ for key, value in headers.items():
21
+ response.headers[key] = value
22
+
23
+
24
+ class FoldsetMiddleware(BaseHTTPMiddleware):
25
+ def __init__(self, app: Any, options: FoldsetOptions) -> None:
26
+ super().__init__(app)
27
+ self._options = FoldsetOptions(
28
+ api_key=options.api_key,
29
+ redis_credentials=options.redis_credentials,
30
+ platform="fastapi",
31
+ sdk_version=PACKAGE_VERSION,
32
+ )
33
+ self._disabled = not options.api_key
34
+ if self._disabled:
35
+ import warnings
36
+ warnings.warn("[foldset] No API key provided, middleware disabled")
37
+
38
+ async def dispatch(
39
+ self, request: Request, call_next: RequestResponseEndpoint
40
+ ) -> Response:
41
+ if self._disabled:
42
+ return await call_next(request)
43
+
44
+ try:
45
+ core = await WorkerCore.from_options(self._options)
46
+ adapter = FastAPIAdapter(request)
47
+ result = await core.process_request(adapter)
48
+
49
+ if result.type == "health-check":
50
+ response = Response(
51
+ content=result.response.body,
52
+ status_code=result.response.status,
53
+ media_type="application/json",
54
+ )
55
+ _set_headers(response, result.response.headers)
56
+ return response
57
+
58
+ if result.type == "no-payment-required":
59
+ response = await call_next(request)
60
+ if result.headers:
61
+ _set_headers(response, result.headers)
62
+ return response
63
+
64
+ if result.type == "payment-error":
65
+ response = Response(
66
+ content=result.response.body,
67
+ status_code=result.response.status,
68
+ )
69
+ _set_headers(response, result.response.headers)
70
+ return response
71
+
72
+ if result.type == "payment-verified":
73
+ response = await call_next(request)
74
+
75
+ settlement = await core.process_settlement(
76
+ adapter,
77
+ result.payment_payload,
78
+ result.payment_requirements,
79
+ response.status_code,
80
+ result.metadata.request_id,
81
+ )
82
+
83
+ if settlement.success:
84
+ _set_headers(response, settlement.headers)
85
+ else:
86
+ response = Response(
87
+ content=json.dumps({
88
+ "error": "Settlement failed",
89
+ "details": settlement.error_reason,
90
+ }),
91
+ status_code=402,
92
+ media_type="application/json",
93
+ )
94
+
95
+ return response
96
+
97
+ except Exception as error:
98
+ # On any error, allow the request through rather than blocking the user.
99
+ await report_error(self._options.api_key, error, FastAPIAdapter(request))
100
+ return await call_next(request)
101
+
102
+ return await call_next(request)
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "foldset-fastapi"
3
+ version = "0.1.0"
4
+ description = "FastAPI middleware for Foldset payment gating"
5
+ license = "MIT"
6
+ requires-python = ">=3.11"
7
+ authors = [{ name = "Foldset", email = "team@foldset.com" }]
8
+ keywords = ["foldset", "fastapi", "middleware", "micropayments", "ai", "agents"]
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "License :: OSI Approved :: MIT License",
12
+ "Programming Language :: Python :: 3",
13
+ "Programming Language :: Python :: 3.11",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python :: 3.13",
16
+ "Framework :: FastAPI",
17
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
18
+ ]
19
+ dependencies = [
20
+ "foldset>=0.1.0",
21
+ "fastapi>=0.100.0",
22
+ "starlette>=0.27.0",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://foldset.com"
27
+ Documentation = "https://docs.foldset.com"
28
+ Repository = "https://github.com/foldset/sdks"
29
+
30
+ [build-system]
31
+ requires = ["hatchling"]
32
+ build-backend = "hatchling.build"
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["foldset_fastapi"]