opensettle 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.
Files changed (44) hide show
  1. opensettle-0.1.0/.gitignore +19 -0
  2. opensettle-0.1.0/CHANGELOG.md +18 -0
  3. opensettle-0.1.0/LICENSE +21 -0
  4. opensettle-0.1.0/PKG-INFO +206 -0
  5. opensettle-0.1.0/README.md +144 -0
  6. opensettle-0.1.0/pyproject.toml +188 -0
  7. opensettle-0.1.0/src/opensettle/__init__.py +54 -0
  8. opensettle-0.1.0/src/opensettle/_errors.py +255 -0
  9. opensettle-0.1.0/src/opensettle/_http.py +204 -0
  10. opensettle-0.1.0/src/opensettle/_http_async.py +194 -0
  11. opensettle-0.1.0/src/opensettle/_transport.py +242 -0
  12. opensettle-0.1.0/src/opensettle/_version.py +7 -0
  13. opensettle-0.1.0/src/opensettle/_webhooks.py +195 -0
  14. opensettle-0.1.0/src/opensettle/client.py +230 -0
  15. opensettle-0.1.0/src/opensettle/py.typed +0 -0
  16. opensettle-0.1.0/src/opensettle/resources/__init__.py +3 -0
  17. opensettle-0.1.0/src/opensettle/resources/checkouts.py +48 -0
  18. opensettle-0.1.0/src/opensettle/resources/customers.py +88 -0
  19. opensettle-0.1.0/src/opensettle/resources/invoices.py +118 -0
  20. opensettle-0.1.0/src/opensettle/resources/payments.py +100 -0
  21. opensettle-0.1.0/src/opensettle/resources/products.py +120 -0
  22. opensettle-0.1.0/src/opensettle/resources/subscriptions.py +179 -0
  23. opensettle-0.1.0/src/opensettle/resources/webhook_endpoints.py +183 -0
  24. opensettle-0.1.0/src/opensettle/types.py +331 -0
  25. opensettle-0.1.0/tests/conftest.py +78 -0
  26. opensettle-0.1.0/tests/helpers.py +38 -0
  27. opensettle-0.1.0/tests/test_client.py +201 -0
  28. opensettle-0.1.0/tests/test_errors_taxonomy.py +222 -0
  29. opensettle-0.1.0/tests/test_http_async.py +280 -0
  30. opensettle-0.1.0/tests/test_http_config.py +141 -0
  31. opensettle-0.1.0/tests/test_http_errors.py +209 -0
  32. opensettle-0.1.0/tests/test_http_request_shape.py +298 -0
  33. opensettle-0.1.0/tests/test_http_retries.py +274 -0
  34. opensettle-0.1.0/tests/test_resources_async_parity.py +437 -0
  35. opensettle-0.1.0/tests/test_resources_checkouts.py +154 -0
  36. opensettle-0.1.0/tests/test_resources_customers.py +159 -0
  37. opensettle-0.1.0/tests/test_resources_invoices.py +266 -0
  38. opensettle-0.1.0/tests/test_resources_payments.py +178 -0
  39. opensettle-0.1.0/tests/test_resources_products.py +240 -0
  40. opensettle-0.1.0/tests/test_resources_subscriptions.py +255 -0
  41. opensettle-0.1.0/tests/test_resources_webhook_endpoints.py +286 -0
  42. opensettle-0.1.0/tests/test_version.py +31 -0
  43. opensettle-0.1.0/tests/test_webhooks.py +348 -0
  44. opensettle-0.1.0/tests/test_webhooks_parity.py +133 -0
