@syscli/oneclickdz 0.1.0
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/LICENSE +21 -0
- package/README.md +298 -0
- package/dist/index.cjs +934 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +730 -0
- package/dist/index.d.ts +730 -0
- package/dist/index.js +890 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,934 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var OneClickError = class extends Error {
|
|
5
|
+
/** HTTP status, or undefined for a client-side or network error. */
|
|
6
|
+
status;
|
|
7
|
+
code;
|
|
8
|
+
details;
|
|
9
|
+
requestId;
|
|
10
|
+
body;
|
|
11
|
+
constructor(message, code, options = {}) {
|
|
12
|
+
super(
|
|
13
|
+
message,
|
|
14
|
+
options.cause !== void 0 ? { cause: options.cause } : void 0
|
|
15
|
+
);
|
|
16
|
+
this.name = new.target.name;
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.status = options.status;
|
|
19
|
+
this.details = options.details;
|
|
20
|
+
this.requestId = options.requestId;
|
|
21
|
+
this.body = options.body;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var AuthError = class extends OneClickError {
|
|
25
|
+
};
|
|
26
|
+
var ForbiddenError = class extends OneClickError {
|
|
27
|
+
};
|
|
28
|
+
var InsufficientBalanceError = class extends ForbiddenError {
|
|
29
|
+
};
|
|
30
|
+
var DuplicateRefError = class extends ForbiddenError {
|
|
31
|
+
};
|
|
32
|
+
var ValidationError = class extends OneClickError {
|
|
33
|
+
issues;
|
|
34
|
+
constructor(message, options = {}) {
|
|
35
|
+
super(message, options.code ?? "ERR_VALIDATION", options);
|
|
36
|
+
this.issues = options.issues ?? [];
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var NotFoundError = class extends OneClickError {
|
|
40
|
+
};
|
|
41
|
+
var RateLimitError = class extends OneClickError {
|
|
42
|
+
retryAfter;
|
|
43
|
+
constructor(message, options = {}) {
|
|
44
|
+
super(message, "RATE_LIMIT_EXCEEDED", options);
|
|
45
|
+
this.retryAfter = options.retryAfter;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var ServiceError = class extends OneClickError {
|
|
49
|
+
};
|
|
50
|
+
var NetworkError = class extends OneClickError {
|
|
51
|
+
constructor(message, options = {}) {
|
|
52
|
+
super(message, "network", options);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var RequestError = class extends OneClickError {
|
|
56
|
+
};
|
|
57
|
+
function errorFromResponse(status, body, retryAfter) {
|
|
58
|
+
const apiError = readApiError(body);
|
|
59
|
+
const code = apiError.code;
|
|
60
|
+
const message = apiError.message ?? `Request failed with status ${status}.`;
|
|
61
|
+
const options = {
|
|
62
|
+
status,
|
|
63
|
+
body,
|
|
64
|
+
requestId: readRequestId(body),
|
|
65
|
+
details: apiError.details
|
|
66
|
+
};
|
|
67
|
+
switch (code) {
|
|
68
|
+
case "MISSING_ACCESS_TOKEN":
|
|
69
|
+
case "INVALID_ACCESS_TOKEN":
|
|
70
|
+
case "ERR_AUTH":
|
|
71
|
+
return new AuthError(message, code, options);
|
|
72
|
+
case "NO_BALANCE":
|
|
73
|
+
case "INSUFFICIENT_BALANCE":
|
|
74
|
+
return new InsufficientBalanceError(message, code, options);
|
|
75
|
+
case "DUPLICATED_REF":
|
|
76
|
+
return new DuplicateRefError(message, code, options);
|
|
77
|
+
case "IP_BLOCKED":
|
|
78
|
+
case "IP_NOT_ALLOWED":
|
|
79
|
+
return new ForbiddenError(message, code, options);
|
|
80
|
+
case "ERR_VALIDATION":
|
|
81
|
+
case "ERR_PHONE":
|
|
82
|
+
case "ERR_STOCK":
|
|
83
|
+
return new ValidationError(message, { ...options, code });
|
|
84
|
+
case "NOT_FOUND":
|
|
85
|
+
return new NotFoundError(message, code, options);
|
|
86
|
+
case "RATE_LIMIT_EXCEEDED":
|
|
87
|
+
return new RateLimitError(message, { ...options, retryAfter });
|
|
88
|
+
case "INTERNAL_SERVER_ERROR":
|
|
89
|
+
case "INTERNAL_ERROR":
|
|
90
|
+
case "ERR_SERVICE":
|
|
91
|
+
return new ServiceError(message, code, options);
|
|
92
|
+
}
|
|
93
|
+
return errorFromStatus(status, message, options, code, retryAfter);
|
|
94
|
+
}
|
|
95
|
+
function errorFromStatus(status, message, options, code, retryAfter) {
|
|
96
|
+
if (status === 401) return new AuthError(message, code ?? "ERR_AUTH", options);
|
|
97
|
+
if (status === 403)
|
|
98
|
+
return new ForbiddenError(message, code ?? "ERR_AUTH", options);
|
|
99
|
+
if (status === 404)
|
|
100
|
+
return new NotFoundError(message, code ?? "NOT_FOUND", options);
|
|
101
|
+
if (status === 429)
|
|
102
|
+
return new RateLimitError(message, { ...options, retryAfter });
|
|
103
|
+
if (status >= 500)
|
|
104
|
+
return new ServiceError(message, code ?? "INTERNAL_SERVER_ERROR", options);
|
|
105
|
+
return new RequestError(message, code ?? "ERR_VALIDATION", options);
|
|
106
|
+
}
|
|
107
|
+
function readApiError(body) {
|
|
108
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
109
|
+
const inner = body.error;
|
|
110
|
+
if (inner && typeof inner === "object") {
|
|
111
|
+
const e = inner;
|
|
112
|
+
return {
|
|
113
|
+
code: typeof e.code === "string" ? e.code : void 0,
|
|
114
|
+
message: typeof e.message === "string" ? e.message : void 0,
|
|
115
|
+
details: e.details
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
function readRequestId(body) {
|
|
122
|
+
if (body && typeof body === "object" && "requestId" in body) {
|
|
123
|
+
const id = body.requestId;
|
|
124
|
+
if (typeof id === "string") return id;
|
|
125
|
+
}
|
|
126
|
+
return void 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/http.ts
|
|
130
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
131
|
+
function encodeQuery(params) {
|
|
132
|
+
if (!params) return "";
|
|
133
|
+
const parts = [];
|
|
134
|
+
for (const [key, value] of Object.entries(params)) {
|
|
135
|
+
if (value === void 0 || value === null) continue;
|
|
136
|
+
parts.push(
|
|
137
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return parts.length ? `?${parts.join("&")}` : "";
|
|
141
|
+
}
|
|
142
|
+
function buildHeaders(config, hasBody) {
|
|
143
|
+
const headers = {
|
|
144
|
+
"X-Access-Token": config.key,
|
|
145
|
+
Accept: "application/json"
|
|
146
|
+
};
|
|
147
|
+
if (hasBody) headers["Content-Type"] = "application/json";
|
|
148
|
+
return headers;
|
|
149
|
+
}
|
|
150
|
+
function parseRetryAfter(value) {
|
|
151
|
+
if (!value) return void 0;
|
|
152
|
+
const seconds = Number(value);
|
|
153
|
+
if (Number.isFinite(seconds)) return Math.max(0, seconds);
|
|
154
|
+
const at = Date.parse(value);
|
|
155
|
+
if (Number.isNaN(at)) return void 0;
|
|
156
|
+
return Math.max(0, Math.round((at - Date.now()) / 1e3));
|
|
157
|
+
}
|
|
158
|
+
var HttpClient = class {
|
|
159
|
+
config;
|
|
160
|
+
constructor(config) {
|
|
161
|
+
this.config = {
|
|
162
|
+
...config,
|
|
163
|
+
baseUrl: config.baseUrl.endsWith("/") ? config.baseUrl : `${config.baseUrl}/`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Send a request and return the unwrapped envelope.
|
|
168
|
+
*
|
|
169
|
+
* Retry policy is deliberately narrow. A 429 is always safe to retry, since
|
|
170
|
+
* the request was rejected before processing. A 5xx or a dropped connection is
|
|
171
|
+
* retried only for GET: a send that timed out may still have gone through, and
|
|
172
|
+
* a blind retry could charge the account twice. Use a `ref` for safe resends.
|
|
173
|
+
*/
|
|
174
|
+
async request(method, path, options = {}) {
|
|
175
|
+
const maxAttempts = Math.max(1, this.config.retry.attempts);
|
|
176
|
+
const idempotent = method === "GET";
|
|
177
|
+
for (let attempt = 1; ; attempt++) {
|
|
178
|
+
try {
|
|
179
|
+
return await this.send(method, path, options);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (attempt >= maxAttempts || !this.retryable(error, idempotent)) {
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
await sleep(this.backoffMs(error, attempt));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
retryable(error, idempotent) {
|
|
189
|
+
if (error instanceof RateLimitError) return true;
|
|
190
|
+
if (error instanceof ServiceError) return idempotent;
|
|
191
|
+
if (error instanceof NetworkError) return idempotent;
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
/** Wait the API's Retry-After when offered, otherwise an exponential backoff. */
|
|
195
|
+
backoffMs(error, attempt) {
|
|
196
|
+
if (error instanceof RateLimitError && this.config.retry.respectRetryAfter && error.retryAfter !== void 0) {
|
|
197
|
+
return error.retryAfter * 1e3;
|
|
198
|
+
}
|
|
199
|
+
return Math.min(2 ** (attempt - 1) * 500, 3e4);
|
|
200
|
+
}
|
|
201
|
+
async send(method, path, options) {
|
|
202
|
+
const hasBody = options.body !== void 0;
|
|
203
|
+
const url = new URL(path, this.config.baseUrl);
|
|
204
|
+
const target = `${url.toString()}${encodeQuery(options.query)}`;
|
|
205
|
+
const headers = buildHeaders(this.config, hasBody);
|
|
206
|
+
const controller = new AbortController();
|
|
207
|
+
const timer = this.config.timeoutMs > 0 ? setTimeout(() => controller.abort(), this.config.timeoutMs) : void 0;
|
|
208
|
+
if (options.signal) {
|
|
209
|
+
if (options.signal.aborted) controller.abort();
|
|
210
|
+
else
|
|
211
|
+
options.signal.addEventListener("abort", () => controller.abort(), {
|
|
212
|
+
once: true
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
let response;
|
|
216
|
+
try {
|
|
217
|
+
response = await this.config.fetch(target, {
|
|
218
|
+
method,
|
|
219
|
+
headers,
|
|
220
|
+
body: hasBody ? JSON.stringify(options.body) : void 0,
|
|
221
|
+
signal: controller.signal
|
|
222
|
+
});
|
|
223
|
+
} catch (cause) {
|
|
224
|
+
if (options.signal?.aborted) throw cause;
|
|
225
|
+
const reason = cause instanceof Error ? cause.message : "request failed";
|
|
226
|
+
throw new NetworkError(`Could not reach the API: ${reason}.`, { cause });
|
|
227
|
+
} finally {
|
|
228
|
+
if (timer) clearTimeout(timer);
|
|
229
|
+
}
|
|
230
|
+
const text = await response.text();
|
|
231
|
+
let body;
|
|
232
|
+
try {
|
|
233
|
+
body = text ? JSON.parse(text) : void 0;
|
|
234
|
+
} catch {
|
|
235
|
+
body = text;
|
|
236
|
+
}
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
239
|
+
throw errorFromResponse(response.status, body, retryAfter);
|
|
240
|
+
}
|
|
241
|
+
return this.toEnvelope(body, response.status);
|
|
242
|
+
}
|
|
243
|
+
/** Confirm the body is a success envelope before handing it back. */
|
|
244
|
+
toEnvelope(body, status) {
|
|
245
|
+
if (body && typeof body === "object" && "success" in body) {
|
|
246
|
+
if (body.success === false) {
|
|
247
|
+
throw errorFromResponse(status, body);
|
|
248
|
+
}
|
|
249
|
+
return body;
|
|
250
|
+
}
|
|
251
|
+
throw new OneClickError(
|
|
252
|
+
"The API returned a response in an unexpected shape.",
|
|
253
|
+
"ERR_SERVICE",
|
|
254
|
+
{ status, body }
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// src/resources/base.ts
|
|
260
|
+
var Resource = class {
|
|
261
|
+
constructor(http) {
|
|
262
|
+
this.http = http;
|
|
263
|
+
}
|
|
264
|
+
http;
|
|
265
|
+
/** GET a path and return its `data` payload. */
|
|
266
|
+
async getData(path, query, signal) {
|
|
267
|
+
const env = await this.http.request("GET", path, { query, signal });
|
|
268
|
+
return env.data;
|
|
269
|
+
}
|
|
270
|
+
/** POST a body and return the `data` payload. */
|
|
271
|
+
async postData(path, body, signal) {
|
|
272
|
+
const env = await this.http.request("POST", path, { body, signal });
|
|
273
|
+
return env.data;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Fetch one page of a list. `extract` pulls the items and the pagination block
|
|
277
|
+
* out of whatever shape the endpoint nests them in; it gets the whole envelope
|
|
278
|
+
* because some endpoints keep pagination in `data` and others in `meta`.
|
|
279
|
+
* `next()` reissues the same request with the page number bumped.
|
|
280
|
+
*/
|
|
281
|
+
async fetchPage(path, query, extract) {
|
|
282
|
+
const env = await this.http.request("GET", path, { query });
|
|
283
|
+
const { items, pagination } = extract(env);
|
|
284
|
+
const hasMore = pagination.page < pagination.totalPages;
|
|
285
|
+
return {
|
|
286
|
+
items,
|
|
287
|
+
...pagination,
|
|
288
|
+
hasMore,
|
|
289
|
+
next: hasMore ? () => this.fetchPage(
|
|
290
|
+
path,
|
|
291
|
+
{ ...query, page: pagination.page + 1 },
|
|
292
|
+
extract
|
|
293
|
+
) : async () => void 0
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/** Walk every item across every page, starting from a first-page promise. */
|
|
297
|
+
async *iterate(first) {
|
|
298
|
+
let page = await first;
|
|
299
|
+
while (page) {
|
|
300
|
+
for (const item of page.items) yield item;
|
|
301
|
+
page = await page.next();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// src/resources/core.ts
|
|
307
|
+
var Core = class extends Resource {
|
|
308
|
+
/** Check the API and each operator's reachability. */
|
|
309
|
+
ping(signal) {
|
|
310
|
+
return this.getData("ping", void 0, signal);
|
|
311
|
+
}
|
|
312
|
+
/** Confirm the key and read its environment, scope, and IP allowlist. */
|
|
313
|
+
validate(signal) {
|
|
314
|
+
return this.getData("validate", void 0, signal);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// src/resources/account.ts
|
|
319
|
+
function extractTransactions(env) {
|
|
320
|
+
const d = env.data ?? {};
|
|
321
|
+
return {
|
|
322
|
+
items: d.items ?? [],
|
|
323
|
+
pagination: d.pagination ?? { page: 1, pageSize: 0, totalPages: 1, totalResults: 0 }
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
var Account = class extends Resource {
|
|
327
|
+
/** The current balance in dinars. */
|
|
328
|
+
balance(signal) {
|
|
329
|
+
return this.getData("account/balance", void 0, signal);
|
|
330
|
+
}
|
|
331
|
+
/** One page of the transaction ledger, newest first. */
|
|
332
|
+
transactions(params = {}) {
|
|
333
|
+
return this.fetchPage(
|
|
334
|
+
"account/transactions",
|
|
335
|
+
{ ...params },
|
|
336
|
+
extractTransactions
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
/** Every transaction across every page, as an async iterator. */
|
|
340
|
+
paginateTransactions(params = {}) {
|
|
341
|
+
return this.iterate(this.transactions(params));
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// src/types.ts
|
|
346
|
+
var TOPUP_TERMINAL = [
|
|
347
|
+
"FULFILLED",
|
|
348
|
+
"REFUNDED",
|
|
349
|
+
"UNKNOWN_ERROR"
|
|
350
|
+
];
|
|
351
|
+
var GIFT_TERMINAL = [
|
|
352
|
+
"FULFILLED",
|
|
353
|
+
"PARTIALLY_FILLED",
|
|
354
|
+
"REFUNDED"
|
|
355
|
+
];
|
|
356
|
+
var PAYMENT_TERMINAL = [
|
|
357
|
+
"CONFIRMED",
|
|
358
|
+
"FAILED"
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
// src/poll.ts
|
|
362
|
+
var PollTimeoutError = class extends OneClickError {
|
|
363
|
+
/** The most recent value seen before time ran out. */
|
|
364
|
+
last;
|
|
365
|
+
constructor(message, last, attempts) {
|
|
366
|
+
super(message, "poll_timeout", { details: { attempts } });
|
|
367
|
+
this.last = last;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
async function pollUntil(fetchOnce, isDone, options = {}) {
|
|
371
|
+
const intervalMs = options.intervalMs ?? 5e3;
|
|
372
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? 60);
|
|
373
|
+
let last;
|
|
374
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
375
|
+
throwIfAborted(options.signal);
|
|
376
|
+
last = await fetchOnce();
|
|
377
|
+
if (isDone(last)) return last;
|
|
378
|
+
if (attempt < maxAttempts) await delay(intervalMs, options.signal);
|
|
379
|
+
}
|
|
380
|
+
throw new PollTimeoutError(
|
|
381
|
+
`Gave up after ${maxAttempts} checks without a final status.`,
|
|
382
|
+
last,
|
|
383
|
+
maxAttempts
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
function throwIfAborted(signal) {
|
|
387
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
388
|
+
}
|
|
389
|
+
function abortReason(signal) {
|
|
390
|
+
const reason = signal.reason;
|
|
391
|
+
if (reason !== void 0) return reason;
|
|
392
|
+
const error = new Error("The wait was aborted.");
|
|
393
|
+
error.name = "AbortError";
|
|
394
|
+
return error;
|
|
395
|
+
}
|
|
396
|
+
function delay(ms, signal) {
|
|
397
|
+
return new Promise((resolve, reject) => {
|
|
398
|
+
const onAbort = () => {
|
|
399
|
+
clearTimeout(timer);
|
|
400
|
+
reject(abortReason(signal));
|
|
401
|
+
};
|
|
402
|
+
const timer = setTimeout(() => {
|
|
403
|
+
signal?.removeEventListener("abort", onAbort);
|
|
404
|
+
resolve();
|
|
405
|
+
}, ms);
|
|
406
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/ref.ts
|
|
411
|
+
function generateRef(prefix = "ocd") {
|
|
412
|
+
const uuid = typeof globalThis.crypto?.randomUUID === "function" ? globalThis.crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
413
|
+
return `${prefix}-${uuid}`;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// src/validate.ts
|
|
417
|
+
var MOBILE_NUMBER = /^0[567]\d{8}$/;
|
|
418
|
+
var ADSL_NUMBER = /^0\d{8}$/;
|
|
419
|
+
var FOURG_NUMBER = /^213\d{9}$/;
|
|
420
|
+
function isMobileNumber(value) {
|
|
421
|
+
return MOBILE_NUMBER.test(value);
|
|
422
|
+
}
|
|
423
|
+
function isAdslNumber(value) {
|
|
424
|
+
return ADSL_NUMBER.test(value);
|
|
425
|
+
}
|
|
426
|
+
function isFourgNumber(value) {
|
|
427
|
+
return FOURG_NUMBER.test(value);
|
|
428
|
+
}
|
|
429
|
+
function isWholeAmount(value) {
|
|
430
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
431
|
+
}
|
|
432
|
+
var Issues = class {
|
|
433
|
+
list = [];
|
|
434
|
+
/** Record a problem. The ref ties an issue to one order when checking a batch. */
|
|
435
|
+
add(field, message, ref) {
|
|
436
|
+
this.list.push(ref ? { field, message, ref } : { field, message });
|
|
437
|
+
return this;
|
|
438
|
+
}
|
|
439
|
+
/** Record a problem only when the condition holds. */
|
|
440
|
+
addIf(condition, field, message, ref) {
|
|
441
|
+
if (condition) this.add(field, message, ref);
|
|
442
|
+
return this;
|
|
443
|
+
}
|
|
444
|
+
get any() {
|
|
445
|
+
return this.list.length > 0;
|
|
446
|
+
}
|
|
447
|
+
all() {
|
|
448
|
+
return this.list;
|
|
449
|
+
}
|
|
450
|
+
/** Throw if anything was collected; otherwise do nothing. */
|
|
451
|
+
throwIfAny(summary = "The request did not pass validation.") {
|
|
452
|
+
if (this.list.length > 0) {
|
|
453
|
+
throw new ValidationError(summary, { issues: this.list });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
function requireText(issues, value, field, ref) {
|
|
458
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
459
|
+
issues.add(field, "is required", ref);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// src/resources/mobile.ts
|
|
464
|
+
function extractTopups(env) {
|
|
465
|
+
const d = env.data ?? {};
|
|
466
|
+
return {
|
|
467
|
+
items: d.items ?? [],
|
|
468
|
+
pagination: d.pagination ?? { page: 1, pageSize: 0, totalPages: 1, totalResults: 0 }
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
function validateSend(input) {
|
|
472
|
+
const issues = new Issues();
|
|
473
|
+
requireText(issues, input.plan_code, "plan_code");
|
|
474
|
+
if (typeof input.MSSIDN !== "string" || !isMobileNumber(input.MSSIDN)) {
|
|
475
|
+
issues.add("MSSIDN", "is not a valid mobile number");
|
|
476
|
+
}
|
|
477
|
+
if (input.amount !== void 0 && !isWholeAmount(input.amount)) {
|
|
478
|
+
issues.add("amount", "must be a whole number of dinars");
|
|
479
|
+
}
|
|
480
|
+
issues.throwIfAny("The top-up did not pass validation.");
|
|
481
|
+
}
|
|
482
|
+
var isTerminal = (t) => TOPUP_TERMINAL.includes(t.status);
|
|
483
|
+
var Mobile = class extends Resource {
|
|
484
|
+
/** The plans available to your account, split into dynamic and fixed. */
|
|
485
|
+
plans(signal) {
|
|
486
|
+
return this.getData("mobile/plans", void 0, signal);
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Send a top-up. The number and amount are checked first, and a reference is
|
|
490
|
+
* generated when you do not pass one, so the returned `topupRef` is always set.
|
|
491
|
+
*/
|
|
492
|
+
async send(input, signal) {
|
|
493
|
+
validateSend(input);
|
|
494
|
+
const body = { ...input, ref: input.ref ?? generateRef() };
|
|
495
|
+
return this.postData("mobile/send", body, signal);
|
|
496
|
+
}
|
|
497
|
+
/** Look a top-up up by your own reference. */
|
|
498
|
+
checkByRef(ref, signal) {
|
|
499
|
+
return this.getData(
|
|
500
|
+
`mobile/check-ref/${encodeURIComponent(ref)}`,
|
|
501
|
+
void 0,
|
|
502
|
+
signal
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
/** Look a top-up up by the id from `send`. */
|
|
506
|
+
checkById(id, signal) {
|
|
507
|
+
return this.getData(
|
|
508
|
+
`mobile/check-id/${encodeURIComponent(id)}`,
|
|
509
|
+
void 0,
|
|
510
|
+
signal
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Send a top-up and wait for it to settle, polling by reference. Resolves with
|
|
515
|
+
* the final top-up once it is FULFILLED, REFUNDED, or UNKNOWN_ERROR. Defaults
|
|
516
|
+
* to a check every 5 seconds for up to 5 minutes, which the docs recommend.
|
|
517
|
+
*/
|
|
518
|
+
async sendAndWait(input, options = {}) {
|
|
519
|
+
const ref = input.ref ?? generateRef();
|
|
520
|
+
await this.send({ ...input, ref }, options.signal);
|
|
521
|
+
return pollUntil(() => this.checkByRef(ref, options.signal), isTerminal, {
|
|
522
|
+
intervalMs: 5e3,
|
|
523
|
+
maxAttempts: 60,
|
|
524
|
+
...options
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
/** One page of your past top-ups, newest first. */
|
|
528
|
+
list(params = {}) {
|
|
529
|
+
return this.fetchPage("mobile/list", { ...params }, extractTopups);
|
|
530
|
+
}
|
|
531
|
+
/** Every past top-up across every page, as an async iterator. */
|
|
532
|
+
paginate(params = {}) {
|
|
533
|
+
return this.iterate(this.list(params));
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// src/secret.ts
|
|
538
|
+
var REDACTED = "[redacted]";
|
|
539
|
+
var INSPECT = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
540
|
+
var Secret = class _Secret {
|
|
541
|
+
// A private field is neither enumerable nor reachable by Object.keys or a
|
|
542
|
+
// spread, so the value cannot leak through ordinary object handling.
|
|
543
|
+
#value;
|
|
544
|
+
constructor(value) {
|
|
545
|
+
this.#value = value;
|
|
546
|
+
}
|
|
547
|
+
/** The real value. Calling this is the deliberate, searchable moment a secret is read. */
|
|
548
|
+
reveal() {
|
|
549
|
+
return this.#value;
|
|
550
|
+
}
|
|
551
|
+
/** Used by JSON.stringify, so a secret nested in an object serializes redacted. */
|
|
552
|
+
toJSON() {
|
|
553
|
+
return REDACTED;
|
|
554
|
+
}
|
|
555
|
+
toString() {
|
|
556
|
+
return REDACTED;
|
|
557
|
+
}
|
|
558
|
+
[INSPECT]() {
|
|
559
|
+
return `Secret(${REDACTED})`;
|
|
560
|
+
}
|
|
561
|
+
static of(value) {
|
|
562
|
+
return new _Secret(value);
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
function isSecret(value) {
|
|
566
|
+
return value instanceof Secret;
|
|
567
|
+
}
|
|
568
|
+
function reveal(value) {
|
|
569
|
+
return value instanceof Secret ? value.reveal() : value;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// src/resources/internet.ts
|
|
573
|
+
function toTopup(raw) {
|
|
574
|
+
const { card_code, ...rest } = raw;
|
|
575
|
+
return card_code ? { ...rest, card_code: Secret.of(card_code) } : { ...rest };
|
|
576
|
+
}
|
|
577
|
+
function extractInternetTopups(env) {
|
|
578
|
+
const data = env.data ?? {};
|
|
579
|
+
const page = env.meta?.pagination ?? {};
|
|
580
|
+
const items = (data.topups ?? []).map(toTopup);
|
|
581
|
+
return {
|
|
582
|
+
items,
|
|
583
|
+
pagination: {
|
|
584
|
+
page: page.currentPage ?? page.page ?? 1,
|
|
585
|
+
pageSize: items.length,
|
|
586
|
+
totalPages: page.totalPages ?? 1,
|
|
587
|
+
totalResults: page.totalItems ?? page.totalResults ?? items.length
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function validateSend2(input) {
|
|
592
|
+
const issues = new Issues();
|
|
593
|
+
if (input.type !== "ADSL" && input.type !== "4G") {
|
|
594
|
+
issues.add("type", "must be ADSL or 4G");
|
|
595
|
+
} else if (input.type === "ADSL" && !isAdslNumber(input.number)) {
|
|
596
|
+
issues.add("number", "is not a valid ADSL number");
|
|
597
|
+
} else if (input.type === "4G" && !isFourgNumber(input.number)) {
|
|
598
|
+
issues.add("number", "is not a valid 4G number");
|
|
599
|
+
}
|
|
600
|
+
if (!isWholeAmount(input.value)) {
|
|
601
|
+
issues.add("value", "must be a whole number of dinars");
|
|
602
|
+
}
|
|
603
|
+
issues.throwIfAny("The internet top-up did not pass validation.");
|
|
604
|
+
}
|
|
605
|
+
var isTerminal2 = (t) => TOPUP_TERMINAL.includes(t.status);
|
|
606
|
+
var Internet = class extends Resource {
|
|
607
|
+
/** The cards on offer for a service type, with their cost and stock. */
|
|
608
|
+
products(type, signal) {
|
|
609
|
+
return this.getData("internet/products", { type }, signal);
|
|
610
|
+
}
|
|
611
|
+
/** Confirm a number is valid and active before sending to it. */
|
|
612
|
+
checkNumber(type, number, signal) {
|
|
613
|
+
return this.getData(
|
|
614
|
+
"internet/check-number",
|
|
615
|
+
{ type, number },
|
|
616
|
+
signal
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
/** Send a recharge. The number and value are checked first; a ref is filled in. */
|
|
620
|
+
async send(input, signal) {
|
|
621
|
+
validateSend2(input);
|
|
622
|
+
const body = { ...input, ref: input.ref ?? generateRef() };
|
|
623
|
+
return this.postData("internet/send", body, signal);
|
|
624
|
+
}
|
|
625
|
+
async checkByRef(ref, signal) {
|
|
626
|
+
const raw = await this.getData(
|
|
627
|
+
`internet/check-ref/${encodeURIComponent(ref)}`,
|
|
628
|
+
void 0,
|
|
629
|
+
signal
|
|
630
|
+
);
|
|
631
|
+
return toTopup(raw);
|
|
632
|
+
}
|
|
633
|
+
async checkById(id, signal) {
|
|
634
|
+
const raw = await this.getData(
|
|
635
|
+
`internet/check-id/${encodeURIComponent(id)}`,
|
|
636
|
+
void 0,
|
|
637
|
+
signal
|
|
638
|
+
);
|
|
639
|
+
return toTopup(raw);
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Send and wait for the recharge to settle. Resolves with the final order,
|
|
643
|
+
* its `card_code` ready to reveal once FULFILLED. A QUEUED order can take up to
|
|
644
|
+
* 48 hours, well past the default polling window, so expect a timeout there.
|
|
645
|
+
*/
|
|
646
|
+
async sendAndWait(input, options = {}) {
|
|
647
|
+
const ref = input.ref ?? generateRef();
|
|
648
|
+
await this.send({ ...input, ref }, options.signal);
|
|
649
|
+
return pollUntil(() => this.checkByRef(ref, options.signal), isTerminal2, {
|
|
650
|
+
intervalMs: 5e3,
|
|
651
|
+
maxAttempts: 60,
|
|
652
|
+
...options
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
list(params = {}) {
|
|
656
|
+
return this.fetchPage(
|
|
657
|
+
"internet/list",
|
|
658
|
+
{ ...params },
|
|
659
|
+
extractInternetTopups
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
paginate(params = {}) {
|
|
663
|
+
return this.iterate(this.list(params));
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// src/resources/giftcards.ts
|
|
668
|
+
function toOrder(raw) {
|
|
669
|
+
const cards = (raw.cards ?? []).map((card) => ({
|
|
670
|
+
value: Secret.of(card.value ?? ""),
|
|
671
|
+
serial: Secret.of(card.serial ?? "")
|
|
672
|
+
}));
|
|
673
|
+
return { ...raw, cards };
|
|
674
|
+
}
|
|
675
|
+
function extractOrders(env) {
|
|
676
|
+
const d = env.data ?? {};
|
|
677
|
+
return {
|
|
678
|
+
items: (d.items ?? []).map(toOrder),
|
|
679
|
+
pagination: d.pagination ?? { page: 1, pageSize: 0, totalPages: 1, totalResults: 0 }
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
function validateOrder(input) {
|
|
683
|
+
const issues = new Issues();
|
|
684
|
+
requireText(issues, input.productId, "productId");
|
|
685
|
+
requireText(issues, input.typeId, "typeId");
|
|
686
|
+
if (!Number.isInteger(input.quantity) || input.quantity < 1) {
|
|
687
|
+
issues.add("quantity", "must be a whole number of one or more");
|
|
688
|
+
}
|
|
689
|
+
issues.throwIfAny("The gift-card order did not pass validation.");
|
|
690
|
+
}
|
|
691
|
+
var isTerminal3 = (o) => GIFT_TERMINAL.includes(o.status);
|
|
692
|
+
var GiftCards = class extends Resource {
|
|
693
|
+
/** The full product catalog. Cache it; the docs say it changes rarely. */
|
|
694
|
+
async catalog(signal) {
|
|
695
|
+
const env = await this.http.request(
|
|
696
|
+
"GET",
|
|
697
|
+
"gift-cards/catalog",
|
|
698
|
+
{ signal }
|
|
699
|
+
);
|
|
700
|
+
return {
|
|
701
|
+
categories: env.data.categories ?? [],
|
|
702
|
+
totalCategories: env.totalCategories,
|
|
703
|
+
totalProducts: env.totalProducts
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
/** A product's denominations, with live pricing and stock. */
|
|
707
|
+
checkProduct(productId, signal) {
|
|
708
|
+
return this.getData(
|
|
709
|
+
`gift-cards/checkProduct/${encodeURIComponent(productId)}`,
|
|
710
|
+
void 0,
|
|
711
|
+
signal
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
/** Place an order. The product, type, and quantity are checked first. */
|
|
715
|
+
async placeOrder(input, signal) {
|
|
716
|
+
validateOrder(input);
|
|
717
|
+
return this.postData(
|
|
718
|
+
"gift-cards/placeOrder",
|
|
719
|
+
input,
|
|
720
|
+
signal
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
/** Read an order's status and, once settled, its cards. */
|
|
724
|
+
async checkOrder(orderId, signal) {
|
|
725
|
+
const raw = await this.getData(
|
|
726
|
+
`gift-cards/checkOrder/${encodeURIComponent(orderId)}`,
|
|
727
|
+
void 0,
|
|
728
|
+
signal
|
|
729
|
+
);
|
|
730
|
+
return toOrder(raw);
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Place an order and wait for it to settle. Resolves once the order is
|
|
734
|
+
* FULFILLED, PARTIALLY_FILLED, or REFUNDED. Gift cards can take longer than a
|
|
735
|
+
* top-up, so this checks every 5 seconds for up to 10 minutes by default.
|
|
736
|
+
*/
|
|
737
|
+
async placeOrderAndWait(input, options = {}) {
|
|
738
|
+
const { orderId } = await this.placeOrder(input, options.signal);
|
|
739
|
+
return pollUntil(
|
|
740
|
+
() => this.checkOrder(orderId, options.signal),
|
|
741
|
+
isTerminal3,
|
|
742
|
+
{ intervalMs: 5e3, maxAttempts: 120, ...options }
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
/** One page of past orders, newest first. */
|
|
746
|
+
listOrders(params = {}) {
|
|
747
|
+
return this.fetchPage(
|
|
748
|
+
"gift-cards/list",
|
|
749
|
+
{ ...params },
|
|
750
|
+
extractOrders
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
/** Every past order across every page, as an async iterator. */
|
|
754
|
+
paginateOrders(params = {}) {
|
|
755
|
+
return this.iterate(this.listOrders(params));
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
// src/resources/payments.ts
|
|
760
|
+
var MIN_PAYMENT_AMOUNT = 500;
|
|
761
|
+
var MAX_PAYMENT_AMOUNT = 5e5;
|
|
762
|
+
function validateCreateLink(input) {
|
|
763
|
+
const issues = new Issues();
|
|
764
|
+
const info = input.productInfo ?? {};
|
|
765
|
+
requireText(issues, info.title, "productInfo.title");
|
|
766
|
+
if (typeof info.title === "string" && info.title.length > 200) {
|
|
767
|
+
issues.add("productInfo.title", "must be at most 200 characters");
|
|
768
|
+
}
|
|
769
|
+
if (!isWholeAmount(info.amount)) {
|
|
770
|
+
issues.add("productInfo.amount", "must be a whole number of dinars");
|
|
771
|
+
} else if (info.amount < MIN_PAYMENT_AMOUNT || info.amount > MAX_PAYMENT_AMOUNT) {
|
|
772
|
+
issues.add(
|
|
773
|
+
"productInfo.amount",
|
|
774
|
+
`must be between ${MIN_PAYMENT_AMOUNT} and ${MAX_PAYMENT_AMOUNT}`
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
if (info.description !== void 0 && info.description.length > 1e3) {
|
|
778
|
+
issues.add("productInfo.description", "must be at most 1000 characters");
|
|
779
|
+
}
|
|
780
|
+
if (input.successMessage !== void 0 && input.successMessage.length > 500) {
|
|
781
|
+
issues.add("successMessage", "must be at most 500 characters");
|
|
782
|
+
}
|
|
783
|
+
if (input.feeMode !== void 0 && input.feeMode !== "NO_FEE" && input.feeMode !== "SPLIT_FEE" && input.feeMode !== "CUSTOMER_FEE") {
|
|
784
|
+
issues.add("feeMode", "must be NO_FEE, SPLIT_FEE, or CUSTOMER_FEE");
|
|
785
|
+
}
|
|
786
|
+
if (input.redirectUrl !== void 0 && !/^https?:\/\//.test(input.redirectUrl)) {
|
|
787
|
+
issues.add("redirectUrl", "must start with http:// or https://");
|
|
788
|
+
}
|
|
789
|
+
issues.throwIfAny("The payment link did not pass validation.");
|
|
790
|
+
}
|
|
791
|
+
var isTerminal4 = (p) => PAYMENT_TERMINAL.includes(p.status);
|
|
792
|
+
var Payments = class extends Resource {
|
|
793
|
+
/** Create a payment link. Returns the URL to send the customer to. */
|
|
794
|
+
async createLink(input, signal) {
|
|
795
|
+
validateCreateLink(input);
|
|
796
|
+
return this.postData("ocpay/createLink", input, signal);
|
|
797
|
+
}
|
|
798
|
+
/** Check a payment's status by its reference. */
|
|
799
|
+
checkPayment(ref, signal) {
|
|
800
|
+
return this.getData(
|
|
801
|
+
`ocpay/checkPayment/${encodeURIComponent(ref)}`,
|
|
802
|
+
void 0,
|
|
803
|
+
signal
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Poll a payment until it is CONFIRMED or FAILED. The default window is twenty
|
|
808
|
+
* minutes, matching how long an unpaid link stays open; a payment that is
|
|
809
|
+
* never started times out with a PollTimeoutError. Pass a signal to stop early.
|
|
810
|
+
*/
|
|
811
|
+
waitForPayment(ref, options = {}) {
|
|
812
|
+
return pollUntil(
|
|
813
|
+
() => this.checkPayment(ref, options.signal),
|
|
814
|
+
isTerminal4,
|
|
815
|
+
{ intervalMs: 5e3, maxAttempts: 240, ...options }
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// src/client.ts
|
|
821
|
+
var DEFAULT_BASE_URL = "https://api.oneclickdz.com/v3";
|
|
822
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
823
|
+
var DEFAULT_RETRY = { attempts: 3, respectRetryAfter: true };
|
|
824
|
+
var OneClickDZ = class {
|
|
825
|
+
core;
|
|
826
|
+
account;
|
|
827
|
+
mobile;
|
|
828
|
+
internet;
|
|
829
|
+
giftCards;
|
|
830
|
+
payments;
|
|
831
|
+
http;
|
|
832
|
+
constructor(options) {
|
|
833
|
+
if (!options.key) {
|
|
834
|
+
throw new Error("OneClickDZ needs an access token in options.key.");
|
|
835
|
+
}
|
|
836
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
837
|
+
if (typeof fetchImpl !== "function") {
|
|
838
|
+
throw new Error(
|
|
839
|
+
"No fetch is available. Run on Node 18+ or pass one in options.fetch."
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
this.http = new HttpClient({
|
|
843
|
+
key: options.key,
|
|
844
|
+
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
845
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
846
|
+
retry: { ...DEFAULT_RETRY, ...options.retry },
|
|
847
|
+
fetch: fetchImpl
|
|
848
|
+
});
|
|
849
|
+
this.core = new Core(this.http);
|
|
850
|
+
this.account = new Account(this.http);
|
|
851
|
+
this.mobile = new Mobile(this.http);
|
|
852
|
+
this.internet = new Internet(this.http);
|
|
853
|
+
this.giftCards = new GiftCards(this.http);
|
|
854
|
+
this.payments = new Payments(this.http);
|
|
855
|
+
}
|
|
856
|
+
/** Shortcut for `core.ping()`: the API and operator status. */
|
|
857
|
+
ping(signal) {
|
|
858
|
+
return this.core.ping(signal);
|
|
859
|
+
}
|
|
860
|
+
/** Shortcut for `core.validate()`: confirm the key and read its environment. */
|
|
861
|
+
validate(signal) {
|
|
862
|
+
return this.core.validate(signal);
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
// src/pricing.ts
|
|
867
|
+
function sellPrice(cost, markup) {
|
|
868
|
+
const raw = "percent" in markup ? cost * (1 + markup.percent / 100) : cost + markup.flat;
|
|
869
|
+
return Math.round(raw);
|
|
870
|
+
}
|
|
871
|
+
function priceInternetProducts(products, markup) {
|
|
872
|
+
return products.map((p) => ({
|
|
873
|
+
value: p.value,
|
|
874
|
+
price: sellPrice(p.cost, markup),
|
|
875
|
+
available: p.available
|
|
876
|
+
}));
|
|
877
|
+
}
|
|
878
|
+
function priceGiftTypes(types, markup) {
|
|
879
|
+
return types.map((t) => ({
|
|
880
|
+
id: t.id,
|
|
881
|
+
name: t.name,
|
|
882
|
+
price: sellPrice(t.price, markup),
|
|
883
|
+
inStock: t.quantity > 0
|
|
884
|
+
}));
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// src/index.ts
|
|
888
|
+
var VERSION = "0.1.0";
|
|
889
|
+
|
|
890
|
+
exports.ADSL_NUMBER = ADSL_NUMBER;
|
|
891
|
+
exports.Account = Account;
|
|
892
|
+
exports.AuthError = AuthError;
|
|
893
|
+
exports.Core = Core;
|
|
894
|
+
exports.DuplicateRefError = DuplicateRefError;
|
|
895
|
+
exports.FOURG_NUMBER = FOURG_NUMBER;
|
|
896
|
+
exports.ForbiddenError = ForbiddenError;
|
|
897
|
+
exports.GIFT_TERMINAL = GIFT_TERMINAL;
|
|
898
|
+
exports.GiftCards = GiftCards;
|
|
899
|
+
exports.InsufficientBalanceError = InsufficientBalanceError;
|
|
900
|
+
exports.Internet = Internet;
|
|
901
|
+
exports.Issues = Issues;
|
|
902
|
+
exports.MAX_PAYMENT_AMOUNT = MAX_PAYMENT_AMOUNT;
|
|
903
|
+
exports.MIN_PAYMENT_AMOUNT = MIN_PAYMENT_AMOUNT;
|
|
904
|
+
exports.MOBILE_NUMBER = MOBILE_NUMBER;
|
|
905
|
+
exports.Mobile = Mobile;
|
|
906
|
+
exports.NetworkError = NetworkError;
|
|
907
|
+
exports.NotFoundError = NotFoundError;
|
|
908
|
+
exports.OneClickDZ = OneClickDZ;
|
|
909
|
+
exports.OneClickError = OneClickError;
|
|
910
|
+
exports.PAYMENT_TERMINAL = PAYMENT_TERMINAL;
|
|
911
|
+
exports.Payments = Payments;
|
|
912
|
+
exports.PollTimeoutError = PollTimeoutError;
|
|
913
|
+
exports.RateLimitError = RateLimitError;
|
|
914
|
+
exports.RequestError = RequestError;
|
|
915
|
+
exports.Secret = Secret;
|
|
916
|
+
exports.ServiceError = ServiceError;
|
|
917
|
+
exports.TOPUP_TERMINAL = TOPUP_TERMINAL;
|
|
918
|
+
exports.VERSION = VERSION;
|
|
919
|
+
exports.ValidationError = ValidationError;
|
|
920
|
+
exports.errorFromResponse = errorFromResponse;
|
|
921
|
+
exports.generateRef = generateRef;
|
|
922
|
+
exports.isAdslNumber = isAdslNumber;
|
|
923
|
+
exports.isFourgNumber = isFourgNumber;
|
|
924
|
+
exports.isMobileNumber = isMobileNumber;
|
|
925
|
+
exports.isSecret = isSecret;
|
|
926
|
+
exports.isWholeAmount = isWholeAmount;
|
|
927
|
+
exports.pollUntil = pollUntil;
|
|
928
|
+
exports.priceGiftTypes = priceGiftTypes;
|
|
929
|
+
exports.priceInternetProducts = priceInternetProducts;
|
|
930
|
+
exports.requireText = requireText;
|
|
931
|
+
exports.reveal = reveal;
|
|
932
|
+
exports.sellPrice = sellPrice;
|
|
933
|
+
//# sourceMappingURL=index.cjs.map
|
|
934
|
+
//# sourceMappingURL=index.cjs.map
|