bazik-sdk 1.0.1
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 +3 -0
- package/LICENSE +21 -0
- package/README.md +237 -0
- package/package.json +73 -0
- package/src/index.d.ts +266 -0
- package/src/index.js +692 -0
- package/src/index.mjs +16 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Bazik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="logo.svg" alt="Bazik SDK" width="320" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h3 align="center">Unofficial JavaScript SDK for the Bazik API</h3>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
MonCash & NatCash payments, transfers, and wallet management for Haiti.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://bazik.io/docs/endpoints">Documentation</a> ·
|
|
13
|
+
<a href="#quick-start">Quick Start</a> ·
|
|
14
|
+
<a href="#api-reference">API Reference</a> ·
|
|
15
|
+
<a href="https://github.com/bazik-io/bazik-sdk-js/issues">Report Bug</a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Zero dependencies** — Uses native `fetch` (Node.js 18+)
|
|
23
|
+
- **ESM & CommonJS** — Works with `import` and `require` out of the box
|
|
24
|
+
- **Automatic token management** — Handles auth token lifecycle, refresh, and retry
|
|
25
|
+
- **Full TypeScript support** — Complete `.d.ts` type definitions included
|
|
26
|
+
- **Input validation** — Catches errors before they hit the API
|
|
27
|
+
- **Structured errors** — Typed error classes for every failure mode
|
|
28
|
+
- **MonCash payments** — Create, verify, and poll payment status
|
|
29
|
+
- **MonCash & NatCash transfers** — Send money to wallets directly
|
|
30
|
+
- **Wallet management** — Check balance, get fee quotes
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install bazik-sdk
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
// ESM
|
|
42
|
+
import { Bazik } from "bazik-sdk";
|
|
43
|
+
|
|
44
|
+
// CommonJS
|
|
45
|
+
const { Bazik } = require("bazik-sdk");
|
|
46
|
+
|
|
47
|
+
const bazik = new Bazik({
|
|
48
|
+
userID: "bzk_c5b754a0_1757383229",
|
|
49
|
+
secretKey: "sk_5b0ff521b331c73db55313dc82f17cab",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Authentication happens automatically on first API call.
|
|
53
|
+
// You can also authenticate explicitly:
|
|
54
|
+
await bazik.authenticate();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Accept a Payment
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// 1. Create the payment
|
|
61
|
+
const payment = await bazik.payments.create({
|
|
62
|
+
gdes: 1284.0,
|
|
63
|
+
successUrl: "https://mysite.com/success",
|
|
64
|
+
errorUrl: "https://mysite.com/error",
|
|
65
|
+
description: "iPhone Pro Max",
|
|
66
|
+
referenceId: "ORDER-001",
|
|
67
|
+
customerFirstName: "Franck",
|
|
68
|
+
customerLastName: "Jean",
|
|
69
|
+
customerEmail: "franck@example.com",
|
|
70
|
+
webhookUrl: "https://mysite.com/webhook",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// 2. Redirect customer to MonCash payment page
|
|
74
|
+
console.log("Redirect →", payment.redirectUrl);
|
|
75
|
+
|
|
76
|
+
// 3. Verify the payment (after redirect or via webhook)
|
|
77
|
+
const status = await bazik.payments.verify(payment.orderId);
|
|
78
|
+
console.log("Status:", status.status); // "successful" | "pending" | "failed"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Send Money (Transfer)
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// Check recipient wallet first
|
|
85
|
+
const customer = await bazik.transfers.checkCustomer("37123456");
|
|
86
|
+
console.log("KYC:", customer.customerStatus.type);
|
|
87
|
+
|
|
88
|
+
// Get a fee quote
|
|
89
|
+
const quote = await bazik.transfers.getQuote(500, "moncash");
|
|
90
|
+
console.log(`Fee: ${quote.fee} HTG | Total: ${quote.total_cost} HTG`);
|
|
91
|
+
|
|
92
|
+
// Send via MonCash
|
|
93
|
+
const transfer = await bazik.transfers.moncash({
|
|
94
|
+
gdes: 500,
|
|
95
|
+
wallet: "47556677",
|
|
96
|
+
customerFirstName: "Melissa",
|
|
97
|
+
customerLastName: "Francois",
|
|
98
|
+
description: "Salary payment",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
console.log("Transaction:", transfer.transaction_id);
|
|
102
|
+
|
|
103
|
+
// Or send via NatCash
|
|
104
|
+
const natTransfer = await bazik.transfers.natcash({
|
|
105
|
+
gdes: 50,
|
|
106
|
+
wallet: "44556677",
|
|
107
|
+
customerFirstName: "Marie",
|
|
108
|
+
customerLastName: "Pierre",
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Check Balance
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
const wallet = await bazik.wallet.getBalance();
|
|
116
|
+
console.log(`Available: ${wallet.available} ${wallet.currency}`);
|
|
117
|
+
console.log(`Reserved: ${wallet.reserved} ${wallet.currency}`);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## API Reference
|
|
121
|
+
|
|
122
|
+
### `new Bazik(config)`
|
|
123
|
+
|
|
124
|
+
| Parameter | Type | Default | Description |
|
|
125
|
+
|-----------|------|---------|-------------|
|
|
126
|
+
| `userID` | `string` | *required* | Your Bazik user ID |
|
|
127
|
+
| `secretKey` | `string` | *required* | Your secret key |
|
|
128
|
+
| `baseURL` | `string` | `https://api.bazik.io` | API base URL |
|
|
129
|
+
| `autoRefresh` | `boolean` | `true` | Auto-refresh token before expiry |
|
|
130
|
+
| `timeout` | `number` | `30000` | Request timeout (ms) |
|
|
131
|
+
| `onTokenRefresh` | `function` | — | Callback when token refreshes |
|
|
132
|
+
|
|
133
|
+
### `bazik.payments`
|
|
134
|
+
|
|
135
|
+
| Method | Description |
|
|
136
|
+
|--------|-------------|
|
|
137
|
+
| `.create(params)` | Create a MonCash payment (max 75,000 HTG) |
|
|
138
|
+
| `.verify(orderId)` | Get payment status by order ID |
|
|
139
|
+
| `.waitForCompletion(orderId, opts?)` | Poll until payment resolves |
|
|
140
|
+
| `.withdraw(params)` | Send money to a MonCash wallet |
|
|
141
|
+
| `.getBalance()` | Get account balance |
|
|
142
|
+
|
|
143
|
+
### `bazik.transfers`
|
|
144
|
+
|
|
145
|
+
| Method | Description |
|
|
146
|
+
|--------|-------------|
|
|
147
|
+
| `.checkCustomer(wallet)` | Check MonCash wallet KYC status |
|
|
148
|
+
| `.moncash(params)` | Create a MonCash transfer |
|
|
149
|
+
| `.natcash(params)` | Create a NatCash transfer |
|
|
150
|
+
| `.getStatus(transactionId)` | Get transfer status |
|
|
151
|
+
| `.getQuote(amount, provider)` | Get fee quote before transfer |
|
|
152
|
+
|
|
153
|
+
### `bazik.wallet`
|
|
154
|
+
|
|
155
|
+
| Method | Description |
|
|
156
|
+
|--------|-------------|
|
|
157
|
+
| `.getBalance()` | Get wallet balance (available + reserved) |
|
|
158
|
+
|
|
159
|
+
## Error Handling
|
|
160
|
+
|
|
161
|
+
The SDK provides typed error classes for precise error handling:
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
import {
|
|
165
|
+
Bazik,
|
|
166
|
+
BazikError, // Base error
|
|
167
|
+
BazikAuthError, // 401 — Invalid credentials
|
|
168
|
+
BazikValidationError, // 400 — Invalid input
|
|
169
|
+
BazikInsufficientFundsError, // 402 — Not enough balance
|
|
170
|
+
BazikRateLimitError, // 429 — Too many requests
|
|
171
|
+
} from "bazik-sdk";
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
await bazik.payments.create({ gdes: 500 });
|
|
175
|
+
} catch (err) {
|
|
176
|
+
if (err instanceof BazikInsufficientFundsError) {
|
|
177
|
+
console.error("Top up your account!");
|
|
178
|
+
} else if (err instanceof BazikAuthError) {
|
|
179
|
+
console.error("Check your credentials.");
|
|
180
|
+
} else if (err instanceof BazikValidationError) {
|
|
181
|
+
console.error("Invalid input:", err.details);
|
|
182
|
+
} else if (err instanceof BazikRateLimitError) {
|
|
183
|
+
console.error("Slow down — retry later.");
|
|
184
|
+
} else {
|
|
185
|
+
console.error("Unexpected:", err.message);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Every error has these properties:
|
|
191
|
+
|
|
192
|
+
| Property | Type | Description |
|
|
193
|
+
|----------|------|-------------|
|
|
194
|
+
| `message` | `string` | Human-readable description |
|
|
195
|
+
| `status` | `number \| null` | HTTP status code |
|
|
196
|
+
| `code` | `string \| null` | Machine-readable error code |
|
|
197
|
+
| `details` | `any` | Additional context |
|
|
198
|
+
|
|
199
|
+
## Polling Payment Status
|
|
200
|
+
|
|
201
|
+
Instead of using webhooks, you can poll for payment completion:
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
try {
|
|
205
|
+
const result = await bazik.payments.waitForCompletion(payment.orderId, {
|
|
206
|
+
intervalMs: 5000, // check every 5s
|
|
207
|
+
timeoutMs: 120000, // give up after 2min
|
|
208
|
+
});
|
|
209
|
+
console.log("Final status:", result.status);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
if (err.code === "timeout") {
|
|
212
|
+
console.log("Customer hasn't completed payment yet.");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Testing
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
node --test tests/
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
No external test framework required — uses Node.js built-in test runner.
|
|
224
|
+
|
|
225
|
+
## Requirements
|
|
226
|
+
|
|
227
|
+
- Node.js >= 18.0.0 (for native `fetch`)
|
|
228
|
+
|
|
229
|
+
## Links
|
|
230
|
+
|
|
231
|
+
- [Bazik Documentation](https://bazik.io/docs/endpoints)
|
|
232
|
+
- [Bazik Dashboard](https://bazik.io)
|
|
233
|
+
- [API Status](https://bazik.io)
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
MIT — see [LICENSE](LICENSE) for details.
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bazik-sdk",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Unofficial JavaScript/Node.js SDK for the Bazik API — MonCash & NatCash payments, transfers, and wallet management for Haiti.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"module": "src/index.mjs",
|
|
7
|
+
"types": "src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./src/index.d.ts",
|
|
12
|
+
"default": "./src/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./src/index.d.ts",
|
|
16
|
+
"default": "./src/index.js"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "node --test tests/bazik.test.js",
|
|
22
|
+
"test:verbose": "node --test --test-reporter spec tests/bazik.test.js",
|
|
23
|
+
"lint": "echo 'No linter configured'",
|
|
24
|
+
"example": "node examples/quick-start.js",
|
|
25
|
+
"release": "release-it"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"bazik",
|
|
29
|
+
"moncash",
|
|
30
|
+
"natcash",
|
|
31
|
+
"haiti",
|
|
32
|
+
"payment",
|
|
33
|
+
"gateway",
|
|
34
|
+
"api",
|
|
35
|
+
"fintech",
|
|
36
|
+
"mobile-money",
|
|
37
|
+
"gourdes",
|
|
38
|
+
"htg",
|
|
39
|
+
"digicel",
|
|
40
|
+
"transfer",
|
|
41
|
+
"wallet"
|
|
42
|
+
],
|
|
43
|
+
"author": "Bazik <support@bazik.io>",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"homepage": "https://bazik.io",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/josephjoberno/basik-sdk"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/josephjoberno/basik-sdk/issues"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"provenance": true
|
|
59
|
+
},
|
|
60
|
+
"files": [
|
|
61
|
+
"src/",
|
|
62
|
+
"README.md",
|
|
63
|
+
"LICENSE",
|
|
64
|
+
"CHANGELOG.md"
|
|
65
|
+
],
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@commitlint/cli": "^20.4.2",
|
|
68
|
+
"@commitlint/config-conventional": "^20.4.2",
|
|
69
|
+
"@release-it/conventional-changelog": "^10.0.5",
|
|
70
|
+
"husky": "^9.1.7",
|
|
71
|
+
"release-it": "^19.2.4"
|
|
72
|
+
}
|
|
73
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bazik SDK — TypeScript Definitions
|
|
3
|
+
* @see https://bazik.io/docs/endpoints
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ─── Configuration ───────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export interface BazikConfig {
|
|
9
|
+
/** Your Bazik user ID (e.g. "bzk_c5b754a0_1757383229") */
|
|
10
|
+
userID: string;
|
|
11
|
+
/** Your secret key */
|
|
12
|
+
secretKey: string;
|
|
13
|
+
/** API base URL (default: "https://api.bazik.io") */
|
|
14
|
+
baseURL?: string;
|
|
15
|
+
/** Automatically refresh token before expiry (default: true) */
|
|
16
|
+
autoRefresh?: boolean;
|
|
17
|
+
/** Request timeout in ms (default: 30000) */
|
|
18
|
+
timeout?: number;
|
|
19
|
+
/** Callback fired when token is refreshed */
|
|
20
|
+
onTokenRefresh?: (token: string) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ─── Auth ────────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export interface AuthResponse {
|
|
26
|
+
success: boolean;
|
|
27
|
+
token: string;
|
|
28
|
+
user_id: string;
|
|
29
|
+
expires_at: number;
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ─── Payments ────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
export interface CreatePaymentParams {
|
|
36
|
+
/** Amount in Gourdes (max 75,000) */
|
|
37
|
+
gdes: number;
|
|
38
|
+
/** User identifier */
|
|
39
|
+
userID?: string;
|
|
40
|
+
successUrl?: string;
|
|
41
|
+
errorUrl?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
referenceId?: string;
|
|
44
|
+
customerFirstName?: string;
|
|
45
|
+
customerLastName?: string;
|
|
46
|
+
customerEmail?: string;
|
|
47
|
+
webhookUrl?: string;
|
|
48
|
+
metadata?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface PaymentResponse {
|
|
52
|
+
orderId: string;
|
|
53
|
+
redirectUrl: string;
|
|
54
|
+
status: "pending" | "successful" | "failed" | "cancelled";
|
|
55
|
+
gourdes: number;
|
|
56
|
+
referenceId: string;
|
|
57
|
+
environment: "sandbox" | "production";
|
|
58
|
+
sender: string;
|
|
59
|
+
receiver: string;
|
|
60
|
+
customerFullName: string;
|
|
61
|
+
successUrl: string;
|
|
62
|
+
errorUrl: string;
|
|
63
|
+
userID: string;
|
|
64
|
+
transactionType: string;
|
|
65
|
+
metadata: Record<string, unknown>;
|
|
66
|
+
payment: {
|
|
67
|
+
mode: string;
|
|
68
|
+
path: string;
|
|
69
|
+
payment_token: { expired: string; created: string; token: string };
|
|
70
|
+
timestamp: number;
|
|
71
|
+
status: number;
|
|
72
|
+
httpStatusCode: number;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface PaymentVerification {
|
|
77
|
+
orderId: string;
|
|
78
|
+
referenceId: string;
|
|
79
|
+
status: "pending" | "successful" | "failed" | "cancelled";
|
|
80
|
+
amount: number;
|
|
81
|
+
currency: string;
|
|
82
|
+
createdAt: string;
|
|
83
|
+
updatedAt: string;
|
|
84
|
+
metadata: {
|
|
85
|
+
description?: string;
|
|
86
|
+
customerEmail?: string;
|
|
87
|
+
customerName?: string;
|
|
88
|
+
[key: string]: unknown;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface WithdrawParams {
|
|
93
|
+
/** Amount in HTG */
|
|
94
|
+
gdes: number;
|
|
95
|
+
/** Recipient phone (8 or 11 digits) */
|
|
96
|
+
wallet: string;
|
|
97
|
+
customerFirstName: string;
|
|
98
|
+
customerLastName: string;
|
|
99
|
+
description?: string;
|
|
100
|
+
referenceId?: string;
|
|
101
|
+
customerEmail?: string;
|
|
102
|
+
webhookUrl?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface WithdrawResponse {
|
|
106
|
+
transaction_id: string;
|
|
107
|
+
status: "pending" | "completed" | "failed";
|
|
108
|
+
provider: "moncash";
|
|
109
|
+
amount: number;
|
|
110
|
+
fees: number;
|
|
111
|
+
total: number;
|
|
112
|
+
currency: string;
|
|
113
|
+
wallet: string;
|
|
114
|
+
recipient: { first_name: string; last_name: string };
|
|
115
|
+
description: string;
|
|
116
|
+
referenceId: string;
|
|
117
|
+
customerEmail: string;
|
|
118
|
+
webhookUrl: string;
|
|
119
|
+
created_at: string;
|
|
120
|
+
environment: "sandbox" | "production";
|
|
121
|
+
message: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface BalanceResponse {
|
|
125
|
+
available: number;
|
|
126
|
+
reserved: number;
|
|
127
|
+
currency: string;
|
|
128
|
+
environment: "sandbox" | "production";
|
|
129
|
+
last_updated: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface WaitOptions {
|
|
133
|
+
/** Polling interval in ms (default: 5000) */
|
|
134
|
+
intervalMs?: number;
|
|
135
|
+
/** Max wait time in ms (default: 300000) */
|
|
136
|
+
timeoutMs?: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ─── Transfers ───────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
export interface TransferParams {
|
|
142
|
+
gdes: number;
|
|
143
|
+
wallet: string;
|
|
144
|
+
customerFirstName: string;
|
|
145
|
+
customerLastName: string;
|
|
146
|
+
description?: string;
|
|
147
|
+
referenceId?: string;
|
|
148
|
+
customerEmail?: string;
|
|
149
|
+
webhookUrl?: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface TransferResponse {
|
|
153
|
+
transaction_id: string;
|
|
154
|
+
status: "pending" | "completed" | "failed";
|
|
155
|
+
provider: "moncash" | "natcash";
|
|
156
|
+
amount: number;
|
|
157
|
+
fees: number;
|
|
158
|
+
total: number;
|
|
159
|
+
currency: string;
|
|
160
|
+
wallet: string;
|
|
161
|
+
recipient: { first_name: string; last_name: string };
|
|
162
|
+
description: string;
|
|
163
|
+
referenceId: string;
|
|
164
|
+
customerEmail: string;
|
|
165
|
+
webhookUrl: string;
|
|
166
|
+
created_at: string;
|
|
167
|
+
environment: "sandbox" | "production";
|
|
168
|
+
message: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface TransferStatusResponse {
|
|
172
|
+
type: "transfer.succeeded" | "transfer.failed";
|
|
173
|
+
transactionId: string;
|
|
174
|
+
status: "successful" | "processing" | "failed" | "cancelled";
|
|
175
|
+
amount: number;
|
|
176
|
+
fees: number;
|
|
177
|
+
total: number;
|
|
178
|
+
currency: string;
|
|
179
|
+
wallet: string;
|
|
180
|
+
description: string;
|
|
181
|
+
recipient: { firstName: string; lastName: string };
|
|
182
|
+
referenceId: string;
|
|
183
|
+
failureReason: string | null;
|
|
184
|
+
timestamp: string;
|
|
185
|
+
provider: "moncash" | "natcash";
|
|
186
|
+
environment: "sandbox" | "production";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface CustomerStatusResponse {
|
|
190
|
+
customerStatus: {
|
|
191
|
+
type: string;
|
|
192
|
+
status: string[];
|
|
193
|
+
};
|
|
194
|
+
timestamp: number;
|
|
195
|
+
status: number;
|
|
196
|
+
environment: "sandbox" | "production";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface QuoteResponse {
|
|
200
|
+
delivery_amount: number;
|
|
201
|
+
fee: number;
|
|
202
|
+
total_cost: number;
|
|
203
|
+
currency: string;
|
|
204
|
+
provider: "moncash" | "natcash";
|
|
205
|
+
fee_percentage: number;
|
|
206
|
+
timestamp: string;
|
|
207
|
+
environment: "sandbox" | "production";
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─── Sub-modules ─────────────────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
export declare class Payments {
|
|
213
|
+
create(params: CreatePaymentParams): Promise<PaymentResponse>;
|
|
214
|
+
verify(orderId: string): Promise<PaymentVerification>;
|
|
215
|
+
waitForCompletion(orderId: string, options?: WaitOptions): Promise<PaymentVerification>;
|
|
216
|
+
withdraw(params: WithdrawParams): Promise<WithdrawResponse>;
|
|
217
|
+
getBalance(): Promise<BalanceResponse>;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export declare class Transfers {
|
|
221
|
+
checkCustomer(wallet: string): Promise<CustomerStatusResponse>;
|
|
222
|
+
moncash(params: TransferParams): Promise<TransferResponse>;
|
|
223
|
+
natcash(params: TransferParams): Promise<TransferResponse>;
|
|
224
|
+
getStatus(transactionId: string): Promise<TransferStatusResponse>;
|
|
225
|
+
getQuote(amount: number, provider: "moncash" | "natcash"): Promise<QuoteResponse>;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export declare class Wallet {
|
|
229
|
+
getBalance(): Promise<BalanceResponse>;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ─── Main Client ─────────────────────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
export declare class Bazik {
|
|
235
|
+
constructor(config: BazikConfig);
|
|
236
|
+
|
|
237
|
+
/** MonCash payment operations */
|
|
238
|
+
readonly payments: Payments;
|
|
239
|
+
/** MonCash & NatCash transfer operations */
|
|
240
|
+
readonly transfers: Transfers;
|
|
241
|
+
/** Wallet balance operations */
|
|
242
|
+
readonly wallet: Wallet;
|
|
243
|
+
|
|
244
|
+
/** Authenticate and obtain an access token */
|
|
245
|
+
authenticate(): Promise<AuthResponse>;
|
|
246
|
+
/** Check if current token is still valid */
|
|
247
|
+
isTokenValid(): boolean;
|
|
248
|
+
/** Get a valid token (auto-refreshes if needed) */
|
|
249
|
+
getToken(): Promise<string>;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ─── Errors ──────────────────────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
export declare class BazikError extends Error {
|
|
255
|
+
status: number | null;
|
|
256
|
+
code: string | null;
|
|
257
|
+
details: unknown;
|
|
258
|
+
constructor(message: string, status?: number, code?: string, details?: unknown);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export declare class BazikAuthError extends BazikError {}
|
|
262
|
+
export declare class BazikValidationError extends BazikError {}
|
|
263
|
+
export declare class BazikInsufficientFundsError extends BazikError {}
|
|
264
|
+
export declare class BazikRateLimitError extends BazikError {}
|
|
265
|
+
|
|
266
|
+
export default Bazik;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bazik SDK for JavaScript/Node.js
|
|
3
|
+
* Unofficial SDK for the Bazik API — MonCash & NatCash payments, transfers, and wallet management.
|
|
4
|
+
*
|
|
5
|
+
* @see https://bazik.io/docs/endpoints
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
const DEFAULT_BASE_URL = "https://api.bazik.io";
|
|
15
|
+
const TOKEN_REFRESH_MARGIN_MS = 60 * 60 * 1000; // 1 hour before expiry
|
|
16
|
+
const MAX_MONCASH_AMOUNT = 75_000;
|
|
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
|
+
}
|
|
682
|
+
|
|
683
|
+
// ─── Exports ─────────────────────────────────────────────────────────────────
|
|
684
|
+
|
|
685
|
+
module.exports = {
|
|
686
|
+
Bazik,
|
|
687
|
+
BazikError,
|
|
688
|
+
BazikAuthError,
|
|
689
|
+
BazikValidationError,
|
|
690
|
+
BazikInsufficientFundsError,
|
|
691
|
+
BazikRateLimitError,
|
|
692
|
+
};
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bazik SDK — ESM entry point
|
|
3
|
+
* Re-exports all public APIs from the CommonJS source.
|
|
4
|
+
*/
|
|
5
|
+
import pkg from "./index.js";
|
|
6
|
+
|
|
7
|
+
export const {
|
|
8
|
+
Bazik,
|
|
9
|
+
BazikError,
|
|
10
|
+
BazikAuthError,
|
|
11
|
+
BazikValidationError,
|
|
12
|
+
BazikInsufficientFundsError,
|
|
13
|
+
BazikRateLimitError,
|
|
14
|
+
} = pkg;
|
|
15
|
+
|
|
16
|
+
export default Bazik;
|