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
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
DEFAULT_BASE_URL,
|
|
5
|
+
TOKEN_REFRESH_MARGIN_MS,
|
|
6
|
+
} = require("../constants");
|
|
7
|
+
const BazikError = require("../errors/BazikError");
|
|
8
|
+
const BazikAuthError = require("../errors/BazikAuthError");
|
|
9
|
+
const BazikValidationError = require("../errors/BazikValidationError");
|
|
10
|
+
const BazikInsufficientFundsError = require("../errors/BazikInsufficientFundsError");
|
|
11
|
+
const BazikRateLimitError = require("../errors/BazikRateLimitError");
|
|
12
|
+
const request = require("../http/request");
|
|
13
|
+
const Payments = require("./Payments");
|
|
14
|
+
const Transfers = require("./Transfers");
|
|
15
|
+
const Wallet = require("./Wallet");
|
|
16
|
+
|
|
17
|
+
// ─── Bazik Client ────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
class Bazik {
|
|
20
|
+
#userID;
|
|
21
|
+
#secretKey;
|
|
22
|
+
#baseURL;
|
|
23
|
+
#token;
|
|
24
|
+
#tokenExpiresAt;
|
|
25
|
+
#autoRefresh;
|
|
26
|
+
#timeout;
|
|
27
|
+
#onTokenRefresh;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a new Bazik client.
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} config
|
|
33
|
+
* @param {string} config.userID — Your Bazik user ID (e.g. "bzk_c5b754a0_1757383229")
|
|
34
|
+
* @param {string} config.secretKey — Your secret key (e.g. "sk_...")
|
|
35
|
+
* @param {string} [config.baseURL] — API base URL (default: https://api.bazik.io)
|
|
36
|
+
* @param {boolean} [config.autoRefresh] — Automatically refresh token before expiry (default: true)
|
|
37
|
+
* @param {number} [config.timeout] — Request timeout in ms (default: 30000)
|
|
38
|
+
* @param {(token: string) => void} [config.onTokenRefresh] — Callback when token is refreshed
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // CommonJS
|
|
42
|
+
* const { Bazik } = require("bazik-sdk");
|
|
43
|
+
*
|
|
44
|
+
* // ESM
|
|
45
|
+
* import { Bazik } from "bazik-sdk";
|
|
46
|
+
*
|
|
47
|
+
* const bazik = new Bazik({
|
|
48
|
+
* userID: "bzk_c5b754a0_1757383229",
|
|
49
|
+
* secretKey: "sk_5b0ff521b331c73db55313dc82f17cab",
|
|
50
|
+
* });
|
|
51
|
+
*/
|
|
52
|
+
constructor(config) {
|
|
53
|
+
if (!config || !config.userID || !config.secretKey) {
|
|
54
|
+
throw new BazikValidationError(
|
|
55
|
+
"Both userID and secretKey are required to initialize the Bazik client."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.#userID = config.userID;
|
|
60
|
+
this.#secretKey = config.secretKey;
|
|
61
|
+
this.#baseURL = (config.baseURL || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
62
|
+
this.#autoRefresh = config.autoRefresh !== false;
|
|
63
|
+
this.#timeout = config.timeout || 30_000;
|
|
64
|
+
this.#onTokenRefresh = config.onTokenRefresh || null;
|
|
65
|
+
this.#token = null;
|
|
66
|
+
this.#tokenExpiresAt = 0;
|
|
67
|
+
|
|
68
|
+
// Bind sub-modules
|
|
69
|
+
this.payments = new Payments(this);
|
|
70
|
+
this.transfers = new Transfers(this);
|
|
71
|
+
this.wallet = new Wallet(this);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Token management ────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Authenticate and obtain an access token.
|
|
78
|
+
* The token is cached internally and reused for subsequent requests.
|
|
79
|
+
*
|
|
80
|
+
* @returns {Promise<{ success: boolean, token: string, user_id: string, expires_at: number, message: string }>}
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const auth = await bazik.authenticate();
|
|
84
|
+
* console.log("Token expires at:", new Date(auth.expires_at));
|
|
85
|
+
*/
|
|
86
|
+
async authenticate() {
|
|
87
|
+
const { status, data } = await request(`${this.#baseURL}/token`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: { "Content-Type": "application/json" },
|
|
90
|
+
timeout: this.#timeout,
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
userID: this.#userID,
|
|
93
|
+
secretKey: this.#secretKey,
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (status === 429) {
|
|
98
|
+
throw new BazikRateLimitError(
|
|
99
|
+
"Too many authentication attempts. Please wait before retrying.",
|
|
100
|
+
data
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (status === 401) {
|
|
105
|
+
throw new BazikAuthError(
|
|
106
|
+
data?.error?.message || "Invalid credentials.",
|
|
107
|
+
status,
|
|
108
|
+
data?.error?.code,
|
|
109
|
+
data?.error?.details
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (status !== 200 || !data?.token) {
|
|
114
|
+
throw new BazikError(
|
|
115
|
+
data?.error?.message || "Authentication failed.",
|
|
116
|
+
status,
|
|
117
|
+
data?.error?.code,
|
|
118
|
+
data
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.#token = data.token;
|
|
123
|
+
this.#tokenExpiresAt = data.expires_at;
|
|
124
|
+
|
|
125
|
+
if (this.#onTokenRefresh) {
|
|
126
|
+
this.#onTokenRefresh(data.token);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return data;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Returns true if the current token is still valid (with a safety margin).
|
|
134
|
+
* @returns {boolean}
|
|
135
|
+
*/
|
|
136
|
+
isTokenValid() {
|
|
137
|
+
if (!this.#token) return false;
|
|
138
|
+
return Date.now() < this.#tokenExpiresAt - TOKEN_REFRESH_MARGIN_MS;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get a valid token, refreshing if needed.
|
|
143
|
+
* @returns {Promise<string>}
|
|
144
|
+
*/
|
|
145
|
+
async getToken() {
|
|
146
|
+
if (!this.#token || (this.#autoRefresh && !this.isTokenValid())) {
|
|
147
|
+
await this.authenticate();
|
|
148
|
+
}
|
|
149
|
+
return this.#token;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Internal: make an authenticated API request.
|
|
154
|
+
* @param {string} method
|
|
155
|
+
* @param {string} path
|
|
156
|
+
* @param {*} [body]
|
|
157
|
+
* @returns {Promise<*>}
|
|
158
|
+
*/
|
|
159
|
+
async _request(method, path, body) {
|
|
160
|
+
const token = await this.getToken();
|
|
161
|
+
|
|
162
|
+
const headers = {
|
|
163
|
+
Authorization: `Bearer ${token}`,
|
|
164
|
+
"Content-Type": "application/json",
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const opts = { method, headers, timeout: this.#timeout };
|
|
168
|
+
if (body !== undefined) {
|
|
169
|
+
opts.body = JSON.stringify(body);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const { status, data } = await request(
|
|
173
|
+
`${this.#baseURL}${path}`,
|
|
174
|
+
opts
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// Handle common error statuses
|
|
178
|
+
if (status === 401) {
|
|
179
|
+
// Token may have expired — try one refresh
|
|
180
|
+
if (this.#autoRefresh) {
|
|
181
|
+
await this.authenticate();
|
|
182
|
+
const retryHeaders = {
|
|
183
|
+
Authorization: `Bearer ${this.#token}`,
|
|
184
|
+
"Content-Type": "application/json",
|
|
185
|
+
};
|
|
186
|
+
const retry = await request(`${this.#baseURL}${path}`, {
|
|
187
|
+
...opts,
|
|
188
|
+
headers: retryHeaders,
|
|
189
|
+
});
|
|
190
|
+
if (retry.status === 401) {
|
|
191
|
+
throw new BazikAuthError(
|
|
192
|
+
"Authentication failed after token refresh.",
|
|
193
|
+
401,
|
|
194
|
+
"unauthorized",
|
|
195
|
+
retry.data
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
return retry.data;
|
|
199
|
+
}
|
|
200
|
+
throw new BazikAuthError(
|
|
201
|
+
data?.error?.message || "Unauthorized.",
|
|
202
|
+
401,
|
|
203
|
+
data?.error?.code,
|
|
204
|
+
data
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (status === 402) {
|
|
209
|
+
throw new BazikInsufficientFundsError(
|
|
210
|
+
data?.error?.message || "Insufficient funds.",
|
|
211
|
+
data
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (status === 429) {
|
|
216
|
+
throw new BazikRateLimitError(
|
|
217
|
+
data?.error?.message || "Rate limit exceeded.",
|
|
218
|
+
data
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (status >= 400) {
|
|
223
|
+
throw new BazikError(
|
|
224
|
+
data?.error?.message || data?.message || `Request failed with status ${status}`,
|
|
225
|
+
status,
|
|
226
|
+
data?.error?.code,
|
|
227
|
+
data
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return data;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = Bazik;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const validateRequired = require("../helpers/validateRequired");
|
|
4
|
+
const validateAmount = require("../helpers/validateAmount");
|
|
5
|
+
const validateWallet = require("../helpers/validateWallet");
|
|
6
|
+
const { MAX_MONCASH_AMOUNT } = require("../constants");
|
|
7
|
+
const BazikError = require("../errors/BazikError");
|
|
8
|
+
const BazikValidationError = require("../errors/BazikValidationError");
|
|
9
|
+
|
|
10
|
+
// ─── Payments sub-module ─────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
class Payments {
|
|
13
|
+
#client;
|
|
14
|
+
|
|
15
|
+
constructor(client) {
|
|
16
|
+
this.#client = client;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create a MonCash payment. The customer must be redirected to `redirectUrl`.
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} params
|
|
23
|
+
* @param {number} params.gdes — Amount in Gourdes (max 75,000)
|
|
24
|
+
* @param {string} [params.successUrl] — Redirect URL on success
|
|
25
|
+
* @param {string} [params.errorUrl] — Redirect URL on error
|
|
26
|
+
* @param {string} [params.description] — Payment description
|
|
27
|
+
* @param {string} [params.referenceId] — Your reference ID
|
|
28
|
+
* @param {string} [params.customerFirstName]
|
|
29
|
+
* @param {string} [params.customerLastName]
|
|
30
|
+
* @param {string} [params.customerEmail]
|
|
31
|
+
* @param {string} [params.webhookUrl] — Webhook for status updates
|
|
32
|
+
* @param {Object} [params.metadata] — Arbitrary metadata
|
|
33
|
+
* @returns {Promise<Object>} — Contains orderId, redirectUrl, status, etc.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* const payment = await bazik.payments.create({
|
|
37
|
+
* gdes: 1284.00,
|
|
38
|
+
* successUrl: "https://mysite.com/success",
|
|
39
|
+
* errorUrl: "https://mysite.com/error",
|
|
40
|
+
* description: "iPhone Pro Max",
|
|
41
|
+
* referenceId: "ORDER-001",
|
|
42
|
+
* customerFirstName: "Franck",
|
|
43
|
+
* customerLastName: "Jean",
|
|
44
|
+
* customerEmail: "franck@example.com",
|
|
45
|
+
* webhookUrl: "https://mysite.com/webhook",
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* // Redirect the customer to complete the payment
|
|
49
|
+
* console.log("Redirect to:", payment.redirectUrl);
|
|
50
|
+
*/
|
|
51
|
+
async create(params) {
|
|
52
|
+
validateRequired(params, ["gdes"]);
|
|
53
|
+
validateAmount(params.gdes, MAX_MONCASH_AMOUNT);
|
|
54
|
+
|
|
55
|
+
return this.#client._request("POST", "/moncash/token", params);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Verify a payment status by order ID.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} orderId — The orderId returned by `create()`
|
|
62
|
+
* @returns {Promise<Object>} — Payment details with status, amount, metadata, etc.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const status = await bazik.payments.verify("BZK_production_98630749_1760032902277_2ibu");
|
|
66
|
+
* if (status.status === "successful") {
|
|
67
|
+
* console.log("Payment confirmed!");
|
|
68
|
+
* }
|
|
69
|
+
*/
|
|
70
|
+
async verify(orderId) {
|
|
71
|
+
if (!orderId) {
|
|
72
|
+
throw new BazikValidationError("orderId is required.");
|
|
73
|
+
}
|
|
74
|
+
return this.#client._request("GET", `/order/${encodeURIComponent(orderId)}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Poll payment status until it resolves or times out.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} orderId
|
|
81
|
+
* @param {Object} [options]
|
|
82
|
+
* @param {number} [options.intervalMs=5000] — Polling interval
|
|
83
|
+
* @param {number} [options.timeoutMs=300000] — Max wait time (5 min)
|
|
84
|
+
* @returns {Promise<Object>} — Final payment status
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* const result = await bazik.payments.waitForCompletion("BZK_...", {
|
|
88
|
+
* intervalMs: 5000,
|
|
89
|
+
* timeoutMs: 120000,
|
|
90
|
+
* });
|
|
91
|
+
*/
|
|
92
|
+
async waitForCompletion(orderId, options = {}) {
|
|
93
|
+
const { intervalMs = 5000, timeoutMs = 300_000 } = options;
|
|
94
|
+
const deadline = Date.now() + timeoutMs;
|
|
95
|
+
|
|
96
|
+
while (Date.now() < deadline) {
|
|
97
|
+
const payment = await this.verify(orderId);
|
|
98
|
+
if (payment.status !== "pending") {
|
|
99
|
+
return payment;
|
|
100
|
+
}
|
|
101
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
throw new BazikError(
|
|
105
|
+
`Payment verification timed out after ${timeoutMs}ms.`,
|
|
106
|
+
null,
|
|
107
|
+
"timeout"
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Send money to a MonCash wallet (withdraw / payout).
|
|
113
|
+
*
|
|
114
|
+
* @param {Object} params
|
|
115
|
+
* @param {number} params.gdes — Amount in HTG
|
|
116
|
+
* @param {string} params.wallet — Recipient phone (8 or 11 digits)
|
|
117
|
+
* @param {string} params.customerFirstName — Recipient first name
|
|
118
|
+
* @param {string} params.customerLastName — Recipient last name
|
|
119
|
+
* @param {string} [params.description]
|
|
120
|
+
* @param {string} [params.referenceId]
|
|
121
|
+
* @param {string} [params.customerEmail]
|
|
122
|
+
* @param {string} [params.webhookUrl]
|
|
123
|
+
* @returns {Promise<Object>}
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* const withdrawal = await bazik.payments.withdraw({
|
|
127
|
+
* gdes: 500,
|
|
128
|
+
* wallet: "47556677",
|
|
129
|
+
* customerFirstName: "Melissa",
|
|
130
|
+
* customerLastName: "Francois",
|
|
131
|
+
* description: "Weekly earnings",
|
|
132
|
+
* });
|
|
133
|
+
*/
|
|
134
|
+
async withdraw(params) {
|
|
135
|
+
validateRequired(params, [
|
|
136
|
+
"gdes",
|
|
137
|
+
"wallet",
|
|
138
|
+
"customerFirstName",
|
|
139
|
+
"customerLastName",
|
|
140
|
+
]);
|
|
141
|
+
validateAmount(params.gdes);
|
|
142
|
+
validateWallet(params.wallet);
|
|
143
|
+
|
|
144
|
+
return this.#client._request("POST", "/moncash/withdraw", params);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get account balance (MonCash).
|
|
149
|
+
*
|
|
150
|
+
* @returns {Promise<{ available: number, reserved: number, currency: string, environment: string, last_updated: string }>}
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* const balance = await bazik.payments.getBalance();
|
|
154
|
+
* console.log(`Available: ${balance.available} ${balance.currency}`);
|
|
155
|
+
*/
|
|
156
|
+
async getBalance() {
|
|
157
|
+
return this.#client._request("GET", "/balance");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = Payments;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const validateRequired = require("../helpers/validateRequired");
|
|
4
|
+
const validateAmount = require("../helpers/validateAmount");
|
|
5
|
+
const validateWallet = require("../helpers/validateWallet");
|
|
6
|
+
const BazikValidationError = require("../errors/BazikValidationError");
|
|
7
|
+
|
|
8
|
+
// ─── Transfers sub-module ────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
class Transfers {
|
|
11
|
+
#client;
|
|
12
|
+
|
|
13
|
+
constructor(client) {
|
|
14
|
+
this.#client = client;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check MonCash customer/wallet status before sending a transfer.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} wallet — MonCash phone number (8 digits)
|
|
21
|
+
* @returns {Promise<Object>} — Customer KYC level and status flags
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const status = await bazik.transfers.checkCustomer("37123456");
|
|
25
|
+
* console.log(status.customerStatus.type); // "fullkyc"
|
|
26
|
+
*/
|
|
27
|
+
async checkCustomer(wallet) {
|
|
28
|
+
validateWallet(wallet);
|
|
29
|
+
return this.#client._request("POST", "/moncash/customers/status", {
|
|
30
|
+
wallet,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a MonCash transfer (send money to a wallet).
|
|
36
|
+
*
|
|
37
|
+
* @param {Object} params
|
|
38
|
+
* @param {number} params.gdes — Amount in HTG
|
|
39
|
+
* @param {string} params.wallet — Recipient phone (8 digits)
|
|
40
|
+
* @param {string} params.customerFirstName
|
|
41
|
+
* @param {string} params.customerLastName
|
|
42
|
+
* @param {string} [params.description]
|
|
43
|
+
* @param {string} [params.referenceId]
|
|
44
|
+
* @param {string} [params.customerEmail]
|
|
45
|
+
* @param {string} [params.webhookUrl]
|
|
46
|
+
* @returns {Promise<Object>}
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const transfer = await bazik.transfers.moncash({
|
|
50
|
+
* gdes: 500,
|
|
51
|
+
* wallet: "47556677",
|
|
52
|
+
* customerFirstName: "Melissa",
|
|
53
|
+
* customerLastName: "Francois",
|
|
54
|
+
* description: "Pou ou peye lekol la",
|
|
55
|
+
* });
|
|
56
|
+
* console.log(transfer.transaction_id); // "TRF_..."
|
|
57
|
+
*/
|
|
58
|
+
async moncash(params) {
|
|
59
|
+
validateRequired(params, [
|
|
60
|
+
"gdes",
|
|
61
|
+
"wallet",
|
|
62
|
+
"customerFirstName",
|
|
63
|
+
"customerLastName",
|
|
64
|
+
]);
|
|
65
|
+
validateAmount(params.gdes);
|
|
66
|
+
validateWallet(params.wallet);
|
|
67
|
+
|
|
68
|
+
return this.#client._request("POST", "/moncash/transfers", params);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create a NatCash transfer.
|
|
73
|
+
*
|
|
74
|
+
* @param {Object} params
|
|
75
|
+
* @param {number} params.gdes — Amount in HTG
|
|
76
|
+
* @param {string} params.wallet — Recipient phone (8 digits)
|
|
77
|
+
* @param {string} params.customerFirstName
|
|
78
|
+
* @param {string} params.customerLastName
|
|
79
|
+
* @param {string} [params.description]
|
|
80
|
+
* @param {string} [params.referenceId]
|
|
81
|
+
* @param {string} [params.customerEmail]
|
|
82
|
+
* @param {string} [params.webhookUrl]
|
|
83
|
+
* @returns {Promise<Object>}
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* const transfer = await bazik.transfers.natcash({
|
|
87
|
+
* gdes: 50,
|
|
88
|
+
* wallet: "44556677",
|
|
89
|
+
* customerFirstName: "Marie",
|
|
90
|
+
* customerLastName: "Pierre",
|
|
91
|
+
* });
|
|
92
|
+
*/
|
|
93
|
+
async natcash(params) {
|
|
94
|
+
validateRequired(params, [
|
|
95
|
+
"gdes",
|
|
96
|
+
"wallet",
|
|
97
|
+
"customerFirstName",
|
|
98
|
+
"customerLastName",
|
|
99
|
+
]);
|
|
100
|
+
validateAmount(params.gdes);
|
|
101
|
+
validateWallet(params.wallet);
|
|
102
|
+
|
|
103
|
+
return this.#client._request("POST", "/natcash/transfers", params);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get transfer status by transaction ID.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} transactionId — e.g. "TRF_1761961466_eafd0ac3"
|
|
110
|
+
* @returns {Promise<Object>}
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* const status = await bazik.transfers.getStatus("TRF_1761961466_eafd0ac3");
|
|
114
|
+
* if (status.status === "successful") {
|
|
115
|
+
* console.log("Transfer completed!");
|
|
116
|
+
* }
|
|
117
|
+
*/
|
|
118
|
+
async getStatus(transactionId) {
|
|
119
|
+
if (!transactionId) {
|
|
120
|
+
throw new BazikValidationError("transactionId is required.");
|
|
121
|
+
}
|
|
122
|
+
return this.#client._request(
|
|
123
|
+
"GET",
|
|
124
|
+
`/transfers/${encodeURIComponent(transactionId)}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get a fee quote before creating a transfer.
|
|
130
|
+
*
|
|
131
|
+
* @param {number} amount — Delivery amount in HTG
|
|
132
|
+
* @param {"moncash"|"natcash"} provider
|
|
133
|
+
* @returns {Promise<{ delivery_amount: number, fee: number, total_cost: number, currency: string, provider: string, fee_percentage: number }>}
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* const quote = await bazik.transfers.getQuote(1000, "moncash");
|
|
137
|
+
* console.log(`Fee: ${quote.fee} HTG | Total: ${quote.total_cost} HTG`);
|
|
138
|
+
*/
|
|
139
|
+
async getQuote(amount, provider) {
|
|
140
|
+
validateAmount(amount);
|
|
141
|
+
if (!["moncash", "natcash"].includes(provider)) {
|
|
142
|
+
throw new BazikValidationError(
|
|
143
|
+
`Invalid provider "${provider}". Must be "moncash" or "natcash".`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return this.#client._request("POST", "/transfers/quote", {
|
|
147
|
+
amount,
|
|
148
|
+
provider,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = Transfers;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// ─── Wallet sub-module ───────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
class Wallet {
|
|
6
|
+
#client;
|
|
7
|
+
|
|
8
|
+
constructor(client) {
|
|
9
|
+
this.#client = client;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get wallet balance.
|
|
14
|
+
*
|
|
15
|
+
* @returns {Promise<{ available: number, reserved: number, currency: string, environment: string, last_updated: string }>}
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const wallet = await bazik.wallet.getBalance();
|
|
19
|
+
* console.log(`Available: ${wallet.available} HTG`);
|
|
20
|
+
*/
|
|
21
|
+
async getBalance() {
|
|
22
|
+
return this.#client._request("GET", "/wallet");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = Wallet;
|