@@ -0,0 +1,19 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .pyright_cache/
12
+ .coverage
13
+ .coverage.*
14
+ htmlcov/
15
+ coverage.xml
16
+ .tox/
17
+ .venv/
18
+ .python-version
19
+ uv.lock
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-05-12
4
+
5
+ Initial release. Mirrors the surface area of the Node SDK
6
+ (`@opensettle/sdk@0.2.0`).
7
+
8
+ - Sync `OpenSettle` and async `AsyncOpenSettle` clients sharing a
9
+ single transport core.
10
+ - 7 resources: customers, products, invoices, payments, subscriptions,
11
+ checkouts, webhook_endpoints.
12
+ - Typed error hierarchy mirroring the API's 12 stable error codes.
13
+ - HMAC-SHA256 signed-webhook verifier (`t=…,v1=…` scheme, constant-time
14
+ comparison, 300 s default tolerance).
15
+ - Idempotent writes by default on money-adjacent routes.
16
+ - Bounded retries with exponential backoff on 5xx, 429, and network
17
+ errors; `Retry-After` honored.
18
+ - `py.typed` marker; passes `mypy --strict` and `pyright` strict.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenSettle
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.4
2
+ Name: opensettle
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the OpenSettle API. Stablecoin billing on Base, Ethereum, Polygon, Arbitrum, Solana, and Tron.
5
+ Project-URL: Homepage, https://opensettle.io
6
+ Project-URL: Documentation, https://opensettle.io/docs/sdks
7
+ Project-URL: Repository, https://github.com/OpenSettle/opensettle-sdk-python
8
+ Project-URL: Issues, https://github.com/OpenSettle/opensettle-sdk-python/issues
9
+ Project-URL: Changelog, https://github.com/OpenSettle/opensettle-sdk-python/blob/main/CHANGELOG.md
10
+ Author-email: OpenSettle <support@opensettle.io>
11
+ License: MIT License
12
+
13
+ Copyright (c) 2026 OpenSettle
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
32
+ License-File: LICENSE
33
+ Keywords: billing,evm,non-custodial,opensettle,solana,stablecoin,subscriptions,tron,usdc,usdt
34
+ Classifier: Development Status :: 4 - Beta
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Programming Language :: Python
39
+ Classifier: Programming Language :: Python :: 3
40
+ Classifier: Programming Language :: Python :: 3.9
41
+ Classifier: Programming Language :: Python :: 3.10
42
+ Classifier: Programming Language :: Python :: 3.11
43
+ Classifier: Programming Language :: Python :: 3.12
44
+ Classifier: Programming Language :: Python :: 3.13
45
+ Classifier: Programming Language :: Python :: Implementation :: CPython
46
+ Classifier: Topic :: Office/Business :: Financial
47
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
48
+ Classifier: Typing :: Typed
49
+ Requires-Python: >=3.9
50
+ Requires-Dist: httpx<1,>=0.27
51
+ Requires-Dist: typing-extensions>=4.7; python_version < '3.11'
52
+ Provides-Extra: dev
53
+ Requires-Dist: freezegun>=1.5; extra == 'dev'
54
+ Requires-Dist: mypy>=1.10; extra == 'dev'
55
+ Requires-Dist: pyright>=1.1.370; extra == 'dev'
56
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
57
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
58
+ Requires-Dist: pytest>=8.0; extra == 'dev'
59
+ Requires-Dist: respx>=0.21; extra == 'dev'
60
+ Requires-Dist: ruff>=0.6; extra == 'dev'
61
+ Description-Content-Type: text/markdown
62
+
63
+ # opensettle
64
+
65
+ Official Python SDK for the [OpenSettle](https://opensettle.io) API.
66
+
67
+ Non-custodial stablecoin billing on Base, Ethereum, Polygon, Arbitrum,
68
+ Solana and Tron. Typed end-to-end, sync **and** async, signed-webhook
69
+ verifier included, idempotent writes by default.
70
+
71
+ ## Install
72
+
73
+ ```bash
74
+ pip install opensettle
75
+ ```
76
+
77
+ Requires Python 3.9+.
78
+
79
+ ## Quickstart
80
+
81
+ ```python
82
+ from opensettle import OpenSettle
83
+
84
+ os = OpenSettle(
85
+ api_key="sk_test_…", # or os.environ["OPENSETTLE_KEY"]
86
+ workspace_id="ws_…", # or os.environ["OPENSETTLE_WORKSPACE"]
87
+ )
88
+
89
+ customer = os.customers.create(email="ada@example.com")
90
+ invoice = os.invoices.create(
91
+ customer_id=customer["id"],
92
+ amount_minor=19_900,
93
+ currency="USD",
94
+ chain="base",
95
+ token="USDC",
96
+ line_items=[
97
+ {"description": "Pro plan", "quantity": 1, "unit_amount_minor": 19_900}
98
+ ],
99
+ )
100
+ os.invoices.send(invoice["id"])
101
+ ```
102
+
103
+ ### Async
104
+
105
+ ```python
106
+ import asyncio
107
+ from opensettle import AsyncOpenSettle
108
+
109
+ async def main() -> None:
110
+ async with AsyncOpenSettle(api_key="sk_test_…", workspace_id="ws_…") as os:
111
+ customer = await os.customers.create(email="ada@example.com")
112
+ print(customer["id"])
113
+
114
+ asyncio.run(main())
115
+ ```
116
+
117
+ ### Webhooks
118
+
119
+ ```python
120
+ from opensettle import verify_webhook, WebhookVerificationError
121
+
122
+ @app.post("/webhooks/opensettle")
123
+ async def handler(request):
124
+ raw = await request.body()
125
+ try:
126
+ event = verify_webhook(
127
+ raw_body=raw,
128
+ signature_header=request.headers.get("x-opensettle-signature"),
129
+ secret=os.environ["WHSEC"],
130
+ )
131
+ except WebhookVerificationError as e:
132
+ return Response(status=400, content=f"bad signature: {e.reason}")
133
+ # event.data is the decoded JSON; event.timestamp is the signed epoch
134
+ return Response(status=200)
135
+ ```
136
+
137
+ ### Error handling
138
+
139
+ ```python
140
+ from opensettle import (
141
+ OpenSettle,
142
+ RateLimitError,
143
+ SettlementError,
144
+ )
145
+
146
+ try:
147
+ os.checkouts.create(...)
148
+ except RateLimitError as e:
149
+ time.sleep(e.retry_after or 1)
150
+ retry()
151
+ except SettlementError as e:
152
+ if e.code == "insufficient_confirmations":
153
+ wait_and_retry()
154
+ ```
155
+
156
+ The full error hierarchy:
157
+
158
+ ```
159
+ OpenSettleError
160
+ ├── InvalidRequestError
161
+ ├── InvalidStateTransitionError
162
+ ├── AuthenticationError
163
+ ├── ForbiddenError
164
+ ├── NotFoundError
165
+ ├── ConflictError
166
+ ├── RateLimitError # carries `retry_after: float | None`
167
+ ├── SettlementError # chain_reverted | insufficient_confirmations | signing_required
168
+ ├── StepUpRequiredError # aal_required
169
+ ├── APIError # internal_error or forward-compat unknown codes
170
+ └── NetworkError # transport-layer / timeout
171
+ ```
172
+
173
+ Every error carries `code`, `status`, `request_id`, and `param` so you
174
+ can quote the request ID in support tickets.
175
+
176
+ ## Configuration
177
+
178
+ ```python
179
+ os = OpenSettle(
180
+ api_key="sk_test_…",
181
+ workspace_id="ws_…",
182
+ base_url="https://api.opensettle.io", # default; override for self-host
183
+ test_mode=None, # None | True | False env-assertion gate
184
+ timeout=30.0, # seconds; default 30s
185
+ max_network_retries=3, # 0 to disable retries
186
+ )
187
+ ```
188
+
189
+ `test_mode=True` refuses `sk_live_…` keys; `test_mode=False` refuses
190
+ `sk_test_…`. Leave `None` to accept either and let the API decide.
191
+
192
+ ## Resources
193
+
194
+ | Resource | Methods |
195
+ |---|---|
196
+ | `customers` | `list`, `retrieve`, `create`, `update`, `delete` |
197
+ | `products` | `list`, `retrieve`, `create`, `update`, `list_prices`, `create_price`, `delete`, `delete_price` |
198
+ | `invoices` | `list`, `retrieve`, `create`, `send`, `remind`, `void` |
199
+ | `payments` | `list`, `retrieve`, `refund`, `refund_broadcast` |
200
+ | `subscriptions` | `list`, `retrieve`, `create`, `pause`, `resume`, `cancel`, `change_plan` |
201
+ | `checkouts` | `create`, `retrieve` |
202
+ | `webhook_endpoints` | `list`, `retrieve`, `create`, `update`, `delete`, `rotate_secret`, `test` |
203
+
204
+ ## License
205
+
206
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,144 @@
1
+ # opensettle
2
+
3
+ Official Python SDK for the [OpenSettle](https://opensettle.io) API.
4
+
5
+ Non-custodial stablecoin billing on Base, Ethereum, Polygon, Arbitrum,
6
+ Solana and Tron. Typed end-to-end, sync **and** async, signed-webhook
7
+ verifier included, idempotent writes by default.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install opensettle
13
+ ```
14
+
15
+ Requires Python 3.9+.
16
+
17
+ ## Quickstart
18
+
19
+ ```python
20
+ from opensettle import OpenSettle
21
+
22
+ os = OpenSettle(
23
+ api_key="sk_test_…", # or os.environ["OPENSETTLE_KEY"]
24
+ workspace_id="ws_…", # or os.environ["OPENSETTLE_WORKSPACE"]
25
+ )
26
+
27
+ customer = os.customers.create(email="ada@example.com")
28
+ invoice = os.invoices.create(
29
+ customer_id=customer["id"],
30
+ amount_minor=19_900,
31
+ currency="USD",
32
+ chain="base",
33
+ token="USDC",
34
+ line_items=[
35
+ {"description": "Pro plan", "quantity": 1, "unit_amount_minor": 19_900}
36
+ ],
37
+ )
38
+ os.invoices.send(invoice["id"])
39
+ ```
40
+
41
+ ### Async
42
+
43
+ ```python
44
+ import asyncio
45
+ from opensettle import AsyncOpenSettle
46
+
47
+ async def main() -> None:
48
+ async with AsyncOpenSettle(api_key="sk_test_…", workspace_id="ws_…") as os:
49
+ customer = await os.customers.create(email="ada@example.com")
50
+ print(customer["id"])
51
+
52
+ asyncio.run(main())
53
+ ```
54
+
55
+ ### Webhooks
56
+
57
+ ```python
58
+ from opensettle import verify_webhook, WebhookVerificationError
59
+
60
+ @app.post("/webhooks/opensettle")
61
+ async def handler(request):
62
+ raw = await request.body()
63
+ try:
64
+ event = verify_webhook(
65
+ raw_body=raw,
66
+ signature_header=request.headers.get("x-opensettle-signature"),
67
+ secret=os.environ["WHSEC"],
68
+ )
69
+ except WebhookVerificationError as e:
70
+ return Response(status=400, content=f"bad signature: {e.reason}")
71
+ # event.data is the decoded JSON; event.timestamp is the signed epoch
72
+ return Response(status=200)
73
+ ```
74
+
75
+ ### Error handling
76
+
77
+ ```python
78
+ from opensettle import (
79
+ OpenSettle,
80
+ RateLimitError,
81
+ SettlementError,
82
+ )
83
+
84
+ try:
85
+ os.checkouts.create(...)
86
+ except RateLimitError as e:
87
+ time.sleep(e.retry_after or 1)
88
+ retry()
89
+ except SettlementError as e:
90
+ if e.code == "insufficient_confirmations":
91
+ wait_and_retry()
92
+ ```
93
+
94
+ The full error hierarchy:
95
+
96
+ ```
97
+ OpenSettleError
98
+ ├── InvalidRequestError
99
+ ├── InvalidStateTransitionError
100
+ ├── AuthenticationError
101
+ ├── ForbiddenError
102
+ ├── NotFoundError
103
+ ├── ConflictError
104
+ ├── RateLimitError # carries `retry_after: float | None`
105
+ ├── SettlementError # chain_reverted | insufficient_confirmations | signing_required
106
+ ├── StepUpRequiredError # aal_required
107
+ ├── APIError # internal_error or forward-compat unknown codes
108
+ └── NetworkError # transport-layer / timeout
109
+ ```
110
+
111
+ Every error carries `code`, `status`, `request_id`, and `param` so you
112
+ can quote the request ID in support tickets.
113
+
114
+ ## Configuration
115
+
116
+ ```python
117
+ os = OpenSettle(
118
+ api_key="sk_test_…",
119
+ workspace_id="ws_…",
120
+ base_url="https://api.opensettle.io", # default; override for self-host
121
+ test_mode=None, # None | True | False env-assertion gate
122
+ timeout=30.0, # seconds; default 30s
123
+ max_network_retries=3, # 0 to disable retries
124
+ )
125
+ ```
126
+
127
+ `test_mode=True` refuses `sk_live_…` keys; `test_mode=False` refuses
128
+ `sk_test_…`. Leave `None` to accept either and let the API decide.
129
+
130
+ ## Resources
131
+
132
+ | Resource | Methods |
133
+ |---|---|
134
+ | `customers` | `list`, `retrieve`, `create`, `update`, `delete` |
135
+ | `products` | `list`, `retrieve`, `create`, `update`, `list_prices`, `create_price`, `delete`, `delete_price` |
136
+ | `invoices` | `list`, `retrieve`, `create`, `send`, `remind`, `void` |
137
+ | `payments` | `list`, `retrieve`, `refund`, `refund_broadcast` |
138
+ | `subscriptions` | `list`, `retrieve`, `create`, `pause`, `resume`, `cancel`, `change_plan` |
139
+ | `checkouts` | `create`, `retrieve` |
140
+ | `webhook_endpoints` | `list`, `retrieve`, `create`, `update`, `delete`, `rotate_secret`, `test` |
141
+
142
+ ## License
143
+
144
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,188 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "opensettle"
7
+ version = "0.1.0"
8
+ description = "Official Python SDK for the OpenSettle API. Stablecoin billing on Base, Ethereum, Polygon, Arbitrum, Solana, and Tron."
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ requires-python = ">=3.9"
12
+ authors = [{ name = "OpenSettle", email = "support@opensettle.io" }]
13
+ keywords = [
14
+ "opensettle",
15
+ "stablecoin",
16
+ "billing",
17
+ "subscriptions",
18
+ "usdc",
19
+ "usdt",
20
+ "evm",
21
+ "solana",
22
+ "tron",
23
+ "non-custodial",
24
+ ]
25
+ classifiers = [
26
+ "Development Status :: 4 - Beta",
27
+ "Intended Audience :: Developers",
28
+ "License :: OSI Approved :: MIT License",
29
+ "Operating System :: OS Independent",
30
+ "Programming Language :: Python",
31
+ "Programming Language :: Python :: 3",
32
+ "Programming Language :: Python :: 3.9",
33
+ "Programming Language :: Python :: 3.10",
34
+ "Programming Language :: Python :: 3.11",
35
+ "Programming Language :: Python :: 3.12",
36
+ "Programming Language :: Python :: 3.13",
37
+ "Programming Language :: Python :: Implementation :: CPython",
38
+ "Topic :: Office/Business :: Financial",
39
+ "Topic :: Software Development :: Libraries :: Python Modules",
40
+ "Typing :: Typed",
41
+ ]
42
+ dependencies = [
43
+ "httpx>=0.27,<1",
44
+ "typing-extensions>=4.7; python_version < '3.11'",
45
+ ]
46
+
47
+ [project.urls]
48
+ Homepage = "https://opensettle.io"
49
+ Documentation = "https://opensettle.io/docs/sdks"
50
+ Repository = "https://github.com/OpenSettle/opensettle-sdk-python"
51
+ Issues = "https://github.com/OpenSettle/opensettle-sdk-python/issues"
52
+ Changelog = "https://github.com/OpenSettle/opensettle-sdk-python/blob/main/CHANGELOG.md"
53
+
54
+ [project.optional-dependencies]
55
+ dev = [
56
+ "pytest>=8.0",
57
+ "pytest-asyncio>=0.23",
58
+ "pytest-cov>=5.0",
59
+ "respx>=0.21",
60
+ "freezegun>=1.5",
61
+ "mypy>=1.10",
62
+ "pyright>=1.1.370",
63
+ "ruff>=0.6",
64
+ ]
65
+
66
+ [tool.hatch.build.targets.wheel]
67
+ packages = ["src/opensettle"]
68
+
69
+ [tool.hatch.build.targets.sdist]
70
+ include = [
71
+ "/src",
72
+ "/tests",
73
+ "/README.md",
74
+ "/CHANGELOG.md",
75
+ "/LICENSE",
76
+ "/pyproject.toml",
77
+ ]
78
+
79
+ [tool.pytest.ini_options]
80
+ minversion = "8.0"
81
+ testpaths = ["tests"]
82
+ addopts = [
83
+ "-ra",
84
+ "--strict-markers",
85
+ "--strict-config",
86
+ ]
87
+ asyncio_mode = "auto"
88
+ filterwarnings = [
89
+ "error",
90
+ "ignore::DeprecationWarning:respx.*",
91
+ ]
92
+
93
+ [tool.coverage.run]
94
+ branch = true
95
+ source = ["opensettle"]
96
+
97
+ [tool.coverage.report]
98
+ show_missing = true
99
+ exclude_lines = [
100
+ "pragma: no cover",
101
+ "raise NotImplementedError",
102
+ "if TYPE_CHECKING:",
103
+ "if typing.TYPE_CHECKING:",
104
+ ]
105
+
106
+ [tool.ruff]
107
+ target-version = "py39"
108
+ line-length = 100
109
+ src = ["src", "tests"]
110
+
111
+ [tool.ruff.lint]
112
+ select = [
113
+ "E", # pycodestyle errors
114
+ "W", # pycodestyle warnings
115
+ "F", # pyflakes
116
+ "I", # isort
117
+ "B", # bugbear
118
+ "UP", # pyupgrade
119
+ "SIM", # simplify
120
+ "PT", # pytest style
121
+ "RUF", # ruff-specific
122
+ "TID", # tidy imports
123
+ "PIE", # misc lints
124
+ "PYI", # stub files
125
+ "S", # security
126
+ ]
127
+ ignore = [
128
+ "E501", # line length handled by formatter
129
+ "S101", # asserts are fine in tests
130
+ "S311", # standard-random is fine for idempotency-key fallback
131
+ "PT011", # broad raises in parametrize — explicit message coverage instead
132
+ # 3.9-compat: keep ``Optional[X]`` / ``Union[X, Y]`` / ``Dict[…]`` forms.
133
+ # We have ``from __future__ import annotations`` everywhere so the
134
+ # modern syntax would work for *annotations*, but the SDK also uses
135
+ # these in non-annotation positions (``cast``, ``isinstance``-adjacent
136
+ # type aliases) and the matching industry practice from openai-python
137
+ # and anthropic-sdk is to ship the ``Optional[X]`` form for max reach.
138
+ "UP006",
139
+ "UP007",
140
+ "UP045",
141
+ # Relative imports between sibling private modules within the package
142
+ # are idiomatic and intended; ``from ._http import HttpClient`` is the
143
+ # right form, not ``from opensettle._http import HttpClient``.
144
+ "TID252",
145
+ ]
146
+
147
+ [tool.ruff.lint.per-file-ignores]
148
+ "tests/**" = ["S", "B", "PT006", "PT007", "PT017", "SIM117"]
149
+ "src/opensettle/types.py" = ["UP035"]
150
+ "src/opensettle/resources/subscriptions.py" = ["UP035"]
151
+ "src/opensettle/resources/webhook_endpoints.py" = ["UP035"]
152
+ "src/opensettle/_errors.py" = ["UP035"]
153
+ "src/opensettle/_webhooks.py" = ["UP035"]
154
+
155
+ [tool.ruff.format]
156
+ quote-style = "double"
157
+ indent-style = "space"
158
+
159
+ [tool.mypy]
160
+ # Source supports Python 3.9 at runtime (see ``requires-python`` and the
161
+ # test suite running on 3.11). Static analysis runs against 3.10+ because
162
+ # modern mypy dropped 3.9 type-check support; we still verify 3.9 runtime
163
+ # behavior through the pytest matrix.
164
+ python_version = "3.10"
165
+ strict = true
166
+ warn_unreachable = true
167
+ warn_unused_ignores = true
168
+ warn_redundant_casts = true
169
+ no_implicit_reexport = false
170
+ show_error_codes = true
171
+ files = ["src/opensettle"]
172
+
173
+ [[tool.mypy.overrides]]
174
+ module = "tests.*"
175
+ disallow_untyped_defs = false
176
+ check_untyped_defs = true
177
+
178
+ [tool.pyright]
179
+ include = ["src/opensettle"]
180
+ pythonVersion = "3.10"
181
+ # Standard mode rather than strict: strict reports false positives on
182
+ # our dynamic-JSON envelope-parsing path (every ``Any.get()`` becomes an
183
+ # "Unknown" propagation error). The mypy --strict run is the primary
184
+ # typecheck gate; pyright standard is the cross-checker.
185
+ typeCheckingMode = "standard"
186
+ reportMissingTypeStubs = false
187
+ venvPath = "."
188
+ venv = ".venv"
@@ -0,0 +1,54 @@
1
+ """Official Python SDK for the OpenSettle API.
2
+
3
+ Public surface is intentionally narrow: the high-level clients
4
+ (:class:`OpenSettle`, :class:`AsyncOpenSettle`), the typed error
5
+ hierarchy, and the webhook verifier. Everything else is private and may
6
+ change without a major-version bump.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from ._errors import (
12
+ APIError,
13
+ AuthenticationError,
14
+ ConflictError,
15
+ ErrorCode,
16
+ ForbiddenError,
17
+ InvalidRequestError,
18
+ InvalidStateTransitionError,
19
+ NetworkError,
20
+ NotFoundError,
21
+ OpenSettleError,
22
+ RateLimitError,
23
+ SettlementError,
24
+ StepUpRequiredError,
25
+ )
26
+ from ._version import SDK_VERSION
27
+ from ._webhooks import (
28
+ VerifiedWebhook,
29
+ WebhookVerificationError,
30
+ verify_webhook,
31
+ )
32
+ from .client import AsyncOpenSettle, OpenSettle
33
+
34
+ __all__ = [
35
+ "SDK_VERSION",
36
+ "APIError",
37
+ "AsyncOpenSettle",
38
+ "AuthenticationError",
39
+ "ConflictError",
40
+ "ErrorCode",
41
+ "ForbiddenError",
42
+ "InvalidRequestError",
43
+ "InvalidStateTransitionError",
44
+ "NetworkError",
45
+ "NotFoundError",
46
+ "OpenSettle",
47
+ "OpenSettleError",
48
+ "RateLimitError",
49
+ "SettlementError",
50
+ "StepUpRequiredError",
51
+ "VerifiedWebhook",
52
+ "WebhookVerificationError",
53
+ "verify_webhook",
54
+ ]