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
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# This file is auto-generated from Nomba's OpenAPI spec. Do not edit by hand;
|
|
2
|
+
# regenerate via scripts/generate_resources.py instead.
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from ..http import AsyncNombaClient, NombaClient
|
|
7
|
+
from ..validation import validate_body
|
|
8
|
+
from .. import models as _models
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Transfers:
|
|
12
|
+
"""Sync resource methods for the Transfers group."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, client: NombaClient) -> None:
|
|
15
|
+
self._client = client
|
|
16
|
+
|
|
17
|
+
def fetch_bank_codes_and_names(self, **extra: object) -> _models.FetchBankCodesAndNamesResponse:
|
|
18
|
+
"""
|
|
19
|
+
Fetch bank codes and names
|
|
20
|
+
|
|
21
|
+
You can use this endpoint to fetch all banks, their names and codes.
|
|
22
|
+
"""
|
|
23
|
+
path = "/v1/transfers/banks"
|
|
24
|
+
params = None
|
|
25
|
+
return self._client.get(path, params=params) # type: ignore[return-value]
|
|
26
|
+
|
|
27
|
+
def perform_bank_account_lookup(self, account_number, bank_code, **extra: object) -> _models.PerformBankAccountLookupResponse:
|
|
28
|
+
"""
|
|
29
|
+
Perform bank account lookup
|
|
30
|
+
|
|
31
|
+
You can use this endpoint to perform bank account lookup.
|
|
32
|
+
|
|
33
|
+
Body fields:
|
|
34
|
+
accountNumber (required): The account number to be looked up.
|
|
35
|
+
bankCode (required): The bankCode of the bank the account number belongs to. This can be obtained from a call to `/v1/transfers/bank`
|
|
36
|
+
"""
|
|
37
|
+
path = "/v1/transfers/bank/lookup"
|
|
38
|
+
params = None
|
|
39
|
+
body: dict[str, object] = {}
|
|
40
|
+
body["accountNumber"] = account_number
|
|
41
|
+
body["bankCode"] = bank_code
|
|
42
|
+
body.update(extra)
|
|
43
|
+
validate_body("post", "/v1/transfers/bank/lookup", body)
|
|
44
|
+
return self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
45
|
+
|
|
46
|
+
def perform_bank_account_transfer_the_parent_account(self, amount, account_number, account_name, bank_code, merchant_tx_ref, sender_name, *, narration: object | None = None, **extra: object) -> _models.PerformBankAccountTransferTheParentAccountResponse:
|
|
47
|
+
"""
|
|
48
|
+
Perform bank account transfer from the parent account
|
|
49
|
+
|
|
50
|
+
You can use this endpoint to perform bank account transfer.
|
|
51
|
+
|
|
52
|
+
Body fields:
|
|
53
|
+
amount (required): The amount to be transferred.
|
|
54
|
+
accountNumber (required): The destination bank account number.
|
|
55
|
+
accountName (required): The name on the account.
|
|
56
|
+
bankCode (required): The code of the recipient bank.
|
|
57
|
+
merchantTxRef (required): Unique reference used to track a transaction from an external process.
|
|
58
|
+
senderName (required): Sender name
|
|
59
|
+
narration: The payment narration
|
|
60
|
+
"""
|
|
61
|
+
path = "/v1/transfers/bank"
|
|
62
|
+
params = None
|
|
63
|
+
body: dict[str, object] = {}
|
|
64
|
+
body["amount"] = amount
|
|
65
|
+
body["accountNumber"] = account_number
|
|
66
|
+
body["accountName"] = account_name
|
|
67
|
+
body["bankCode"] = bank_code
|
|
68
|
+
body["merchantTxRef"] = merchant_tx_ref
|
|
69
|
+
body["senderName"] = sender_name
|
|
70
|
+
if narration is not None:
|
|
71
|
+
body["narration"] = narration
|
|
72
|
+
body.update(extra)
|
|
73
|
+
validate_body("post", "/v1/transfers/bank", body)
|
|
74
|
+
return self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
75
|
+
|
|
76
|
+
def perform_bank_account_transfer_from_account(self, sub_account_id: str, amount, account_number, account_name, bank_code, merchant_tx_ref, sender_name, *, narration: object | None = None, **extra: object) -> _models.PerformBankAccountTransferFromAccountResponse:
|
|
77
|
+
"""
|
|
78
|
+
Perform bank account transfer from a sub account
|
|
79
|
+
|
|
80
|
+
You can use this endpoint to perform bank account transfer using a sub account
|
|
81
|
+
|
|
82
|
+
Body fields:
|
|
83
|
+
amount (required): The amount to be transferred.
|
|
84
|
+
accountNumber (required): The destination bank account number.
|
|
85
|
+
accountName (required): The name on the account.
|
|
86
|
+
bankCode (required): The code of the recipient bank.
|
|
87
|
+
merchantTxRef (required): Unique reference used to track a transaction from an external process.
|
|
88
|
+
senderName (required): Sender name
|
|
89
|
+
narration: The payment narration
|
|
90
|
+
"""
|
|
91
|
+
path = f"/v1/transfers/bank/{sub_account_id}"
|
|
92
|
+
params = None
|
|
93
|
+
body: dict[str, object] = {}
|
|
94
|
+
body["amount"] = amount
|
|
95
|
+
body["accountNumber"] = account_number
|
|
96
|
+
body["accountName"] = account_name
|
|
97
|
+
body["bankCode"] = bank_code
|
|
98
|
+
body["merchantTxRef"] = merchant_tx_ref
|
|
99
|
+
body["senderName"] = sender_name
|
|
100
|
+
if narration is not None:
|
|
101
|
+
body["narration"] = narration
|
|
102
|
+
body.update(extra)
|
|
103
|
+
validate_body("post", "/v1/transfers/bank/{subAccountId}", body)
|
|
104
|
+
return self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
105
|
+
|
|
106
|
+
def perform_wallet_transfer_from_the_parent_account(self, amount, receiver_account_id, merchant_tx_ref, *, narration: object | None = None, **extra: object) -> _models.PerformWalletTransferFromTheParentAccountResponse:
|
|
107
|
+
"""
|
|
108
|
+
Perform wallet transfer from the parent account
|
|
109
|
+
|
|
110
|
+
You can use this endpoint to perform a wallet transfer.
|
|
111
|
+
|
|
112
|
+
Body fields:
|
|
113
|
+
amount (required): The amount to be transferred.
|
|
114
|
+
receiverAccountId (required): The receiver's accountId.
|
|
115
|
+
merchantTxRef (required): Unique reference used to track a transaction from an external process.
|
|
116
|
+
narration: The payment narration
|
|
117
|
+
"""
|
|
118
|
+
path = "/v1/transfers/wallet"
|
|
119
|
+
params = None
|
|
120
|
+
body: dict[str, object] = {}
|
|
121
|
+
body["amount"] = amount
|
|
122
|
+
body["receiverAccountId"] = receiver_account_id
|
|
123
|
+
body["merchantTxRef"] = merchant_tx_ref
|
|
124
|
+
if narration is not None:
|
|
125
|
+
body["narration"] = narration
|
|
126
|
+
body.update(extra)
|
|
127
|
+
validate_body("post", "/v1/transfers/wallet", body)
|
|
128
|
+
return self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
129
|
+
|
|
130
|
+
def perform_wallet_transfer_from_a_sub_account(self, sub_account_id: str, amount, receiver_account_id, merchant_tx_ref, *, narration: object | None = None, **extra: object) -> _models.PerformWalletTransferFromASubAccountResponse:
|
|
131
|
+
"""
|
|
132
|
+
Perform wallet transfer from a sub account
|
|
133
|
+
|
|
134
|
+
You can use this endpoint to perform a wallet transfer from a sub account
|
|
135
|
+
|
|
136
|
+
Body fields:
|
|
137
|
+
amount (required): The amount to be transferred.
|
|
138
|
+
receiverAccountId (required): The receiver's accountId.
|
|
139
|
+
merchantTxRef (required): Unique reference used to track a transaction from an external process.
|
|
140
|
+
narration: The payment narration
|
|
141
|
+
"""
|
|
142
|
+
path = f"/v1/transfers/wallet/{sub_account_id}"
|
|
143
|
+
params = None
|
|
144
|
+
body: dict[str, object] = {}
|
|
145
|
+
body["amount"] = amount
|
|
146
|
+
body["receiverAccountId"] = receiver_account_id
|
|
147
|
+
body["merchantTxRef"] = merchant_tx_ref
|
|
148
|
+
if narration is not None:
|
|
149
|
+
body["narration"] = narration
|
|
150
|
+
body.update(extra)
|
|
151
|
+
validate_body("post", "/v1/transfers/wallet/{subAccountId}", body)
|
|
152
|
+
return self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class AsyncTransfers:
|
|
157
|
+
"""Async resource methods for the Transfers group."""
|
|
158
|
+
|
|
159
|
+
def __init__(self, client: AsyncNombaClient) -> None:
|
|
160
|
+
self._client = client
|
|
161
|
+
|
|
162
|
+
async def fetch_bank_codes_and_names(self, **extra: object) -> _models.FetchBankCodesAndNamesResponse:
|
|
163
|
+
"""
|
|
164
|
+
Fetch bank codes and names
|
|
165
|
+
|
|
166
|
+
You can use this endpoint to fetch all banks, their names and codes.
|
|
167
|
+
"""
|
|
168
|
+
path = "/v1/transfers/banks"
|
|
169
|
+
params = None
|
|
170
|
+
return await self._client.get(path, params=params) # type: ignore[return-value]
|
|
171
|
+
|
|
172
|
+
async def perform_bank_account_lookup(self, account_number, bank_code, **extra: object) -> _models.PerformBankAccountLookupResponse:
|
|
173
|
+
"""
|
|
174
|
+
Perform bank account lookup
|
|
175
|
+
|
|
176
|
+
You can use this endpoint to perform bank account lookup.
|
|
177
|
+
|
|
178
|
+
Body fields:
|
|
179
|
+
accountNumber (required): The account number to be looked up.
|
|
180
|
+
bankCode (required): The bankCode of the bank the account number belongs to. This can be obtained from a call to `/v1/transfers/bank`
|
|
181
|
+
"""
|
|
182
|
+
path = "/v1/transfers/bank/lookup"
|
|
183
|
+
params = None
|
|
184
|
+
body: dict[str, object] = {}
|
|
185
|
+
body["accountNumber"] = account_number
|
|
186
|
+
body["bankCode"] = bank_code
|
|
187
|
+
body.update(extra)
|
|
188
|
+
validate_body("post", "/v1/transfers/bank/lookup", body)
|
|
189
|
+
return await self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
190
|
+
|
|
191
|
+
async def perform_bank_account_transfer_the_parent_account(self, amount, account_number, account_name, bank_code, merchant_tx_ref, sender_name, *, narration: object | None = None, **extra: object) -> _models.PerformBankAccountTransferTheParentAccountResponse:
|
|
192
|
+
"""
|
|
193
|
+
Perform bank account transfer from the parent account
|
|
194
|
+
|
|
195
|
+
You can use this endpoint to perform bank account transfer.
|
|
196
|
+
|
|
197
|
+
Body fields:
|
|
198
|
+
amount (required): The amount to be transferred.
|
|
199
|
+
accountNumber (required): The destination bank account number.
|
|
200
|
+
accountName (required): The name on the account.
|
|
201
|
+
bankCode (required): The code of the recipient bank.
|
|
202
|
+
merchantTxRef (required): Unique reference used to track a transaction from an external process.
|
|
203
|
+
senderName (required): Sender name
|
|
204
|
+
narration: The payment narration
|
|
205
|
+
"""
|
|
206
|
+
path = "/v1/transfers/bank"
|
|
207
|
+
params = None
|
|
208
|
+
body: dict[str, object] = {}
|
|
209
|
+
body["amount"] = amount
|
|
210
|
+
body["accountNumber"] = account_number
|
|
211
|
+
body["accountName"] = account_name
|
|
212
|
+
body["bankCode"] = bank_code
|
|
213
|
+
body["merchantTxRef"] = merchant_tx_ref
|
|
214
|
+
body["senderName"] = sender_name
|
|
215
|
+
if narration is not None:
|
|
216
|
+
body["narration"] = narration
|
|
217
|
+
body.update(extra)
|
|
218
|
+
validate_body("post", "/v1/transfers/bank", body)
|
|
219
|
+
return await self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
220
|
+
|
|
221
|
+
async def perform_bank_account_transfer_from_account(self, sub_account_id: str, amount, account_number, account_name, bank_code, merchant_tx_ref, sender_name, *, narration: object | None = None, **extra: object) -> _models.PerformBankAccountTransferFromAccountResponse:
|
|
222
|
+
"""
|
|
223
|
+
Perform bank account transfer from a sub account
|
|
224
|
+
|
|
225
|
+
You can use this endpoint to perform bank account transfer using a sub account
|
|
226
|
+
|
|
227
|
+
Body fields:
|
|
228
|
+
amount (required): The amount to be transferred.
|
|
229
|
+
accountNumber (required): The destination bank account number.
|
|
230
|
+
accountName (required): The name on the account.
|
|
231
|
+
bankCode (required): The code of the recipient bank.
|
|
232
|
+
merchantTxRef (required): Unique reference used to track a transaction from an external process.
|
|
233
|
+
senderName (required): Sender name
|
|
234
|
+
narration: The payment narration
|
|
235
|
+
"""
|
|
236
|
+
path = f"/v1/transfers/bank/{sub_account_id}"
|
|
237
|
+
params = None
|
|
238
|
+
body: dict[str, object] = {}
|
|
239
|
+
body["amount"] = amount
|
|
240
|
+
body["accountNumber"] = account_number
|
|
241
|
+
body["accountName"] = account_name
|
|
242
|
+
body["bankCode"] = bank_code
|
|
243
|
+
body["merchantTxRef"] = merchant_tx_ref
|
|
244
|
+
body["senderName"] = sender_name
|
|
245
|
+
if narration is not None:
|
|
246
|
+
body["narration"] = narration
|
|
247
|
+
body.update(extra)
|
|
248
|
+
validate_body("post", "/v1/transfers/bank/{subAccountId}", body)
|
|
249
|
+
return await self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
250
|
+
|
|
251
|
+
async def perform_wallet_transfer_from_the_parent_account(self, amount, receiver_account_id, merchant_tx_ref, *, narration: object | None = None, **extra: object) -> _models.PerformWalletTransferFromTheParentAccountResponse:
|
|
252
|
+
"""
|
|
253
|
+
Perform wallet transfer from the parent account
|
|
254
|
+
|
|
255
|
+
You can use this endpoint to perform a wallet transfer.
|
|
256
|
+
|
|
257
|
+
Body fields:
|
|
258
|
+
amount (required): The amount to be transferred.
|
|
259
|
+
receiverAccountId (required): The receiver's accountId.
|
|
260
|
+
merchantTxRef (required): Unique reference used to track a transaction from an external process.
|
|
261
|
+
narration: The payment narration
|
|
262
|
+
"""
|
|
263
|
+
path = "/v1/transfers/wallet"
|
|
264
|
+
params = None
|
|
265
|
+
body: dict[str, object] = {}
|
|
266
|
+
body["amount"] = amount
|
|
267
|
+
body["receiverAccountId"] = receiver_account_id
|
|
268
|
+
body["merchantTxRef"] = merchant_tx_ref
|
|
269
|
+
if narration is not None:
|
|
270
|
+
body["narration"] = narration
|
|
271
|
+
body.update(extra)
|
|
272
|
+
validate_body("post", "/v1/transfers/wallet", body)
|
|
273
|
+
return await self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
274
|
+
|
|
275
|
+
async def perform_wallet_transfer_from_a_sub_account(self, sub_account_id: str, amount, receiver_account_id, merchant_tx_ref, *, narration: object | None = None, **extra: object) -> _models.PerformWalletTransferFromASubAccountResponse:
|
|
276
|
+
"""
|
|
277
|
+
Perform wallet transfer from a sub account
|
|
278
|
+
|
|
279
|
+
You can use this endpoint to perform a wallet transfer from a sub account
|
|
280
|
+
|
|
281
|
+
Body fields:
|
|
282
|
+
amount (required): The amount to be transferred.
|
|
283
|
+
receiverAccountId (required): The receiver's accountId.
|
|
284
|
+
merchantTxRef (required): Unique reference used to track a transaction from an external process.
|
|
285
|
+
narration: The payment narration
|
|
286
|
+
"""
|
|
287
|
+
path = f"/v1/transfers/wallet/{sub_account_id}"
|
|
288
|
+
params = None
|
|
289
|
+
body: dict[str, object] = {}
|
|
290
|
+
body["amount"] = amount
|
|
291
|
+
body["receiverAccountId"] = receiver_account_id
|
|
292
|
+
body["merchantTxRef"] = merchant_tx_ref
|
|
293
|
+
if narration is not None:
|
|
294
|
+
body["narration"] = narration
|
|
295
|
+
body.update(extra)
|
|
296
|
+
validate_body("post", "/v1/transfers/wallet/{subAccountId}", body)
|
|
297
|
+
return await self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
298
|
+
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# This file is auto-generated from Nomba's OpenAPI spec. Do not edit by hand;
|
|
2
|
+
# regenerate via scripts/generate_resources.py instead.
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from ..http import AsyncNombaClient, NombaClient
|
|
7
|
+
from ..validation import validate_body
|
|
8
|
+
from .. import models as _models
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class VirtualAccounts:
|
|
12
|
+
"""Sync resource methods for the VirtualAccounts group."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, client: NombaClient) -> None:
|
|
15
|
+
self._client = client
|
|
16
|
+
|
|
17
|
+
def create_virtual_account(self, account_ref, account_name, **extra: object) -> _models.CreateVirtualAccountResponse:
|
|
18
|
+
"""
|
|
19
|
+
Create virtual account
|
|
20
|
+
|
|
21
|
+
You can use this endpoint to create a virtual account to receive payments.
|
|
22
|
+
|
|
23
|
+
Body fields:
|
|
24
|
+
accountRef (required): Account reference
|
|
25
|
+
accountName (required): Account holder's name
|
|
26
|
+
"""
|
|
27
|
+
path = "/v1/accounts/virtual"
|
|
28
|
+
params = None
|
|
29
|
+
body: dict[str, object] = {}
|
|
30
|
+
body["accountRef"] = account_ref
|
|
31
|
+
body["accountName"] = account_name
|
|
32
|
+
body.update(extra)
|
|
33
|
+
validate_body("post", "/v1/accounts/virtual", body)
|
|
34
|
+
return self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
35
|
+
|
|
36
|
+
def filter_virtual_accounts(self, *, limit: str | None = None, cursor: str | None = None, account_name: object | None = None, account_ref: object | None = None, bvn: object | None = None, bank_account_number: object | None = None, date_created_from: object | None = None, date_created_to: object | None = None, expired: object | None = None, resource_acquired: object | None = None, **extra: object) -> _models.FilterVirtualAccountsResponse:
|
|
37
|
+
"""
|
|
38
|
+
Filter virtual accounts
|
|
39
|
+
|
|
40
|
+
You can use this endpoint to filter your virtual accounts.
|
|
41
|
+
|
|
42
|
+
Body fields:
|
|
43
|
+
accountName: Account holder's name
|
|
44
|
+
accountRef: Account reference
|
|
45
|
+
bvn: Bank Verification Number (BVN)
|
|
46
|
+
bankAccountNumber: Bank account number
|
|
47
|
+
dateCreatedFrom: Date created from
|
|
48
|
+
dateCreatedTo: Date created to
|
|
49
|
+
expired: Whether the virtual account is expired or not
|
|
50
|
+
resourceAcquired: Whether the virtual account is in use or not
|
|
51
|
+
"""
|
|
52
|
+
path = "/v1/accounts/virtual/list"
|
|
53
|
+
params: dict[str, object] = {}
|
|
54
|
+
if limit is not None:
|
|
55
|
+
params["limit"] = limit
|
|
56
|
+
if cursor is not None:
|
|
57
|
+
params["cursor"] = cursor
|
|
58
|
+
body: dict[str, object] = {}
|
|
59
|
+
if account_name is not None:
|
|
60
|
+
body["accountName"] = account_name
|
|
61
|
+
if account_ref is not None:
|
|
62
|
+
body["accountRef"] = account_ref
|
|
63
|
+
if bvn is not None:
|
|
64
|
+
body["bvn"] = bvn
|
|
65
|
+
if bank_account_number is not None:
|
|
66
|
+
body["bankAccountNumber"] = bank_account_number
|
|
67
|
+
if date_created_from is not None:
|
|
68
|
+
body["dateCreatedFrom"] = date_created_from
|
|
69
|
+
if date_created_to is not None:
|
|
70
|
+
body["dateCreatedTo"] = date_created_to
|
|
71
|
+
if expired is not None:
|
|
72
|
+
body["expired"] = expired
|
|
73
|
+
if resource_acquired is not None:
|
|
74
|
+
body["resourceAcquired"] = resource_acquired
|
|
75
|
+
body.update(extra)
|
|
76
|
+
validate_body("post", "/v1/accounts/virtual/list", body)
|
|
77
|
+
return self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
78
|
+
|
|
79
|
+
def update_a_virtual_account(self, account_ref: str, *, account_name: object | None = None, callback_url: object | None = None, **extra: object) -> _models.UpdateAVirtualAccountResponse:
|
|
80
|
+
"""
|
|
81
|
+
Update a virtual account
|
|
82
|
+
|
|
83
|
+
You can use this endpoint to update a virtual account.
|
|
84
|
+
|
|
85
|
+
Body fields:
|
|
86
|
+
accountName: Account holder's name
|
|
87
|
+
callbackUrl: Callback url
|
|
88
|
+
"""
|
|
89
|
+
path = f"/v1/accounts/virtual/{account_ref}"
|
|
90
|
+
params = None
|
|
91
|
+
body: dict[str, object] = {}
|
|
92
|
+
if account_name is not None:
|
|
93
|
+
body["accountName"] = account_name
|
|
94
|
+
if callback_url is not None:
|
|
95
|
+
body["callbackUrl"] = callback_url
|
|
96
|
+
body.update(extra)
|
|
97
|
+
validate_body("put", "/v1/accounts/virtual/{accountRef}", body)
|
|
98
|
+
return self._client.put(path, json=body, params=params) # type: ignore[return-value]
|
|
99
|
+
|
|
100
|
+
def fetch_a_virtual_account(self, account_ref: str, **extra: object) -> _models.FetchAVirtualAccountResponse:
|
|
101
|
+
"""
|
|
102
|
+
Fetch a virtual account
|
|
103
|
+
|
|
104
|
+
You can use this endpoint to fetch a virtual account.
|
|
105
|
+
"""
|
|
106
|
+
path = f"/v1/accounts/virtual/{account_ref}"
|
|
107
|
+
params = None
|
|
108
|
+
return self._client.get(path, params=params) # type: ignore[return-value]
|
|
109
|
+
|
|
110
|
+
def expire_a_virtual_account(self, account_ref: str, **extra: object) -> _models.ExpireAVirtualAccountResponse:
|
|
111
|
+
"""
|
|
112
|
+
Expire a virtual account
|
|
113
|
+
|
|
114
|
+
You can use this endpoint to expire a virtual account.
|
|
115
|
+
"""
|
|
116
|
+
path = f"/v1/accounts/virtual/{account_ref}"
|
|
117
|
+
params = None
|
|
118
|
+
return self._client.delete(path, params=params) # type: ignore[return-value]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class AsyncVirtualAccounts:
|
|
123
|
+
"""Async resource methods for the VirtualAccounts group."""
|
|
124
|
+
|
|
125
|
+
def __init__(self, client: AsyncNombaClient) -> None:
|
|
126
|
+
self._client = client
|
|
127
|
+
|
|
128
|
+
async def create_virtual_account(self, account_ref, account_name, **extra: object) -> _models.CreateVirtualAccountResponse:
|
|
129
|
+
"""
|
|
130
|
+
Create virtual account
|
|
131
|
+
|
|
132
|
+
You can use this endpoint to create a virtual account to receive payments.
|
|
133
|
+
|
|
134
|
+
Body fields:
|
|
135
|
+
accountRef (required): Account reference
|
|
136
|
+
accountName (required): Account holder's name
|
|
137
|
+
"""
|
|
138
|
+
path = "/v1/accounts/virtual"
|
|
139
|
+
params = None
|
|
140
|
+
body: dict[str, object] = {}
|
|
141
|
+
body["accountRef"] = account_ref
|
|
142
|
+
body["accountName"] = account_name
|
|
143
|
+
body.update(extra)
|
|
144
|
+
validate_body("post", "/v1/accounts/virtual", body)
|
|
145
|
+
return await self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
146
|
+
|
|
147
|
+
async def filter_virtual_accounts(self, *, limit: str | None = None, cursor: str | None = None, account_name: object | None = None, account_ref: object | None = None, bvn: object | None = None, bank_account_number: object | None = None, date_created_from: object | None = None, date_created_to: object | None = None, expired: object | None = None, resource_acquired: object | None = None, **extra: object) -> _models.FilterVirtualAccountsResponse:
|
|
148
|
+
"""
|
|
149
|
+
Filter virtual accounts
|
|
150
|
+
|
|
151
|
+
You can use this endpoint to filter your virtual accounts.
|
|
152
|
+
|
|
153
|
+
Body fields:
|
|
154
|
+
accountName: Account holder's name
|
|
155
|
+
accountRef: Account reference
|
|
156
|
+
bvn: Bank Verification Number (BVN)
|
|
157
|
+
bankAccountNumber: Bank account number
|
|
158
|
+
dateCreatedFrom: Date created from
|
|
159
|
+
dateCreatedTo: Date created to
|
|
160
|
+
expired: Whether the virtual account is expired or not
|
|
161
|
+
resourceAcquired: Whether the virtual account is in use or not
|
|
162
|
+
"""
|
|
163
|
+
path = "/v1/accounts/virtual/list"
|
|
164
|
+
params: dict[str, object] = {}
|
|
165
|
+
if limit is not None:
|
|
166
|
+
params["limit"] = limit
|
|
167
|
+
if cursor is not None:
|
|
168
|
+
params["cursor"] = cursor
|
|
169
|
+
body: dict[str, object] = {}
|
|
170
|
+
if account_name is not None:
|
|
171
|
+
body["accountName"] = account_name
|
|
172
|
+
if account_ref is not None:
|
|
173
|
+
body["accountRef"] = account_ref
|
|
174
|
+
if bvn is not None:
|
|
175
|
+
body["bvn"] = bvn
|
|
176
|
+
if bank_account_number is not None:
|
|
177
|
+
body["bankAccountNumber"] = bank_account_number
|
|
178
|
+
if date_created_from is not None:
|
|
179
|
+
body["dateCreatedFrom"] = date_created_from
|
|
180
|
+
if date_created_to is not None:
|
|
181
|
+
body["dateCreatedTo"] = date_created_to
|
|
182
|
+
if expired is not None:
|
|
183
|
+
body["expired"] = expired
|
|
184
|
+
if resource_acquired is not None:
|
|
185
|
+
body["resourceAcquired"] = resource_acquired
|
|
186
|
+
body.update(extra)
|
|
187
|
+
validate_body("post", "/v1/accounts/virtual/list", body)
|
|
188
|
+
return await self._client.post(path, json=body, params=params) # type: ignore[return-value]
|
|
189
|
+
|
|
190
|
+
async def update_a_virtual_account(self, account_ref: str, *, account_name: object | None = None, callback_url: object | None = None, **extra: object) -> _models.UpdateAVirtualAccountResponse:
|
|
191
|
+
"""
|
|
192
|
+
Update a virtual account
|
|
193
|
+
|
|
194
|
+
You can use this endpoint to update a virtual account.
|
|
195
|
+
|
|
196
|
+
Body fields:
|
|
197
|
+
accountName: Account holder's name
|
|
198
|
+
callbackUrl: Callback url
|
|
199
|
+
"""
|
|
200
|
+
path = f"/v1/accounts/virtual/{account_ref}"
|
|
201
|
+
params = None
|
|
202
|
+
body: dict[str, object] = {}
|
|
203
|
+
if account_name is not None:
|
|
204
|
+
body["accountName"] = account_name
|
|
205
|
+
if callback_url is not None:
|
|
206
|
+
body["callbackUrl"] = callback_url
|
|
207
|
+
body.update(extra)
|
|
208
|
+
validate_body("put", "/v1/accounts/virtual/{accountRef}", body)
|
|
209
|
+
return await self._client.put(path, json=body, params=params) # type: ignore[return-value]
|
|
210
|
+
|
|
211
|
+
async def fetch_a_virtual_account(self, account_ref: str, **extra: object) -> _models.FetchAVirtualAccountResponse:
|
|
212
|
+
"""
|
|
213
|
+
Fetch a virtual account
|
|
214
|
+
|
|
215
|
+
You can use this endpoint to fetch a virtual account.
|
|
216
|
+
"""
|
|
217
|
+
path = f"/v1/accounts/virtual/{account_ref}"
|
|
218
|
+
params = None
|
|
219
|
+
return await self._client.get(path, params=params) # type: ignore[return-value]
|
|
220
|
+
|
|
221
|
+
async def expire_a_virtual_account(self, account_ref: str, **extra: object) -> _models.ExpireAVirtualAccountResponse:
|
|
222
|
+
"""
|
|
223
|
+
Expire a virtual account
|
|
224
|
+
|
|
225
|
+
You can use this endpoint to expire a virtual account.
|
|
226
|
+
"""
|
|
227
|
+
path = f"/v1/accounts/virtual/{account_ref}"
|
|
228
|
+
params = None
|
|
229
|
+
return await self._client.delete(path, params=params) # type: ignore[return-value]
|
|
230
|
+
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lightweight, dependency-free validation of request bodies against Nomba's
|
|
3
|
+
own OpenAPI spec — specifically nested required fields that a flat method
|
|
4
|
+
signature can't enforce (e.g. `order={...}` in checkout order creation).
|
|
5
|
+
|
|
6
|
+
This only checks presence of required keys recursively (and a coarse
|
|
7
|
+
type check for "object"/"array"), not full JSON-Schema validation. It's a
|
|
8
|
+
local fast-fail before any network call, not a replacement for Nomba's own
|
|
9
|
+
server-side validation.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from functools import lru_cache
|
|
15
|
+
from importlib import resources
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from .exceptions import NombaValidationError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@lru_cache(maxsize=1)
|
|
22
|
+
def _load_spec() -> dict[str, Any]:
|
|
23
|
+
with resources.files("nomba.data").joinpath("nomba_openapi.json").open(
|
|
24
|
+
"r", encoding="utf-8"
|
|
25
|
+
) as f:
|
|
26
|
+
return json.load(f)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _resolve(spec: dict[str, Any], schema: Any) -> Any:
|
|
30
|
+
if not isinstance(schema, dict):
|
|
31
|
+
return schema
|
|
32
|
+
if "$ref" in schema:
|
|
33
|
+
name = schema["$ref"].split("/")[-1]
|
|
34
|
+
return spec["components"]["schemas"].get(name, {})
|
|
35
|
+
return schema
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@lru_cache(maxsize=None)
|
|
39
|
+
def _request_schema_key(verb: str, path_template: str) -> Any:
|
|
40
|
+
spec = _load_spec()
|
|
41
|
+
op = spec.get("paths", {}).get(path_template, {}).get(verb.lower())
|
|
42
|
+
if not op:
|
|
43
|
+
return None
|
|
44
|
+
rb = op.get("requestBody")
|
|
45
|
+
if not rb:
|
|
46
|
+
return None
|
|
47
|
+
schema = rb.get("content", {}).get("application/json", {}).get("schema")
|
|
48
|
+
return _resolve(spec, schema)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _check(spec: dict[str, Any], schema: Any, value: Any, path: str, missing: list[str]) -> None:
|
|
52
|
+
schema = _resolve(spec, schema)
|
|
53
|
+
if not isinstance(schema, dict):
|
|
54
|
+
return
|
|
55
|
+
schema_type = schema.get("type")
|
|
56
|
+
|
|
57
|
+
if schema_type == "object" or "properties" in schema:
|
|
58
|
+
if not isinstance(value, dict):
|
|
59
|
+
if value is not None:
|
|
60
|
+
missing.append(f"{path} (expected an object)")
|
|
61
|
+
return
|
|
62
|
+
for required_name in schema.get("required", []):
|
|
63
|
+
if required_name not in value or value[required_name] is None:
|
|
64
|
+
missing.append(f"{path}.{required_name}" if path else required_name)
|
|
65
|
+
props = schema.get("properties", {})
|
|
66
|
+
for key, sub_value in value.items():
|
|
67
|
+
if key in props:
|
|
68
|
+
_check(spec, props[key], sub_value, f"{path}.{key}" if path else key, missing)
|
|
69
|
+
|
|
70
|
+
elif schema_type == "array":
|
|
71
|
+
if not isinstance(value, list):
|
|
72
|
+
return
|
|
73
|
+
items_schema = schema.get("items")
|
|
74
|
+
if items_schema:
|
|
75
|
+
for i, item in enumerate(value):
|
|
76
|
+
_check(spec, items_schema, item, f"{path}[{i}]", missing)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def validate_body(verb: str, path_template: str, body: dict[str, Any]) -> None:
|
|
80
|
+
"""
|
|
81
|
+
Validate `body` against the requestBody schema for `verb`/`path_template`
|
|
82
|
+
in Nomba's OpenAPI spec, recursively checking required fields on nested
|
|
83
|
+
objects/arrays. Raises NombaValidationError listing every missing field
|
|
84
|
+
if any are absent. No-ops if the spec has no requestBody for this
|
|
85
|
+
operation, or no nested object/array fields to check.
|
|
86
|
+
"""
|
|
87
|
+
schema = _request_schema_key(verb, path_template)
|
|
88
|
+
if schema is None:
|
|
89
|
+
return
|
|
90
|
+
spec = _load_spec()
|
|
91
|
+
missing: list[str] = []
|
|
92
|
+
_check(spec, schema, body, "", missing)
|
|
93
|
+
if missing:
|
|
94
|
+
raise NombaValidationError(
|
|
95
|
+
f"Missing required field(s) in request body: {', '.join(missing)}",
|
|
96
|
+
missing=missing,
|
|
97
|
+
)
|