@nile-squad/nylonpay-ts 1.0.7 → 1.0.9
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/README.md +8 -6
- package/dist/index.cjs +141 -263
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -27
- package/dist/index.d.ts +58 -27
- package/dist/index.js +141 -263
- 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,11 +403,7 @@ 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;
|
|
@@ -555,6 +439,9 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
555
439
|
stopUpdates();
|
|
556
440
|
}
|
|
557
441
|
async function handleStatusUpdate(response) {
|
|
442
|
+
if (state.resolved) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
558
445
|
if (response.reference !== state.reference) {
|
|
559
446
|
resolveWithError(
|
|
560
447
|
`Reference mismatch: expected ${state.reference} but got ${response.reference}`
|
|
@@ -588,10 +475,11 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
588
475
|
if (state.resolved || state.pollingTimer) {
|
|
589
476
|
return;
|
|
590
477
|
}
|
|
478
|
+
const delay2 = state.pollIntervalMs + Math.random() * POLL_JITTER_MS;
|
|
591
479
|
state.pollingTimer = setTimeout(() => {
|
|
592
480
|
state.pollingTimer = null;
|
|
593
481
|
void pollStatus();
|
|
594
|
-
},
|
|
482
|
+
}, delay2);
|
|
595
483
|
}
|
|
596
484
|
async function pollStatus() {
|
|
597
485
|
if (state.resolved) {
|
|
@@ -619,42 +507,6 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
619
507
|
}
|
|
620
508
|
scheduleNextPoll();
|
|
621
509
|
}
|
|
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
|
-
}
|
|
656
|
-
scheduleNextPoll();
|
|
657
|
-
}
|
|
658
510
|
function startUpdates() {
|
|
659
511
|
if (TERMINAL_STATES.has(state.status)) {
|
|
660
512
|
setTimeout(() => {
|
|
@@ -662,10 +514,6 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
662
514
|
}, 0);
|
|
663
515
|
return;
|
|
664
516
|
}
|
|
665
|
-
if (state.streaming && state.openStream) {
|
|
666
|
-
startStream();
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
517
|
scheduleNextPoll();
|
|
670
518
|
}
|
|
671
519
|
function stopUpdates() {
|
|
@@ -673,7 +521,6 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
673
521
|
clearTimeout(state.pollingTimer);
|
|
674
522
|
state.pollingTimer = null;
|
|
675
523
|
}
|
|
676
|
-
closeStream();
|
|
677
524
|
}
|
|
678
525
|
function on(event, handler) {
|
|
679
526
|
state.emitter.on(event, handler);
|
|
@@ -746,21 +593,78 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
746
593
|
}
|
|
747
594
|
return paymentInstance;
|
|
748
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
|
+
}
|
|
749
620
|
function verifyWebhookSignature(input) {
|
|
750
|
-
const
|
|
621
|
+
const payloadString = decodePayload(input.payload);
|
|
622
|
+
const payloadBytes = Buffer.from(payloadString, "utf8");
|
|
751
623
|
const expectedSignature = createHmac("sha256", input.secret).update(payloadBytes).digest("hex");
|
|
752
624
|
const providedBuffer = Buffer.from(input.signature, "hex");
|
|
753
625
|
const expectedBuffer = Buffer.from(expectedSignature, "hex");
|
|
754
626
|
if (providedBuffer.length !== expectedBuffer.length) {
|
|
755
627
|
return false;
|
|
756
628
|
}
|
|
757
|
-
|
|
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;
|
|
758
642
|
}
|
|
759
643
|
|
|
760
644
|
// src/sdk.ts
|
|
761
645
|
function generateReference() {
|
|
762
646
|
return randomBytes(16).toString("hex").slice(0, 15);
|
|
763
647
|
}
|
|
648
|
+
var REFERENCE_MIN_LENGTH = 13;
|
|
649
|
+
var REFERENCE_MAX_LENGTH = 15;
|
|
650
|
+
function resolveReference(reference) {
|
|
651
|
+
if (reference === void 0) {
|
|
652
|
+
return generateReference();
|
|
653
|
+
}
|
|
654
|
+
if (reference.length < REFERENCE_MIN_LENGTH || reference.length > REFERENCE_MAX_LENGTH) {
|
|
655
|
+
throwValidation(
|
|
656
|
+
`reference must be ${REFERENCE_MIN_LENGTH}\u2013${REFERENCE_MAX_LENGTH} characters`
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
return reference;
|
|
660
|
+
}
|
|
661
|
+
async function runHook(hook, ...args) {
|
|
662
|
+
if (!hook || hook.enabled === false) return void 0;
|
|
663
|
+
const result = await safeTry(async () => hook.fn(...args));
|
|
664
|
+
if (result.isOk) return result.value;
|
|
665
|
+
await safeTry(async () => hook.onError(result.error));
|
|
666
|
+
return void 0;
|
|
667
|
+
}
|
|
764
668
|
function throwValidation(message) {
|
|
765
669
|
throw createSdkError({ category: "validation", message });
|
|
766
670
|
}
|
|
@@ -794,12 +698,10 @@ function createSdkInstance(config) {
|
|
|
794
698
|
}),
|
|
795
699
|
pollIntervalMs: config.maxPollIntervalMs,
|
|
796
700
|
maxPollDuration: config.maxPollDurationMs,
|
|
797
|
-
maxPollAttempts: config.maxPollAttempts
|
|
798
|
-
streaming: config.streaming,
|
|
799
|
-
openStream: config.streaming ? transport.openStream : void 0
|
|
701
|
+
maxPollAttempts: config.maxPollAttempts
|
|
800
702
|
};
|
|
801
703
|
async function collectPayment(input) {
|
|
802
|
-
const reference = input.reference
|
|
704
|
+
const reference = resolveReference(input.reference);
|
|
803
705
|
validateAmount(input.amount);
|
|
804
706
|
validateNonEmpty(input.customer.name, "customer.name");
|
|
805
707
|
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
@@ -808,24 +710,18 @@ function createSdkInstance(config) {
|
|
|
808
710
|
throwValidation('bank details are required when method is "bank"');
|
|
809
711
|
}
|
|
810
712
|
let payload = { ...input, reference };
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
815
|
-
}
|
|
713
|
+
const mutated = await runHook(config.hooks?.beforeCollect, payload);
|
|
714
|
+
if (mutated != null)
|
|
715
|
+
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
816
716
|
const result = await transport.send({
|
|
817
717
|
action: SDK_ACTIONS.collectPayment,
|
|
818
718
|
payload
|
|
819
719
|
});
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
}) : Err(result.error),
|
|
826
|
-
payload
|
|
827
|
-
);
|
|
828
|
-
}
|
|
720
|
+
await runHook(
|
|
721
|
+
config.hooks?.afterCollect,
|
|
722
|
+
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
723
|
+
payload
|
|
724
|
+
);
|
|
829
725
|
if (result.isErr) {
|
|
830
726
|
const sdkErr = parseError(result.error);
|
|
831
727
|
return createPaymentInstance(
|
|
@@ -836,7 +732,7 @@ function createSdkInstance(config) {
|
|
|
836
732
|
return createPaymentInstance(result.value, commonDeps);
|
|
837
733
|
}
|
|
838
734
|
async function collectPaymentAndResolve(input) {
|
|
839
|
-
const reference = input.reference
|
|
735
|
+
const reference = resolveReference(input.reference);
|
|
840
736
|
validateAmount(input.amount);
|
|
841
737
|
validateNonEmpty(input.customer.name, "customer.name");
|
|
842
738
|
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
@@ -845,31 +741,25 @@ function createSdkInstance(config) {
|
|
|
845
741
|
throwValidation('bank details are required when method is "bank"');
|
|
846
742
|
}
|
|
847
743
|
let payload = { ...input, reference };
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
852
|
-
}
|
|
744
|
+
const mutated = await runHook(config.hooks?.beforeCollect, payload);
|
|
745
|
+
if (mutated != null)
|
|
746
|
+
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
853
747
|
const result = await transport.send({
|
|
854
748
|
action: SDK_ACTIONS.collectPaymentAndResolve,
|
|
855
749
|
payload
|
|
856
750
|
});
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
}) : Err(result.error),
|
|
863
|
-
payload
|
|
864
|
-
);
|
|
865
|
-
}
|
|
751
|
+
await runHook(
|
|
752
|
+
config.hooks?.afterCollect,
|
|
753
|
+
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
754
|
+
payload
|
|
755
|
+
);
|
|
866
756
|
if (result.isOk) {
|
|
867
757
|
return Ok(result.value);
|
|
868
758
|
}
|
|
869
759
|
return Err(result.error);
|
|
870
760
|
}
|
|
871
761
|
async function makePayout(input) {
|
|
872
|
-
const reference = input.reference
|
|
762
|
+
const reference = resolveReference(input.reference);
|
|
873
763
|
validateAmount(input.amount);
|
|
874
764
|
validateNonEmpty(input.customer.name, "customer.name");
|
|
875
765
|
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
@@ -883,24 +773,18 @@ function createSdkInstance(config) {
|
|
|
883
773
|
"destination.accountNumber"
|
|
884
774
|
);
|
|
885
775
|
let payload = { ...input, reference };
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
890
|
-
}
|
|
776
|
+
const mutated = await runHook(config.hooks?.beforePayout, payload);
|
|
777
|
+
if (mutated != null)
|
|
778
|
+
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
891
779
|
const result = await transport.send({
|
|
892
780
|
action: SDK_ACTIONS.makePayout,
|
|
893
781
|
payload
|
|
894
782
|
});
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
}) : Err(result.error),
|
|
901
|
-
payload
|
|
902
|
-
);
|
|
903
|
-
}
|
|
783
|
+
await runHook(
|
|
784
|
+
config.hooks?.afterPayout,
|
|
785
|
+
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
786
|
+
payload
|
|
787
|
+
);
|
|
904
788
|
if (result.isErr) {
|
|
905
789
|
const sdkErr = parseError(result.error);
|
|
906
790
|
return createPaymentInstance(
|
|
@@ -911,7 +795,7 @@ function createSdkInstance(config) {
|
|
|
911
795
|
return createPaymentInstance(result.value, commonDeps);
|
|
912
796
|
}
|
|
913
797
|
async function makePayoutAndResolve(input) {
|
|
914
|
-
const reference = input.reference
|
|
798
|
+
const reference = resolveReference(input.reference);
|
|
915
799
|
validateAmount(input.amount);
|
|
916
800
|
validateNonEmpty(input.customer.name, "customer.name");
|
|
917
801
|
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
@@ -925,24 +809,18 @@ function createSdkInstance(config) {
|
|
|
925
809
|
"destination.accountNumber"
|
|
926
810
|
);
|
|
927
811
|
let payload = { ...input, reference };
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
932
|
-
}
|
|
812
|
+
const mutated = await runHook(config.hooks?.beforePayout, payload);
|
|
813
|
+
if (mutated != null)
|
|
814
|
+
payload = { ...mutated, reference: mutated.reference ?? reference };
|
|
933
815
|
const result = await transport.send({
|
|
934
816
|
action: SDK_ACTIONS.makePayoutAndResolve,
|
|
935
817
|
payload
|
|
936
818
|
});
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
}) : Err(result.error),
|
|
943
|
-
payload
|
|
944
|
-
);
|
|
945
|
-
}
|
|
819
|
+
await runHook(
|
|
820
|
+
config.hooks?.afterPayout,
|
|
821
|
+
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
822
|
+
payload
|
|
823
|
+
);
|
|
946
824
|
if (result.isOk) {
|
|
947
825
|
return Ok(result.value);
|
|
948
826
|
}
|
|
@@ -984,7 +862,7 @@ function createSdkInstance(config) {
|
|
|
984
862
|
return Err(result.error);
|
|
985
863
|
}
|
|
986
864
|
async function createInvoice(input) {
|
|
987
|
-
const reference = input.reference
|
|
865
|
+
const reference = resolveReference(input.reference);
|
|
988
866
|
validateAmount(input.amount);
|
|
989
867
|
validateNonEmpty(input.description, "description");
|
|
990
868
|
if (input.items) {
|
|
@@ -1042,7 +920,8 @@ function createNylonPay(config) {
|
|
|
1042
920
|
throw new Error('apiSecret must start with "nps_"');
|
|
1043
921
|
}
|
|
1044
922
|
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
1045
|
-
const
|
|
923
|
+
const secretHash = createHash("sha256").update(config.apiSecret).digest("hex").slice(0, 16);
|
|
924
|
+
const instanceKey = `${config.apiKey}:${baseUrl}:${secretHash}`;
|
|
1046
925
|
if (!config.force) {
|
|
1047
926
|
const existing = instances.get(instanceKey);
|
|
1048
927
|
if (existing) return existing;
|
|
@@ -1056,7 +935,6 @@ function createNylonPay(config) {
|
|
|
1056
935
|
maxPollIntervalMs: config.maxPollIntervalMs ?? DEFAULT_MAX_POLL_INTERVAL_MS,
|
|
1057
936
|
maxPollDurationMs: config.maxPollDurationMs ?? DEFAULT_MAX_POLL_DURATION_MS,
|
|
1058
937
|
maxPollAttempts: config.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS,
|
|
1059
|
-
streaming: config.streaming ?? DEFAULT_STREAMING,
|
|
1060
938
|
fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
|
|
1061
939
|
hooks: config.hooks
|
|
1062
940
|
};
|