nomba-python 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.
- nomba_python/__init__.py +40 -0
- nomba_python/client.py +161 -0
- nomba_python/concurrency.py +54 -0
- nomba_python/data/__init__.py +0 -0
- nomba_python/data/nomba_openapi.json +13321 -0
- nomba_python/exceptions.py +49 -0
- nomba_python/flows/__init__.py +3 -0
- nomba_python/flows/card_payment.py +204 -0
- nomba_python/http.py +418 -0
- nomba_python/models.py +749 -0
- nomba_python/pagination.py +111 -0
- nomba_python/py.typed +0 -0
- nomba_python/resources/__init__.py +33 -0
- nomba_python/resources/accounts.py +379 -0
- nomba_python/resources/airtime_data.py +252 -0
- nomba_python/resources/cabletv.py +173 -0
- nomba_python/resources/charge.py +410 -0
- nomba_python/resources/checkout.py +239 -0
- nomba_python/resources/electricity.py +204 -0
- nomba_python/resources/terminals.py +184 -0
- nomba_python/resources/transactions.py +460 -0
- nomba_python/resources/transfers.py +298 -0
- nomba_python/resources/virtual_accounts.py +230 -0
- nomba_python/validation.py +97 -0
- nomba_python/webhooks.py +190 -0
- nomba_python-0.1.0.dist-info/METADATA +312 -0
- nomba_python-0.1.0.dist-info/RECORD +29 -0
- nomba_python-0.1.0.dist-info/WHEEL +4 -0
- nomba_python-0.1.0.dist-info/licenses/LICENSE +21 -0
nomba_python/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from .client import AsyncNomba, Nomba
|
|
2
|
+
from .concurrency import gather_limited
|
|
3
|
+
from .exceptions import (
|
|
4
|
+
NombaAPIError,
|
|
5
|
+
NombaAuthError,
|
|
6
|
+
NombaError,
|
|
7
|
+
NombaValidationError,
|
|
8
|
+
)
|
|
9
|
+
from .flows import AsyncCardPaymentFlow, CardPaymentFlow, CardPaymentStep
|
|
10
|
+
from .http import AsyncNombaClient, NombaClient
|
|
11
|
+
from .pagination import apaginate, paginate
|
|
12
|
+
from .webhooks import (
|
|
13
|
+
check_timestamp_freshness,
|
|
14
|
+
compute_signature,
|
|
15
|
+
verify_webhook_request,
|
|
16
|
+
verify_webhook_signature,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"Nomba",
|
|
21
|
+
"AsyncNomba",
|
|
22
|
+
"NombaClient",
|
|
23
|
+
"AsyncNombaClient",
|
|
24
|
+
"NombaError",
|
|
25
|
+
"NombaAPIError",
|
|
26
|
+
"NombaAuthError",
|
|
27
|
+
"NombaValidationError",
|
|
28
|
+
"CardPaymentFlow",
|
|
29
|
+
"AsyncCardPaymentFlow",
|
|
30
|
+
"CardPaymentStep",
|
|
31
|
+
"paginate",
|
|
32
|
+
"apaginate",
|
|
33
|
+
"gather_limited",
|
|
34
|
+
"verify_webhook_signature",
|
|
35
|
+
"verify_webhook_request",
|
|
36
|
+
"compute_signature",
|
|
37
|
+
"check_timestamp_freshness",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
__version__ = "0.1.0"
|
nomba_python/client.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .http import AsyncNombaClient, NombaClient
|
|
4
|
+
from .resources import (
|
|
5
|
+
Accounts,
|
|
6
|
+
AirtimeData,
|
|
7
|
+
AsyncAccounts,
|
|
8
|
+
AsyncAirtimeData,
|
|
9
|
+
AsyncCableTv,
|
|
10
|
+
AsyncCharge,
|
|
11
|
+
AsyncCheckout,
|
|
12
|
+
AsyncElectricity,
|
|
13
|
+
AsyncTerminals,
|
|
14
|
+
AsyncTransactions,
|
|
15
|
+
AsyncTransfers,
|
|
16
|
+
AsyncVirtualAccounts,
|
|
17
|
+
CableTv,
|
|
18
|
+
Charge,
|
|
19
|
+
Checkout,
|
|
20
|
+
Electricity,
|
|
21
|
+
Terminals,
|
|
22
|
+
Transactions,
|
|
23
|
+
Transfers,
|
|
24
|
+
VirtualAccounts,
|
|
25
|
+
)
|
|
26
|
+
from .flows import CardPaymentFlow, AsyncCardPaymentFlow
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Nomba:
|
|
30
|
+
"""
|
|
31
|
+
High-level entry point for the Nomba SDK. Covers every endpoint in
|
|
32
|
+
Nomba's published OpenAPI spec.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
from nomba import Nomba
|
|
36
|
+
|
|
37
|
+
nomba = Nomba(
|
|
38
|
+
client_id="...",
|
|
39
|
+
client_secret="...",
|
|
40
|
+
account_id="...",
|
|
41
|
+
sandbox=True,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
account = nomba.virtual_accounts.create_virtual_account(
|
|
45
|
+
account_ref="ref-123",
|
|
46
|
+
account_name="Jane Doe",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
Resource groups:
|
|
50
|
+
accounts, virtual_accounts, checkout, charge, transfers,
|
|
51
|
+
terminals, transactions, airtime_data, cabletv, electricity
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
client_id: str,
|
|
57
|
+
client_secret: str,
|
|
58
|
+
account_id: str,
|
|
59
|
+
*,
|
|
60
|
+
sandbox: bool = False,
|
|
61
|
+
timeout: float = 30.0,
|
|
62
|
+
) -> None:
|
|
63
|
+
self.client = NombaClient(
|
|
64
|
+
client_id=client_id,
|
|
65
|
+
client_secret=client_secret,
|
|
66
|
+
account_id=account_id,
|
|
67
|
+
sandbox=sandbox,
|
|
68
|
+
timeout=timeout,
|
|
69
|
+
)
|
|
70
|
+
self.accounts = Accounts(self.client)
|
|
71
|
+
self.virtual_accounts = VirtualAccounts(self.client)
|
|
72
|
+
self.checkout = Checkout(self.client)
|
|
73
|
+
self.charge = Charge(self.client)
|
|
74
|
+
self.transfers = Transfers(self.client)
|
|
75
|
+
self.terminals = Terminals(self.client)
|
|
76
|
+
self.transactions = Transactions(self.client)
|
|
77
|
+
self.airtime_data = AirtimeData(self.client)
|
|
78
|
+
self.cabletv = CableTv(self.client)
|
|
79
|
+
self.electricity = Electricity(self.client)
|
|
80
|
+
|
|
81
|
+
def close(self) -> None:
|
|
82
|
+
self.client.close()
|
|
83
|
+
|
|
84
|
+
def card_payment(self, order_reference: str) -> "CardPaymentFlow":
|
|
85
|
+
"""Start a guided card-payment flow for an existing checkout order.
|
|
86
|
+
See `nomba.flows.CardPaymentFlow` for the full step-by-step API."""
|
|
87
|
+
from .flows import CardPaymentFlow
|
|
88
|
+
|
|
89
|
+
return CardPaymentFlow(self.charge, order_reference=order_reference)
|
|
90
|
+
|
|
91
|
+
def __enter__(self) -> "Nomba":
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def __exit__(self, *exc_info: object) -> None:
|
|
95
|
+
self.close()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class AsyncNomba:
|
|
99
|
+
"""
|
|
100
|
+
Async high-level entry point for the Nomba SDK (uses httpx.AsyncClient).
|
|
101
|
+
Covers every endpoint in Nomba's published OpenAPI spec.
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
from nomba import AsyncNomba
|
|
105
|
+
|
|
106
|
+
async def main():
|
|
107
|
+
nomba = AsyncNomba(
|
|
108
|
+
client_id="...",
|
|
109
|
+
client_secret="...",
|
|
110
|
+
account_id="...",
|
|
111
|
+
sandbox=True,
|
|
112
|
+
)
|
|
113
|
+
account = await nomba.virtual_accounts.create_virtual_account(
|
|
114
|
+
account_ref="ref-123",
|
|
115
|
+
account_name="Jane Doe",
|
|
116
|
+
)
|
|
117
|
+
await nomba.close()
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
def __init__(
|
|
121
|
+
self,
|
|
122
|
+
client_id: str,
|
|
123
|
+
client_secret: str,
|
|
124
|
+
account_id: str,
|
|
125
|
+
*,
|
|
126
|
+
sandbox: bool = False,
|
|
127
|
+
timeout: float = 30.0,
|
|
128
|
+
) -> None:
|
|
129
|
+
self.client = AsyncNombaClient(
|
|
130
|
+
client_id=client_id,
|
|
131
|
+
client_secret=client_secret,
|
|
132
|
+
account_id=account_id,
|
|
133
|
+
sandbox=sandbox,
|
|
134
|
+
timeout=timeout,
|
|
135
|
+
)
|
|
136
|
+
self.accounts = AsyncAccounts(self.client)
|
|
137
|
+
self.virtual_accounts = AsyncVirtualAccounts(self.client)
|
|
138
|
+
self.checkout = AsyncCheckout(self.client)
|
|
139
|
+
self.charge = AsyncCharge(self.client)
|
|
140
|
+
self.transfers = AsyncTransfers(self.client)
|
|
141
|
+
self.terminals = AsyncTerminals(self.client)
|
|
142
|
+
self.transactions = AsyncTransactions(self.client)
|
|
143
|
+
self.airtime_data = AsyncAirtimeData(self.client)
|
|
144
|
+
self.cabletv = AsyncCableTv(self.client)
|
|
145
|
+
self.electricity = AsyncElectricity(self.client)
|
|
146
|
+
|
|
147
|
+
async def close(self) -> None:
|
|
148
|
+
await self.client.close()
|
|
149
|
+
|
|
150
|
+
def card_payment(self, order_reference: str) -> "AsyncCardPaymentFlow":
|
|
151
|
+
"""Start a guided async card-payment flow for an existing checkout
|
|
152
|
+
order. See `nomba.flows.AsyncCardPaymentFlow` for the full API."""
|
|
153
|
+
from .flows import AsyncCardPaymentFlow
|
|
154
|
+
|
|
155
|
+
return AsyncCardPaymentFlow(self.charge, order_reference=order_reference)
|
|
156
|
+
|
|
157
|
+
async def __aenter__(self) -> "AsyncNomba":
|
|
158
|
+
return self
|
|
159
|
+
|
|
160
|
+
async def __aexit__(self, *exc_info: object) -> None:
|
|
161
|
+
await self.close()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bounded concurrency helper.
|
|
3
|
+
|
|
4
|
+
`asyncio.gather` has no built-in concurrency limit -- firing off 200 async
|
|
5
|
+
calls with `asyncio.gather(*[nomba.transfers.send(...) for ...])` sends all
|
|
6
|
+
200 at once. If several start failing (e.g. Nomba rate-limiting you), each
|
|
7
|
+
one independently retries with its own backoff, multiplying load right when
|
|
8
|
+
the API is already struggling, and you may also just trip the rate limit by
|
|
9
|
+
bursting too many requests in the same window.
|
|
10
|
+
|
|
11
|
+
`gather_limited` runs the same calls but caps how many are in flight at
|
|
12
|
+
once, which keeps both the retry storm and the burst size bounded.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import asyncio
|
|
17
|
+
from typing import Awaitable, Callable, Sequence, TypeVar
|
|
18
|
+
|
|
19
|
+
T = TypeVar("T")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def gather_limited(
|
|
23
|
+
calls: Sequence[Callable[[], Awaitable[T]]],
|
|
24
|
+
*,
|
|
25
|
+
limit: int = 5,
|
|
26
|
+
return_exceptions: bool = False,
|
|
27
|
+
) -> list[T| BaseException]:
|
|
28
|
+
"""
|
|
29
|
+
Run a sequence of zero-arg async callables with at most `limit` running
|
|
30
|
+
concurrently, preserving input order in the returned results.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
from nomba import AsyncNomba
|
|
34
|
+
from nomba.concurrency import gather_limited
|
|
35
|
+
|
|
36
|
+
async with AsyncNomba(...) as nomba:
|
|
37
|
+
calls = [
|
|
38
|
+
(lambda ref=ref: nomba.virtual_accounts.fetch_a_virtual_account(ref))
|
|
39
|
+
for ref in account_refs
|
|
40
|
+
]
|
|
41
|
+
results = await gather_limited(calls, limit=5)
|
|
42
|
+
|
|
43
|
+
`return_exceptions=True` behaves like `asyncio.gather(..., return_exceptions=True)`:
|
|
44
|
+
exceptions are returned in place of results instead of propagating.
|
|
45
|
+
"""
|
|
46
|
+
semaphore = asyncio.Semaphore(limit)
|
|
47
|
+
|
|
48
|
+
async def _run(call: Callable[[], Awaitable[T]]) -> T:
|
|
49
|
+
async with semaphore:
|
|
50
|
+
return await call()
|
|
51
|
+
|
|
52
|
+
return await asyncio.gather(
|
|
53
|
+
*(_run(call) for call in calls), return_exceptions=return_exceptions
|
|
54
|
+
)
|
|
File without changes
|