blindpay 0.1.0__py3-none-any.whl

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 (37) hide show
  1. blindpay/__init__.py +54 -0
  2. blindpay/_internal/__init__.py +4 -0
  3. blindpay/_internal/api_client.py +54 -0
  4. blindpay/_internal/exceptions.py +6 -0
  5. blindpay/client.py +536 -0
  6. blindpay/resources/__init__.py +29 -0
  7. blindpay/resources/api_keys/__init__.py +21 -0
  8. blindpay/resources/api_keys/api_keys.py +82 -0
  9. blindpay/resources/available/__init__.py +25 -0
  10. blindpay/resources/available/available.py +113 -0
  11. blindpay/resources/bank_accounts/__init__.py +69 -0
  12. blindpay/resources/bank_accounts/bank_accounts.py +473 -0
  13. blindpay/resources/instances/__init__.py +23 -0
  14. blindpay/resources/instances/instances.py +82 -0
  15. blindpay/resources/partner_fees/__init__.py +21 -0
  16. blindpay/resources/partner_fees/partner_fees.py +100 -0
  17. blindpay/resources/payins/__init__.py +69 -0
  18. blindpay/resources/payins/payins.py +246 -0
  19. blindpay/resources/payins/quotes.py +100 -0
  20. blindpay/resources/payouts/__init__.py +35 -0
  21. blindpay/resources/payouts/payouts.py +241 -0
  22. blindpay/resources/quotes/__init__.py +25 -0
  23. blindpay/resources/quotes/quotes.py +114 -0
  24. blindpay/resources/receivers/__init__.py +66 -0
  25. blindpay/resources/receivers/receivers.py +452 -0
  26. blindpay/resources/virtual_accounts/__init__.py +31 -0
  27. blindpay/resources/virtual_accounts/virtual_accounts.py +106 -0
  28. blindpay/resources/wallets/__init__.py +63 -0
  29. blindpay/resources/wallets/blockchain.py +167 -0
  30. blindpay/resources/wallets/offramp.py +117 -0
  31. blindpay/resources/webhooks/__init__.py +27 -0
  32. blindpay/resources/webhooks/webhooks.py +103 -0
  33. blindpay/types.py +361 -0
  34. blindpay-0.1.0.dist-info/METADATA +94 -0
  35. blindpay-0.1.0.dist-info/RECORD +37 -0
  36. blindpay-0.1.0.dist-info/WHEEL +4 -0
  37. blindpay-0.1.0.dist-info/licenses/LICENSE +21 -0
