bazik-sdk 1.0.1 → 1.0.2
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.
- package/CHANGELOG.md +2 -0
- package/README.md +25 -0
- package/package.json +1 -1
- package/src/constants.js +13 -0
- package/src/errors/BazikAuthError.js +12 -0
- package/src/errors/BazikError.js +21 -0
- package/src/errors/BazikInsufficientFundsError.js +12 -0
- package/src/errors/BazikRateLimitError.js +12 -0
- package/src/errors/BazikValidationError.js +12 -0
- package/src/helpers/validateAmount.js +23 -0
- package/src/helpers/validateRequired.js +21 -0
- package/src/helpers/validateWallet.js +19 -0
- package/src/http/request.js +37 -0
- package/src/index.js +6 -670
- package/src/modules/Bazik.js +235 -0
- package/src/modules/Payments.js +161 -0
- package/src/modules/Transfers.js +153 -0
- package/src/modules/Wallet.js +26 -0
package/src/index.js
CHANGED
|
@@ -9,676 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
"use strict";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
// ─── Errors ──────────────────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
class BazikError extends Error {
|
|
21
|
-
/**
|
|
22
|
-
* @param {string} message
|
|
23
|
-
* @param {number} [status]
|
|
24
|
-
* @param {string} [code]
|
|
25
|
-
* @param {*} [details]
|
|
26
|
-
*/
|
|
27
|
-
constructor(message, status, code, details) {
|
|
28
|
-
super(message);
|
|
29
|
-
this.name = "BazikError";
|
|
30
|
-
this.status = status ?? null;
|
|
31
|
-
this.code = code ?? null;
|
|
32
|
-
this.details = details ?? null;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
class BazikAuthError extends BazikError {
|
|
37
|
-
constructor(message, status, code, details) {
|
|
38
|
-
super(message, status, code, details);
|
|
39
|
-
this.name = "BazikAuthError";
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
class BazikValidationError extends BazikError {
|
|
44
|
-
constructor(message, details) {
|
|
45
|
-
super(message, 400, "validation_error", details);
|
|
46
|
-
this.name = "BazikValidationError";
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
class BazikInsufficientFundsError extends BazikError {
|
|
51
|
-
constructor(message, details) {
|
|
52
|
-
super(message, 402, "insufficient_funds", details);
|
|
53
|
-
this.name = "BazikInsufficientFundsError";
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
class BazikRateLimitError extends BazikError {
|
|
58
|
-
constructor(message, details) {
|
|
59
|
-
super(message, 429, "rate_limit_exceeded", details);
|
|
60
|
-
this.name = "BazikRateLimitError";
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Validate that a wallet number is 8 or 11 digits.
|
|
68
|
-
* @param {string} wallet
|
|
69
|
-
*/
|
|
70
|
-
function validateWallet(wallet) {
|
|
71
|
-
if (typeof wallet !== "string" || !/^\d{8}(\d{3})?$/.test(wallet)) {
|
|
72
|
-
throw new BazikValidationError(
|
|
73
|
-
`Invalid wallet number "${wallet}". Must be 8 or 11 digits.`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Validate a positive numeric amount.
|
|
80
|
-
* @param {number} amount
|
|
81
|
-
* @param {number} [max]
|
|
82
|
-
*/
|
|
83
|
-
function validateAmount(amount, max) {
|
|
84
|
-
if (typeof amount !== "number" || !isFinite(amount) || amount <= 0) {
|
|
85
|
-
throw new BazikValidationError(
|
|
86
|
-
`Invalid amount: ${amount}. Must be a positive number.`
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (max !== undefined && amount > max) {
|
|
90
|
-
throw new BazikValidationError(
|
|
91
|
-
`Amount ${amount} exceeds maximum of ${max} HTG.`
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Validate that required fields are present in an object.
|
|
98
|
-
* @param {Record<string, *>} obj
|
|
99
|
-
* @param {string[]} fields
|
|
100
|
-
*/
|
|
101
|
-
function validateRequired(obj, fields) {
|
|
102
|
-
const missing = fields.filter(
|
|
103
|
-
(f) => obj[f] === undefined || obj[f] === null || obj[f] === ""
|
|
104
|
-
);
|
|
105
|
-
if (missing.length > 0) {
|
|
106
|
-
throw new BazikValidationError(
|
|
107
|
-
`Missing required field(s): ${missing.join(", ")}`
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ─── HTTP Client (zero dependencies) ─────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Minimal fetch wrapper.
|
|
116
|
-
* @param {string} url
|
|
117
|
-
* @param {RequestInit & { timeout?: number }} options
|
|
118
|
-
* @returns {Promise<{ status: number, data: * }>}
|
|
119
|
-
*/
|
|
120
|
-
async function request(url, options = {}) {
|
|
121
|
-
const { timeout = 30_000, ...fetchOptions } = options;
|
|
122
|
-
|
|
123
|
-
const controller = new AbortController();
|
|
124
|
-
const timer = setTimeout(() => controller.abort(), timeout);
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
const res = await fetch(url, {
|
|
128
|
-
...fetchOptions,
|
|
129
|
-
signal: controller.signal,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
let data;
|
|
133
|
-
const contentType = res.headers.get("content-type") || "";
|
|
134
|
-
if (contentType.includes("application/json")) {
|
|
135
|
-
data = await res.json();
|
|
136
|
-
} else {
|
|
137
|
-
data = await res.text();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return { status: res.status, data };
|
|
141
|
-
} finally {
|
|
142
|
-
clearTimeout(timer);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// ─── Bazik Client ────────────────────────────────────────────────────────────
|
|
147
|
-
|
|
148
|
-
class Bazik {
|
|
149
|
-
#userID;
|
|
150
|
-
#secretKey;
|
|
151
|
-
#baseURL;
|
|
152
|
-
#token;
|
|
153
|
-
#tokenExpiresAt;
|
|
154
|
-
#autoRefresh;
|
|
155
|
-
#timeout;
|
|
156
|
-
#onTokenRefresh;
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Create a new Bazik client.
|
|
160
|
-
*
|
|
161
|
-
* @param {Object} config
|
|
162
|
-
* @param {string} config.userID — Your Bazik user ID (e.g. "bzk_c5b754a0_1757383229")
|
|
163
|
-
* @param {string} config.secretKey — Your secret key (e.g. "sk_...")
|
|
164
|
-
* @param {string} [config.baseURL] — API base URL (default: https://api.bazik.io)
|
|
165
|
-
* @param {boolean} [config.autoRefresh] — Automatically refresh token before expiry (default: true)
|
|
166
|
-
* @param {number} [config.timeout] — Request timeout in ms (default: 30000)
|
|
167
|
-
* @param {(token: string) => void} [config.onTokenRefresh] — Callback when token is refreshed
|
|
168
|
-
*
|
|
169
|
-
* @example
|
|
170
|
-
* // CommonJS
|
|
171
|
-
* const { Bazik } = require("bazik-sdk");
|
|
172
|
-
*
|
|
173
|
-
* // ESM
|
|
174
|
-
* import { Bazik } from "bazik-sdk";
|
|
175
|
-
*
|
|
176
|
-
* const bazik = new Bazik({
|
|
177
|
-
* userID: "bzk_c5b754a0_1757383229",
|
|
178
|
-
* secretKey: "sk_5b0ff521b331c73db55313dc82f17cab",
|
|
179
|
-
* });
|
|
180
|
-
*/
|
|
181
|
-
constructor(config) {
|
|
182
|
-
if (!config || !config.userID || !config.secretKey) {
|
|
183
|
-
throw new BazikValidationError(
|
|
184
|
-
"Both userID and secretKey are required to initialize the Bazik client."
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
this.#userID = config.userID;
|
|
189
|
-
this.#secretKey = config.secretKey;
|
|
190
|
-
this.#baseURL = (config.baseURL || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
191
|
-
this.#autoRefresh = config.autoRefresh !== false;
|
|
192
|
-
this.#timeout = config.timeout || 30_000;
|
|
193
|
-
this.#onTokenRefresh = config.onTokenRefresh || null;
|
|
194
|
-
this.#token = null;
|
|
195
|
-
this.#tokenExpiresAt = 0;
|
|
196
|
-
|
|
197
|
-
// Bind sub-modules
|
|
198
|
-
this.payments = new Payments(this);
|
|
199
|
-
this.transfers = new Transfers(this);
|
|
200
|
-
this.wallet = new Wallet(this);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// ── Token management ────────────────────────────────────────────────────
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Authenticate and obtain an access token.
|
|
207
|
-
* The token is cached internally and reused for subsequent requests.
|
|
208
|
-
*
|
|
209
|
-
* @returns {Promise<{ success: boolean, token: string, user_id: string, expires_at: number, message: string }>}
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* const auth = await bazik.authenticate();
|
|
213
|
-
* console.log("Token expires at:", new Date(auth.expires_at));
|
|
214
|
-
*/
|
|
215
|
-
async authenticate() {
|
|
216
|
-
const { status, data } = await request(`${this.#baseURL}/token`, {
|
|
217
|
-
method: "POST",
|
|
218
|
-
headers: { "Content-Type": "application/json" },
|
|
219
|
-
timeout: this.#timeout,
|
|
220
|
-
body: JSON.stringify({
|
|
221
|
-
userID: this.#userID,
|
|
222
|
-
secretKey: this.#secretKey,
|
|
223
|
-
}),
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
if (status === 429) {
|
|
227
|
-
throw new BazikRateLimitError(
|
|
228
|
-
"Too many authentication attempts. Please wait before retrying.",
|
|
229
|
-
data
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (status === 401) {
|
|
234
|
-
throw new BazikAuthError(
|
|
235
|
-
data?.error?.message || "Invalid credentials.",
|
|
236
|
-
status,
|
|
237
|
-
data?.error?.code,
|
|
238
|
-
data?.error?.details
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (status !== 200 || !data?.token) {
|
|
243
|
-
throw new BazikError(
|
|
244
|
-
data?.error?.message || "Authentication failed.",
|
|
245
|
-
status,
|
|
246
|
-
data?.error?.code,
|
|
247
|
-
data
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
this.#token = data.token;
|
|
252
|
-
this.#tokenExpiresAt = data.expires_at;
|
|
253
|
-
|
|
254
|
-
if (this.#onTokenRefresh) {
|
|
255
|
-
this.#onTokenRefresh(data.token);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return data;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Returns true if the current token is still valid (with a safety margin).
|
|
263
|
-
* @returns {boolean}
|
|
264
|
-
*/
|
|
265
|
-
isTokenValid() {
|
|
266
|
-
if (!this.#token) return false;
|
|
267
|
-
return Date.now() < this.#tokenExpiresAt - TOKEN_REFRESH_MARGIN_MS;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get a valid token, refreshing if needed.
|
|
272
|
-
* @returns {Promise<string>}
|
|
273
|
-
*/
|
|
274
|
-
async getToken() {
|
|
275
|
-
if (!this.#token || (this.#autoRefresh && !this.isTokenValid())) {
|
|
276
|
-
await this.authenticate();
|
|
277
|
-
}
|
|
278
|
-
return this.#token;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Internal: make an authenticated API request.
|
|
283
|
-
* @param {string} method
|
|
284
|
-
* @param {string} path
|
|
285
|
-
* @param {*} [body]
|
|
286
|
-
* @returns {Promise<*>}
|
|
287
|
-
*/
|
|
288
|
-
async _request(method, path, body) {
|
|
289
|
-
const token = await this.getToken();
|
|
290
|
-
|
|
291
|
-
const headers = {
|
|
292
|
-
Authorization: `Bearer ${token}`,
|
|
293
|
-
"Content-Type": "application/json",
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
const opts = { method, headers, timeout: this.#timeout };
|
|
297
|
-
if (body !== undefined) {
|
|
298
|
-
opts.body = JSON.stringify(body);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const { status, data } = await request(
|
|
302
|
-
`${this.#baseURL}${path}`,
|
|
303
|
-
opts
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
// Handle common error statuses
|
|
307
|
-
if (status === 401) {
|
|
308
|
-
// Token may have expired — try one refresh
|
|
309
|
-
if (this.#autoRefresh) {
|
|
310
|
-
await this.authenticate();
|
|
311
|
-
const retryHeaders = {
|
|
312
|
-
Authorization: `Bearer ${this.#token}`,
|
|
313
|
-
"Content-Type": "application/json",
|
|
314
|
-
};
|
|
315
|
-
const retry = await request(`${this.#baseURL}${path}`, {
|
|
316
|
-
...opts,
|
|
317
|
-
headers: retryHeaders,
|
|
318
|
-
});
|
|
319
|
-
if (retry.status === 401) {
|
|
320
|
-
throw new BazikAuthError(
|
|
321
|
-
"Authentication failed after token refresh.",
|
|
322
|
-
401,
|
|
323
|
-
"unauthorized",
|
|
324
|
-
retry.data
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
return retry.data;
|
|
328
|
-
}
|
|
329
|
-
throw new BazikAuthError(
|
|
330
|
-
data?.error?.message || "Unauthorized.",
|
|
331
|
-
401,
|
|
332
|
-
data?.error?.code,
|
|
333
|
-
data
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (status === 402) {
|
|
338
|
-
throw new BazikInsufficientFundsError(
|
|
339
|
-
data?.error?.message || "Insufficient funds.",
|
|
340
|
-
data
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (status === 429) {
|
|
345
|
-
throw new BazikRateLimitError(
|
|
346
|
-
data?.error?.message || "Rate limit exceeded.",
|
|
347
|
-
data
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (status >= 400) {
|
|
352
|
-
throw new BazikError(
|
|
353
|
-
data?.error?.message || data?.message || `Request failed with status ${status}`,
|
|
354
|
-
status,
|
|
355
|
-
data?.error?.code,
|
|
356
|
-
data
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return data;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// ─── Payments sub-module ─────────────────────────────────────────────────────
|
|
365
|
-
|
|
366
|
-
class Payments {
|
|
367
|
-
#client;
|
|
368
|
-
|
|
369
|
-
constructor(client) {
|
|
370
|
-
this.#client = client;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Create a MonCash payment. The customer must be redirected to `redirectUrl`.
|
|
375
|
-
*
|
|
376
|
-
* @param {Object} params
|
|
377
|
-
* @param {number} params.gdes — Amount in Gourdes (max 75,000)
|
|
378
|
-
* @param {string} [params.successUrl] — Redirect URL on success
|
|
379
|
-
* @param {string} [params.errorUrl] — Redirect URL on error
|
|
380
|
-
* @param {string} [params.description] — Payment description
|
|
381
|
-
* @param {string} [params.referenceId] — Your reference ID
|
|
382
|
-
* @param {string} [params.customerFirstName]
|
|
383
|
-
* @param {string} [params.customerLastName]
|
|
384
|
-
* @param {string} [params.customerEmail]
|
|
385
|
-
* @param {string} [params.webhookUrl] — Webhook for status updates
|
|
386
|
-
* @param {Object} [params.metadata] — Arbitrary metadata
|
|
387
|
-
* @returns {Promise<Object>} — Contains orderId, redirectUrl, status, etc.
|
|
388
|
-
*
|
|
389
|
-
* @example
|
|
390
|
-
* const payment = await bazik.payments.create({
|
|
391
|
-
* gdes: 1284.00,
|
|
392
|
-
* successUrl: "https://mysite.com/success",
|
|
393
|
-
* errorUrl: "https://mysite.com/error",
|
|
394
|
-
* description: "iPhone Pro Max",
|
|
395
|
-
* referenceId: "ORDER-001",
|
|
396
|
-
* customerFirstName: "Franck",
|
|
397
|
-
* customerLastName: "Jean",
|
|
398
|
-
* customerEmail: "franck@example.com",
|
|
399
|
-
* webhookUrl: "https://mysite.com/webhook",
|
|
400
|
-
* });
|
|
401
|
-
*
|
|
402
|
-
* // Redirect the customer to complete the payment
|
|
403
|
-
* console.log("Redirect to:", payment.redirectUrl);
|
|
404
|
-
*/
|
|
405
|
-
async create(params) {
|
|
406
|
-
validateRequired(params, ["gdes"]);
|
|
407
|
-
validateAmount(params.gdes, MAX_MONCASH_AMOUNT);
|
|
408
|
-
|
|
409
|
-
return this.#client._request("POST", "/moncash/token", params);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Verify a payment status by order ID.
|
|
414
|
-
*
|
|
415
|
-
* @param {string} orderId — The orderId returned by `create()`
|
|
416
|
-
* @returns {Promise<Object>} — Payment details with status, amount, metadata, etc.
|
|
417
|
-
*
|
|
418
|
-
* @example
|
|
419
|
-
* const status = await bazik.payments.verify("BZK_production_98630749_1760032902277_2ibu");
|
|
420
|
-
* if (status.status === "successful") {
|
|
421
|
-
* console.log("Payment confirmed!");
|
|
422
|
-
* }
|
|
423
|
-
*/
|
|
424
|
-
async verify(orderId) {
|
|
425
|
-
if (!orderId) {
|
|
426
|
-
throw new BazikValidationError("orderId is required.");
|
|
427
|
-
}
|
|
428
|
-
return this.#client._request("GET", `/order/${encodeURIComponent(orderId)}`);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Poll payment status until it resolves or times out.
|
|
433
|
-
*
|
|
434
|
-
* @param {string} orderId
|
|
435
|
-
* @param {Object} [options]
|
|
436
|
-
* @param {number} [options.intervalMs=5000] — Polling interval
|
|
437
|
-
* @param {number} [options.timeoutMs=300000] — Max wait time (5 min)
|
|
438
|
-
* @returns {Promise<Object>} — Final payment status
|
|
439
|
-
*
|
|
440
|
-
* @example
|
|
441
|
-
* const result = await bazik.payments.waitForCompletion("BZK_...", {
|
|
442
|
-
* intervalMs: 5000,
|
|
443
|
-
* timeoutMs: 120000,
|
|
444
|
-
* });
|
|
445
|
-
*/
|
|
446
|
-
async waitForCompletion(orderId, options = {}) {
|
|
447
|
-
const { intervalMs = 5000, timeoutMs = 300_000 } = options;
|
|
448
|
-
const deadline = Date.now() + timeoutMs;
|
|
449
|
-
|
|
450
|
-
while (Date.now() < deadline) {
|
|
451
|
-
const payment = await this.verify(orderId);
|
|
452
|
-
if (payment.status !== "pending") {
|
|
453
|
-
return payment;
|
|
454
|
-
}
|
|
455
|
-
await new Promise((r) => setTimeout(r, intervalMs));
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
throw new BazikError(
|
|
459
|
-
`Payment verification timed out after ${timeoutMs}ms.`,
|
|
460
|
-
null,
|
|
461
|
-
"timeout"
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* Send money to a MonCash wallet (withdraw / payout).
|
|
467
|
-
*
|
|
468
|
-
* @param {Object} params
|
|
469
|
-
* @param {number} params.gdes — Amount in HTG
|
|
470
|
-
* @param {string} params.wallet — Recipient phone (8 or 11 digits)
|
|
471
|
-
* @param {string} params.customerFirstName — Recipient first name
|
|
472
|
-
* @param {string} params.customerLastName — Recipient last name
|
|
473
|
-
* @param {string} [params.description]
|
|
474
|
-
* @param {string} [params.referenceId]
|
|
475
|
-
* @param {string} [params.customerEmail]
|
|
476
|
-
* @param {string} [params.webhookUrl]
|
|
477
|
-
* @returns {Promise<Object>}
|
|
478
|
-
*
|
|
479
|
-
* @example
|
|
480
|
-
* const withdrawal = await bazik.payments.withdraw({
|
|
481
|
-
* gdes: 500,
|
|
482
|
-
* wallet: "47556677",
|
|
483
|
-
* customerFirstName: "Melissa",
|
|
484
|
-
* customerLastName: "Francois",
|
|
485
|
-
* description: "Weekly earnings",
|
|
486
|
-
* });
|
|
487
|
-
*/
|
|
488
|
-
async withdraw(params) {
|
|
489
|
-
validateRequired(params, [
|
|
490
|
-
"gdes",
|
|
491
|
-
"wallet",
|
|
492
|
-
"customerFirstName",
|
|
493
|
-
"customerLastName",
|
|
494
|
-
]);
|
|
495
|
-
validateAmount(params.gdes);
|
|
496
|
-
validateWallet(params.wallet);
|
|
497
|
-
|
|
498
|
-
return this.#client._request("POST", "/moncash/withdraw", params);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* Get account balance (MonCash).
|
|
503
|
-
*
|
|
504
|
-
* @returns {Promise<{ available: number, reserved: number, currency: string, environment: string, last_updated: string }>}
|
|
505
|
-
*
|
|
506
|
-
* @example
|
|
507
|
-
* const balance = await bazik.payments.getBalance();
|
|
508
|
-
* console.log(`Available: ${balance.available} ${balance.currency}`);
|
|
509
|
-
*/
|
|
510
|
-
async getBalance() {
|
|
511
|
-
return this.#client._request("GET", "/balance");
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
// ─── Transfers sub-module ────────────────────────────────────────────────────
|
|
516
|
-
|
|
517
|
-
class Transfers {
|
|
518
|
-
#client;
|
|
519
|
-
|
|
520
|
-
constructor(client) {
|
|
521
|
-
this.#client = client;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Check MonCash customer/wallet status before sending a transfer.
|
|
526
|
-
*
|
|
527
|
-
* @param {string} wallet — MonCash phone number (8 digits)
|
|
528
|
-
* @returns {Promise<Object>} — Customer KYC level and status flags
|
|
529
|
-
*
|
|
530
|
-
* @example
|
|
531
|
-
* const status = await bazik.transfers.checkCustomer("37123456");
|
|
532
|
-
* console.log(status.customerStatus.type); // "fullkyc"
|
|
533
|
-
*/
|
|
534
|
-
async checkCustomer(wallet) {
|
|
535
|
-
validateWallet(wallet);
|
|
536
|
-
return this.#client._request("POST", "/moncash/customers/status", {
|
|
537
|
-
wallet,
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/**
|
|
542
|
-
* Create a MonCash transfer (send money to a wallet).
|
|
543
|
-
*
|
|
544
|
-
* @param {Object} params
|
|
545
|
-
* @param {number} params.gdes — Amount in HTG
|
|
546
|
-
* @param {string} params.wallet — Recipient phone (8 digits)
|
|
547
|
-
* @param {string} params.customerFirstName
|
|
548
|
-
* @param {string} params.customerLastName
|
|
549
|
-
* @param {string} [params.description]
|
|
550
|
-
* @param {string} [params.referenceId]
|
|
551
|
-
* @param {string} [params.customerEmail]
|
|
552
|
-
* @param {string} [params.webhookUrl]
|
|
553
|
-
* @returns {Promise<Object>}
|
|
554
|
-
*
|
|
555
|
-
* @example
|
|
556
|
-
* const transfer = await bazik.transfers.moncash({
|
|
557
|
-
* gdes: 500,
|
|
558
|
-
* wallet: "47556677",
|
|
559
|
-
* customerFirstName: "Melissa",
|
|
560
|
-
* customerLastName: "Francois",
|
|
561
|
-
* description: "Pou ou peye lekol la",
|
|
562
|
-
* });
|
|
563
|
-
* console.log(transfer.transaction_id); // "TRF_..."
|
|
564
|
-
*/
|
|
565
|
-
async moncash(params) {
|
|
566
|
-
validateRequired(params, [
|
|
567
|
-
"gdes",
|
|
568
|
-
"wallet",
|
|
569
|
-
"customerFirstName",
|
|
570
|
-
"customerLastName",
|
|
571
|
-
]);
|
|
572
|
-
validateAmount(params.gdes);
|
|
573
|
-
validateWallet(params.wallet);
|
|
574
|
-
|
|
575
|
-
return this.#client._request("POST", "/moncash/transfers", params);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Create a NatCash transfer.
|
|
580
|
-
*
|
|
581
|
-
* @param {Object} params
|
|
582
|
-
* @param {number} params.gdes — Amount in HTG
|
|
583
|
-
* @param {string} params.wallet — Recipient phone (8 digits)
|
|
584
|
-
* @param {string} params.customerFirstName
|
|
585
|
-
* @param {string} params.customerLastName
|
|
586
|
-
* @param {string} [params.description]
|
|
587
|
-
* @param {string} [params.referenceId]
|
|
588
|
-
* @param {string} [params.customerEmail]
|
|
589
|
-
* @param {string} [params.webhookUrl]
|
|
590
|
-
* @returns {Promise<Object>}
|
|
591
|
-
*
|
|
592
|
-
* @example
|
|
593
|
-
* const transfer = await bazik.transfers.natcash({
|
|
594
|
-
* gdes: 50,
|
|
595
|
-
* wallet: "44556677",
|
|
596
|
-
* customerFirstName: "Marie",
|
|
597
|
-
* customerLastName: "Pierre",
|
|
598
|
-
* });
|
|
599
|
-
*/
|
|
600
|
-
async natcash(params) {
|
|
601
|
-
validateRequired(params, [
|
|
602
|
-
"gdes",
|
|
603
|
-
"wallet",
|
|
604
|
-
"customerFirstName",
|
|
605
|
-
"customerLastName",
|
|
606
|
-
]);
|
|
607
|
-
validateAmount(params.gdes);
|
|
608
|
-
validateWallet(params.wallet);
|
|
609
|
-
|
|
610
|
-
return this.#client._request("POST", "/natcash/transfers", params);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* Get transfer status by transaction ID.
|
|
615
|
-
*
|
|
616
|
-
* @param {string} transactionId — e.g. "TRF_1761961466_eafd0ac3"
|
|
617
|
-
* @returns {Promise<Object>}
|
|
618
|
-
*
|
|
619
|
-
* @example
|
|
620
|
-
* const status = await bazik.transfers.getStatus("TRF_1761961466_eafd0ac3");
|
|
621
|
-
* if (status.status === "successful") {
|
|
622
|
-
* console.log("Transfer completed!");
|
|
623
|
-
* }
|
|
624
|
-
*/
|
|
625
|
-
async getStatus(transactionId) {
|
|
626
|
-
if (!transactionId) {
|
|
627
|
-
throw new BazikValidationError("transactionId is required.");
|
|
628
|
-
}
|
|
629
|
-
return this.#client._request(
|
|
630
|
-
"GET",
|
|
631
|
-
`/transfers/${encodeURIComponent(transactionId)}`
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
/**
|
|
636
|
-
* Get a fee quote before creating a transfer.
|
|
637
|
-
*
|
|
638
|
-
* @param {number} amount — Delivery amount in HTG
|
|
639
|
-
* @param {"moncash"|"natcash"} provider
|
|
640
|
-
* @returns {Promise<{ delivery_amount: number, fee: number, total_cost: number, currency: string, provider: string, fee_percentage: number }>}
|
|
641
|
-
*
|
|
642
|
-
* @example
|
|
643
|
-
* const quote = await bazik.transfers.getQuote(1000, "moncash");
|
|
644
|
-
* console.log(`Fee: ${quote.fee} HTG | Total: ${quote.total_cost} HTG`);
|
|
645
|
-
*/
|
|
646
|
-
async getQuote(amount, provider) {
|
|
647
|
-
validateAmount(amount);
|
|
648
|
-
if (!["moncash", "natcash"].includes(provider)) {
|
|
649
|
-
throw new BazikValidationError(
|
|
650
|
-
`Invalid provider "${provider}". Must be "moncash" or "natcash".`
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
return this.#client._request("POST", "/transfers/quote", {
|
|
654
|
-
amount,
|
|
655
|
-
provider,
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// ─── Wallet sub-module ───────────────────────────────────────────────────────
|
|
661
|
-
|
|
662
|
-
class Wallet {
|
|
663
|
-
#client;
|
|
664
|
-
|
|
665
|
-
constructor(client) {
|
|
666
|
-
this.#client = client;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
/**
|
|
670
|
-
* Get wallet balance.
|
|
671
|
-
*
|
|
672
|
-
* @returns {Promise<{ available: number, reserved: number, currency: string, environment: string, last_updated: string }>}
|
|
673
|
-
*
|
|
674
|
-
* @example
|
|
675
|
-
* const wallet = await bazik.wallet.getBalance();
|
|
676
|
-
* console.log(`Available: ${wallet.available} HTG`);
|
|
677
|
-
*/
|
|
678
|
-
async getBalance() {
|
|
679
|
-
return this.#client._request("GET", "/wallet");
|
|
680
|
-
}
|
|
681
|
-
}
|
|
12
|
+
const Bazik = require("./modules/Bazik");
|
|
13
|
+
const BazikError = require("./errors/BazikError");
|
|
14
|
+
const BazikAuthError = require("./errors/BazikAuthError");
|
|
15
|
+
const BazikValidationError = require("./errors/BazikValidationError");
|
|
16
|
+
const BazikInsufficientFundsError = require("./errors/BazikInsufficientFundsError");
|
|
17
|
+
const BazikRateLimitError = require("./errors/BazikRateLimitError");
|
|
682
18
|
|
|
683
19
|
// ─── Exports ─────────────────────────────────────────────────────────────────
|
|
684
20
|
|