stablesea 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.
- stablesea-0.1.0/PKG-INFO +152 -0
- stablesea-0.1.0/README.md +126 -0
- stablesea-0.1.0/pyproject.toml +44 -0
- stablesea-0.1.0/setup.cfg +4 -0
- stablesea-0.1.0/stablesea/__init__.py +14 -0
- stablesea-0.1.0/stablesea/_http.py +155 -0
- stablesea-0.1.0/stablesea/async_client.py +80 -0
- stablesea-0.1.0/stablesea/client.py +96 -0
- stablesea-0.1.0/stablesea/errors.py +32 -0
- stablesea-0.1.0/stablesea/resources/__init__.py +19 -0
- stablesea-0.1.0/stablesea/resources/api_keys.py +57 -0
- stablesea-0.1.0/stablesea/resources/async_resources.py +248 -0
- stablesea-0.1.0/stablesea/resources/base.py +19 -0
- stablesea-0.1.0/stablesea/resources/external_payment_instruments.py +66 -0
- stablesea-0.1.0/stablesea/resources/liquidity_providers.py +26 -0
- stablesea-0.1.0/stablesea/resources/offerings.py +42 -0
- stablesea-0.1.0/stablesea/resources/orders.py +54 -0
- stablesea-0.1.0/stablesea/resources/organizations.py +51 -0
- stablesea-0.1.0/stablesea/resources/quotes.py +60 -0
- stablesea-0.1.0/stablesea.egg-info/PKG-INFO +152 -0
- stablesea-0.1.0/stablesea.egg-info/SOURCES.txt +22 -0
- stablesea-0.1.0/stablesea.egg-info/dependency_links.txt +1 -0
- stablesea-0.1.0/stablesea.egg-info/requires.txt +5 -0
- stablesea-0.1.0/stablesea.egg-info/top_level.txt +1 -0
stablesea-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stablesea
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the Stable Sea Terminal API
|
|
5
|
+
Author: Stable Sea
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://stablesea.com
|
|
8
|
+
Project-URL: Documentation, https://docs.stablesea.com
|
|
9
|
+
Project-URL: Repository, https://github.com/stablesea/stablesea-python
|
|
10
|
+
Keywords: stablesea,api,shipping,terminal
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
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
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: httpx>=0.24.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
26
|
+
|
|
27
|
+
# Stable Sea Python SDK
|
|
28
|
+
|
|
29
|
+
Official Python client for the [Stable Sea Terminal API](https://stablesea.com), for managing organizations, quotes, orders, and API keys.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install stablesea
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or install from source:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install /path/to/sdk/python
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from stablesea import StableseaClient
|
|
47
|
+
|
|
48
|
+
# Initialize with your API key (sandbox by default)
|
|
49
|
+
client = StableseaClient(api_key="sk_your_api_key")
|
|
50
|
+
|
|
51
|
+
# List organizations
|
|
52
|
+
organizations = client.organizations.list()
|
|
53
|
+
|
|
54
|
+
# Create an organization
|
|
55
|
+
org = client.organizations.create(
|
|
56
|
+
name="Acme Corp",
|
|
57
|
+
contact={"email": "john@acme.com", "first_name": "John", "last_name": "Doe"},
|
|
58
|
+
idempotency_key="unique-key-123",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Create a quote (organization-scoped)
|
|
62
|
+
quote = client.quotes.create(
|
|
63
|
+
organization_id="org_01k2cm4r59e5z8k5ggrbbxjcwy",
|
|
64
|
+
offering_id="off_01k4qph5ezfsga7fkvbygsqq93",
|
|
65
|
+
payin_amount="100",
|
|
66
|
+
idempotency_key="quote-key-456",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Create an order from the quote
|
|
70
|
+
order = client.orders.create(
|
|
71
|
+
organization_id="org_01k2cm4r59e5z8k5ggrbbxjcwy",
|
|
72
|
+
quote_id=quote["id"],
|
|
73
|
+
idempotency_key="order-key-789",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# List API keys
|
|
77
|
+
api_keys = client.api_keys.list()
|
|
78
|
+
|
|
79
|
+
# Create a new API key
|
|
80
|
+
new_key = client.api_keys.create(
|
|
81
|
+
name="Production API",
|
|
82
|
+
environment="PRODUCTION",
|
|
83
|
+
permission_level="STANDARD",
|
|
84
|
+
)
|
|
85
|
+
# Store new_key["api_key"] securely - it's only returned once!
|
|
86
|
+
|
|
87
|
+
# List liquidity providers
|
|
88
|
+
providers = client.liquidity_providers.list()
|
|
89
|
+
|
|
90
|
+
# Get exchange rate for a provider
|
|
91
|
+
rate = client.liquidity_providers.get_exchange_rate("ALPHA")
|
|
92
|
+
|
|
93
|
+
client.close()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Async Support
|
|
97
|
+
|
|
98
|
+
For async/await support, use `AsyncStableseaClient`:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import asyncio
|
|
102
|
+
from stablesea import AsyncStableseaClient
|
|
103
|
+
|
|
104
|
+
async def main():
|
|
105
|
+
async with AsyncStableseaClient(api_key="sk_your_api_key") as client:
|
|
106
|
+
orgs = await client.organizations.list()
|
|
107
|
+
quote = await client.quotes.create(
|
|
108
|
+
organization_id="org_...",
|
|
109
|
+
offering_id="off_...",
|
|
110
|
+
payin_amount="100",
|
|
111
|
+
idempotency_key="unique-key",
|
|
112
|
+
)
|
|
113
|
+
print(orgs, quote)
|
|
114
|
+
|
|
115
|
+
asyncio.run(main())
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
| Parameter | Default | Description |
|
|
121
|
+
|------------|------------------------------------|--------------------------------|
|
|
122
|
+
| `api_key` | (required) | Your Stable Sea API key |
|
|
123
|
+
| `base_url` | `https://api-sandbox.stablesea.com/v1` | API base URL (use production URL for live) |
|
|
124
|
+
| `timeout` | `30.0` | Request timeout in seconds |
|
|
125
|
+
|
|
126
|
+
## API Resources
|
|
127
|
+
|
|
128
|
+
| Resource | Methods |
|
|
129
|
+
|----------|---------|
|
|
130
|
+
| `client.organizations` | `list()`, `create()`, `get()` |
|
|
131
|
+
| `client.quotes` | `create()`, `get()` |
|
|
132
|
+
| `client.orders` | `create()`, `list()`, `get()` |
|
|
133
|
+
| `client.api_keys` | `list()`, `create()`, `rotate()`, `revoke()` |
|
|
134
|
+
| `client.liquidity_providers` | `list()`, `get_exchange_rate()` |
|
|
135
|
+
| `client.offerings` | `create()`, `get()` |
|
|
136
|
+
| `client.external_payment_instruments` | `list()`, `create()`, `get()`, `archive()` |
|
|
137
|
+
|
|
138
|
+
## Error Handling
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from stablesea import StableseaClient, APIError
|
|
142
|
+
|
|
143
|
+
client = StableseaClient(api_key="sk_...")
|
|
144
|
+
try:
|
|
145
|
+
client.organizations.create(...)
|
|
146
|
+
except APIError as e:
|
|
147
|
+
print(f"API error {e.status_code}: {e.message}")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Stable Sea Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python client for the [Stable Sea Terminal API](https://stablesea.com), for managing organizations, quotes, orders, and API keys.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install stablesea
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install from source:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install /path/to/sdk/python
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from stablesea import StableseaClient
|
|
21
|
+
|
|
22
|
+
# Initialize with your API key (sandbox by default)
|
|
23
|
+
client = StableseaClient(api_key="sk_your_api_key")
|
|
24
|
+
|
|
25
|
+
# List organizations
|
|
26
|
+
organizations = client.organizations.list()
|
|
27
|
+
|
|
28
|
+
# Create an organization
|
|
29
|
+
org = client.organizations.create(
|
|
30
|
+
name="Acme Corp",
|
|
31
|
+
contact={"email": "john@acme.com", "first_name": "John", "last_name": "Doe"},
|
|
32
|
+
idempotency_key="unique-key-123",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Create a quote (organization-scoped)
|
|
36
|
+
quote = client.quotes.create(
|
|
37
|
+
organization_id="org_01k2cm4r59e5z8k5ggrbbxjcwy",
|
|
38
|
+
offering_id="off_01k4qph5ezfsga7fkvbygsqq93",
|
|
39
|
+
payin_amount="100",
|
|
40
|
+
idempotency_key="quote-key-456",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Create an order from the quote
|
|
44
|
+
order = client.orders.create(
|
|
45
|
+
organization_id="org_01k2cm4r59e5z8k5ggrbbxjcwy",
|
|
46
|
+
quote_id=quote["id"],
|
|
47
|
+
idempotency_key="order-key-789",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# List API keys
|
|
51
|
+
api_keys = client.api_keys.list()
|
|
52
|
+
|
|
53
|
+
# Create a new API key
|
|
54
|
+
new_key = client.api_keys.create(
|
|
55
|
+
name="Production API",
|
|
56
|
+
environment="PRODUCTION",
|
|
57
|
+
permission_level="STANDARD",
|
|
58
|
+
)
|
|
59
|
+
# Store new_key["api_key"] securely - it's only returned once!
|
|
60
|
+
|
|
61
|
+
# List liquidity providers
|
|
62
|
+
providers = client.liquidity_providers.list()
|
|
63
|
+
|
|
64
|
+
# Get exchange rate for a provider
|
|
65
|
+
rate = client.liquidity_providers.get_exchange_rate("ALPHA")
|
|
66
|
+
|
|
67
|
+
client.close()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Async Support
|
|
71
|
+
|
|
72
|
+
For async/await support, use `AsyncStableseaClient`:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import asyncio
|
|
76
|
+
from stablesea import AsyncStableseaClient
|
|
77
|
+
|
|
78
|
+
async def main():
|
|
79
|
+
async with AsyncStableseaClient(api_key="sk_your_api_key") as client:
|
|
80
|
+
orgs = await client.organizations.list()
|
|
81
|
+
quote = await client.quotes.create(
|
|
82
|
+
organization_id="org_...",
|
|
83
|
+
offering_id="off_...",
|
|
84
|
+
payin_amount="100",
|
|
85
|
+
idempotency_key="unique-key",
|
|
86
|
+
)
|
|
87
|
+
print(orgs, quote)
|
|
88
|
+
|
|
89
|
+
asyncio.run(main())
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
| Parameter | Default | Description |
|
|
95
|
+
|------------|------------------------------------|--------------------------------|
|
|
96
|
+
| `api_key` | (required) | Your Stable Sea API key |
|
|
97
|
+
| `base_url` | `https://api-sandbox.stablesea.com/v1` | API base URL (use production URL for live) |
|
|
98
|
+
| `timeout` | `30.0` | Request timeout in seconds |
|
|
99
|
+
|
|
100
|
+
## API Resources
|
|
101
|
+
|
|
102
|
+
| Resource | Methods |
|
|
103
|
+
|----------|---------|
|
|
104
|
+
| `client.organizations` | `list()`, `create()`, `get()` |
|
|
105
|
+
| `client.quotes` | `create()`, `get()` |
|
|
106
|
+
| `client.orders` | `create()`, `list()`, `get()` |
|
|
107
|
+
| `client.api_keys` | `list()`, `create()`, `rotate()`, `revoke()` |
|
|
108
|
+
| `client.liquidity_providers` | `list()`, `get_exchange_rate()` |
|
|
109
|
+
| `client.offerings` | `create()`, `get()` |
|
|
110
|
+
| `client.external_payment_instruments` | `list()`, `create()`, `get()`, `archive()` |
|
|
111
|
+
|
|
112
|
+
## Error Handling
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from stablesea import StableseaClient, APIError
|
|
116
|
+
|
|
117
|
+
client = StableseaClient(api_key="sk_...")
|
|
118
|
+
try:
|
|
119
|
+
client.organizations.create(...)
|
|
120
|
+
except APIError as e:
|
|
121
|
+
print(f"API error {e.status_code}: {e.message}")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "stablesea"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for the Stable Sea Terminal API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Stable Sea" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["stablesea", "api", "shipping", "terminal"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"httpx>=0.24.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
dev = [
|
|
33
|
+
"pytest>=7.0",
|
|
34
|
+
"pytest-asyncio>=0.21",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://stablesea.com"
|
|
39
|
+
Documentation = "https://docs.stablesea.com"
|
|
40
|
+
Repository = "https://github.com/stablesea/stablesea-python"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["."]
|
|
44
|
+
include = ["stablesea*"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Stable Sea Python SDK."""
|
|
2
|
+
|
|
3
|
+
from stablesea.async_client import AsyncStableseaClient
|
|
4
|
+
from stablesea.client import StableseaClient
|
|
5
|
+
from stablesea.errors import APIError, StableseaError
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"StableseaClient",
|
|
9
|
+
"AsyncStableseaClient",
|
|
10
|
+
"StableseaError",
|
|
11
|
+
"APIError",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""HTTP client with Bearer auth."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HTTPClient:
|
|
9
|
+
"""Low-level HTTP client with Bearer token authentication."""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
api_key: str,
|
|
14
|
+
base_url: str = "https://api-sandbox.stablesea.com/v1",
|
|
15
|
+
timeout: float = 30.0,
|
|
16
|
+
http_client: Optional[httpx.Client] = None,
|
|
17
|
+
):
|
|
18
|
+
self.base_url = base_url.rstrip("/")
|
|
19
|
+
self.api_key = api_key
|
|
20
|
+
self.timeout = timeout
|
|
21
|
+
self._client = http_client or httpx.Client(
|
|
22
|
+
base_url=self.base_url,
|
|
23
|
+
headers={
|
|
24
|
+
"Authorization": f"Bearer {api_key}",
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
"Accept": "application/json",
|
|
27
|
+
},
|
|
28
|
+
timeout=timeout,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def request(
|
|
32
|
+
self,
|
|
33
|
+
method: str,
|
|
34
|
+
path: str,
|
|
35
|
+
*,
|
|
36
|
+
json: Optional[Dict[str, Any]] = None,
|
|
37
|
+
params: Optional[Dict[str, Any]] = None,
|
|
38
|
+
headers: Optional[Dict[str, str]] = None,
|
|
39
|
+
) -> Dict[str, Any]:
|
|
40
|
+
"""Make an HTTP request and return parsed JSON."""
|
|
41
|
+
merged_headers = dict(headers or {})
|
|
42
|
+
resp = self._client.request(
|
|
43
|
+
method,
|
|
44
|
+
path,
|
|
45
|
+
json=json,
|
|
46
|
+
params=params,
|
|
47
|
+
headers=merged_headers or None,
|
|
48
|
+
)
|
|
49
|
+
return self._handle_response(resp)
|
|
50
|
+
|
|
51
|
+
def _handle_response(self, resp: httpx.Response) -> Dict[str, Any]:
|
|
52
|
+
from stablesea.errors import APIError
|
|
53
|
+
|
|
54
|
+
if resp.status_code >= 400:
|
|
55
|
+
try:
|
|
56
|
+
body = resp.json()
|
|
57
|
+
msg = body.get("message", resp.text or f"HTTP {resp.status_code}")
|
|
58
|
+
err_code = body.get("error")
|
|
59
|
+
except Exception:
|
|
60
|
+
msg = resp.text or f"HTTP {resp.status_code}"
|
|
61
|
+
err_code = None
|
|
62
|
+
raise APIError(
|
|
63
|
+
message=msg,
|
|
64
|
+
error_code=err_code,
|
|
65
|
+
status_code=resp.status_code,
|
|
66
|
+
response=resp,
|
|
67
|
+
)
|
|
68
|
+
if resp.status_code in (200, 201) and resp.content:
|
|
69
|
+
return resp.json()
|
|
70
|
+
return {}
|
|
71
|
+
|
|
72
|
+
def close(self) -> None:
|
|
73
|
+
"""Close the HTTP client."""
|
|
74
|
+
self._client.close()
|
|
75
|
+
|
|
76
|
+
def __enter__(self) -> "HTTPClient":
|
|
77
|
+
return self
|
|
78
|
+
|
|
79
|
+
def __exit__(self, *args: object) -> None:
|
|
80
|
+
self.close()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class AsyncHTTPClient:
|
|
84
|
+
"""Async HTTP client with Bearer token authentication."""
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
api_key: str,
|
|
89
|
+
base_url: str = "https://api-sandbox.stablesea.com/v1",
|
|
90
|
+
timeout: float = 30.0,
|
|
91
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
|
92
|
+
):
|
|
93
|
+
self.base_url = base_url.rstrip("/")
|
|
94
|
+
self.api_key = api_key
|
|
95
|
+
self.timeout = timeout
|
|
96
|
+
self._client = http_client or httpx.AsyncClient(
|
|
97
|
+
base_url=self.base_url,
|
|
98
|
+
headers={
|
|
99
|
+
"Authorization": f"Bearer {api_key}",
|
|
100
|
+
"Content-Type": "application/json",
|
|
101
|
+
"Accept": "application/json",
|
|
102
|
+
},
|
|
103
|
+
timeout=timeout,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
async def request(
|
|
107
|
+
self,
|
|
108
|
+
method: str,
|
|
109
|
+
path: str,
|
|
110
|
+
*,
|
|
111
|
+
json: Optional[Dict[str, Any]] = None,
|
|
112
|
+
params: Optional[Dict[str, Any]] = None,
|
|
113
|
+
headers: Optional[Dict[str, str]] = None,
|
|
114
|
+
) -> Dict[str, Any]:
|
|
115
|
+
"""Make an HTTP request and return parsed JSON."""
|
|
116
|
+
merged_headers = dict(headers or {})
|
|
117
|
+
resp = await self._client.request(
|
|
118
|
+
method,
|
|
119
|
+
path,
|
|
120
|
+
json=json,
|
|
121
|
+
params=params,
|
|
122
|
+
headers=merged_headers or None,
|
|
123
|
+
)
|
|
124
|
+
return self._handle_response(resp)
|
|
125
|
+
|
|
126
|
+
def _handle_response(self, resp: httpx.Response) -> Dict[str, Any]:
|
|
127
|
+
from stablesea.errors import APIError
|
|
128
|
+
|
|
129
|
+
if resp.status_code >= 400:
|
|
130
|
+
try:
|
|
131
|
+
body = resp.json()
|
|
132
|
+
msg = body.get("message", resp.text or f"HTTP {resp.status_code}")
|
|
133
|
+
err_code = body.get("error")
|
|
134
|
+
except Exception:
|
|
135
|
+
msg = resp.text or f"HTTP {resp.status_code}"
|
|
136
|
+
err_code = None
|
|
137
|
+
raise APIError(
|
|
138
|
+
message=msg,
|
|
139
|
+
error_code=err_code,
|
|
140
|
+
status_code=resp.status_code,
|
|
141
|
+
response=resp,
|
|
142
|
+
)
|
|
143
|
+
if resp.status_code in (200, 201) and resp.content:
|
|
144
|
+
return resp.json()
|
|
145
|
+
return {}
|
|
146
|
+
|
|
147
|
+
async def close(self) -> None:
|
|
148
|
+
"""Close the HTTP client."""
|
|
149
|
+
await self._client.aclose()
|
|
150
|
+
|
|
151
|
+
async def __aenter__(self) -> "AsyncHTTPClient":
|
|
152
|
+
return self
|
|
153
|
+
|
|
154
|
+
async def __aexit__(self, *args: object) -> None:
|
|
155
|
+
await self.close()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Async Stable Sea API client."""
|
|
2
|
+
|
|
3
|
+
from stablesea._http import AsyncHTTPClient
|
|
4
|
+
from stablesea.resources.async_resources import (
|
|
5
|
+
AsyncApiKeysResource,
|
|
6
|
+
AsyncExternalPaymentInstrumentsResource,
|
|
7
|
+
AsyncLiquidityProvidersResource,
|
|
8
|
+
AsyncOfferingsResource,
|
|
9
|
+
AsyncOrganizationsResource,
|
|
10
|
+
AsyncOrdersResource,
|
|
11
|
+
AsyncQuotesResource,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AsyncStableseaClient:
|
|
16
|
+
"""Async Python client for the Stable Sea Terminal API.
|
|
17
|
+
|
|
18
|
+
Uses Bearer token authentication. Supports async/await for non-blocking requests.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> async with AsyncStableseaClient(api_key="sk_...") as client:
|
|
22
|
+
... orgs = await client.organizations.list()
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
api_key: str,
|
|
28
|
+
base_url: str = "https://api-sandbox.stablesea.com/v1",
|
|
29
|
+
timeout: float = 30.0,
|
|
30
|
+
):
|
|
31
|
+
"""Initialize the async client.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
api_key: Your Stable Sea API key (Bearer token)
|
|
35
|
+
base_url: API base URL (default: sandbox)
|
|
36
|
+
timeout: Request timeout in seconds
|
|
37
|
+
"""
|
|
38
|
+
self._http = AsyncHTTPClient(
|
|
39
|
+
api_key=api_key,
|
|
40
|
+
base_url=base_url,
|
|
41
|
+
timeout=timeout,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def organizations(self) -> AsyncOrganizationsResource:
|
|
46
|
+
return AsyncOrganizationsResource(self._http)
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def quotes(self) -> AsyncQuotesResource:
|
|
50
|
+
return AsyncQuotesResource(self._http)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def orders(self) -> AsyncOrdersResource:
|
|
54
|
+
return AsyncOrdersResource(self._http)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def api_keys(self) -> AsyncApiKeysResource:
|
|
58
|
+
return AsyncApiKeysResource(self._http)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def liquidity_providers(self) -> AsyncLiquidityProvidersResource:
|
|
62
|
+
return AsyncLiquidityProvidersResource(self._http)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def offerings(self) -> AsyncOfferingsResource:
|
|
66
|
+
return AsyncOfferingsResource(self._http)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def external_payment_instruments(self) -> AsyncExternalPaymentInstrumentsResource:
|
|
70
|
+
return AsyncExternalPaymentInstrumentsResource(self._http)
|
|
71
|
+
|
|
72
|
+
async def close(self) -> None:
|
|
73
|
+
"""Close the HTTP client."""
|
|
74
|
+
await self._http.close()
|
|
75
|
+
|
|
76
|
+
async def __aenter__(self) -> "AsyncStableseaClient":
|
|
77
|
+
return self
|
|
78
|
+
|
|
79
|
+
async def __aexit__(self, *args: object) -> None:
|
|
80
|
+
await self.close()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Stable Sea API client."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from stablesea._http import HTTPClient
|
|
6
|
+
from stablesea.resources import (
|
|
7
|
+
ApiKeysResource,
|
|
8
|
+
ExternalPaymentInstrumentsResource,
|
|
9
|
+
LiquidityProvidersResource,
|
|
10
|
+
OfferingsResource,
|
|
11
|
+
OrganizationsResource,
|
|
12
|
+
OrdersResource,
|
|
13
|
+
QuotesResource,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class StableseaClient:
|
|
18
|
+
"""Python client for the Stable Sea Terminal API.
|
|
19
|
+
|
|
20
|
+
Uses Bearer token authentication. All requests include the API key
|
|
21
|
+
in the Authorization header.
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
>>> client = StableseaClient(api_key="sk_...")
|
|
25
|
+
>>> orgs = client.organizations.list()
|
|
26
|
+
>>> quote = client.quotes.create(
|
|
27
|
+
... organization_id="org_...",
|
|
28
|
+
... offering_id="off_...",
|
|
29
|
+
... payin_amount="100",
|
|
30
|
+
... idempotency_key="unique-key-123"
|
|
31
|
+
... )
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
api_key: str,
|
|
37
|
+
base_url: str = "https://api-sandbox.stablesea.com/v1",
|
|
38
|
+
timeout: float = 30.0,
|
|
39
|
+
):
|
|
40
|
+
"""Initialize the client.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
api_key: Your Stable Sea API key (Bearer token)
|
|
44
|
+
base_url: API base URL (default: sandbox)
|
|
45
|
+
timeout: Request timeout in seconds
|
|
46
|
+
"""
|
|
47
|
+
self._http = HTTPClient(
|
|
48
|
+
api_key=api_key,
|
|
49
|
+
base_url=base_url,
|
|
50
|
+
timeout=timeout,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def organizations(self) -> OrganizationsResource:
|
|
55
|
+
"""Organizations: list, create, get."""
|
|
56
|
+
return OrganizationsResource(self._http)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def quotes(self) -> QuotesResource:
|
|
60
|
+
"""Quotes: create, get (organization-scoped)."""
|
|
61
|
+
return QuotesResource(self._http)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def orders(self) -> OrdersResource:
|
|
65
|
+
"""Orders: create, list, get (organization-scoped)."""
|
|
66
|
+
return OrdersResource(self._http)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def api_keys(self) -> ApiKeysResource:
|
|
70
|
+
"""API Keys: list, create, rotate, revoke."""
|
|
71
|
+
return ApiKeysResource(self._http)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def liquidity_providers(self) -> LiquidityProvidersResource:
|
|
75
|
+
"""Liquidity providers: list, get exchange rate."""
|
|
76
|
+
return LiquidityProvidersResource(self._http)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def offerings(self) -> OfferingsResource:
|
|
80
|
+
"""Offerings: create, get (organization-scoped)."""
|
|
81
|
+
return OfferingsResource(self._http)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def external_payment_instruments(self) -> ExternalPaymentInstrumentsResource:
|
|
85
|
+
"""External payment instruments (organization-scoped)."""
|
|
86
|
+
return ExternalPaymentInstrumentsResource(self._http)
|
|
87
|
+
|
|
88
|
+
def close(self) -> None:
|
|
89
|
+
"""Close the HTTP client and release resources."""
|
|
90
|
+
self._http.close()
|
|
91
|
+
|
|
92
|
+
def __enter__(self) -> "StableseaClient":
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
def __exit__(self, *args: object) -> None:
|
|
96
|
+
self.close()
|