@nile-squad/nylonpay-ts 1.0.3 → 1.0.5
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/dist/index.cjs +306 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -17
- package/dist/index.d.ts +47 -17
- package/dist/index.js +307 -98
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -55,21 +55,6 @@ function createEmitter() {
|
|
|
55
55
|
const emitter = { on, once, off, emit, clear, listenerCount };
|
|
56
56
|
return emitter;
|
|
57
57
|
}
|
|
58
|
-
function generateFingerprint() {
|
|
59
|
-
const components = [
|
|
60
|
-
`type:${os.type()}`,
|
|
61
|
-
`platform:${os.platform()}`,
|
|
62
|
-
`arch:${os.arch()}`,
|
|
63
|
-
`release:${os.release()}`,
|
|
64
|
-
`hostname:${os.hostname()}`,
|
|
65
|
-
`node:${process.versions.node}`,
|
|
66
|
-
`v8:${process.versions.v8}`
|
|
67
|
-
].join("|");
|
|
68
|
-
return crypto.createHash("sha256").update(components).digest("hex");
|
|
69
|
-
}
|
|
70
|
-
function generateNonce(length = 16) {
|
|
71
|
-
return crypto.randomBytes(length).toString("hex");
|
|
72
|
-
}
|
|
73
58
|
|
|
74
59
|
// src/sdk.config.ts
|
|
75
60
|
var DEFAULT_BASE_URL = "https://api.nylonpay.nilesquad.com/api/services";
|
|
@@ -78,6 +63,9 @@ var DEFAULT_MAX_RETRIES = 3;
|
|
|
78
63
|
var DEFAULT_MAX_POLL_INTERVAL_MS = 2e3;
|
|
79
64
|
var DEFAULT_MAX_POLL_DURATION_MS = 3e5;
|
|
80
65
|
var DEFAULT_MAX_POLL_ATTEMPTS = 150;
|
|
66
|
+
var DEFAULT_STREAMING = true;
|
|
67
|
+
var STREAM_PATH = "/sse/transaction";
|
|
68
|
+
var MAX_STREAM_RECONNECTS = 2;
|
|
81
69
|
var SDK_SERVICE = "sdk";
|
|
82
70
|
var SDK_ACTIONS = {
|
|
83
71
|
collectPayment: "sdk-collect-payment",
|
|
@@ -90,6 +78,21 @@ var SDK_ACTIONS = {
|
|
|
90
78
|
createInvoice: "sdk-create-invoice"
|
|
91
79
|
};
|
|
92
80
|
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
81
|
+
function generateFingerprint() {
|
|
82
|
+
const components = [
|
|
83
|
+
`type:${os.type()}`,
|
|
84
|
+
`platform:${os.platform()}`,
|
|
85
|
+
`arch:${os.arch()}`,
|
|
86
|
+
`release:${os.release()}`,
|
|
87
|
+
`hostname:${os.hostname()}`,
|
|
88
|
+
`node:${process.versions.node}`,
|
|
89
|
+
`v8:${process.versions.v8}`
|
|
90
|
+
].join("|");
|
|
91
|
+
return crypto.createHash("sha256").update(components).digest("hex");
|
|
92
|
+
}
|
|
93
|
+
function generateNonce(length = 16) {
|
|
94
|
+
return crypto.randomBytes(length).toString("hex");
|
|
95
|
+
}
|
|
93
96
|
function sortValue(value) {
|
|
94
97
|
if (Array.isArray(value)) {
|
|
95
98
|
return value.map((entry) => sortValue(entry));
|
|
@@ -120,6 +123,42 @@ function createSignature(input) {
|
|
|
120
123
|
function createTimestamp() {
|
|
121
124
|
return Date.now().toString();
|
|
122
125
|
}
|
|
126
|
+
|
|
127
|
+
// src/sse-parse.ts
|
|
128
|
+
function parseBlock(block) {
|
|
129
|
+
let event = "message";
|
|
130
|
+
const dataLines = [];
|
|
131
|
+
for (const rawLine of block.split("\n")) {
|
|
132
|
+
const line = rawLine.replace(/\r$/, "");
|
|
133
|
+
if (line.startsWith(":")) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (line.startsWith("event:")) {
|
|
137
|
+
event = line.slice("event:".length).trim();
|
|
138
|
+
} else if (line.startsWith("data:")) {
|
|
139
|
+
dataLines.push(line.slice("data:".length).trim());
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (dataLines.length === 0) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return { event, data: dataLines.join("\n") };
|
|
146
|
+
}
|
|
147
|
+
function parseSseBuffer(buffer) {
|
|
148
|
+
const messages = [];
|
|
149
|
+
let rest = buffer;
|
|
150
|
+
let separator = rest.indexOf("\n\n");
|
|
151
|
+
while (separator !== -1) {
|
|
152
|
+
const block = rest.slice(0, separator);
|
|
153
|
+
rest = rest.slice(separator + 2);
|
|
154
|
+
const message = parseBlock(block);
|
|
155
|
+
if (message) {
|
|
156
|
+
messages.push(message);
|
|
157
|
+
}
|
|
158
|
+
separator = rest.indexOf("\n\n");
|
|
159
|
+
}
|
|
160
|
+
return { messages, rest };
|
|
161
|
+
}
|
|
123
162
|
function verifyResponseSignature(data, signature, secret) {
|
|
124
163
|
const expectedSignature = crypto.createHmac("sha256", secret).update(createCanonicalPayload(data)).digest("hex");
|
|
125
164
|
const providedBuffer = Buffer.from(signature, "hex");
|
|
@@ -132,6 +171,55 @@ function verifyResponseSignature(data, signature, secret) {
|
|
|
132
171
|
|
|
133
172
|
// src/transport.ts
|
|
134
173
|
var CACHED_FINGERPRINT = generateFingerprint();
|
|
174
|
+
function streamUrl(baseUrl) {
|
|
175
|
+
try {
|
|
176
|
+
return new URL(baseUrl).origin + STREAM_PATH;
|
|
177
|
+
} catch {
|
|
178
|
+
return baseUrl.replace(/\/api\/services\/?$/u, "") + STREAM_PATH;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
var KNOWN_CATEGORIES = /* @__PURE__ */ new Set([
|
|
182
|
+
"auth",
|
|
183
|
+
"validation",
|
|
184
|
+
"limit",
|
|
185
|
+
"rate_limit",
|
|
186
|
+
"account",
|
|
187
|
+
"provider",
|
|
188
|
+
"not_found",
|
|
189
|
+
"internal",
|
|
190
|
+
"network",
|
|
191
|
+
"timeout"
|
|
192
|
+
]);
|
|
193
|
+
var STATUS_CATEGORY = {
|
|
194
|
+
408: "timeout",
|
|
195
|
+
429: "rate_limit"
|
|
196
|
+
};
|
|
197
|
+
var ERROR_TYPE_SUFFIX = /^(.*?)\s*--\s*error-type:\s*([a-z_]+)\s*$/is;
|
|
198
|
+
function parseCategoryFromMessage(message) {
|
|
199
|
+
const match = ERROR_TYPE_SUFFIX.exec(message);
|
|
200
|
+
if (match?.[2] && KNOWN_CATEGORIES.has(match[2])) {
|
|
201
|
+
return {
|
|
202
|
+
category: match[2],
|
|
203
|
+
message: match[1] ?? message
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return { category: null, message };
|
|
207
|
+
}
|
|
208
|
+
function buildHttpError(params) {
|
|
209
|
+
const parsed = parseCategoryFromMessage(params.message);
|
|
210
|
+
const category = parsed.category ?? STATUS_CATEGORY[params.statusCode] ?? (params.statusCode >= 500 ? "internal" : "validation");
|
|
211
|
+
return {
|
|
212
|
+
category,
|
|
213
|
+
message: parsed.message,
|
|
214
|
+
retryable: RETRYABLE_STATUS_CODES.has(params.statusCode)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function createSdkError(error) {
|
|
218
|
+
return Object.assign(new Error(error.message), {
|
|
219
|
+
category: error.category,
|
|
220
|
+
retryable: error.retryable
|
|
221
|
+
});
|
|
222
|
+
}
|
|
135
223
|
function calculateBackoff(attempt) {
|
|
136
224
|
const base = 2 ** attempt * 1e3;
|
|
137
225
|
const jitter = Math.random() * 500;
|
|
@@ -234,21 +322,19 @@ function createTransport({
|
|
|
234
322
|
await delay(calculateBackoff(currentAttempt));
|
|
235
323
|
return attempt(currentAttempt + 1);
|
|
236
324
|
}
|
|
237
|
-
const sdkError = {
|
|
238
|
-
code: `HTTP_${statusCode}`,
|
|
239
|
-
message: errorMessage,
|
|
240
|
-
statusCode,
|
|
241
|
-
retryable
|
|
242
|
-
};
|
|
243
325
|
cleanup();
|
|
244
|
-
return slangTs.Err(
|
|
326
|
+
return slangTs.Err(
|
|
327
|
+
JSON.stringify(
|
|
328
|
+
buildHttpError({ message: errorMessage, statusCode })
|
|
329
|
+
)
|
|
330
|
+
);
|
|
245
331
|
}
|
|
246
332
|
const responseBody = await response.json();
|
|
247
333
|
if (!responseBody || typeof responseBody !== "object" || !("status" in responseBody)) {
|
|
248
334
|
cleanup();
|
|
249
335
|
return slangTs.Err(
|
|
250
336
|
JSON.stringify({
|
|
251
|
-
|
|
337
|
+
category: "internal",
|
|
252
338
|
message: "Response missing status field",
|
|
253
339
|
retryable: false
|
|
254
340
|
})
|
|
@@ -267,7 +353,7 @@ function createTransport({
|
|
|
267
353
|
cleanup();
|
|
268
354
|
return slangTs.Err(
|
|
269
355
|
JSON.stringify({
|
|
270
|
-
|
|
356
|
+
category: "internal",
|
|
271
357
|
message: "Response signature verification failed",
|
|
272
358
|
retryable: false
|
|
273
359
|
})
|
|
@@ -284,7 +370,7 @@ function createTransport({
|
|
|
284
370
|
cleanup();
|
|
285
371
|
const isAbort = error instanceof DOMException && error.name === "AbortError";
|
|
286
372
|
const sdkError = {
|
|
287
|
-
|
|
373
|
+
category: isAbort ? "timeout" : "network",
|
|
288
374
|
message: isAbort ? `Request timed out after ${timeoutMs}ms` : String(error),
|
|
289
375
|
retryable: true
|
|
290
376
|
};
|
|
@@ -297,17 +383,105 @@ function createTransport({
|
|
|
297
383
|
}
|
|
298
384
|
return attempt(0);
|
|
299
385
|
}
|
|
300
|
-
|
|
386
|
+
function openStream(input) {
|
|
387
|
+
const controller = new AbortController();
|
|
388
|
+
let closed = false;
|
|
389
|
+
const close = () => {
|
|
390
|
+
if (closed) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
closed = true;
|
|
394
|
+
controller.abort();
|
|
395
|
+
};
|
|
396
|
+
const payload = {
|
|
397
|
+
reference: input.reference,
|
|
398
|
+
_fingerprint: CACHED_FINGERPRINT
|
|
399
|
+
};
|
|
400
|
+
const headers = buildAuthHeaders({ apiKey, apiSecret, payload });
|
|
401
|
+
const url = streamUrl(baseUrl);
|
|
402
|
+
const run = async () => {
|
|
403
|
+
const fetchResult = await slangTs.safeTry(
|
|
404
|
+
() => fetchImpl(url, {
|
|
405
|
+
method: "POST",
|
|
406
|
+
headers,
|
|
407
|
+
body: JSON.stringify(payload),
|
|
408
|
+
signal: controller.signal
|
|
409
|
+
})
|
|
410
|
+
);
|
|
411
|
+
if (fetchResult.isErr) {
|
|
412
|
+
if (!closed) {
|
|
413
|
+
input.onError(fetchResult.error);
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const response = fetchResult.value;
|
|
418
|
+
if (!response.ok || !response.body) {
|
|
419
|
+
const textResult = await slangTs.safeTry(() => response.text());
|
|
420
|
+
const message = textResult.isOk ? textResult.value : `HTTP ${response.status}`;
|
|
421
|
+
if (!closed) {
|
|
422
|
+
input.onError(message || `HTTP ${response.status}`);
|
|
423
|
+
}
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const reader = response.body.getReader();
|
|
427
|
+
const decoder = new TextDecoder();
|
|
428
|
+
let buffer = "";
|
|
429
|
+
while (!closed) {
|
|
430
|
+
const chunkResult = await slangTs.safeTry(() => reader.read());
|
|
431
|
+
if (chunkResult.isErr) {
|
|
432
|
+
if (!closed) {
|
|
433
|
+
input.onError(chunkResult.error);
|
|
434
|
+
}
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const chunk = chunkResult.value;
|
|
438
|
+
if (chunk.done) {
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
buffer += decoder.decode(chunk.value, { stream: true });
|
|
442
|
+
const { messages, rest } = parseSseBuffer(buffer);
|
|
443
|
+
buffer = rest;
|
|
444
|
+
for (const message of messages) {
|
|
445
|
+
if (message.event === "status") {
|
|
446
|
+
const statusResult = await slangTs.safeTry(
|
|
447
|
+
() => JSON.parse(message.data)
|
|
448
|
+
);
|
|
449
|
+
if (statusResult.isOk) {
|
|
450
|
+
input.onStatus(statusResult.value);
|
|
451
|
+
}
|
|
452
|
+
} else if (message.event === "error") {
|
|
453
|
+
const errDataResult = await slangTs.safeTry(
|
|
454
|
+
() => JSON.parse(message.data)
|
|
455
|
+
);
|
|
456
|
+
const text = errDataResult.isOk ? errDataResult.value.message ?? message.data : message.data;
|
|
457
|
+
close();
|
|
458
|
+
input.onError(text);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (!closed) {
|
|
464
|
+
input.onClose();
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
void run();
|
|
468
|
+
return { close };
|
|
469
|
+
}
|
|
470
|
+
return { send, openStream, parseError };
|
|
301
471
|
}
|
|
302
472
|
function parseError(error) {
|
|
303
473
|
try {
|
|
304
474
|
const parsed = JSON.parse(error);
|
|
305
|
-
if (parsed && typeof parsed === "object" && "
|
|
475
|
+
if (parsed && typeof parsed === "object" && "category" in parsed && "message" in parsed && typeof parsed.category === "string" && typeof parsed.message === "string") {
|
|
306
476
|
return parsed;
|
|
307
477
|
}
|
|
308
478
|
} catch {
|
|
309
479
|
}
|
|
310
|
-
|
|
480
|
+
const fromSuffix = parseCategoryFromMessage(error);
|
|
481
|
+
return {
|
|
482
|
+
category: fromSuffix.category ?? "internal",
|
|
483
|
+
message: fromSuffix.message
|
|
484
|
+
};
|
|
311
485
|
}
|
|
312
486
|
|
|
313
487
|
// src/payment.ts
|
|
@@ -325,10 +499,14 @@ var TERMINAL_STATES = /* @__PURE__ */ new Set([
|
|
|
325
499
|
"failed",
|
|
326
500
|
"cancelled"
|
|
327
501
|
]);
|
|
502
|
+
function normalizeStatus(raw) {
|
|
503
|
+
if (raw === "completed") return "successful";
|
|
504
|
+
return raw;
|
|
505
|
+
}
|
|
328
506
|
function createPaymentInstance(initialResponse, deps) {
|
|
329
507
|
const state = {
|
|
330
508
|
reference: initialResponse.reference,
|
|
331
|
-
status: initialResponse.status,
|
|
509
|
+
status: normalizeStatus(initialResponse.status),
|
|
332
510
|
transaction: null,
|
|
333
511
|
pollingTimer: null,
|
|
334
512
|
resolved: false,
|
|
@@ -339,11 +517,15 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
339
517
|
fetchTransaction: deps.fetchTransaction,
|
|
340
518
|
pollIntervalMs: deps.pollIntervalMs ?? 2e3,
|
|
341
519
|
maxPollDuration: deps.maxPollDuration ?? 3e5,
|
|
342
|
-
maxPollAttempts: deps.maxPollAttempts ?? 150
|
|
520
|
+
maxPollAttempts: deps.maxPollAttempts ?? 150,
|
|
521
|
+
streaming: deps.streaming ?? false,
|
|
522
|
+
openStream: deps.openStream,
|
|
523
|
+
streamHandle: null,
|
|
524
|
+
streamReconnects: 0
|
|
343
525
|
};
|
|
344
526
|
function resolveWithError(error) {
|
|
345
527
|
state.resolved = true;
|
|
346
|
-
|
|
528
|
+
stopUpdates();
|
|
347
529
|
emitEvent("error", parseError(error).message);
|
|
348
530
|
}
|
|
349
531
|
function emitEvent(event, error) {
|
|
@@ -363,13 +545,14 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
363
545
|
state.transaction = txResult.value;
|
|
364
546
|
const event = statusToEvent(status);
|
|
365
547
|
if (event) {
|
|
366
|
-
|
|
548
|
+
const error = status === "failed" ? state.transaction.failureReason ?? void 0 : void 0;
|
|
549
|
+
emitEvent(event, error);
|
|
367
550
|
}
|
|
368
551
|
} else {
|
|
369
552
|
emitEvent("error", `Failed to fetch transaction: ${txResult.error}`);
|
|
370
553
|
}
|
|
371
554
|
state.resolved = true;
|
|
372
|
-
|
|
555
|
+
stopUpdates();
|
|
373
556
|
}
|
|
374
557
|
async function handleStatusUpdate(response) {
|
|
375
558
|
if (response.reference !== state.reference) {
|
|
@@ -378,7 +561,7 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
378
561
|
);
|
|
379
562
|
return;
|
|
380
563
|
}
|
|
381
|
-
const newStatus = response.status;
|
|
564
|
+
const newStatus = normalizeStatus(response.status);
|
|
382
565
|
const oldStatus = state.status;
|
|
383
566
|
state.status = newStatus;
|
|
384
567
|
if (newStatus !== oldStatus) {
|
|
@@ -393,13 +576,13 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
393
576
|
}
|
|
394
577
|
}
|
|
395
578
|
function handlePollError(error) {
|
|
396
|
-
const
|
|
397
|
-
if (
|
|
579
|
+
const parsed = parseError(error);
|
|
580
|
+
if (parsed.category === "not_found") {
|
|
398
581
|
return;
|
|
399
582
|
}
|
|
400
|
-
emitEvent("error",
|
|
583
|
+
emitEvent("error", parsed.message);
|
|
401
584
|
state.resolved = true;
|
|
402
|
-
|
|
585
|
+
stopUpdates();
|
|
403
586
|
}
|
|
404
587
|
function scheduleNextPoll() {
|
|
405
588
|
if (state.resolved || state.pollingTimer) {
|
|
@@ -412,7 +595,7 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
412
595
|
}
|
|
413
596
|
async function pollStatus() {
|
|
414
597
|
if (state.resolved) {
|
|
415
|
-
|
|
598
|
+
stopUpdates();
|
|
416
599
|
return;
|
|
417
600
|
}
|
|
418
601
|
if (state.pollAttempts >= state.maxPollAttempts) {
|
|
@@ -431,19 +614,66 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
431
614
|
handlePollError(result.error);
|
|
432
615
|
}
|
|
433
616
|
if (state.resolved) {
|
|
434
|
-
|
|
617
|
+
stopUpdates();
|
|
435
618
|
return;
|
|
436
619
|
}
|
|
437
620
|
scheduleNextPoll();
|
|
438
621
|
}
|
|
439
|
-
function
|
|
622
|
+
function closeStream() {
|
|
623
|
+
if (state.streamHandle) {
|
|
624
|
+
state.streamHandle.close();
|
|
625
|
+
state.streamHandle = null;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function startStream() {
|
|
629
|
+
if (state.resolved || !state.openStream) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
state.streamHandle = state.openStream({
|
|
633
|
+
reference: state.reference,
|
|
634
|
+
onStatus: (status) => {
|
|
635
|
+
void handleStatusUpdate(status);
|
|
636
|
+
},
|
|
637
|
+
onError: () => handleStreamFailure(),
|
|
638
|
+
onClose: () => handleStreamFailure()
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
function handleStreamFailure() {
|
|
642
|
+
closeStream();
|
|
643
|
+
if (state.resolved) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (state.streamReconnects < MAX_STREAM_RECONNECTS) {
|
|
647
|
+
state.streamReconnects += 1;
|
|
648
|
+
const backoff = 500 * 2 ** (state.streamReconnects - 1) + Math.random() * 250;
|
|
649
|
+
setTimeout(() => {
|
|
650
|
+
if (!state.resolved) {
|
|
651
|
+
startStream();
|
|
652
|
+
}
|
|
653
|
+
}, backoff);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
440
656
|
scheduleNextPoll();
|
|
441
657
|
}
|
|
442
|
-
function
|
|
658
|
+
function startUpdates() {
|
|
659
|
+
if (TERMINAL_STATES.has(state.status)) {
|
|
660
|
+
setTimeout(() => {
|
|
661
|
+
void handleTerminalState(state.status);
|
|
662
|
+
}, 0);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (state.streaming && state.openStream) {
|
|
666
|
+
startStream();
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
scheduleNextPoll();
|
|
670
|
+
}
|
|
671
|
+
function stopUpdates() {
|
|
443
672
|
if (state.pollingTimer) {
|
|
444
673
|
clearTimeout(state.pollingTimer);
|
|
445
674
|
state.pollingTimer = null;
|
|
446
675
|
}
|
|
676
|
+
closeStream();
|
|
447
677
|
}
|
|
448
678
|
function on(event, handler) {
|
|
449
679
|
state.emitter.on(event, handler);
|
|
@@ -458,37 +688,28 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
458
688
|
return paymentInstance;
|
|
459
689
|
}
|
|
460
690
|
function wait() {
|
|
461
|
-
return new Promise((resolve
|
|
691
|
+
return new Promise((resolve) => {
|
|
462
692
|
if (state.resolved) {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
reject(new Error(`Payment ${state.status ?? "error"}`));
|
|
467
|
-
}
|
|
693
|
+
resolve(
|
|
694
|
+
state.status === "successful" && state.transaction ? state.transaction : null
|
|
695
|
+
);
|
|
468
696
|
return;
|
|
469
697
|
}
|
|
470
698
|
function onSuccess() {
|
|
471
699
|
cleanup();
|
|
472
|
-
|
|
473
|
-
resolve(state.transaction);
|
|
474
|
-
} else {
|
|
475
|
-
reject(
|
|
476
|
-
new Error("Payment successful but transaction data unavailable")
|
|
477
|
-
);
|
|
478
|
-
}
|
|
700
|
+
resolve(state.transaction ?? null);
|
|
479
701
|
}
|
|
480
702
|
function onFailed() {
|
|
481
703
|
cleanup();
|
|
482
|
-
|
|
704
|
+
resolve(null);
|
|
483
705
|
}
|
|
484
706
|
function onCancelled() {
|
|
485
707
|
cleanup();
|
|
486
|
-
|
|
708
|
+
resolve(null);
|
|
487
709
|
}
|
|
488
|
-
function onError(
|
|
710
|
+
function onError() {
|
|
489
711
|
cleanup();
|
|
490
|
-
|
|
491
|
-
reject(new Error(eventData.error ?? "Payment error"));
|
|
712
|
+
resolve(null);
|
|
492
713
|
}
|
|
493
714
|
function cleanup() {
|
|
494
715
|
state.emitter.off("success", onSuccess);
|
|
@@ -514,7 +735,7 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
514
735
|
off,
|
|
515
736
|
wait
|
|
516
737
|
};
|
|
517
|
-
|
|
738
|
+
startUpdates();
|
|
518
739
|
return paymentInstance;
|
|
519
740
|
}
|
|
520
741
|
function verifyWebhookSignature(input) {
|
|
@@ -532,14 +753,17 @@ function verifyWebhookSignature(input) {
|
|
|
532
753
|
function generateReference() {
|
|
533
754
|
return crypto.randomBytes(16).toString("hex").slice(0, 15);
|
|
534
755
|
}
|
|
756
|
+
function throwValidation(message) {
|
|
757
|
+
throw createSdkError({ category: "validation", message });
|
|
758
|
+
}
|
|
535
759
|
function validateAmount(amount) {
|
|
536
760
|
if (!Number.isInteger(amount) || amount <= 0) {
|
|
537
|
-
|
|
761
|
+
throwValidation("amount must be a positive integer");
|
|
538
762
|
}
|
|
539
763
|
}
|
|
540
764
|
function validateNonEmpty(value, fieldName) {
|
|
541
765
|
if (!value || value.trim() === "") {
|
|
542
|
-
|
|
766
|
+
throwValidation(`${fieldName} is required`);
|
|
543
767
|
}
|
|
544
768
|
}
|
|
545
769
|
function createSdkInstance(config) {
|
|
@@ -562,7 +786,9 @@ function createSdkInstance(config) {
|
|
|
562
786
|
}),
|
|
563
787
|
pollIntervalMs: config.maxPollIntervalMs,
|
|
564
788
|
maxPollDuration: config.maxPollDurationMs,
|
|
565
|
-
maxPollAttempts: config.maxPollAttempts
|
|
789
|
+
maxPollAttempts: config.maxPollAttempts,
|
|
790
|
+
streaming: config.streaming,
|
|
791
|
+
openStream: config.streaming ? transport.openStream : void 0
|
|
566
792
|
};
|
|
567
793
|
async function collectPayment(input) {
|
|
568
794
|
const reference = input.reference ?? generateReference();
|
|
@@ -571,7 +797,7 @@ function createSdkInstance(config) {
|
|
|
571
797
|
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
572
798
|
validateNonEmpty(input.description, "description");
|
|
573
799
|
if (input.method === "bank" && !input.bank) {
|
|
574
|
-
|
|
800
|
+
throwValidation('bank details are required when method is "bank"');
|
|
575
801
|
}
|
|
576
802
|
let payload = { ...input, reference };
|
|
577
803
|
if (config.hooks?.beforeCollect) {
|
|
@@ -592,19 +818,10 @@ function createSdkInstance(config) {
|
|
|
592
818
|
payload
|
|
593
819
|
);
|
|
594
820
|
}
|
|
595
|
-
if (result.
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
return createPaymentInstance(
|
|
599
|
-
{ reference, status: "pending" },
|
|
600
|
-
{
|
|
601
|
-
fetchStatus: async () => slangTs.Err(result.error),
|
|
602
|
-
fetchTransaction: async () => slangTs.Err(result.error),
|
|
603
|
-
pollIntervalMs: 0,
|
|
604
|
-
maxPollAttempts: 1,
|
|
605
|
-
maxPollDuration: Number.MAX_SAFE_INTEGER
|
|
606
|
-
}
|
|
607
|
-
);
|
|
821
|
+
if (result.isErr) {
|
|
822
|
+
throw createSdkError(parseError(result.error));
|
|
823
|
+
}
|
|
824
|
+
return createPaymentInstance(result.value, commonDeps);
|
|
608
825
|
}
|
|
609
826
|
async function collectPaymentAndResolve(input) {
|
|
610
827
|
const reference = input.reference ?? generateReference();
|
|
@@ -613,7 +830,7 @@ function createSdkInstance(config) {
|
|
|
613
830
|
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
614
831
|
validateNonEmpty(input.description, "description");
|
|
615
832
|
if (input.method === "bank" && !input.bank) {
|
|
616
|
-
|
|
833
|
+
throwValidation('bank details are required when method is "bank"');
|
|
617
834
|
}
|
|
618
835
|
let payload = { ...input, reference };
|
|
619
836
|
if (config.hooks?.beforeCollect) {
|
|
@@ -672,19 +889,10 @@ function createSdkInstance(config) {
|
|
|
672
889
|
payload
|
|
673
890
|
);
|
|
674
891
|
}
|
|
675
|
-
if (result.
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
return createPaymentInstance(
|
|
679
|
-
{ reference, status: "pending" },
|
|
680
|
-
{
|
|
681
|
-
fetchStatus: async () => slangTs.Err(result.error),
|
|
682
|
-
fetchTransaction: async () => slangTs.Err(result.error),
|
|
683
|
-
pollIntervalMs: 0,
|
|
684
|
-
maxPollAttempts: 1,
|
|
685
|
-
maxPollDuration: Number.MAX_SAFE_INTEGER
|
|
686
|
-
}
|
|
687
|
-
);
|
|
892
|
+
if (result.isErr) {
|
|
893
|
+
throw createSdkError(parseError(result.error));
|
|
894
|
+
}
|
|
895
|
+
return createPaymentInstance(result.value, commonDeps);
|
|
688
896
|
}
|
|
689
897
|
async function makePayoutAndResolve(input) {
|
|
690
898
|
const reference = input.reference ?? generateReference();
|
|
@@ -737,7 +945,7 @@ function createSdkInstance(config) {
|
|
|
737
945
|
}
|
|
738
946
|
async function getTransaction(input) {
|
|
739
947
|
if (!input.id && !input.reference) {
|
|
740
|
-
|
|
948
|
+
throwValidation("id or reference is required");
|
|
741
949
|
}
|
|
742
950
|
const result = await transport.send({
|
|
743
951
|
action: SDK_ACTIONS.getTransaction,
|
|
@@ -765,14 +973,14 @@ function createSdkInstance(config) {
|
|
|
765
973
|
validateNonEmpty(input.description, "description");
|
|
766
974
|
if (input.items) {
|
|
767
975
|
if (input.items.length > 50) {
|
|
768
|
-
|
|
976
|
+
throwValidation("items must not exceed 50");
|
|
769
977
|
}
|
|
770
978
|
for (const item of input.items) {
|
|
771
979
|
if (!Number.isInteger(item.quantity) || item.quantity <= 0) {
|
|
772
|
-
|
|
980
|
+
throwValidation("item quantity must be a positive integer");
|
|
773
981
|
}
|
|
774
982
|
if (!Number.isInteger(item.unitPrice) || item.unitPrice <= 0) {
|
|
775
|
-
|
|
983
|
+
throwValidation("item unitPrice must be a positive integer");
|
|
776
984
|
}
|
|
777
985
|
}
|
|
778
986
|
}
|
|
@@ -832,6 +1040,7 @@ function createNylonPay(config) {
|
|
|
832
1040
|
maxPollIntervalMs: config.maxPollIntervalMs ?? DEFAULT_MAX_POLL_INTERVAL_MS,
|
|
833
1041
|
maxPollDurationMs: config.maxPollDurationMs ?? DEFAULT_MAX_POLL_DURATION_MS,
|
|
834
1042
|
maxPollAttempts: config.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS,
|
|
1043
|
+
streaming: config.streaming ?? DEFAULT_STREAMING,
|
|
835
1044
|
fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
|
|
836
1045
|
hooks: config.hooks
|
|
837
1046
|
};
|
|
@@ -841,6 +1050,7 @@ function createNylonPay(config) {
|
|
|
841
1050
|
}
|
|
842
1051
|
|
|
843
1052
|
exports.createNylonPay = createNylonPay;
|
|
1053
|
+
exports.createSdkError = createSdkError;
|
|
844
1054
|
exports.parseError = parseError;
|
|
845
1055
|
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
846
1056
|
//# sourceMappingURL=index.cjs.map
|