@nile-squad/nylonpay-ts 1.1.0 → 1.2.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/dist/index.cjs +112 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -3
- package/dist/index.d.ts +14 -3
- package/dist/index.js +112 -95
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -255,13 +255,13 @@ function createTransport({
|
|
|
255
255
|
async function send(request) {
|
|
256
256
|
const envelope = buildEnvelope(request);
|
|
257
257
|
const signedPayload = envelope.payload;
|
|
258
|
-
const headers = buildAuthHeaders({
|
|
259
|
-
apiKey,
|
|
260
|
-
apiSecret,
|
|
261
|
-
payload: signedPayload
|
|
262
|
-
});
|
|
263
258
|
const bodyString = JSON.stringify(envelope);
|
|
264
259
|
async function attempt(currentAttempt) {
|
|
260
|
+
const headers = buildAuthHeaders({
|
|
261
|
+
apiKey,
|
|
262
|
+
apiSecret,
|
|
263
|
+
payload: signedPayload
|
|
264
|
+
});
|
|
265
265
|
const { controller, cleanup } = withTimeout(timeoutMs);
|
|
266
266
|
try {
|
|
267
267
|
const response = await fetchImpl(baseUrl, {
|
|
@@ -300,7 +300,7 @@ function createTransport({
|
|
|
300
300
|
return slangTs.Err(
|
|
301
301
|
JSON.stringify({
|
|
302
302
|
category: "internal",
|
|
303
|
-
message: "
|
|
303
|
+
message: "Received an invalid response from the server",
|
|
304
304
|
retryable: false
|
|
305
305
|
})
|
|
306
306
|
);
|
|
@@ -313,7 +313,7 @@ function createTransport({
|
|
|
313
313
|
return slangTs.Err(
|
|
314
314
|
JSON.stringify({
|
|
315
315
|
category: "internal",
|
|
316
|
-
message: "
|
|
316
|
+
message: "Could not verify the server response",
|
|
317
317
|
retryable: false
|
|
318
318
|
})
|
|
319
319
|
);
|
|
@@ -328,7 +328,7 @@ function createTransport({
|
|
|
328
328
|
return slangTs.Err(
|
|
329
329
|
JSON.stringify({
|
|
330
330
|
category: "internal",
|
|
331
|
-
message: "
|
|
331
|
+
message: "Could not verify the server response",
|
|
332
332
|
retryable: false
|
|
333
333
|
})
|
|
334
334
|
);
|
|
@@ -344,7 +344,7 @@ function createTransport({
|
|
|
344
344
|
const isAbort = error instanceof DOMException && error.name === "AbortError";
|
|
345
345
|
const sdkError = {
|
|
346
346
|
category: isAbort ? "timeout" : "network",
|
|
347
|
-
message: isAbort ?
|
|
347
|
+
message: isAbort ? "The request timed out" : "Could not reach the server, check your network connection and try again",
|
|
348
348
|
retryable: true
|
|
349
349
|
};
|
|
350
350
|
if (currentAttempt < maxRetries) {
|
|
@@ -410,10 +410,16 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
410
410
|
maxPollDuration: deps.maxPollDuration ?? 3e5,
|
|
411
411
|
maxPollAttempts: deps.maxPollAttempts ?? 150
|
|
412
412
|
};
|
|
413
|
-
function resolveWithError(error) {
|
|
413
|
+
function resolveWithError(error, category, retryable) {
|
|
414
414
|
state.resolved = true;
|
|
415
415
|
stopUpdates();
|
|
416
|
-
|
|
416
|
+
const parsed = parseError(error);
|
|
417
|
+
emitEvent(
|
|
418
|
+
"error",
|
|
419
|
+
parsed.message,
|
|
420
|
+
category ?? parsed.category,
|
|
421
|
+
parsed.retryable
|
|
422
|
+
);
|
|
417
423
|
}
|
|
418
424
|
function emitEvent(event, error, category, retryable) {
|
|
419
425
|
const data = {
|
|
@@ -450,7 +456,8 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
450
456
|
}
|
|
451
457
|
if (response.reference !== state.reference) {
|
|
452
458
|
resolveWithError(
|
|
453
|
-
|
|
459
|
+
"Received a status update for a different transaction",
|
|
460
|
+
"internal"
|
|
454
461
|
);
|
|
455
462
|
return;
|
|
456
463
|
}
|
|
@@ -472,7 +479,7 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
472
479
|
if (parsed.category === "not_found") {
|
|
473
480
|
return;
|
|
474
481
|
}
|
|
475
|
-
emitEvent("error", parsed.message);
|
|
482
|
+
emitEvent("error", parsed.message, parsed.category, parsed.retryable);
|
|
476
483
|
state.resolved = true;
|
|
477
484
|
stopUpdates();
|
|
478
485
|
}
|
|
@@ -492,11 +499,17 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
492
499
|
return;
|
|
493
500
|
}
|
|
494
501
|
if (state.pollAttempts >= state.maxPollAttempts) {
|
|
495
|
-
resolveWithError(
|
|
502
|
+
resolveWithError(
|
|
503
|
+
"Timed out waiting for the transaction status to update",
|
|
504
|
+
"timeout"
|
|
505
|
+
);
|
|
496
506
|
return;
|
|
497
507
|
}
|
|
498
508
|
if (Date.now() - state.pollStartTime >= state.maxPollDuration) {
|
|
499
|
-
resolveWithError(
|
|
509
|
+
resolveWithError(
|
|
510
|
+
"Timed out waiting for the transaction status to update",
|
|
511
|
+
"timeout"
|
|
512
|
+
);
|
|
500
513
|
return;
|
|
501
514
|
}
|
|
502
515
|
state.pollAttempts += 1;
|
|
@@ -616,6 +629,9 @@ function normalizePhone(phone) {
|
|
|
616
629
|
}
|
|
617
630
|
return normalized;
|
|
618
631
|
}
|
|
632
|
+
function isValidPhoneFormat(normalizedPhone) {
|
|
633
|
+
return /^\d{9,15}$/.test(normalizedPhone);
|
|
634
|
+
}
|
|
619
635
|
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
620
636
|
function decodePayload(payload) {
|
|
621
637
|
return typeof payload === "string" ? payload : Buffer.from(payload).toString("utf8");
|
|
@@ -712,6 +728,56 @@ function validateNonEmpty(value, fieldName) {
|
|
|
712
728
|
throwValidation(`${fieldName} is required`);
|
|
713
729
|
}
|
|
714
730
|
}
|
|
731
|
+
function validatePhoneFormat(normalizedPhone, fieldName) {
|
|
732
|
+
if (!isValidPhoneFormat(normalizedPhone)) {
|
|
733
|
+
throwValidation(`${fieldName} must be a valid phone number`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
function prepareCollectPayload(input) {
|
|
737
|
+
const reference = resolveReference(input.reference);
|
|
738
|
+
validateCollectionAmount(input.amount);
|
|
739
|
+
validateNonEmpty(input.customer.name, "customer.name");
|
|
740
|
+
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
741
|
+
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
742
|
+
validatePhoneFormat(normalizedPhone, "customer.phoneNumber");
|
|
743
|
+
validateNonEmpty(input.description, "description");
|
|
744
|
+
if (input.method === "bank" && !input.bank) {
|
|
745
|
+
throwValidation('bank details are required when method is "bank"');
|
|
746
|
+
}
|
|
747
|
+
return {
|
|
748
|
+
...input,
|
|
749
|
+
reference,
|
|
750
|
+
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function preparePayoutPayload(input) {
|
|
754
|
+
const reference = resolveReference(input.reference);
|
|
755
|
+
validatePayoutAmount(input.amount);
|
|
756
|
+
validateNonEmpty(input.customer.name, "customer.name");
|
|
757
|
+
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
758
|
+
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
759
|
+
validatePhoneFormat(normalizedPhone, "customer.phoneNumber");
|
|
760
|
+
validateNonEmpty(input.description, "description");
|
|
761
|
+
validateNonEmpty(
|
|
762
|
+
input.destination.accountHolderName,
|
|
763
|
+
"destination.accountHolderName"
|
|
764
|
+
);
|
|
765
|
+
validateNonEmpty(
|
|
766
|
+
input.destination.accountNumber,
|
|
767
|
+
"destination.accountNumber"
|
|
768
|
+
);
|
|
769
|
+
return {
|
|
770
|
+
...input,
|
|
771
|
+
reference,
|
|
772
|
+
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function applyBeforeHookMutation(mutated, current, prepare) {
|
|
776
|
+
return prepare({
|
|
777
|
+
...mutated,
|
|
778
|
+
reference: mutated.reference ?? current.reference
|
|
779
|
+
});
|
|
780
|
+
}
|
|
715
781
|
function createSdkInstance(config) {
|
|
716
782
|
const transport = createTransport({
|
|
717
783
|
apiKey: config.apiKey,
|
|
@@ -735,23 +801,15 @@ function createSdkInstance(config) {
|
|
|
735
801
|
maxPollAttempts: config.maxPollAttempts
|
|
736
802
|
};
|
|
737
803
|
async function collectPayment(input) {
|
|
738
|
-
|
|
739
|
-
validateCollectionAmount(input.amount);
|
|
740
|
-
validateNonEmpty(input.customer.name, "customer.name");
|
|
741
|
-
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
742
|
-
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
743
|
-
validateNonEmpty(input.description, "description");
|
|
744
|
-
if (input.method === "bank" && !input.bank) {
|
|
745
|
-
throwValidation('bank details are required when method is "bank"');
|
|
746
|
-
}
|
|
747
|
-
let payload = {
|
|
748
|
-
...input,
|
|
749
|
-
reference,
|
|
750
|
-
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
751
|
-
};
|
|
804
|
+
let payload = prepareCollectPayload(input);
|
|
752
805
|
const mutated = await runHook(config.hooks?.beforeCollect, payload);
|
|
753
|
-
if (mutated != null)
|
|
754
|
-
payload =
|
|
806
|
+
if (mutated != null) {
|
|
807
|
+
payload = applyBeforeHookMutation(
|
|
808
|
+
mutated,
|
|
809
|
+
payload,
|
|
810
|
+
prepareCollectPayload
|
|
811
|
+
);
|
|
812
|
+
}
|
|
755
813
|
const result = await transport.send({
|
|
756
814
|
action: SDK_ACTIONS.collectPayment,
|
|
757
815
|
payload
|
|
@@ -759,35 +817,27 @@ function createSdkInstance(config) {
|
|
|
759
817
|
await runHook(
|
|
760
818
|
config.hooks?.afterCollect,
|
|
761
819
|
result.isOk ? slangTs.Ok({ reference: result.value.reference, status: result.value.status }) : slangTs.Err(result.error),
|
|
762
|
-
payload
|
|
820
|
+
{ ...payload, raw: input }
|
|
763
821
|
);
|
|
764
822
|
if (result.isErr) {
|
|
765
823
|
const sdkErr = parseError(result.error);
|
|
766
824
|
return createPaymentInstance(
|
|
767
|
-
{ reference, status: "pending" },
|
|
825
|
+
{ reference: payload.reference, status: "pending" },
|
|
768
826
|
{ ...commonDeps, initialError: sdkErr }
|
|
769
827
|
);
|
|
770
828
|
}
|
|
771
829
|
return createPaymentInstance(result.value, commonDeps);
|
|
772
830
|
}
|
|
773
831
|
async function collectPaymentAndResolve(input) {
|
|
774
|
-
|
|
775
|
-
validateCollectionAmount(input.amount);
|
|
776
|
-
validateNonEmpty(input.customer.name, "customer.name");
|
|
777
|
-
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
778
|
-
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
779
|
-
validateNonEmpty(input.description, "description");
|
|
780
|
-
if (input.method === "bank" && !input.bank) {
|
|
781
|
-
throwValidation('bank details are required when method is "bank"');
|
|
782
|
-
}
|
|
783
|
-
let payload = {
|
|
784
|
-
...input,
|
|
785
|
-
reference,
|
|
786
|
-
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
787
|
-
};
|
|
832
|
+
let payload = prepareCollectPayload(input);
|
|
788
833
|
const mutated = await runHook(config.hooks?.beforeCollect, payload);
|
|
789
|
-
if (mutated != null)
|
|
790
|
-
payload =
|
|
834
|
+
if (mutated != null) {
|
|
835
|
+
payload = applyBeforeHookMutation(
|
|
836
|
+
mutated,
|
|
837
|
+
payload,
|
|
838
|
+
prepareCollectPayload
|
|
839
|
+
);
|
|
840
|
+
}
|
|
791
841
|
const result = await transport.send({
|
|
792
842
|
action: SDK_ACTIONS.collectPaymentAndResolve,
|
|
793
843
|
payload
|
|
@@ -795,7 +845,7 @@ function createSdkInstance(config) {
|
|
|
795
845
|
await runHook(
|
|
796
846
|
config.hooks?.afterCollect,
|
|
797
847
|
result.isOk ? slangTs.Ok({ reference: result.value.reference, status: result.value.status }) : slangTs.Err(result.error),
|
|
798
|
-
payload
|
|
848
|
+
{ ...payload, raw: input }
|
|
799
849
|
);
|
|
800
850
|
if (result.isOk) {
|
|
801
851
|
return slangTs.Ok(result.value);
|
|
@@ -803,28 +853,11 @@ function createSdkInstance(config) {
|
|
|
803
853
|
return slangTs.Err(result.error);
|
|
804
854
|
}
|
|
805
855
|
async function makePayout(input) {
|
|
806
|
-
|
|
807
|
-
validatePayoutAmount(input.amount);
|
|
808
|
-
validateNonEmpty(input.customer.name, "customer.name");
|
|
809
|
-
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
810
|
-
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
811
|
-
validateNonEmpty(input.description, "description");
|
|
812
|
-
validateNonEmpty(
|
|
813
|
-
input.destination.accountHolderName,
|
|
814
|
-
"destination.accountHolderName"
|
|
815
|
-
);
|
|
816
|
-
validateNonEmpty(
|
|
817
|
-
input.destination.accountNumber,
|
|
818
|
-
"destination.accountNumber"
|
|
819
|
-
);
|
|
820
|
-
let payload = {
|
|
821
|
-
...input,
|
|
822
|
-
reference,
|
|
823
|
-
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
824
|
-
};
|
|
856
|
+
let payload = preparePayoutPayload(input);
|
|
825
857
|
const mutated = await runHook(config.hooks?.beforePayout, payload);
|
|
826
|
-
if (mutated != null)
|
|
827
|
-
payload =
|
|
858
|
+
if (mutated != null) {
|
|
859
|
+
payload = applyBeforeHookMutation(mutated, payload, preparePayoutPayload);
|
|
860
|
+
}
|
|
828
861
|
const result = await transport.send({
|
|
829
862
|
action: SDK_ACTIONS.makePayout,
|
|
830
863
|
payload
|
|
@@ -832,40 +865,23 @@ function createSdkInstance(config) {
|
|
|
832
865
|
await runHook(
|
|
833
866
|
config.hooks?.afterPayout,
|
|
834
867
|
result.isOk ? slangTs.Ok({ reference: result.value.reference, status: result.value.status }) : slangTs.Err(result.error),
|
|
835
|
-
payload
|
|
868
|
+
{ ...payload, raw: input }
|
|
836
869
|
);
|
|
837
870
|
if (result.isErr) {
|
|
838
871
|
const sdkErr = parseError(result.error);
|
|
839
872
|
return createPaymentInstance(
|
|
840
|
-
{ reference, status: "pending" },
|
|
873
|
+
{ reference: payload.reference, status: "pending" },
|
|
841
874
|
{ ...commonDeps, initialError: sdkErr }
|
|
842
875
|
);
|
|
843
876
|
}
|
|
844
877
|
return createPaymentInstance(result.value, commonDeps);
|
|
845
878
|
}
|
|
846
879
|
async function makePayoutAndResolve(input) {
|
|
847
|
-
|
|
848
|
-
validatePayoutAmount(input.amount);
|
|
849
|
-
validateNonEmpty(input.customer.name, "customer.name");
|
|
850
|
-
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
851
|
-
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
852
|
-
validateNonEmpty(input.description, "description");
|
|
853
|
-
validateNonEmpty(
|
|
854
|
-
input.destination.accountHolderName,
|
|
855
|
-
"destination.accountHolderName"
|
|
856
|
-
);
|
|
857
|
-
validateNonEmpty(
|
|
858
|
-
input.destination.accountNumber,
|
|
859
|
-
"destination.accountNumber"
|
|
860
|
-
);
|
|
861
|
-
let payload = {
|
|
862
|
-
...input,
|
|
863
|
-
reference,
|
|
864
|
-
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
865
|
-
};
|
|
880
|
+
let payload = preparePayoutPayload(input);
|
|
866
881
|
const mutated = await runHook(config.hooks?.beforePayout, payload);
|
|
867
|
-
if (mutated != null)
|
|
868
|
-
payload =
|
|
882
|
+
if (mutated != null) {
|
|
883
|
+
payload = applyBeforeHookMutation(mutated, payload, preparePayoutPayload);
|
|
884
|
+
}
|
|
869
885
|
const result = await transport.send({
|
|
870
886
|
action: SDK_ACTIONS.makePayoutAndResolve,
|
|
871
887
|
payload
|
|
@@ -873,7 +889,7 @@ function createSdkInstance(config) {
|
|
|
873
889
|
await runHook(
|
|
874
890
|
config.hooks?.afterPayout,
|
|
875
891
|
result.isOk ? slangTs.Ok({ reference: result.value.reference, status: result.value.status }) : slangTs.Err(result.error),
|
|
876
|
-
payload
|
|
892
|
+
{ ...payload, raw: input }
|
|
877
893
|
);
|
|
878
894
|
if (result.isOk) {
|
|
879
895
|
return slangTs.Ok(result.value);
|
|
@@ -907,6 +923,7 @@ function createSdkInstance(config) {
|
|
|
907
923
|
async function verifyPhone(input) {
|
|
908
924
|
validateNonEmpty(input.phoneNumber, "phoneNumber");
|
|
909
925
|
const normalizedPhone = normalizePhone(input.phoneNumber);
|
|
926
|
+
validatePhoneFormat(normalizedPhone, "phoneNumber");
|
|
910
927
|
const result = await transport.send({
|
|
911
928
|
action: SDK_ACTIONS.verifyPhone,
|
|
912
929
|
payload: { ...input, phoneNumber: normalizedPhone }
|