blindpay/__init__.py ADDED
@@ -0,0 +1,54 @@
1
+ __version__ = "0.1.0"
2
+
3
+ from ._internal.exceptions import BlindPayError
4
+ from .client import BlindPay, BlindPaySync
5
+ from .types import (
6
+ AccountClass,
7
+ BankAccountType,
8
+ BlindpayApiResponse,
9
+ BlindpayErrorResponse,
10
+ BlindpaySuccessResponse,
11
+ Country,
12
+ Currency,
13
+ CurrencyType,
14
+ ErrorResponse,
15
+ Network,
16
+ PaginationMetadata,
17
+ PaginationParams,
18
+ Rail,
19
+ StablecoinToken,
20
+ TrackingComplete,
21
+ TrackingLiquidity,
22
+ TrackingPartnerFee,
23
+ TrackingPayment,
24
+ TrackingTransaction,
25
+ TransactionDocumentType,
26
+ TransactionStatus,
27
+ )
28
+
29
+ __all__ = [
30
+ "BlindPay",
31
+ "BlindPaySync",
32
+ "BlindPayError",
33
+ "AccountClass",
34
+ "BankAccountType",
35
+ "Country",
36
+ "Currency",
37
+ "CurrencyType",
38
+ "Network",
39
+ "Rail",
40
+ "StablecoinToken",
41
+ "TransactionDocumentType",
42
+ "TransactionStatus",
43
+ "BlindpayApiResponse",
44
+ "BlindpayErrorResponse",
45
+ "BlindpaySuccessResponse",
46
+ "ErrorResponse",
47
+ "PaginationMetadata",
48
+ "PaginationParams",
49
+ "TrackingComplete",
50
+ "TrackingLiquidity",
51
+ "TrackingPartnerFee",
52
+ "TrackingPayment",
53
+ "TrackingTransaction",
54
+ ]
@@ -0,0 +1,4 @@
1
+ from .api_client import InternalApiClient
2
+ from .exceptions import BlindPayError
3
+
4
+ __all__ = ["InternalApiClient", "BlindPayError"]
@@ -0,0 +1,54 @@
1
+ from typing import Any, Mapping, Optional, Protocol
2
+
3
+ from ..types import BlindpayApiResponse
4
+
5
+
6
+ class InternalApiClient(Protocol):
7
+ """
8
+ Internal API client interface that resources use to make HTTP requests.
9
+ This interface is not exposed to SDK users.
10
+ """
11
+
12
+ async def get(self, path: str) -> BlindpayApiResponse[Any]:
13
+ """Make a GET request"""
14
+ ...
15
+
16
+ async def post(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[Any]:
17
+ """Make a POST request"""
18
+ ...
19
+
20
+ async def put(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[Any]:
21
+ """Make a PUT request"""
22
+ ...
23
+
24
+ async def patch(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[Any]:
25
+ """Make a PATCH request"""
26
+ ...
27
+
28
+ async def delete(self, path: str, body: Optional[Mapping[str, Any]] = None) -> BlindpayApiResponse[Any]:
29
+ """Make a DELETE request"""
30
+ ...
31
+
32
+
33
+ class InternalApiClientSync(Protocol):
34
+ """Synchronous version of InternalApiClient"""
35
+
36
+ def get(self, path: str) -> BlindpayApiResponse[Any]:
37
+ """Make a GET request"""
38
+ ...
39
+
40
+ def post(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[Any]:
41
+ """Make a POST request"""
42
+ ...
43
+
44
+ def put(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[Any]:
45
+ """Make a PUT request"""
46
+ ...
47
+
48
+ def patch(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[Any]:
49
+ """Make a PATCH request"""
50
+ ...
51
+
52
+ def delete(self, path: str, body: Optional[Mapping[str, Any]] = None) -> BlindpayApiResponse[Any]:
53
+ """Make a DELETE request"""
54
+ ...
@@ -0,0 +1,6 @@
1
+ class BlindPayError(Exception):
2
+ """BlindPay SDK error"""
3
+
4
+ def __init__(self, message: str):
5
+ super().__init__(message)
6
+ self.name = "BlindpayError"
blindpay/client.py ADDED
@@ -0,0 +1,536 @@
1
+ import base64
2
+ import hashlib
3
+ import hmac
4
+ from functools import cached_property
5
+ from typing import TYPE_CHECKING, Any, Dict, Literal, Mapping, Optional, TypeVar
6
+
7
+ import httpx
8
+
9
+ from ._internal.exceptions import BlindPayError
10
+ from .types import BlindpayApiResponse
11
+
12
+ if TYPE_CHECKING:
13
+ from blindpay.resources.api_keys.api_keys import ApiKeysResource, ApiKeysResourceSync
14
+ from blindpay.resources.available.available import AvailableResource, AvailableResourceSync
15
+ from blindpay.resources.bank_accounts.bank_accounts import BankAccountsResource, BankAccountsResourceSync
16
+ from blindpay.resources.instances.instances import InstancesResource, InstancesResourceSync
17
+ from blindpay.resources.partner_fees.partner_fees import PartnerFeesResource, PartnerFeesResourceSync
18
+ from blindpay.resources.payins.payins import PayinsResource, PayinsResourceSync
19
+ from blindpay.resources.payins.quotes import PayinQuotesResource, PayinQuotesResourceSync
20
+ from blindpay.resources.payouts.payouts import PayoutsResource, PayoutsResourceSync
21
+ from blindpay.resources.quotes.quotes import QuotesResource, QuotesResourceSync
22
+ from blindpay.resources.receivers.receivers import ReceiversResource, ReceiversResourceSync
23
+ from blindpay.resources.virtual_accounts.virtual_accounts import (
24
+ VirtualAccountsResource,
25
+ VirtualAccountsResourceSync,
26
+ )
27
+ from blindpay.resources.wallets.blockchain import BlockchainWalletsResource, BlockchainWalletsResourceSync
28
+ from blindpay.resources.wallets.offramp import OfframpWalletsResource, OfframpWalletsResourceSync
29
+ from blindpay.resources.webhooks.webhooks import WebhookEndpointsResource, WebhookEndpointsResourceSync
30
+
31
+ __version__ = "1.0.0"
32
+
33
+ T = TypeVar("T")
34
+
35
+
36
+ class ApiClientImpl:
37
+ def __init__(self, base_url: str, headers: Dict[str, str]):
38
+ self.base_url = base_url
39
+ self.headers = headers
40
+
41
+ async def get(self, path: str) -> BlindpayApiResponse[T]:
42
+ return await self._request("GET", path)
43
+
44
+ async def post(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[T]:
45
+ return await self._request("POST", path, body)
46
+
47
+ async def put(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[T]:
48
+ return await self._request("PUT", path, body)
49
+
50
+ async def patch(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[T]:
51
+ return await self._request("PATCH", path, body)
52
+
53
+ async def delete(self, path: str, body: Optional[Mapping[str, Any]] = None) -> BlindpayApiResponse[T]:
54
+ return await self._request("DELETE", path, body)
55
+
56
+ async def _request(
57
+ self,
58
+ method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"],
59
+ path: str,
60
+ body: Optional[Mapping[str, Any]] = None,
61
+ ) -> BlindpayApiResponse[T]:
62
+ url = f"{self.base_url}{path}"
63
+
64
+ try:
65
+ async with httpx.AsyncClient() as client:
66
+ if body is not None:
67
+ response = await client.request(method=method, url=url, headers=self.headers, json=body)
68
+ else:
69
+ response = await client.request(method=method, url=url, headers=self.headers)
70
+
71
+ if response.status_code >= 400:
72
+ try:
73
+ error_data = response.json()
74
+ error_message = error_data.get("message", "Unknown error")
75
+ except Exception:
76
+ error_message = "Unknown error"
77
+
78
+ return {"data": None, "error": {"message": error_message}}
79
+
80
+ data = response.json()
81
+ return {"data": data, "error": None}
82
+
83
+ except Exception as e:
84
+ error_message = str(e) if str(e) else "Unknown error"
85
+ return {"data": None, "error": {"message": error_message}}
86
+
87
+
88
+ class ApiClientImplSync:
89
+ def __init__(self, base_url: str, headers: Dict[str, str]):
90
+ self.base_url = base_url
91
+ self.headers = headers
92
+ self.client = httpx.Client(base_url=base_url, headers=headers)
93
+
94
+ def get(self, path: str) -> BlindpayApiResponse[T]:
95
+ return self._request("GET", path)
96
+
97
+ def post(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[T]:
98
+ return self._request("POST", path, body)
99
+
100
+ def put(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[T]:
101
+ return self._request("PUT", path, body)
102
+
103
+ def patch(self, path: str, body: Mapping[str, Any]) -> BlindpayApiResponse[T]:
104
+ return self._request("PATCH", path, body)
105
+
106
+ def delete(self, path: str, body: Optional[Mapping[str, Any]] = None) -> BlindpayApiResponse[T]:
107
+ return self._request("DELETE", path, body)
108
+
109
+ def _request(
110
+ self,
111
+ method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"],
112
+ path: str,
113
+ body: Optional[Mapping[str, Any]] = None,
114
+ ) -> BlindpayApiResponse[T]:
115
+ try:
116
+ if body is not None:
117
+ response = self.client.request(method=method, url=path, json=body)
118
+ else:
119
+ response = self.client.request(method=method, url=path)
120
+
121
+ if response.status_code >= 400:
122
+ try:
123
+ error_data = response.json()
124
+ error_message = error_data.get("message", "Unknown error")
125
+ except Exception:
126
+ error_message = "Unknown error"
127
+
128
+ return {"data": None, "error": {"message": error_message}}
129
+
130
+ data = response.json()
131
+ return {"data": data, "error": None}
132
+
133
+ except Exception as e:
134
+ error_message = str(e) if str(e) else "Unknown error"
135
+ return {"data": None, "error": {"message": error_message}}
136
+
137
+ def close(self) -> None:
138
+ self.client.close()
139
+
140
+
141
+ class _InstancesNamespace:
142
+ def __init__(self, instance_id: str, api_client: ApiClientImpl) -> None:
143
+ self._instance_id = instance_id
144
+ self._api = api_client
145
+
146
+ @cached_property
147
+ def _base(self) -> "InstancesResource":
148
+ from blindpay.resources.instances.instances import create_instances_resource
149
+
150
+ return create_instances_resource(self._instance_id, self._api)
151
+
152
+ @cached_property
153
+ def api_keys(self) -> "ApiKeysResource":
154
+ from blindpay.resources.api_keys.api_keys import create_api_keys_resource
155
+
156
+ return create_api_keys_resource(self._instance_id, self._api)
157
+
158
+ @cached_property
159
+ def webhook_endpoints(self) -> "WebhookEndpointsResource":
160
+ from blindpay.resources.webhooks.webhooks import create_webhook_endpoints_resource
161
+
162
+ return create_webhook_endpoints_resource(self._instance_id, self._api)
163
+
164
+ def __getattr__(self, name: str) -> Any:
165
+ return getattr(self._base, name)
166
+
167
+
168
+ class _PayinsNamespace:
169
+ def __init__(self, instance_id: str, api_client: ApiClientImpl) -> None:
170
+ self._instance_id = instance_id
171
+ self._api = api_client
172
+
173
+ @cached_property
174
+ def _base(self) -> "PayinsResource":
175
+ from blindpay.resources.payins.payins import create_payins_resource
176
+
177
+ return create_payins_resource(self._instance_id, self._api)
178
+
179
+ @cached_property
180
+ def quotes(self) -> "PayinQuotesResource":
181
+ from blindpay.resources.payins.quotes import create_payin_quotes_resource
182
+
183
+ return create_payin_quotes_resource(self._instance_id, self._api)
184
+
185
+ def __getattr__(self, name: str) -> Any:
186
+ return getattr(self._base, name)
187
+
188
+
189
+ class _ReceiversNamespace:
190
+ def __init__(self, instance_id: str, api_client: ApiClientImpl) -> None:
191
+ self._instance_id = instance_id
192
+ self._api = api_client
193
+
194
+ @cached_property
195
+ def _base(self) -> "ReceiversResource":
196
+ from blindpay.resources.receivers.receivers import create_receivers_resource
197
+
198
+ return create_receivers_resource(self._instance_id, self._api)
199
+
200
+ @cached_property
201
+ def bank_accounts(self) -> "BankAccountsResource":
202
+ from blindpay.resources.bank_accounts.bank_accounts import create_bank_accounts_resource
203
+
204
+ return create_bank_accounts_resource(self._instance_id, self._api)
205
+
206
+ def __getattr__(self, name: str) -> Any:
207
+ return getattr(self._base, name)
208
+
209
+
210
+ class _WalletsNamespace:
211
+ def __init__(self, instance_id: str, api_client: ApiClientImpl) -> None:
212
+ self._instance_id = instance_id
213
+ self._api = api_client
214
+
215
+ @cached_property
216
+ def blockchain(self) -> "BlockchainWalletsResource":
217
+ from blindpay.resources.wallets.blockchain import create_blockchain_wallets_resource
218
+
219
+ return create_blockchain_wallets_resource(self._instance_id, self._api)
220
+
221
+ @cached_property
222
+ def offramp(self) -> "OfframpWalletsResource":
223
+ from blindpay.resources.wallets.offramp import create_offramp_wallets_resource
224
+
225
+ return create_offramp_wallets_resource(self._instance_id, self._api)
226
+
227
+
228
+ class BlindPay:
229
+ _api_key: str
230
+ _instance_id: str
231
+ _base_url: str
232
+
233
+ def __init__(self, *, api_key: str, instance_id: str):
234
+ if not api_key:
235
+ raise BlindPayError("Api key not provided, get your api key on blindpay dashboard")
236
+
237
+ if not instance_id:
238
+ raise BlindPayError("Instance id not provided, get your instance id on blindpay dashboard")
239
+
240
+ self._api_key = api_key
241
+ self._instance_id = instance_id
242
+ self._base_url = "https://api.blindpay.com/v1"
243
+
244
+ self._headers = {
245
+ "Content-Type": "application/json",
246
+ "Accept": "application/json",
247
+ "User-Agent": f"blindpay-python/{__version__}",
248
+ "Authorization": f"Bearer {self._api_key}",
249
+ }
250
+
251
+ self._api = ApiClientImpl(self._base_url, self._headers)
252
+
253
+ @cached_property
254
+ def available(self) -> "AvailableResource":
255
+ from blindpay.resources.available import create_available_resource
256
+
257
+ return create_available_resource(self._api)
258
+
259
+ @cached_property
260
+ def instances(self) -> _InstancesNamespace:
261
+ return _InstancesNamespace(self._instance_id, self._api)
262
+
263
+ @cached_property
264
+ def partner_fees(self) -> "PartnerFeesResource":
265
+ from blindpay.resources.partner_fees import create_partner_fees_resource
266
+
267
+ return create_partner_fees_resource(self._instance_id, self._api)
268
+
269
+ @cached_property
270
+ def payins(self) -> _PayinsNamespace:
271
+ return _PayinsNamespace(self._instance_id, self._api)
272
+
273
+ @cached_property
274
+ def quotes(self) -> "QuotesResource":
275
+ from blindpay.resources.quotes import create_quotes_resource
276
+
277
+ return create_quotes_resource(self._instance_id, self._api)
278
+
279
+ @cached_property
280
+ def payouts(self) -> "PayoutsResource":
281
+ from blindpay.resources.payouts import create_payouts_resource
282
+
283
+ return create_payouts_resource(self._instance_id, self._api)
284
+
285
+ @cached_property
286
+ def receivers(self) -> _ReceiversNamespace:
287
+ return _ReceiversNamespace(self._instance_id, self._api)
288
+
289
+ @cached_property
290
+ def virtual_accounts(self) -> "VirtualAccountsResource":
291
+ from blindpay.resources.virtual_accounts import create_virtual_accounts_resource
292
+
293
+ return create_virtual_accounts_resource(self._instance_id, self._api)
294
+
295
+ @cached_property
296
+ def wallets(self) -> _WalletsNamespace:
297
+ return _WalletsNamespace(self._instance_id, self._api)
298
+
299
+ def verify_webhook_signature(
300
+ self, *, secret: str, id: str, timestamp: str, payload: str, svix_signature: str
301
+ ) -> bool:
302
+ """
303
+ Verifies the BlindPay webhook signature
304
+
305
+ Args:
306
+ secret: The webhook secret from BlindPay dashboard
307
+ id: The value of the `svix-id` header
308
+ timestamp: The value of the `svix-timestamp` header
309
+ payload: The raw request body
310
+ svix_signature: The value of the `svix-signature` header
311
+
312
+ Returns:
313
+ True if the signature is valid, False otherwise
314
+ """
315
+ signed_content = f"{id}.{timestamp}.{payload}"
316
+
317
+ secret_parts = secret.split("_")
318
+ if len(secret_parts) < 2:
319
+ return False
320
+
321
+ try:
322
+ secret_bytes = base64.b64decode(secret_parts[1])
323
+ except Exception:
324
+ return False
325
+
326
+ expected_signature = base64.b64encode(
327
+ hmac.new(secret_bytes, signed_content.encode(), hashlib.sha256).digest()
328
+ ).decode()
329
+
330
+ return len(svix_signature) == len(expected_signature) and hmac.compare_digest(
331
+ svix_signature, expected_signature
332
+ )
333
+
334
+
335
+ class _InstancesNamespaceSync:
336
+ def __init__(self, instance_id: str, api_client: ApiClientImplSync) -> None:
337
+ self._instance_id = instance_id
338
+ self._api = api_client
339
+
340
+ @cached_property
341
+ def _base(self) -> "InstancesResourceSync":
342
+ from blindpay.resources.instances.instances import create_instances_resource_sync
343
+
344
+ return create_instances_resource_sync(self._instance_id, self._api)
345
+
346
+ @cached_property
347
+ def api_keys(self) -> "ApiKeysResourceSync":
348
+ from blindpay.resources.api_keys.api_keys import create_api_keys_resource_sync
349
+
350
+ return create_api_keys_resource_sync(self._instance_id, self._api)
351
+
352
+ @cached_property
353
+ def webhook_endpoints(self) -> "WebhookEndpointsResourceSync":
354
+ from blindpay.resources.webhooks.webhooks import create_webhook_endpoints_resource_sync
355
+
356
+ return create_webhook_endpoints_resource_sync(self._instance_id, self._api)
357
+
358
+ def __getattr__(self, name: str) -> Any:
359
+ return getattr(self._base, name)
360
+
361
+
362
+ class _PayinsNamespaceSync:
363
+ def __init__(self, instance_id: str, api_client: ApiClientImplSync) -> None:
364
+ self._instance_id = instance_id
365
+ self._api = api_client
366
+
367
+ @cached_property
368
+ def _base(self) -> "PayinsResourceSync":
369
+ from blindpay.resources.payins.payins import create_payins_resource_sync
370
+
371
+ return create_payins_resource_sync(self._instance_id, self._api)
372
+
373
+ @cached_property
374
+ def quotes(self) -> "PayinQuotesResourceSync":
375
+ from blindpay.resources.payins.quotes import create_payin_quotes_resource_sync
376
+
377
+ return create_payin_quotes_resource_sync(self._instance_id, self._api)
378
+
379
+ def __getattr__(self, name: str) -> Any:
380
+ return getattr(self._base, name)
381
+
382
+
383
+ class _ReceiversNamespaceSync:
384
+ def __init__(self, instance_id: str, api_client: ApiClientImplSync) -> None:
385
+ self._instance_id = instance_id
386
+ self._api = api_client
387
+
388
+ @cached_property
389
+ def _base(self) -> "ReceiversResourceSync":
390
+ from blindpay.resources.receivers.receivers import create_receivers_resource_sync
391
+
392
+ return create_receivers_resource_sync(self._instance_id, self._api)
393
+
394
+ @cached_property
395
+ def bank_accounts(self) -> "BankAccountsResourceSync":
396
+ from blindpay.resources.bank_accounts.bank_accounts import create_bank_accounts_resource_sync
397
+
398
+ return create_bank_accounts_resource_sync(self._instance_id, self._api)
399
+
400
+ def __getattr__(self, name: str) -> Any:
401
+ return getattr(self._base, name)
402
+
403
+
404
+ class _WalletsNamespaceSync:
405
+ def __init__(self, instance_id: str, api_client: ApiClientImplSync) -> None:
406
+ self._instance_id = instance_id
407
+ self._api = api_client
408
+
409
+ @cached_property
410
+ def blockchain(self) -> "BlockchainWalletsResourceSync":
411
+ from blindpay.resources.wallets.blockchain import create_blockchain_wallets_resource_sync
412
+
413
+ return create_blockchain_wallets_resource_sync(self._instance_id, self._api)
414
+
415
+ @cached_property
416
+ def offramp(self) -> "OfframpWalletsResourceSync":
417
+ from blindpay.resources.wallets.offramp import create_offramp_wallets_resource_sync
418
+
419
+ return create_offramp_wallets_resource_sync(self._instance_id, self._api)
420
+
421
+
422
+ class BlindPaySync:
423
+ _api_key: str
424
+ _instance_id: str
425
+ _base_url: str
426
+
427
+ def __init__(self, *, api_key: str, instance_id: str):
428
+ if not api_key:
429
+ raise BlindPayError("Api key not provided, get your api key on blindpay dashboard")
430
+
431
+ if not instance_id:
432
+ raise BlindPayError("Instance id not provided, get your instance id on blindpay dashboard")
433
+
434
+ self._api_key = api_key
435
+ self._instance_id = instance_id
436
+ self._base_url = "https://api.blindpay.com/v1"
437
+
438
+ self._headers = {
439
+ "Content-Type": "application/json",
440
+ "Accept": "application/json",
441
+ "User-Agent": f"blindpay-python/{__version__}",
442
+ "Authorization": f"Bearer {self._api_key}",
443
+ }
444
+
445
+ self._api = ApiClientImplSync(self._base_url, self._headers)
446
+
447
+ @cached_property
448
+ def available(self) -> "AvailableResourceSync":
449
+ from blindpay.resources.available import create_available_resource_sync
450
+
451
+ return create_available_resource_sync(self._api)
452
+
453
+ @cached_property
454
+ def instances(self) -> _InstancesNamespaceSync:
455
+ return _InstancesNamespaceSync(self._instance_id, self._api)
456
+
457
+ @cached_property
458
+ def partner_fees(self) -> "PartnerFeesResourceSync":
459
+ from blindpay.resources.partner_fees import create_partner_fees_resource_sync
460
+
461
+ return create_partner_fees_resource_sync(self._instance_id, self._api)
462
+
463
+ @cached_property
464
+ def payins(self) -> _PayinsNamespaceSync:
465
+ return _PayinsNamespaceSync(self._instance_id, self._api)
466
+
467
+ @cached_property
468
+ def quotes(self) -> "QuotesResourceSync":
469
+ from blindpay.resources.quotes import create_quotes_resource_sync
470
+
471
+ return create_quotes_resource_sync(self._instance_id, self._api)
472
+
473
+ @cached_property
474
+ def payouts(self) -> "PayoutsResourceSync":
475
+ from blindpay.resources.payouts import create_payouts_resource_sync
476
+
477
+ return create_payouts_resource_sync(self._instance_id, self._api)
478
+
479
+ @cached_property
480
+ def receivers(self) -> _ReceiversNamespaceSync:
481
+ return _ReceiversNamespaceSync(self._instance_id, self._api)
482
+
483
+ @cached_property
484
+ def virtual_accounts(self) -> "VirtualAccountsResourceSync":
485
+ from blindpay.resources.virtual_accounts import create_virtual_accounts_resource_sync
486
+
487
+ return create_virtual_accounts_resource_sync(self._instance_id, self._api)
488
+
489
+ @cached_property
490
+ def wallets(self) -> _WalletsNamespaceSync:
491
+ return _WalletsNamespaceSync(self._instance_id, self._api)
492
+
493
+ def verify_webhook_signature(
494
+ self, *, secret: str, id: str, timestamp: str, payload: str, svix_signature: str
495
+ ) -> bool:
496
+ """
497
+ Verifies the BlindPay webhook signature
498
+
499
+ Args:
500
+ secret: The webhook secret from BlindPay dashboard
501
+ id: The value of the `svix-id` header
502
+ timestamp: The value of the `svix-timestamp` header
503
+ payload: The raw request body
504
+ svix_signature: The value of the `svix-signature` header
505
+
506
+ Returns:
507
+ True if the signature is valid, False otherwise
508
+ """
509
+ signed_content = f"{id}.{timestamp}.{payload}"
510
+
511
+ secret_parts = secret.split("_")
512
+ if len(secret_parts) < 2:
513
+ return False
514
+
515
+ try:
516
+ secret_bytes = base64.b64decode(secret_parts[1])
517
+ except Exception:
518
+ return False
519
+
520
+ expected_signature = base64.b64encode(
521
+ hmac.new(secret_bytes, signed_content.encode(), hashlib.sha256).digest()
522
+ ).decode()
523
+
524
+ return len(svix_signature) == len(expected_signature) and hmac.compare_digest(
525
+ svix_signature, expected_signature
526
+ )
527
+
528
+ def close(self) -> None:
529
+ if hasattr(self, "_api"):
530
+ self._api.close()
531
+
532
+ def __enter__(self) -> "BlindPaySync":
533
+ return self
534
+
535
+ def __exit__(self, exc_type: Optional[type], exc_val: Optional[BaseException], exc_tb: Optional[Any]) -> None:
536
+ self.close()
@@ -0,0 +1,29 @@
1
+ from .api_keys import create_api_keys_resource
2
+ from .available import create_available_resource
3
+ from .bank_accounts import create_bank_accounts_resource
4
+ from .instances import create_instances_resource
5
+ from .partner_fees import create_partner_fees_resource
6
+ from .payins import create_payin_quotes_resource, create_payins_resource
7
+ from .payouts import create_payouts_resource
8
+ from .quotes import create_quotes_resource
9
+ from .receivers import create_receivers_resource
10
+ from .virtual_accounts import create_virtual_accounts_resource
11
+ from .wallets import create_blockchain_wallets_resource, create_offramp_wallets_resource
12
+ from .webhooks import create_webhook_endpoints_resource
13
+
14
+ __all__ = [
15
+ "create_api_keys_resource",
16
+ "create_available_resource",
17
+ "create_bank_accounts_resource",
18
+ "create_instances_resource",
19
+ "create_partner_fees_resource",
20
+ "create_payins_resource",
21
+ "create_payin_quotes_resource",
22
+ "create_payouts_resource",
23
+ "create_quotes_resource",
24
+ "create_receivers_resource",
25
+ "create_virtual_accounts_resource",
26
+ "create_blockchain_wallets_resource",
27
+ "create_offramp_wallets_resource",
28
+ "create_webhook_endpoints_resource",
29
+ ]