ontiver 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.
- ontiver-0.1.0/PKG-INFO +238 -0
- ontiver-0.1.0/README.md +213 -0
- ontiver-0.1.0/pyproject.toml +41 -0
- ontiver-0.1.0/setup.cfg +4 -0
- ontiver-0.1.0/src/ontiver/__init__.py +33 -0
- ontiver-0.1.0/src/ontiver/client.py +75 -0
- ontiver-0.1.0/src/ontiver/config.py +67 -0
- ontiver-0.1.0/src/ontiver/errors.py +78 -0
- ontiver-0.1.0/src/ontiver/http.py +161 -0
- ontiver-0.1.0/src/ontiver/py.typed +1 -0
- ontiver-0.1.0/src/ontiver/resources/__init__.py +1 -0
- ontiver-0.1.0/src/ontiver/resources/proofs.py +57 -0
- ontiver-0.1.0/src/ontiver/resources/screening.py +78 -0
- ontiver-0.1.0/src/ontiver/resources/verifications.py +22 -0
- ontiver-0.1.0/src/ontiver/resources/webhook_management.py +60 -0
- ontiver-0.1.0/src/ontiver/webhooks.py +22 -0
- ontiver-0.1.0/src/ontiver.egg-info/PKG-INFO +238 -0
- ontiver-0.1.0/src/ontiver.egg-info/SOURCES.txt +22 -0
- ontiver-0.1.0/src/ontiver.egg-info/dependency_links.txt +1 -0
- ontiver-0.1.0/src/ontiver.egg-info/requires.txt +8 -0
- ontiver-0.1.0/src/ontiver.egg-info/top_level.txt +1 -0
- ontiver-0.1.0/tests/test_client_config.py +26 -0
- ontiver-0.1.0/tests/test_http_and_resources.py +114 -0
- ontiver-0.1.0/tests/test_webhook_signatures.py +25 -0
ontiver-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ontiver
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for the Ontiver API
|
|
5
|
+
Author: Ontiver
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Project-URL: Homepage, https://ontiver.com
|
|
8
|
+
Project-URL: Documentation, https://docs.ontiver.com
|
|
9
|
+
Project-URL: Repository, https://github.com/joinverza/Ontiver
|
|
10
|
+
Keywords: ontiver,kyc,aml,identity,proofs,webhooks
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: httpx<1,>=0.27
|
|
20
|
+
Provides-Extra: test
|
|
21
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
22
|
+
Provides-Extra: build
|
|
23
|
+
Requires-Dist: build>=1.2; extra == "build"
|
|
24
|
+
Requires-Dist: twine>=5; extra == "build"
|
|
25
|
+
|
|
26
|
+
# Ontiver Python SDK
|
|
27
|
+
|
|
28
|
+
Official Python SDK for integrating Ontiver verification, screening, proofs, and webhooks into enterprise backends.
|
|
29
|
+
|
|
30
|
+
The dashboard remains the control plane for API keys, scopes, billing, team roles, webhooks, and audit logs. The SDK is the integration layer for Django, Flask, FastAPI, workers, and internal services.
|
|
31
|
+
|
|
32
|
+
## Install Locally
|
|
33
|
+
|
|
34
|
+
From this repository:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cd sdks/python
|
|
38
|
+
python -m pip install -e ".[test]"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
After publication:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python -m pip install ontiver
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Create an API key from the Ontiver enterprise dashboard and store it in your secret manager.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ONTIVER_API_KEY=prod_xxx
|
|
53
|
+
ONTIVER_BASE_URL=https://api.ontiver.com
|
|
54
|
+
ONTIVER_ENVIRONMENT=production
|
|
55
|
+
ONTIVER_WEBHOOK_SECRET=whsec_xxx
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use sandbox keys in non-production environments:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
ONTIVER_API_KEY=sb_xxx
|
|
62
|
+
ONTIVER_ENVIRONMENT=sandbox
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quickstart
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from ontiver import OntiverClient
|
|
69
|
+
|
|
70
|
+
client = OntiverClient.from_env()
|
|
71
|
+
|
|
72
|
+
startup_check = client.validate_api_key()
|
|
73
|
+
print(startup_check["valid"])
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Verification
|
|
77
|
+
|
|
78
|
+
SDK v1 routes flexible verification payloads to:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
POST /api/v1/banking/kyc/individual/verify
|
|
82
|
+
GET /api/v1/banking/kyc/individual/{verificationId}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
result = client.verifications.create(
|
|
87
|
+
{
|
|
88
|
+
"customerId": "cust_123",
|
|
89
|
+
"firstName": "Ada",
|
|
90
|
+
"lastName": "Okafor",
|
|
91
|
+
"country": "NG",
|
|
92
|
+
"idDocumentType": "passport",
|
|
93
|
+
"idDocumentNumber": "A12345678",
|
|
94
|
+
},
|
|
95
|
+
idempotency_key="verify-cust-123",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
status = client.verifications.get(result["verificationId"])
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Screening
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
screening = client.screening.sanctions_check(
|
|
105
|
+
first_name="Ada",
|
|
106
|
+
last_name="Okafor",
|
|
107
|
+
nationality="NG",
|
|
108
|
+
idempotency_key="sanctions-cust-123",
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Supported methods:
|
|
113
|
+
|
|
114
|
+
- `client.screening.pep_check(...)`
|
|
115
|
+
- `client.screening.sanctions_check(...)`
|
|
116
|
+
- `client.screening.adverse_media_check(first_name, last_name, ...)`
|
|
117
|
+
|
|
118
|
+
## Proofs
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
proof = client.proofs.get_for_verification("ver_123")
|
|
122
|
+
|
|
123
|
+
verified = client.proofs.verify(
|
|
124
|
+
proof_id=proof["proofId"],
|
|
125
|
+
proof=proof.get("proof"),
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Supported methods:
|
|
130
|
+
|
|
131
|
+
- `client.proofs.generate(...)`
|
|
132
|
+
- `client.proofs.get_for_verification(verification_id)`
|
|
133
|
+
- `client.proofs.verify(proof_id, proof=None)`
|
|
134
|
+
- `client.proofs.disclose(proof_id, fields=[...])`
|
|
135
|
+
|
|
136
|
+
## Webhooks
|
|
137
|
+
|
|
138
|
+
Register a webhook:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
webhook = client.webhooks.register(
|
|
142
|
+
"https://example.com/webhooks/ontiver",
|
|
143
|
+
["verification.completed", "proof.issued"],
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Store this once. It will not be returned by list endpoints.
|
|
147
|
+
secret = webhook["secret"]
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Verify webhook signatures using the raw request body:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from ontiver.webhooks import verify_webhook_signature
|
|
154
|
+
|
|
155
|
+
is_valid = verify_webhook_signature(raw_body, request.headers["X-Ontiver-Signature"], secret)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Header format:
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
X-Ontiver-Signature: sha256=<hex_digest>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Raw Request Escape Hatch
|
|
165
|
+
|
|
166
|
+
Use `raw_request()` for routes that do not have first-class wrappers yet:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
result = client.raw_request(
|
|
170
|
+
"POST",
|
|
171
|
+
"/aml/smile-checks",
|
|
172
|
+
json={"fullName": "Ada Okafor", "countries": ["NG"]},
|
|
173
|
+
idempotency_key="aml-cust-123",
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Errors
|
|
178
|
+
|
|
179
|
+
Catch specific exceptions:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from ontiver import AuthenticationError, PermissionDeniedError, RateLimitError, ValidationError
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
client.validate_api_key()
|
|
186
|
+
except AuthenticationError:
|
|
187
|
+
...
|
|
188
|
+
except PermissionDeniedError:
|
|
189
|
+
...
|
|
190
|
+
except RateLimitError:
|
|
191
|
+
...
|
|
192
|
+
except ValidationError:
|
|
193
|
+
...
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The SDK redacts API keys, bearer tokens, webhook secrets, and secret prefixes from exception messages.
|
|
197
|
+
|
|
198
|
+
## Security Notes
|
|
199
|
+
|
|
200
|
+
- Never commit API keys or webhook secrets.
|
|
201
|
+
- Store production secrets in a secret manager.
|
|
202
|
+
- Use least-privilege API key scopes.
|
|
203
|
+
- Use sandbox keys outside production.
|
|
204
|
+
- Verify webhook signatures over the raw body bytes before parsing JSON.
|
|
205
|
+
- Do not log raw identity documents, ID numbers, biometric payloads, or full provider payloads.
|
|
206
|
+
|
|
207
|
+
## Build And Publish
|
|
208
|
+
|
|
209
|
+
Build locally:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
cd sdks/python
|
|
213
|
+
python -m pip install -e ".[test]"
|
|
214
|
+
pytest
|
|
215
|
+
python -m pip install ".[build]"
|
|
216
|
+
python -m build
|
|
217
|
+
python -m twine check dist/*
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Publish first to TestPyPI:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
python -m twine upload --repository testpypi dist/*
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Install from TestPyPI in a clean environment:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple ontiver
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Publish to PyPI after TestPyPI install passes:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
python -m twine upload dist/*
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Target package name: `ontiver`.
|
ontiver-0.1.0/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Ontiver Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for integrating Ontiver verification, screening, proofs, and webhooks into enterprise backends.
|
|
4
|
+
|
|
5
|
+
The dashboard remains the control plane for API keys, scopes, billing, team roles, webhooks, and audit logs. The SDK is the integration layer for Django, Flask, FastAPI, workers, and internal services.
|
|
6
|
+
|
|
7
|
+
## Install Locally
|
|
8
|
+
|
|
9
|
+
From this repository:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd sdks/python
|
|
13
|
+
python -m pip install -e ".[test]"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
After publication:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
python -m pip install ontiver
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
Create an API key from the Ontiver enterprise dashboard and store it in your secret manager.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ONTIVER_API_KEY=prod_xxx
|
|
28
|
+
ONTIVER_BASE_URL=https://api.ontiver.com
|
|
29
|
+
ONTIVER_ENVIRONMENT=production
|
|
30
|
+
ONTIVER_WEBHOOK_SECRET=whsec_xxx
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Use sandbox keys in non-production environments:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ONTIVER_API_KEY=sb_xxx
|
|
37
|
+
ONTIVER_ENVIRONMENT=sandbox
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from ontiver import OntiverClient
|
|
44
|
+
|
|
45
|
+
client = OntiverClient.from_env()
|
|
46
|
+
|
|
47
|
+
startup_check = client.validate_api_key()
|
|
48
|
+
print(startup_check["valid"])
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Verification
|
|
52
|
+
|
|
53
|
+
SDK v1 routes flexible verification payloads to:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
POST /api/v1/banking/kyc/individual/verify
|
|
57
|
+
GET /api/v1/banking/kyc/individual/{verificationId}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
result = client.verifications.create(
|
|
62
|
+
{
|
|
63
|
+
"customerId": "cust_123",
|
|
64
|
+
"firstName": "Ada",
|
|
65
|
+
"lastName": "Okafor",
|
|
66
|
+
"country": "NG",
|
|
67
|
+
"idDocumentType": "passport",
|
|
68
|
+
"idDocumentNumber": "A12345678",
|
|
69
|
+
},
|
|
70
|
+
idempotency_key="verify-cust-123",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
status = client.verifications.get(result["verificationId"])
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Screening
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
screening = client.screening.sanctions_check(
|
|
80
|
+
first_name="Ada",
|
|
81
|
+
last_name="Okafor",
|
|
82
|
+
nationality="NG",
|
|
83
|
+
idempotency_key="sanctions-cust-123",
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Supported methods:
|
|
88
|
+
|
|
89
|
+
- `client.screening.pep_check(...)`
|
|
90
|
+
- `client.screening.sanctions_check(...)`
|
|
91
|
+
- `client.screening.adverse_media_check(first_name, last_name, ...)`
|
|
92
|
+
|
|
93
|
+
## Proofs
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
proof = client.proofs.get_for_verification("ver_123")
|
|
97
|
+
|
|
98
|
+
verified = client.proofs.verify(
|
|
99
|
+
proof_id=proof["proofId"],
|
|
100
|
+
proof=proof.get("proof"),
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Supported methods:
|
|
105
|
+
|
|
106
|
+
- `client.proofs.generate(...)`
|
|
107
|
+
- `client.proofs.get_for_verification(verification_id)`
|
|
108
|
+
- `client.proofs.verify(proof_id, proof=None)`
|
|
109
|
+
- `client.proofs.disclose(proof_id, fields=[...])`
|
|
110
|
+
|
|
111
|
+
## Webhooks
|
|
112
|
+
|
|
113
|
+
Register a webhook:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
webhook = client.webhooks.register(
|
|
117
|
+
"https://example.com/webhooks/ontiver",
|
|
118
|
+
["verification.completed", "proof.issued"],
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Store this once. It will not be returned by list endpoints.
|
|
122
|
+
secret = webhook["secret"]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Verify webhook signatures using the raw request body:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from ontiver.webhooks import verify_webhook_signature
|
|
129
|
+
|
|
130
|
+
is_valid = verify_webhook_signature(raw_body, request.headers["X-Ontiver-Signature"], secret)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Header format:
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
X-Ontiver-Signature: sha256=<hex_digest>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Raw Request Escape Hatch
|
|
140
|
+
|
|
141
|
+
Use `raw_request()` for routes that do not have first-class wrappers yet:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
result = client.raw_request(
|
|
145
|
+
"POST",
|
|
146
|
+
"/aml/smile-checks",
|
|
147
|
+
json={"fullName": "Ada Okafor", "countries": ["NG"]},
|
|
148
|
+
idempotency_key="aml-cust-123",
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Errors
|
|
153
|
+
|
|
154
|
+
Catch specific exceptions:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from ontiver import AuthenticationError, PermissionDeniedError, RateLimitError, ValidationError
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
client.validate_api_key()
|
|
161
|
+
except AuthenticationError:
|
|
162
|
+
...
|
|
163
|
+
except PermissionDeniedError:
|
|
164
|
+
...
|
|
165
|
+
except RateLimitError:
|
|
166
|
+
...
|
|
167
|
+
except ValidationError:
|
|
168
|
+
...
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The SDK redacts API keys, bearer tokens, webhook secrets, and secret prefixes from exception messages.
|
|
172
|
+
|
|
173
|
+
## Security Notes
|
|
174
|
+
|
|
175
|
+
- Never commit API keys or webhook secrets.
|
|
176
|
+
- Store production secrets in a secret manager.
|
|
177
|
+
- Use least-privilege API key scopes.
|
|
178
|
+
- Use sandbox keys outside production.
|
|
179
|
+
- Verify webhook signatures over the raw body bytes before parsing JSON.
|
|
180
|
+
- Do not log raw identity documents, ID numbers, biometric payloads, or full provider payloads.
|
|
181
|
+
|
|
182
|
+
## Build And Publish
|
|
183
|
+
|
|
184
|
+
Build locally:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
cd sdks/python
|
|
188
|
+
python -m pip install -e ".[test]"
|
|
189
|
+
pytest
|
|
190
|
+
python -m pip install ".[build]"
|
|
191
|
+
python -m build
|
|
192
|
+
python -m twine check dist/*
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Publish first to TestPyPI:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
python -m twine upload --repository testpypi dist/*
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Install from TestPyPI in a clean environment:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple ontiver
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Publish to PyPI after TestPyPI install passes:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
python -m twine upload dist/*
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Target package name: `ontiver`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ontiver"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for the Ontiver API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "LicenseRef-Proprietary"
|
|
12
|
+
authors = [{ name = "Ontiver" }]
|
|
13
|
+
keywords = ["ontiver", "kyc", "aml", "identity", "proofs", "webhooks"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
dependencies = ["httpx>=0.27,<1"]
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
test = ["pytest>=8"]
|
|
26
|
+
build = ["build>=1.2", "twine>=5"]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://ontiver.com"
|
|
30
|
+
Documentation = "https://docs.ontiver.com"
|
|
31
|
+
Repository = "https://github.com/joinverza/Ontiver"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["src"]
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.package-data]
|
|
37
|
+
ontiver = ["py.typed"]
|
|
38
|
+
|
|
39
|
+
[tool.pytest.ini_options]
|
|
40
|
+
testpaths = ["tests"]
|
|
41
|
+
pythonpath = ["src"]
|
ontiver-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from .client import OntiverClient
|
|
2
|
+
from .errors import (
|
|
3
|
+
AuthenticationError,
|
|
4
|
+
NetworkError,
|
|
5
|
+
NotFoundError,
|
|
6
|
+
OntiverAPIError,
|
|
7
|
+
OntiverConfigError,
|
|
8
|
+
OntiverError,
|
|
9
|
+
PermissionDeniedError,
|
|
10
|
+
RateLimitError,
|
|
11
|
+
ServerError,
|
|
12
|
+
TimeoutError,
|
|
13
|
+
ValidationError,
|
|
14
|
+
WebhookSignatureError,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"OntiverClient",
|
|
21
|
+
"OntiverError",
|
|
22
|
+
"OntiverConfigError",
|
|
23
|
+
"OntiverAPIError",
|
|
24
|
+
"AuthenticationError",
|
|
25
|
+
"PermissionDeniedError",
|
|
26
|
+
"ValidationError",
|
|
27
|
+
"RateLimitError",
|
|
28
|
+
"NotFoundError",
|
|
29
|
+
"ServerError",
|
|
30
|
+
"TimeoutError",
|
|
31
|
+
"NetworkError",
|
|
32
|
+
"WebhookSignatureError",
|
|
33
|
+
]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from .config import OntiverConfig
|
|
9
|
+
from .http import OntiverHTTPClient
|
|
10
|
+
from .resources.proofs import ProofsResource
|
|
11
|
+
from .resources.screening import ScreeningResource
|
|
12
|
+
from .resources.verifications import VerificationsResource
|
|
13
|
+
from .resources.webhook_management import WebhookManagementResource
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OntiverClient:
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
api_key: str | None = None,
|
|
20
|
+
base_url: str | None = None,
|
|
21
|
+
environment: str | None = None,
|
|
22
|
+
timeout: float = 30,
|
|
23
|
+
max_retries: int = 2,
|
|
24
|
+
webhook_secret: str | None = None,
|
|
25
|
+
http_client: httpx.Client | None = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
self.config = OntiverConfig.from_env(
|
|
28
|
+
api_key=api_key,
|
|
29
|
+
base_url=base_url,
|
|
30
|
+
environment=environment,
|
|
31
|
+
webhook_secret=webhook_secret,
|
|
32
|
+
timeout=timeout,
|
|
33
|
+
max_retries=max_retries,
|
|
34
|
+
)
|
|
35
|
+
self._http = OntiverHTTPClient(self.config, client=http_client)
|
|
36
|
+
self.webhooks = WebhookManagementResource(self._http)
|
|
37
|
+
self.verifications = VerificationsResource(self._http)
|
|
38
|
+
self.screening = ScreeningResource(self._http)
|
|
39
|
+
self.proofs = ProofsResource(self._http)
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def from_env(cls) -> "OntiverClient":
|
|
43
|
+
return cls()
|
|
44
|
+
|
|
45
|
+
def close(self) -> None:
|
|
46
|
+
self._http.close()
|
|
47
|
+
|
|
48
|
+
def __enter__(self) -> "OntiverClient":
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
def __exit__(self, *_: object) -> None:
|
|
52
|
+
self.close()
|
|
53
|
+
|
|
54
|
+
def validate_api_key(self) -> dict[str, Any]:
|
|
55
|
+
return self._http.request("GET", "/api-keys/validate/current")
|
|
56
|
+
|
|
57
|
+
def raw_request(
|
|
58
|
+
self,
|
|
59
|
+
method: str,
|
|
60
|
+
path: str,
|
|
61
|
+
*,
|
|
62
|
+
json: Any | None = None,
|
|
63
|
+
params: Mapping[str, Any] | None = None,
|
|
64
|
+
headers: Mapping[str, str] | None = None,
|
|
65
|
+
idempotency_key: str | None = None,
|
|
66
|
+
) -> Any:
|
|
67
|
+
return self._http.request(
|
|
68
|
+
method,
|
|
69
|
+
path,
|
|
70
|
+
json=json,
|
|
71
|
+
params=params,
|
|
72
|
+
headers=headers,
|
|
73
|
+
idempotency_key=idempotency_key,
|
|
74
|
+
unwrap=False,
|
|
75
|
+
)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from urllib.parse import urlparse
|
|
6
|
+
|
|
7
|
+
from .errors import OntiverConfigError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
DEFAULT_BASE_URL = "https://api.ontiver.com"
|
|
11
|
+
VALID_ENVIRONMENTS = {"production", "sandbox"}
|
|
12
|
+
LOCAL_HOSTS = {"localhost", "127.0.0.1", "0.0.0.0", "::1"}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class OntiverConfig:
|
|
17
|
+
api_key: str | None = None
|
|
18
|
+
base_url: str = DEFAULT_BASE_URL
|
|
19
|
+
environment: str = "production"
|
|
20
|
+
webhook_secret: str | None = None
|
|
21
|
+
timeout: float = 30
|
|
22
|
+
max_retries: int = 2
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_env(
|
|
26
|
+
cls,
|
|
27
|
+
*,
|
|
28
|
+
api_key: str | None = None,
|
|
29
|
+
base_url: str | None = None,
|
|
30
|
+
environment: str | None = None,
|
|
31
|
+
webhook_secret: str | None = None,
|
|
32
|
+
timeout: float = 30,
|
|
33
|
+
max_retries: int = 2,
|
|
34
|
+
) -> "OntiverConfig":
|
|
35
|
+
return cls(
|
|
36
|
+
api_key=api_key if api_key is not None else os.getenv("ONTIVER_API_KEY"),
|
|
37
|
+
base_url=base_url or os.getenv("ONTIVER_BASE_URL") or DEFAULT_BASE_URL,
|
|
38
|
+
environment=environment or os.getenv("ONTIVER_ENVIRONMENT") or "production",
|
|
39
|
+
webhook_secret=webhook_secret if webhook_secret is not None else os.getenv("ONTIVER_WEBHOOK_SECRET"),
|
|
40
|
+
timeout=timeout,
|
|
41
|
+
max_retries=max_retries,
|
|
42
|
+
).validated()
|
|
43
|
+
|
|
44
|
+
def validated(self) -> "OntiverConfig":
|
|
45
|
+
environment = str(self.environment or "").strip().lower()
|
|
46
|
+
if environment not in VALID_ENVIRONMENTS:
|
|
47
|
+
raise OntiverConfigError("environment must be 'production' or 'sandbox'")
|
|
48
|
+
base_url = normalize_base_url(self.base_url)
|
|
49
|
+
return OntiverConfig(
|
|
50
|
+
api_key=str(self.api_key).strip() if self.api_key else None,
|
|
51
|
+
base_url=base_url,
|
|
52
|
+
environment=environment,
|
|
53
|
+
webhook_secret=str(self.webhook_secret).strip() if self.webhook_secret else None,
|
|
54
|
+
timeout=float(self.timeout),
|
|
55
|
+
max_retries=max(0, int(self.max_retries)),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def normalize_base_url(value: str | None) -> str:
|
|
60
|
+
raw = str(value or DEFAULT_BASE_URL).strip().rstrip("/")
|
|
61
|
+
parsed = urlparse(raw)
|
|
62
|
+
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
|
63
|
+
raise OntiverConfigError("base_url must be an absolute HTTP(S) URL")
|
|
64
|
+
hostname = (parsed.hostname or "").lower()
|
|
65
|
+
if parsed.scheme != "https" and hostname not in LOCAL_HOSTS:
|
|
66
|
+
raise OntiverConfigError("HTTPS is required for non-local Ontiver API base URLs")
|
|
67
|
+
return raw
|