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