@nile-squad/nylonpay-ts 1.0.6 → 1.0.8
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 +145 -262
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -23
- package/dist/index.d.ts +54 -23
- package/dist/index.js +145 -262
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createHash, createHmac, timingSafeEqual, randomBytes } from 'crypto';
|
|
|
2
2
|
import { Ok, Err, safeTry } from 'slang-ts';
|
|
3
3
|
import { type, platform, arch, release, hostname } from 'os';
|
|
4
4
|
|
|
5
|
-
// src/
|
|
5
|
+
// src/create-nylon-pay.ts
|
|
6
6
|
|
|
7
7
|
// src/pubsub.ts
|
|
8
8
|
function createEmitter() {
|
|
@@ -61,9 +61,7 @@ var DEFAULT_MAX_RETRIES = 3;
|
|
|
61
61
|
var DEFAULT_MAX_POLL_INTERVAL_MS = 2e3;
|
|
62
62
|
var DEFAULT_MAX_POLL_DURATION_MS = 3e5;
|
|
63
63
|
var DEFAULT_MAX_POLL_ATTEMPTS = 150;
|
|
64
|
-
var
|
|
65
|
-
var STREAM_PATH = "/sse/transaction";
|
|
66
|
-
var MAX_STREAM_RECONNECTS = 2;
|
|
64
|
+
var POLL_JITTER_MS = 250;
|
|
67
65
|
var SDK_SERVICE = "sdk";
|
|
68
66
|
var SDK_ACTIONS = {
|
|
69
67
|
collectPayment: "sdk-collect-payment",
|
|
@@ -91,13 +89,22 @@ function generateFingerprint() {
|
|
|
91
89
|
function generateNonce(length = 16) {
|
|
92
90
|
return randomBytes(length).toString("hex");
|
|
93
91
|
}
|
|
92
|
+
function compareByCodePoint(first, second) {
|
|
93
|
+
if (first < second) {
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
if (first > second) {
|
|
97
|
+
return 1;
|
|
98
|
+
}
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
94
101
|
function sortValue(value) {
|
|
95
102
|
if (Array.isArray(value)) {
|
|
96
103
|
return value.map((entry) => sortValue(entry));
|
|
97
104
|
}
|
|
98
105
|
if (value && typeof value === "object") {
|
|
99
106
|
const sortedEntries = Object.entries(value).sort(
|
|
100
|
-
([firstKey], [secondKey]) => firstKey
|
|
107
|
+
([firstKey], [secondKey]) => compareByCodePoint(firstKey, secondKey)
|
|
101
108
|
);
|
|
102
109
|
return Object.fromEntries(
|
|
103
110
|
sortedEntries.map(([entryKey, entryValue]) => [
|
|
@@ -121,42 +128,6 @@ function createSignature(input) {
|
|
|
121
128
|
function createTimestamp() {
|
|
122
129
|
return Date.now().toString();
|
|
123
130
|
}
|
|
124
|
-
|
|
125
|
-
// src/sse-parse.ts
|
|
126
|
-
function parseBlock(block) {
|
|
127
|
-
let event = "message";
|
|
128
|
-
const dataLines = [];
|
|
129
|
-
for (const rawLine of block.split("\n")) {
|
|
130
|
-
const line = rawLine.replace(/\r$/, "");
|
|
131
|
-
if (line.startsWith(":")) {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (line.startsWith("event:")) {
|
|
135
|
-
event = line.slice("event:".length).trim();
|
|
136
|
-
} else if (line.startsWith("data:")) {
|
|
137
|
-
dataLines.push(line.slice("data:".length).trim());
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (dataLines.length === 0) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
return { event, data: dataLines.join("\n") };
|
|
144
|
-
}
|
|
145
|
-
function parseSseBuffer(buffer) {
|
|
146
|
-
const messages = [];
|
|
147
|
-
let rest = buffer;
|
|
148
|
-
let separator = rest.indexOf("\n\n");
|
|
149
|
-
while (separator !== -1) {
|
|
150
|
-
const block = rest.slice(0, separator);
|
|
151
|
-
rest = rest.slice(separator + 2);
|
|
152
|
-
const message = parseBlock(block);
|
|
153
|
-
if (message) {
|
|
154
|
-
messages.push(message);
|
|
155
|
-
}
|
|
156
|
-
separator = rest.indexOf("\n\n");
|
|
157
|
-
}
|
|
158
|
-
return { messages, rest };
|
|
159
|
-
}
|
|
160
131
|
function verifyResponseSignature(data, signature, secret) {
|
|
161
132
|
const expectedSignature = createHmac("sha256", secret).update(createCanonicalPayload(data)).digest("hex");
|
|
162
133
|
const providedBuffer = Buffer.from(signature, "hex");
|
|
@@ -169,13 +140,6 @@ function verifyResponseSignature(data, signature, secret) {
|
|
|
169
140
|
|
|
170
141
|
// src/transport.ts
|
|
171
142
|
var CACHED_FINGERPRINT = generateFingerprint();
|
|
172
|
-
function streamUrl(baseUrl) {
|
|
173
|
-
try {
|
|
174
|
-
return new URL(baseUrl).origin + STREAM_PATH;
|
|
175
|
-
} catch {
|
|
176
|
-
return baseUrl.replace(/\/api\/services\/?$/u, "") + STREAM_PATH;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
143
|
var KNOWN_CATEGORIES = /* @__PURE__ */ new Set([
|
|
180
144
|
"auth",
|
|
181
145
|
"validation",
|
|
@@ -341,22 +305,30 @@ function createTransport({
|
|
|
341
305
|
const { status, message, data } = responseBody;
|
|
342
306
|
if (status === true) {
|
|
343
307
|
const { data: strippedData, responseSignature } = stripResponseSignature(data);
|
|
344
|
-
if (responseSignature) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
308
|
+
if (!responseSignature) {
|
|
309
|
+
cleanup();
|
|
310
|
+
return Err(
|
|
311
|
+
JSON.stringify({
|
|
312
|
+
category: "internal",
|
|
313
|
+
message: "Response signature missing",
|
|
314
|
+
retryable: false
|
|
315
|
+
})
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
const isValid = verifyResponseSignature(
|
|
319
|
+
strippedData,
|
|
320
|
+
responseSignature,
|
|
321
|
+
apiSecret
|
|
322
|
+
);
|
|
323
|
+
if (!isValid) {
|
|
324
|
+
cleanup();
|
|
325
|
+
return Err(
|
|
326
|
+
JSON.stringify({
|
|
327
|
+
category: "internal",
|
|
328
|
+
message: "Response signature verification failed",
|
|
329
|
+
retryable: false
|
|
330
|
+
})
|
|
349
331
|
);
|
|
350
|
-
if (!isValid) {
|
|
351
|
-
cleanup();
|
|
352
|
-
return Err(
|
|
353
|
-
JSON.stringify({
|
|
354
|
-
category: "internal",
|
|
355
|
-
message: "Response signature verification failed",
|
|
356
|
-
retryable: false
|
|
357
|
-
})
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
332
|
}
|
|
361
333
|
cleanup();
|
|
362
334
|
return Ok(strippedData);
|
|
@@ -381,91 +353,7 @@ function createTransport({
|
|
|
381
353
|
}
|
|
382
354
|
return attempt(0);
|
|
383
355
|
}
|
|
384
|
-
|
|
385
|
-
const controller = new AbortController();
|
|
386
|
-
let closed = false;
|
|
387
|
-
const close = () => {
|
|
388
|
-
if (closed) {
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
closed = true;
|
|
392
|
-
controller.abort();
|
|
393
|
-
};
|
|
394
|
-
const payload = {
|
|
395
|
-
reference: input.reference,
|
|
396
|
-
_fingerprint: CACHED_FINGERPRINT
|
|
397
|
-
};
|
|
398
|
-
const headers = buildAuthHeaders({ apiKey, apiSecret, payload });
|
|
399
|
-
const url = streamUrl(baseUrl);
|
|
400
|
-
const run = async () => {
|
|
401
|
-
const fetchResult = await safeTry(
|
|
402
|
-
() => fetchImpl(url, {
|
|
403
|
-
method: "POST",
|
|
404
|
-
headers,
|
|
405
|
-
body: JSON.stringify(payload),
|
|
406
|
-
signal: controller.signal
|
|
407
|
-
})
|
|
408
|
-
);
|
|
409
|
-
if (fetchResult.isErr) {
|
|
410
|
-
if (!closed) {
|
|
411
|
-
input.onError(fetchResult.error);
|
|
412
|
-
}
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
const response = fetchResult.value;
|
|
416
|
-
if (!response.ok || !response.body) {
|
|
417
|
-
const textResult = await safeTry(() => response.text());
|
|
418
|
-
const message = textResult.isOk ? textResult.value : `HTTP ${response.status}`;
|
|
419
|
-
if (!closed) {
|
|
420
|
-
input.onError(message || `HTTP ${response.status}`);
|
|
421
|
-
}
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
const reader = response.body.getReader();
|
|
425
|
-
const decoder = new TextDecoder();
|
|
426
|
-
let buffer = "";
|
|
427
|
-
while (!closed) {
|
|
428
|
-
const chunkResult = await safeTry(() => reader.read());
|
|
429
|
-
if (chunkResult.isErr) {
|
|
430
|
-
if (!closed) {
|
|
431
|
-
input.onError(chunkResult.error);
|
|
432
|
-
}
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
const chunk = chunkResult.value;
|
|
436
|
-
if (chunk.done) {
|
|
437
|
-
break;
|
|
438
|
-
}
|
|
439
|
-
buffer += decoder.decode(chunk.value, { stream: true });
|
|
440
|
-
const { messages, rest } = parseSseBuffer(buffer);
|
|
441
|
-
buffer = rest;
|
|
442
|
-
for (const message of messages) {
|
|
443
|
-
if (message.event === "status") {
|
|
444
|
-
const statusResult = await safeTry(
|
|
445
|
-
() => JSON.parse(message.data)
|
|
446
|
-
);
|
|
447
|
-
if (statusResult.isOk) {
|
|
448
|
-
input.onStatus(statusResult.value);
|
|
449
|
-
}
|
|
450
|
-
} else if (message.event === "error") {
|
|
451
|
-
const errDataResult = await safeTry(
|
|
452
|
-
() => JSON.parse(message.data)
|
|
453
|
-
);
|
|
454
|
-
const text = errDataResult.isOk ? errDataResult.value.message ?? message.data : message.data;
|
|
455
|
-
close();
|
|
456
|
-
input.onError(text);
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
if (!closed) {
|
|
462
|
-
input.onClose();
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
void run();
|
|
466
|
-
return { close };
|
|
467
|
-
}
|
|
468
|
-
return { send, openStream, parseError };
|
|
356
|
+
return { send, parseError };
|
|
469
357
|
}
|
|
470
358
|
function parseError(error) {
|
|
471
359
|
try {
|
|
@@ -515,22 +403,20 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
515
403
|
fetchTransaction: deps.fetchTransaction,
|
|
516
404
|
pollIntervalMs: deps.pollIntervalMs ?? 2e3,
|
|
517
405
|
maxPollDuration: deps.maxPollDuration ?? 3e5,
|
|
518
|
-
maxPollAttempts: deps.maxPollAttempts ?? 150
|
|
519
|
-
streaming: deps.streaming ?? false,
|
|
520
|
-
openStream: deps.openStream,
|
|
521
|
-
streamHandle: null,
|
|
522
|
-
streamReconnects: 0
|
|
406
|
+
maxPollAttempts: deps.maxPollAttempts ?? 150
|
|
523
407
|
};
|
|
524
408
|
function resolveWithError(error) {
|
|
525
409
|
state.resolved = true;
|
|
526
410
|
stopUpdates();
|
|
527
411
|
emitEvent("error", parseError(error).message);
|
|
528
412
|
}
|
|
529
|
-
function emitEvent(event, error) {
|
|
413
|
+
function emitEvent(event, error, category, retryable) {
|
|
530
414
|
const data = {
|
|
531
415
|
event,
|
|
532
416
|
transaction: state.transaction ?? void 0,
|
|
533
417
|
error,
|
|
418
|
+
category,
|
|
419
|
+
retryable,
|
|
534
420
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
535
421
|
};
|
|
536
422
|
state.emitter.emit(event, data);
|
|
@@ -553,6 +439,9 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
553
439
|
stopUpdates();
|
|
554
440
|
}
|
|
555
441
|
async function handleStatusUpdate(response) {
|
|
442
|
+
if (state.resolved) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
556
445
|
if (response.reference !== state.reference) {
|
|
557
446
|
resolveWithError(
|
|
558
447
|
`Reference mismatch: expected ${state.reference} but got ${response.reference}`
|
|
@@ -586,10 +475,11 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
586
475
|
if (state.resolved || state.pollingTimer) {
|
|
587
476
|
return;
|
|
588
477
|
}
|
|
478
|
+
const delay2 = state.pollIntervalMs + Math.random() * POLL_JITTER_MS;
|
|
589
479
|
state.pollingTimer = setTimeout(() => {
|
|
590
480
|
state.pollingTimer = null;
|
|
591
481
|
void pollStatus();
|
|
592
|
-
},
|
|
482
|
+
}, delay2);
|
|
593
483
|
}
|
|
594
484
|
async function pollStatus() {
|
|
595
485
|
if (state.resolved) {
|
|
@@ -617,42 +507,6 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
617
507
|
}
|
|
618
508
|
scheduleNextPoll();
|
|
619
509
|
}
|
|
620
|
-
function closeStream() {
|
|
621
|
-
if (state.streamHandle) {
|
|
622
|
-
state.streamHandle.close();
|
|
623
|
-
state.streamHandle = null;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
function startStream() {
|
|
627
|
-
if (state.resolved || !state.openStream) {
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
state.streamHandle = state.openStream({
|
|
631
|
-
reference: state.reference,
|
|
632
|
-
onStatus: (status) => {
|
|
633
|
-
void handleStatusUpdate(status);
|
|
634
|
-
},
|
|
635
|
-
onError: () => handleStreamFailure(),
|
|
636
|
-
onClose: () => handleStreamFailure()
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
function handleStreamFailure() {
|
|
640
|
-
closeStream();
|
|
641
|
-
if (state.resolved) {
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
if (state.streamReconnects < MAX_STREAM_RECONNECTS) {
|
|
645
|
-
state.streamReconnects += 1;
|
|
646
|
-
const backoff = 500 * 2 ** (state.streamReconnects - 1) + Math.random() * 250;
|
|
647
|
-
setTimeout(() => {
|
|
648
|
-
if (!state.resolved) {
|
|
649
|
-
startStream();
|
|
650
|
-
}
|
|
651
|
-
}, backoff);
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
scheduleNextPoll();
|
|
655
|
-
}
|
|
656
510
|
function startUpdates() {
|
|
657
511
|
if (TERMINAL_STATES.has(state.status)) {
|
|
658
512
|
setTimeout(() => {
|
|
@@ -660,10 +514,6 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
660
514
|
}, 0);
|
|
661
515
|
return;
|
|
662
516
|
}
|
|
663
|
-
if (state.streaming && state.openStream) {
|
|
664
|
-
startStream();
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
517
|
scheduleNextPoll();
|
|
668
518
|
}
|
|
669
519
|
function stopUpdates() {
|
|
@@ -671,7 +521,6 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
671
521
|
clearTimeout(state.pollingTimer);
|
|
672
522
|
state.pollingTimer = null;
|
|
673
523
|
}
|
|
674
|
-
closeStream();
|
|
675
524
|
}
|
|
676
525
|
function on(event, handler) {
|
|
677
526
|
state.emitter.on(event, handler);
|
|
@@ -733,24 +582,76 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
733
582
|
off,
|
|
734
583
|
wait
|
|
735
584
|
};
|
|
736
|
-
|
|
585
|
+
if (deps.initialError) {
|
|
586
|
+
state.resolved = true;
|
|
587
|
+
const err = deps.initialError;
|
|
588
|
+
setTimeout(() => {
|
|
589
|
+
emitEvent("error", err.message, err.category, err.retryable);
|
|
590
|
+
}, 0);
|
|
591
|
+
} else {
|
|
592
|
+
startUpdates();
|
|
593
|
+
}
|
|
737
594
|
return paymentInstance;
|
|
738
595
|
}
|
|
596
|
+
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
597
|
+
function decodePayload(payload) {
|
|
598
|
+
return typeof payload === "string" ? payload : Buffer.from(payload).toString("utf8");
|
|
599
|
+
}
|
|
600
|
+
function extractSignedTimestampMs(payloadString) {
|
|
601
|
+
let parsed;
|
|
602
|
+
try {
|
|
603
|
+
parsed = JSON.parse(payloadString);
|
|
604
|
+
} catch {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
if (!parsed || typeof parsed !== "object") {
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
const raw = parsed.timestamp;
|
|
611
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
612
|
+
return raw < 1e12 ? raw * 1e3 : raw;
|
|
613
|
+
}
|
|
614
|
+
if (typeof raw === "string") {
|
|
615
|
+
const ms = Date.parse(raw);
|
|
616
|
+
return Number.isNaN(ms) ? null : ms;
|
|
617
|
+
}
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
739
620
|
function verifyWebhookSignature(input) {
|
|
740
|
-
const
|
|
621
|
+
const payloadString = decodePayload(input.payload);
|
|
622
|
+
const payloadBytes = Buffer.from(payloadString, "utf8");
|
|
741
623
|
const expectedSignature = createHmac("sha256", input.secret).update(payloadBytes).digest("hex");
|
|
742
624
|
const providedBuffer = Buffer.from(input.signature, "hex");
|
|
743
625
|
const expectedBuffer = Buffer.from(expectedSignature, "hex");
|
|
744
626
|
if (providedBuffer.length !== expectedBuffer.length) {
|
|
745
627
|
return false;
|
|
746
628
|
}
|
|
747
|
-
|
|
629
|
+
if (!timingSafeEqual(providedBuffer, expectedBuffer)) {
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
const toleranceSeconds = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
|
|
633
|
+
if (toleranceSeconds <= 0) {
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
const timestampMs = extractSignedTimestampMs(payloadString);
|
|
637
|
+
if (timestampMs === null) {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
const ageMs = Math.abs(Date.now() - timestampMs);
|
|
641
|
+
return ageMs <= toleranceSeconds * 1e3;
|
|
748
642
|
}
|
|
749
643
|
|
|
750
644
|
// src/sdk.ts
|
|
751
645
|
function generateReference() {
|
|
752
646
|
return randomBytes(16).toString("hex").slice(0, 15);
|
|
753
647
|
}
|
|
648
|
+
async function runHook(hook, ...args) {
|
|
649
|
+
if (!hook || hook.enabled === false) return void 0;
|
|
650
|
+
const result = await safeTry(async () => hook.fn(...args));
|
|
651
|
+
if (result.isOk) return result.value;
|
|
652
|
+
await safeTry(async () => hook.onError(result.error));
|
|
653
|
+
return void 0;
|
|
654
|
+
}
|
|
754
655
|
function throwValidation(message) {
|
|
755
656
|
throw createSdkError({ category: "validation", message });
|
|
756
657
|
}
|
|
@@ -784,9 +685,7 @@ function createSdkInstance(config) {
|
|
|
784
685
|
}),
|
|
785
686
|
pollIntervalMs: config.maxPollIntervalMs,
|
|
786
687
|
maxPollDuration: config.maxPollDurationMs,
|
|
787
|
-
maxPollAttempts: config.maxPollAttempts
|
|
788
|
-
streaming: config.streaming,
|
|
789
|
-
openStream: config.streaming ? transport.openStream : void 0
|
|
688
|
+
maxPollAttempts: config.maxPollAttempts
|
|
790
689
|
};
|
|
791
690
|
async function collectPayment(input) {
|
|
792
691
|
const reference = input.reference ?? generateReference();
|
|
@@ -798,26 +697,24 @@ function createSdkInstance(config) {
|
|
|
798
697
|
throwValidation('bank details are required when method is "bank"');
|
|
799
698
|
}
|
|
800
699
|
let payload = { ...input, reference };
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
805
|
-
}
|
|
700
|
+
const mutated = await runHook(config.hooks?.beforeCollect, payload);
|
|
701
|
+
if (mutated != null)
|
|
702
|
+
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
806
703
|
const result = await transport.send({
|
|
807
704
|
action: SDK_ACTIONS.collectPayment,
|
|
808
705
|
payload
|
|
809
706
|
});
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
}) : Err(result.error),
|
|
816
|
-
payload
|
|
817
|
-
);
|
|
818
|
-
}
|
|
707
|
+
await runHook(
|
|
708
|
+
config.hooks?.afterCollect,
|
|
709
|
+
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
710
|
+
payload
|
|
711
|
+
);
|
|
819
712
|
if (result.isErr) {
|
|
820
|
-
|
|
713
|
+
const sdkErr = parseError(result.error);
|
|
714
|
+
return createPaymentInstance(
|
|
715
|
+
{ reference, status: "pending" },
|
|
716
|
+
{ ...commonDeps, initialError: sdkErr }
|
|
717
|
+
);
|
|
821
718
|
}
|
|
822
719
|
return createPaymentInstance(result.value, commonDeps);
|
|
823
720
|
}
|
|
@@ -831,24 +728,18 @@ function createSdkInstance(config) {
|
|
|
831
728
|
throwValidation('bank details are required when method is "bank"');
|
|
832
729
|
}
|
|
833
730
|
let payload = { ...input, reference };
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
838
|
-
}
|
|
731
|
+
const mutated = await runHook(config.hooks?.beforeCollect, payload);
|
|
732
|
+
if (mutated != null)
|
|
733
|
+
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
839
734
|
const result = await transport.send({
|
|
840
735
|
action: SDK_ACTIONS.collectPaymentAndResolve,
|
|
841
736
|
payload
|
|
842
737
|
});
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
}) : Err(result.error),
|
|
849
|
-
payload
|
|
850
|
-
);
|
|
851
|
-
}
|
|
738
|
+
await runHook(
|
|
739
|
+
config.hooks?.afterCollect,
|
|
740
|
+
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
741
|
+
payload
|
|
742
|
+
);
|
|
852
743
|
if (result.isOk) {
|
|
853
744
|
return Ok(result.value);
|
|
854
745
|
}
|
|
@@ -869,26 +760,24 @@ function createSdkInstance(config) {
|
|
|
869
760
|
"destination.accountNumber"
|
|
870
761
|
);
|
|
871
762
|
let payload = { ...input, reference };
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
876
|
-
}
|
|
763
|
+
const mutated = await runHook(config.hooks?.beforePayout, payload);
|
|
764
|
+
if (mutated != null)
|
|
765
|
+
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
877
766
|
const result = await transport.send({
|
|
878
767
|
action: SDK_ACTIONS.makePayout,
|
|
879
768
|
payload
|
|
880
769
|
});
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
}) : Err(result.error),
|
|
887
|
-
payload
|
|
888
|
-
);
|
|
889
|
-
}
|
|
770
|
+
await runHook(
|
|
771
|
+
config.hooks?.afterPayout,
|
|
772
|
+
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
773
|
+
payload
|
|
774
|
+
);
|
|
890
775
|
if (result.isErr) {
|
|
891
|
-
|
|
776
|
+
const sdkErr = parseError(result.error);
|
|
777
|
+
return createPaymentInstance(
|
|
778
|
+
{ reference, status: "pending" },
|
|
779
|
+
{ ...commonDeps, initialError: sdkErr }
|
|
780
|
+
);
|
|
892
781
|
}
|
|
893
782
|
return createPaymentInstance(result.value, commonDeps);
|
|
894
783
|
}
|
|
@@ -907,24 +796,18 @@ function createSdkInstance(config) {
|
|
|
907
796
|
"destination.accountNumber"
|
|
908
797
|
);
|
|
909
798
|
let payload = { ...input, reference };
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
914
|
-
}
|
|
799
|
+
const mutated = await runHook(config.hooks?.beforePayout, payload);
|
|
800
|
+
if (mutated != null)
|
|
801
|
+
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
915
802
|
const result = await transport.send({
|
|
916
803
|
action: SDK_ACTIONS.makePayoutAndResolve,
|
|
917
804
|
payload
|
|
918
805
|
});
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
}) : Err(result.error),
|
|
925
|
-
payload
|
|
926
|
-
);
|
|
927
|
-
}
|
|
806
|
+
await runHook(
|
|
807
|
+
config.hooks?.afterPayout,
|
|
808
|
+
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
809
|
+
payload
|
|
810
|
+
);
|
|
928
811
|
if (result.isOk) {
|
|
929
812
|
return Ok(result.value);
|
|
930
813
|
}
|
|
@@ -1024,7 +907,8 @@ function createNylonPay(config) {
|
|
|
1024
907
|
throw new Error('apiSecret must start with "nps_"');
|
|
1025
908
|
}
|
|
1026
909
|
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
1027
|
-
const
|
|
910
|
+
const secretHash = createHash("sha256").update(config.apiSecret).digest("hex").slice(0, 16);
|
|
911
|
+
const instanceKey = `${config.apiKey}:${baseUrl}:${secretHash}`;
|
|
1028
912
|
if (!config.force) {
|
|
1029
913
|
const existing = instances.get(instanceKey);
|
|
1030
914
|
if (existing) return existing;
|
|
@@ -1038,7 +922,6 @@ function createNylonPay(config) {
|
|
|
1038
922
|
maxPollIntervalMs: config.maxPollIntervalMs ?? DEFAULT_MAX_POLL_INTERVAL_MS,
|
|
1039
923
|
maxPollDurationMs: config.maxPollDurationMs ?? DEFAULT_MAX_POLL_DURATION_MS,
|
|
1040
924
|
maxPollAttempts: config.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS,
|
|
1041
|
-
streaming: config.streaming ?? DEFAULT_STREAMING,
|
|
1042
925
|
fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
|
|
1043
926
|
hooks: config.hooks
|
|
1044
927
|
};
|