speedapi-lib 0.1.4__tar.gz → 2.0.2__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 (42) hide show
  1. speedapi_lib-2.0.2/LICENSE +21 -0
  2. speedapi_lib-2.0.2/PKG-INFO +306 -0
  3. speedapi_lib-2.0.2/README.md +268 -0
  4. speedapi_lib-2.0.2/pyproject.toml +89 -0
  5. {speedapi_lib-0.1.4 → speedapi_lib-2.0.2}/setup.cfg +4 -4
  6. speedapi_lib-2.0.2/src/speedapi/__init__.py +41 -0
  7. speedapi_lib-2.0.2/src/speedapi/_auth.py +26 -0
  8. speedapi_lib-2.0.2/src/speedapi/_http.py +233 -0
  9. speedapi_lib-2.0.2/src/speedapi/async_client.py +97 -0
  10. speedapi_lib-2.0.2/src/speedapi/client.py +112 -0
  11. speedapi_lib-2.0.2/src/speedapi/exceptions.py +71 -0
  12. speedapi_lib-2.0.2/src/speedapi/models.py +307 -0
  13. speedapi_lib-2.0.2/src/speedapi/resources/__init__.py +1 -0
  14. speedapi_lib-2.0.2/src/speedapi/resources/checkout_sessions.py +217 -0
  15. speedapi_lib-2.0.2/src/speedapi/resources/invoices.py +181 -0
  16. speedapi_lib-2.0.2/src/speedapi/resources/pay_requests.py +175 -0
  17. speedapi_lib-2.0.2/src/speedapi/resources/webhooks.py +127 -0
  18. speedapi_lib-2.0.2/src/speedapi_lib.egg-info/PKG-INFO +306 -0
  19. speedapi_lib-2.0.2/src/speedapi_lib.egg-info/SOURCES.txt +28 -0
  20. speedapi_lib-2.0.2/src/speedapi_lib.egg-info/requires.txt +12 -0
  21. speedapi_lib-2.0.2/src/speedapi_lib.egg-info/top_level.txt +1 -0
  22. speedapi_lib-2.0.2/tests/test_async_client.py +104 -0
  23. speedapi_lib-2.0.2/tests/test_balances.py +50 -0
  24. speedapi_lib-2.0.2/tests/test_checkout_sessions.py +93 -0
  25. speedapi_lib-2.0.2/tests/test_exceptions.py +90 -0
  26. speedapi_lib-2.0.2/tests/test_invoices.py +65 -0
  27. speedapi_lib-2.0.2/tests/test_models.py +103 -0
  28. speedapi_lib-2.0.2/tests/test_pay_requests.py +60 -0
  29. speedapi_lib-2.0.2/tests/test_webhooks.py +78 -0
  30. speedapi_lib-0.1.4/PKG-INFO +0 -65
  31. speedapi_lib-0.1.4/README.md +0 -41
  32. speedapi_lib-0.1.4/setup.py +0 -29
  33. speedapi_lib-0.1.4/speedapi_lib/__init__.py +0 -0
  34. speedapi_lib-0.1.4/speedapi_lib/speedapi.py +0 -56
  35. speedapi_lib-0.1.4/speedapi_lib.egg-info/PKG-INFO +0 -65
  36. speedapi_lib-0.1.4/speedapi_lib.egg-info/SOURCES.txt +0 -12
  37. speedapi_lib-0.1.4/speedapi_lib.egg-info/entry_points.txt +0 -2
  38. speedapi_lib-0.1.4/speedapi_lib.egg-info/requires.txt +0 -2
  39. speedapi_lib-0.1.4/speedapi_lib.egg-info/top_level.txt +0 -2
  40. speedapi_lib-0.1.4/tests/__init__.py +0 -0
  41. speedapi_lib-0.1.4/tests/test_sample.py +0 -14
  42. {speedapi_lib-0.1.4 → speedapi_lib-2.0.2/src}/speedapi_lib.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Furkan Köykıran
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,306 @@
1
+ Metadata-Version: 2.4
2
+ Name: speedapi-lib
3
+ Version: 2.0.2
4
+ Summary: Python SDK for the Speed Merchant API — sync & async, Pydantic v2, full type hints
5
+ Author-email: Furkan Köykıran <furkankoykiran@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/furkankoykiran/speedapi_lib
8
+ Project-URL: Documentation, https://docs.tryspeed.com
9
+ Project-URL: Repository, https://github.com/furkankoykiran/speedapi_lib
10
+ Project-URL: Bug Tracker, https://github.com/furkankoykiran/speedapi_lib/issues
11
+ Keywords: speed,merchant,api,bitcoin,lightning,payments
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: httpx>=0.27
27
+ Requires-Dist: pydantic>=2.5
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == "dev"
30
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
31
+ Requires-Dist: respx>=0.21; extra == "dev"
32
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.4; extra == "dev"
34
+ Requires-Dist: mypy>=1.10; extra == "dev"
35
+ Requires-Dist: build>=1.2; extra == "dev"
36
+ Requires-Dist: twine>=5.0; extra == "dev"
37
+ Dynamic: license-file
38
+
39
+ <div align="center">
40
+
41
+ # ⚡ speedapi-python
42
+
43
+ **The official Python SDK for the [Speed Merchant API](https://docs.tryspeed.com)**
44
+
45
+ [![PyPI version](https://img.shields.io/pypi/v/speedapi-python.svg?style=flat-square&color=blueviolet)](https://pypi.org/project/speedapi-python/)
46
+ [![Python Versions](https://img.shields.io/pypi/pyversions/speedapi-python.svg?style=flat-square)](https://pypi.org/project/speedapi-python/)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE)
48
+ [![Type Checked](https://img.shields.io/badge/type--checked-mypy-informational?style=flat-square)](https://mypy-lang.org/)
49
+
50
+ > Sync & async support · Pydantic v2 models · Full type hints · Webhook verification
51
+
52
+ </div>
53
+
54
+ ---
55
+
56
+ ## Features
57
+
58
+ - 🔄 **Sync & Async** clients powered by `httpx`
59
+ - 🧩 **Pydantic v2** request / response models with IDE autocomplete
60
+ - 🛡️ **Rich error hierarchy** — `AuthenticationError`, `RateLimitError`, `NotFoundError` and more
61
+ - 🔗 **Checkout Sessions**, **Invoices**, **Pay Requests**, **Balances**
62
+ - 🔒 **Webhook signature verification** (HMAC-SHA256 with replay protection)
63
+ - 🐍 Python 3.9+ with 100% type annotations
64
+
65
+ ---
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ pip install speedapi-python
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Quick Start
76
+
77
+ ### Synchronous
78
+
79
+ ```python
80
+ from speedapi import SpeedAPI
81
+
82
+ client = SpeedAPI(api_key="sk_live_...")
83
+
84
+ # Check balance
85
+ balance = client.balances.retrieve()
86
+ sats = client.balances.retrieve_sats()
87
+ print(f"Available: {sats} SATS")
88
+
89
+ # Create a checkout session
90
+ session = client.checkout_sessions.create(
91
+ amount=5000, # amount in cents (for USD)
92
+ currency="USD",
93
+ success_url="https://example.com/success",
94
+ cancel_url="https://example.com/cancel",
95
+ description="Premium subscription",
96
+ )
97
+ print(f"Redirect to: {session.url}")
98
+
99
+ # Create a Lightning pay request
100
+ pr = client.pay_requests.create(
101
+ amount=21000,
102
+ currency="SATS",
103
+ description="Coffee ☕",
104
+ )
105
+ print(f"Pay via: {pr.lightning_invoice}")
106
+ ```
107
+
108
+ ### Asynchronous (async/await)
109
+
110
+ ```python
111
+ import asyncio
112
+ from speedapi import AsyncSpeedAPI
113
+
114
+ async def main():
115
+ async with AsyncSpeedAPI(api_key="sk_live_...") as client:
116
+ # All resources work identically — just add await
117
+ balance = await client.balances.retrieve()
118
+
119
+ session = await client.checkout_sessions.create(
120
+ amount=5000,
121
+ currency="USD",
122
+ success_url="https://example.com/success",
123
+ )
124
+ print(session.url)
125
+
126
+ asyncio.run(main())
127
+ ```
128
+
129
+ ### FastAPI Integration Example
130
+
131
+ ```python
132
+ from fastapi import FastAPI, Request
133
+ from speedapi import AsyncSpeedAPI
134
+
135
+ app = FastAPI()
136
+ speed = AsyncSpeedAPI(api_key="sk_live_...")
137
+
138
+ @app.post("/create-session")
139
+ async def create_session():
140
+ session = await speed.checkout_sessions.create(
141
+ amount=999,
142
+ currency="USD",
143
+ success_url="https://myapp.com/success",
144
+ )
145
+ return {"checkout_url": session.url}
146
+ ```
147
+
148
+ ---
149
+
150
+ ## API Reference
151
+
152
+ ### `SpeedAPI` / `AsyncSpeedAPI`
153
+
154
+ | Parameter | Type | Default | Description |
155
+ |-----------|------|---------|-------------|
156
+ | `api_key` | `str` | required | Your Speed secret key (`sk_live_...`) |
157
+ | `base_url` | `str` | `https://api.tryspeed.com` | API base URL |
158
+ | `timeout` | `float` | `30.0` | Request timeout in seconds |
159
+
160
+ ### Resources
161
+
162
+ #### `client.balances`
163
+
164
+ | Method | Returns | Description |
165
+ |--------|---------|-------------|
166
+ | `retrieve()` | `Balance` | All available/pending balances |
167
+ | `retrieve_sats()` | `float` | Available SATS balance (convenience) |
168
+
169
+ #### `client.checkout_sessions`
170
+
171
+ | Method | Returns | Description |
172
+ |--------|---------|-------------|
173
+ | `create(amount, currency, ...)` | `CheckoutSession` | Create a hosted checkout page |
174
+ | `retrieve(session_id)` | `CheckoutSession` | Get a session by ID |
175
+ | `list(limit, ...)` | `CheckoutSessionList` | List sessions (paginated) |
176
+ | `expire(session_id)` | `CheckoutSession` | Expire an open session |
177
+
178
+ #### `client.invoices`
179
+
180
+ | Method | Returns | Description |
181
+ |--------|---------|-------------|
182
+ | `create(amount, currency, ...)` | `Invoice` | Create a new invoice |
183
+ | `retrieve(invoice_id)` | `Invoice` | Get an invoice by ID |
184
+ | `list(limit, ...)` | `InvoiceList` | List invoices (paginated) |
185
+
186
+ #### `client.pay_requests`
187
+
188
+ | Method | Returns | Description |
189
+ |--------|---------|-------------|
190
+ | `create(amount, currency, ...)` | `PayRequest` | Create a pay request (Lightning) |
191
+ | `retrieve(pay_request_id)` | `PayRequest` | Get a pay request by ID |
192
+ | `list(limit, ...)` | `PayRequestList` | List pay requests (paginated) |
193
+
194
+ ---
195
+
196
+ ## Error Handling
197
+
198
+ `speedapi-python` raises specific exceptions for every error class — no need to inspect raw status codes.
199
+
200
+ ```python
201
+ from speedapi import (
202
+ SpeedAPI,
203
+ AuthenticationError,
204
+ RateLimitError,
205
+ NotFoundError,
206
+ APIStatusError,
207
+ APIConnectionError,
208
+ )
209
+
210
+ client = SpeedAPI(api_key="sk_live_...")
211
+
212
+ try:
213
+ session = client.checkout_sessions.retrieve("cs_nonexistent")
214
+ except AuthenticationError:
215
+ print("Invalid API key — check your credentials")
216
+ except NotFoundError:
217
+ print("Session not found")
218
+ except RateLimitError:
219
+ print("Too many requests — back off and retry")
220
+ except APIConnectionError as e:
221
+ print(f"Network error: {e}")
222
+ except APIStatusError as e:
223
+ print(f"Unexpected API error {e.status_code}: {e.response_body}")
224
+ ```
225
+
226
+ ### Exception Hierarchy
227
+
228
+ ```
229
+ SpeedAPIError
230
+ ├── AuthenticationError # HTTP 401
231
+ ├── PermissionDeniedError # HTTP 403
232
+ ├── NotFoundError # HTTP 404
233
+ ├── RateLimitError # HTTP 429
234
+ ├── APIStatusError # other 4xx / 5xx (carries .status_code + .response_body)
235
+ └── APIConnectionError # network-level (timeout, DNS, etc.)
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Webhook Verification
241
+
242
+ Speed signs every webhook with HMAC-SHA256. Always verify the signature before processing.
243
+
244
+ ```python
245
+ from fastapi import FastAPI, Request, HTTPException
246
+ from speedapi.resources.webhooks import Webhooks, WebhookSignatureVerificationError
247
+
248
+ app = FastAPI()
249
+ WEBHOOK_SECRET = "whsec_..." # from your Speed Dashboard
250
+
251
+ @app.post("/webhooks/speed")
252
+ async def speed_webhook(request: Request):
253
+ payload = await request.body()
254
+ sig_header = request.headers.get("Speed-Signature", "")
255
+
256
+ try:
257
+ event = Webhooks.construct_event(
258
+ payload=payload,
259
+ sig_header=sig_header,
260
+ secret=WEBHOOK_SECRET,
261
+ )
262
+ except WebhookSignatureVerificationError as e:
263
+ raise HTTPException(status_code=400, detail=str(e))
264
+
265
+ if event.type == "checkout_session.completed":
266
+ session_id = event.data.get("object", {}).get("id")
267
+ print(f"✅ Checkout completed: {session_id}")
268
+ elif event.type == "invoice.paid":
269
+ invoice_id = event.data.get("object", {}).get("id")
270
+ print(f"💰 Invoice paid: {invoice_id}")
271
+
272
+ return {"received": True}
273
+ ```
274
+
275
+ > **Security**: The SDK automatically rejects webhooks older than 5 minutes to prevent replay attacks.
276
+
277
+ ---
278
+
279
+ ## Development
280
+
281
+ ```bash
282
+ # Clone the repo
283
+ git clone https://github.com/furkankoykiran/speedapi_lib
284
+ cd speedapi_lib
285
+
286
+ # Install in editable mode with dev dependencies
287
+ pip install -e ".[dev]"
288
+
289
+ # Run the test suite
290
+ pytest tests/ -v --tb=short --cov=src/speedapi
291
+
292
+ # Type check
293
+ mypy src/speedapi
294
+ ```
295
+
296
+ ---
297
+
298
+ ## License
299
+
300
+ MIT — see [LICENSE](LICENSE) for details.
301
+
302
+ ---
303
+
304
+ <div align="center">
305
+ Built with ❤️ by <a href="https://github.com/furkankoykiran">Furkan Köykıran</a>
306
+ </div>
@@ -0,0 +1,268 @@
1
+ <div align="center">
2
+
3
+ # ⚡ speedapi-python
4
+
5
+ **The official Python SDK for the [Speed Merchant API](https://docs.tryspeed.com)**
6
+
7
+ [![PyPI version](https://img.shields.io/pypi/v/speedapi-python.svg?style=flat-square&color=blueviolet)](https://pypi.org/project/speedapi-python/)
8
+ [![Python Versions](https://img.shields.io/pypi/pyversions/speedapi-python.svg?style=flat-square)](https://pypi.org/project/speedapi-python/)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE)
10
+ [![Type Checked](https://img.shields.io/badge/type--checked-mypy-informational?style=flat-square)](https://mypy-lang.org/)
11
+
12
+ > Sync & async support · Pydantic v2 models · Full type hints · Webhook verification
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ ## Features
19
+
20
+ - 🔄 **Sync & Async** clients powered by `httpx`
21
+ - 🧩 **Pydantic v2** request / response models with IDE autocomplete
22
+ - 🛡️ **Rich error hierarchy** — `AuthenticationError`, `RateLimitError`, `NotFoundError` and more
23
+ - 🔗 **Checkout Sessions**, **Invoices**, **Pay Requests**, **Balances**
24
+ - 🔒 **Webhook signature verification** (HMAC-SHA256 with replay protection)
25
+ - 🐍 Python 3.9+ with 100% type annotations
26
+
27
+ ---
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install speedapi-python
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Quick Start
38
+
39
+ ### Synchronous
40
+
41
+ ```python
42
+ from speedapi import SpeedAPI
43
+
44
+ client = SpeedAPI(api_key="sk_live_...")
45
+
46
+ # Check balance
47
+ balance = client.balances.retrieve()
48
+ sats = client.balances.retrieve_sats()
49
+ print(f"Available: {sats} SATS")
50
+
51
+ # Create a checkout session
52
+ session = client.checkout_sessions.create(
53
+ amount=5000, # amount in cents (for USD)
54
+ currency="USD",
55
+ success_url="https://example.com/success",
56
+ cancel_url="https://example.com/cancel",
57
+ description="Premium subscription",
58
+ )
59
+ print(f"Redirect to: {session.url}")
60
+
61
+ # Create a Lightning pay request
62
+ pr = client.pay_requests.create(
63
+ amount=21000,
64
+ currency="SATS",
65
+ description="Coffee ☕",
66
+ )
67
+ print(f"Pay via: {pr.lightning_invoice}")
68
+ ```
69
+
70
+ ### Asynchronous (async/await)
71
+
72
+ ```python
73
+ import asyncio
74
+ from speedapi import AsyncSpeedAPI
75
+
76
+ async def main():
77
+ async with AsyncSpeedAPI(api_key="sk_live_...") as client:
78
+ # All resources work identically — just add await
79
+ balance = await client.balances.retrieve()
80
+
81
+ session = await client.checkout_sessions.create(
82
+ amount=5000,
83
+ currency="USD",
84
+ success_url="https://example.com/success",
85
+ )
86
+ print(session.url)
87
+
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ### FastAPI Integration Example
92
+
93
+ ```python
94
+ from fastapi import FastAPI, Request
95
+ from speedapi import AsyncSpeedAPI
96
+
97
+ app = FastAPI()
98
+ speed = AsyncSpeedAPI(api_key="sk_live_...")
99
+
100
+ @app.post("/create-session")
101
+ async def create_session():
102
+ session = await speed.checkout_sessions.create(
103
+ amount=999,
104
+ currency="USD",
105
+ success_url="https://myapp.com/success",
106
+ )
107
+ return {"checkout_url": session.url}
108
+ ```
109
+
110
+ ---
111
+
112
+ ## API Reference
113
+
114
+ ### `SpeedAPI` / `AsyncSpeedAPI`
115
+
116
+ | Parameter | Type | Default | Description |
117
+ |-----------|------|---------|-------------|
118
+ | `api_key` | `str` | required | Your Speed secret key (`sk_live_...`) |
119
+ | `base_url` | `str` | `https://api.tryspeed.com` | API base URL |
120
+ | `timeout` | `float` | `30.0` | Request timeout in seconds |
121
+
122
+ ### Resources
123
+
124
+ #### `client.balances`
125
+
126
+ | Method | Returns | Description |
127
+ |--------|---------|-------------|
128
+ | `retrieve()` | `Balance` | All available/pending balances |
129
+ | `retrieve_sats()` | `float` | Available SATS balance (convenience) |
130
+
131
+ #### `client.checkout_sessions`
132
+
133
+ | Method | Returns | Description |
134
+ |--------|---------|-------------|
135
+ | `create(amount, currency, ...)` | `CheckoutSession` | Create a hosted checkout page |
136
+ | `retrieve(session_id)` | `CheckoutSession` | Get a session by ID |
137
+ | `list(limit, ...)` | `CheckoutSessionList` | List sessions (paginated) |
138
+ | `expire(session_id)` | `CheckoutSession` | Expire an open session |
139
+
140
+ #### `client.invoices`
141
+
142
+ | Method | Returns | Description |
143
+ |--------|---------|-------------|
144
+ | `create(amount, currency, ...)` | `Invoice` | Create a new invoice |
145
+ | `retrieve(invoice_id)` | `Invoice` | Get an invoice by ID |
146
+ | `list(limit, ...)` | `InvoiceList` | List invoices (paginated) |
147
+
148
+ #### `client.pay_requests`
149
+
150
+ | Method | Returns | Description |
151
+ |--------|---------|-------------|
152
+ | `create(amount, currency, ...)` | `PayRequest` | Create a pay request (Lightning) |
153
+ | `retrieve(pay_request_id)` | `PayRequest` | Get a pay request by ID |
154
+ | `list(limit, ...)` | `PayRequestList` | List pay requests (paginated) |
155
+
156
+ ---
157
+
158
+ ## Error Handling
159
+
160
+ `speedapi-python` raises specific exceptions for every error class — no need to inspect raw status codes.
161
+
162
+ ```python
163
+ from speedapi import (
164
+ SpeedAPI,
165
+ AuthenticationError,
166
+ RateLimitError,
167
+ NotFoundError,
168
+ APIStatusError,
169
+ APIConnectionError,
170
+ )
171
+
172
+ client = SpeedAPI(api_key="sk_live_...")
173
+
174
+ try:
175
+ session = client.checkout_sessions.retrieve("cs_nonexistent")
176
+ except AuthenticationError:
177
+ print("Invalid API key — check your credentials")
178
+ except NotFoundError:
179
+ print("Session not found")
180
+ except RateLimitError:
181
+ print("Too many requests — back off and retry")
182
+ except APIConnectionError as e:
183
+ print(f"Network error: {e}")
184
+ except APIStatusError as e:
185
+ print(f"Unexpected API error {e.status_code}: {e.response_body}")
186
+ ```
187
+
188
+ ### Exception Hierarchy
189
+
190
+ ```
191
+ SpeedAPIError
192
+ ├── AuthenticationError # HTTP 401
193
+ ├── PermissionDeniedError # HTTP 403
194
+ ├── NotFoundError # HTTP 404
195
+ ├── RateLimitError # HTTP 429
196
+ ├── APIStatusError # other 4xx / 5xx (carries .status_code + .response_body)
197
+ └── APIConnectionError # network-level (timeout, DNS, etc.)
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Webhook Verification
203
+
204
+ Speed signs every webhook with HMAC-SHA256. Always verify the signature before processing.
205
+
206
+ ```python
207
+ from fastapi import FastAPI, Request, HTTPException
208
+ from speedapi.resources.webhooks import Webhooks, WebhookSignatureVerificationError
209
+
210
+ app = FastAPI()
211
+ WEBHOOK_SECRET = "whsec_..." # from your Speed Dashboard
212
+
213
+ @app.post("/webhooks/speed")
214
+ async def speed_webhook(request: Request):
215
+ payload = await request.body()
216
+ sig_header = request.headers.get("Speed-Signature", "")
217
+
218
+ try:
219
+ event = Webhooks.construct_event(
220
+ payload=payload,
221
+ sig_header=sig_header,
222
+ secret=WEBHOOK_SECRET,
223
+ )
224
+ except WebhookSignatureVerificationError as e:
225
+ raise HTTPException(status_code=400, detail=str(e))
226
+
227
+ if event.type == "checkout_session.completed":
228
+ session_id = event.data.get("object", {}).get("id")
229
+ print(f"✅ Checkout completed: {session_id}")
230
+ elif event.type == "invoice.paid":
231
+ invoice_id = event.data.get("object", {}).get("id")
232
+ print(f"💰 Invoice paid: {invoice_id}")
233
+
234
+ return {"received": True}
235
+ ```
236
+
237
+ > **Security**: The SDK automatically rejects webhooks older than 5 minutes to prevent replay attacks.
238
+
239
+ ---
240
+
241
+ ## Development
242
+
243
+ ```bash
244
+ # Clone the repo
245
+ git clone https://github.com/furkankoykiran/speedapi_lib
246
+ cd speedapi_lib
247
+
248
+ # Install in editable mode with dev dependencies
249
+ pip install -e ".[dev]"
250
+
251
+ # Run the test suite
252
+ pytest tests/ -v --tb=short --cov=src/speedapi
253
+
254
+ # Type check
255
+ mypy src/speedapi
256
+ ```
257
+
258
+ ---
259
+
260
+ ## License
261
+
262
+ MIT — see [LICENSE](LICENSE) for details.
263
+
264
+ ---
265
+
266
+ <div align="center">
267
+ Built with ❤️ by <a href="https://github.com/furkankoykiran">Furkan Köykıran</a>
268
+ </div>
@@ -0,0 +1,89 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "speedapi-lib"
7
+ version = "2.0.2"
8
+ description = "Python SDK for the Speed Merchant API — sync & async, Pydantic v2, full type hints"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "Furkan Köykıran", email = "furkankoykiran@gmail.com" }]
12
+ requires-python = ">=3.9"
13
+ keywords = ["speed", "merchant", "api", "bitcoin", "lightning", "payments"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Operating System :: OS Independent",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ "Typing :: Typed",
26
+ ]
27
+ dependencies = [
28
+ "httpx>=0.27",
29
+ "pydantic>=2.5",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest>=8.0",
35
+ "pytest-asyncio>=0.23",
36
+ "respx>=0.21",
37
+ "pytest-cov>=5.0",
38
+ "ruff>=0.4",
39
+ "mypy>=1.10",
40
+ "build>=1.2",
41
+ "twine>=5.0",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/furkankoykiran/speedapi_lib"
46
+ Documentation = "https://docs.tryspeed.com"
47
+ Repository = "https://github.com/furkankoykiran/speedapi_lib"
48
+ "Bug Tracker" = "https://github.com/furkankoykiran/speedapi_lib/issues"
49
+
50
+ [tool.setuptools.packages.find]
51
+ where = ["src"]
52
+
53
+ [tool.pytest.ini_options]
54
+ asyncio_mode = "auto"
55
+ testpaths = ["tests"]
56
+ addopts = "-v --tb=short"
57
+
58
+ [tool.coverage.run]
59
+ source = ["src/speedapi"]
60
+
61
+ [tool.mypy]
62
+ python_version = "3.9"
63
+ strict = true
64
+
65
+ [tool.ruff]
66
+ line-length = 100
67
+ target-version = "py39"
68
+
69
+ [tool.ruff.lint]
70
+ select = [
71
+ "E", # pycodestyle errors
72
+ "W", # pycodestyle warnings
73
+ "F", # pyflakes
74
+ "I", # isort
75
+ "B", # flake8-bugbear
76
+ "UP", # pyupgrade
77
+ ]
78
+ ignore = [
79
+ "E501", # line too long — handled by formatter
80
+ "B008", # do not perform function calls in default arguments
81
+ "UP006", # use dict/list instead of Dict/List — we use __future__.annotations
82
+ "UP007", # use X | Y instead of Union — we use __future__.annotations
83
+ "UP035", # typing module deprecations — we use __future__.annotations
84
+ "UP045", # use X | None — we use __future__.annotations
85
+ "B904", # raise-without-from inside except — acceptable in SDK exception handling
86
+ ]
87
+
88
+ [tool.ruff.lint.per-file-ignores]
89
+ "tests/*" = ["S101"] # allow assert statements in tests
@@ -1,4 +1,4 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+