@nile-squad/nylonpay-ts 1.0.10 → 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 +133 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -9
- package/dist/index.d.ts +35 -9
- package/dist/index.js +133 -105
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -178,6 +178,13 @@ type Transaction = {
|
|
|
178
178
|
* payment was initiated; use a fresh reference to start a new one.
|
|
179
179
|
*/
|
|
180
180
|
duplicate?: boolean;
|
|
181
|
+
/**
|
|
182
|
+
* The underlying operator's (telco's/bank's) own transaction id — what the
|
|
183
|
+
* paying customer sees on their receipt. Use it to cross-validate customer
|
|
184
|
+
* pay claims. Null until the operator reports it (typically at terminal
|
|
185
|
+
* status); may be absent on older backend versions.
|
|
186
|
+
*/
|
|
187
|
+
operatorTid?: string | null;
|
|
181
188
|
phone: string;
|
|
182
189
|
email: string | null;
|
|
183
190
|
failureReason: string | null;
|
|
@@ -235,16 +242,27 @@ type WebhookPayload = {
|
|
|
235
242
|
* Async hooks are awaited before the transport call proceeds.
|
|
236
243
|
*/
|
|
237
244
|
type BeforeCollectHook = (input: CollectPaymentInput) => CollectPaymentInput | undefined | Promise<CollectPaymentInput | undefined>;
|
|
245
|
+
/**
|
|
246
|
+
* The input handed to an `after*` hook. It is the final wire payload — reference
|
|
247
|
+
* resolved, phone normalized, and any `before*`-hook mutations applied — so a
|
|
248
|
+
* hook observes exactly what was sent. The `raw` property additionally carries
|
|
249
|
+
* the untouched original merchant input (pre-normalization, pre-`before*`-hook),
|
|
250
|
+
* so audit logs can record both what the merchant typed and what hit the wire.
|
|
251
|
+
*/
|
|
252
|
+
type AfterHookInput<TInput> = TInput & {
|
|
253
|
+
raw: TInput;
|
|
254
|
+
};
|
|
238
255
|
/**
|
|
239
256
|
* Called after every collect call (both fire-and-forget and resolve variants)
|
|
240
257
|
* regardless of outcome. Use for logging, analytics, or side-effects.
|
|
241
258
|
* The result is normalized to `{ reference, status }` across both variants.
|
|
259
|
+
* `input` is the sent payload; `input.raw` is the original merchant input.
|
|
242
260
|
* Return value is ignored.
|
|
243
261
|
*/
|
|
244
262
|
type AfterCollectHook = (result: Result<{
|
|
245
263
|
reference: string;
|
|
246
264
|
status: string;
|
|
247
|
-
}, string>, input: CollectPaymentInput) => void | Promise<void>;
|
|
265
|
+
}, string>, input: AfterHookInput<CollectPaymentInput>) => void | Promise<void>;
|
|
248
266
|
/**
|
|
249
267
|
* Called before a payout payload is sent to the server.
|
|
250
268
|
* Same semantics as {@link BeforeCollectHook}.
|
|
@@ -258,7 +276,7 @@ type BeforePayoutHook = (input: MakePayoutInput) => MakePayoutInput | undefined
|
|
|
258
276
|
type AfterPayoutHook = (result: Result<{
|
|
259
277
|
reference: string;
|
|
260
278
|
status: string;
|
|
261
|
-
}, string>, input: MakePayoutInput) => void | Promise<void>;
|
|
279
|
+
}, string>, input: AfterHookInput<MakePayoutInput>) => void | Promise<void>;
|
|
262
280
|
/**
|
|
263
281
|
* Wrapper applied to every lifecycle hook. The SDK runs `fn` inside `safeTry`,
|
|
264
282
|
* so a throw or rejection in merchant code never bubbles into the payment flow —
|
|
@@ -348,15 +366,17 @@ type SdkError = {
|
|
|
348
366
|
retryable?: boolean;
|
|
349
367
|
};
|
|
350
368
|
/**
|
|
351
|
-
* Data passed to every payment event handler. `
|
|
352
|
-
* for status
|
|
353
|
-
* `
|
|
354
|
-
* reference
|
|
369
|
+
* Data passed to every payment event handler. `reference` is always present;
|
|
370
|
+
* `transaction` is populated for terminal status events (`success`, `failed`,
|
|
371
|
+
* `cancelled`) — the `processing` event can fire before the full record is
|
|
372
|
+
* fetched, so use `reference` there. `error` is populated for the `"error"`
|
|
373
|
+
* event (network failure, timeout, reference mismatch).
|
|
355
374
|
*
|
|
356
375
|
* @example
|
|
357
376
|
* ```ts
|
|
358
377
|
* payment.on("success", (data: EventData) => {
|
|
359
|
-
* console.log(data.
|
|
378
|
+
* console.log(data.reference); // "ORDER-2026-001"
|
|
379
|
+
* console.log(data.transaction?.id);
|
|
360
380
|
* console.log(data.timestamp); // "2026-05-30T12:00:00.000Z"
|
|
361
381
|
* });
|
|
362
382
|
* ```
|
|
@@ -364,7 +384,13 @@ type SdkError = {
|
|
|
364
384
|
type EventData = {
|
|
365
385
|
/** The event that triggered this handler. */
|
|
366
386
|
event: PaymentEvent;
|
|
367
|
-
/**
|
|
387
|
+
/**
|
|
388
|
+
* The transaction reference. Always present — available on every event,
|
|
389
|
+
* including lifecycle events fired before the full transaction record
|
|
390
|
+
* has been fetched.
|
|
391
|
+
*/
|
|
392
|
+
reference: string;
|
|
393
|
+
/** Full transaction record. Present for terminal status events. */
|
|
368
394
|
transaction?: Transaction;
|
|
369
395
|
/** Error message. Present for the `"error"` event. */
|
|
370
396
|
error?: string;
|
|
@@ -713,4 +739,4 @@ declare function parseError(error: string): SdkError;
|
|
|
713
739
|
*/
|
|
714
740
|
declare function verifyWebhookSignature(input: VerifyWebhookInput): boolean;
|
|
715
741
|
|
|
716
|
-
export { type AfterCollectHook, type AfterPayoutHook, type BankDetails, type BeforeCollectHook, type BeforePayoutHook, type CollectPaymentInput, type CreateInvoiceInput, type Currency, type Customer, type Destination, type EventData, type GetStatusInput, type GetTransactionInput, type InvoiceItem, type InvoiceResponse, type MakePayoutInput, type NylonPayConfig, type NylonPaySdk, type PaymentEvent, type PaymentEventHandler, type PaymentInstance, type PaymentMethod, type PhoneVerification, type SdkError, type SdkErrorCategory, type SdkHooks, type StatusResponse, type Transaction, type TransactionMode, type TransactionStatus, type TransactionType, type VerifyPhoneInput, type VerifyWebhookInput, type WebhookEventType, type WebhookPayload, createNylonPay, createSdkError, parseError, verifyWebhookSignature };
|
|
742
|
+
export { type AfterCollectHook, type AfterHookInput, type AfterPayoutHook, type BankDetails, type BeforeCollectHook, type BeforePayoutHook, type CollectPaymentInput, type CreateInvoiceInput, type Currency, type Customer, type Destination, type EventData, type GetStatusInput, type GetTransactionInput, type InvoiceItem, type InvoiceResponse, type MakePayoutInput, type NylonPayConfig, type NylonPaySdk, type PaymentEvent, type PaymentEventHandler, type PaymentInstance, type PaymentMethod, type PhoneVerification, type SdkError, type SdkErrorCategory, type SdkHooks, type StatusResponse, type Transaction, type TransactionMode, type TransactionStatus, type TransactionType, type VerifyPhoneInput, type VerifyWebhookInput, type WebhookEventType, type WebhookPayload, createNylonPay, createSdkError, parseError, verifyWebhookSignature };
|
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) {
|
|
@@ -373,6 +373,7 @@ function parseError(error) {
|
|
|
373
373
|
|
|
374
374
|
// src/payment.ts
|
|
375
375
|
var STATUS_TO_EVENT = {
|
|
376
|
+
pending: "processing",
|
|
376
377
|
successful: "success",
|
|
377
378
|
failed: "failed",
|
|
378
379
|
processing: "processing",
|
|
@@ -396,6 +397,7 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
396
397
|
status: normalizeStatus(initialResponse.status),
|
|
397
398
|
transaction: null,
|
|
398
399
|
pollingTimer: null,
|
|
400
|
+
lastStatusEvent: null,
|
|
399
401
|
resolved: false,
|
|
400
402
|
pollAttempts: 0,
|
|
401
403
|
pollStartTime: Date.now(),
|
|
@@ -406,14 +408,21 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
406
408
|
maxPollDuration: deps.maxPollDuration ?? 3e5,
|
|
407
409
|
maxPollAttempts: deps.maxPollAttempts ?? 150
|
|
408
410
|
};
|
|
409
|
-
function resolveWithError(error) {
|
|
411
|
+
function resolveWithError(error, category, retryable) {
|
|
410
412
|
state.resolved = true;
|
|
411
413
|
stopUpdates();
|
|
412
|
-
|
|
414
|
+
const parsed = parseError(error);
|
|
415
|
+
emitEvent(
|
|
416
|
+
"error",
|
|
417
|
+
parsed.message,
|
|
418
|
+
category ?? parsed.category,
|
|
419
|
+
parsed.retryable
|
|
420
|
+
);
|
|
413
421
|
}
|
|
414
422
|
function emitEvent(event, error, category, retryable) {
|
|
415
423
|
const data = {
|
|
416
424
|
event,
|
|
425
|
+
reference: state.reference,
|
|
417
426
|
transaction: state.transaction ?? void 0,
|
|
418
427
|
error,
|
|
419
428
|
category,
|
|
@@ -445,30 +454,30 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
445
454
|
}
|
|
446
455
|
if (response.reference !== state.reference) {
|
|
447
456
|
resolveWithError(
|
|
448
|
-
|
|
457
|
+
"Received a status update for a different transaction",
|
|
458
|
+
"internal"
|
|
449
459
|
);
|
|
450
460
|
return;
|
|
451
461
|
}
|
|
452
462
|
const newStatus = normalizeStatus(response.status);
|
|
453
|
-
const oldStatus = state.status;
|
|
454
463
|
state.status = newStatus;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (TERMINAL_STATES.has(newStatus)) {
|
|
459
|
-
await handleTerminalState(newStatus);
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
emitEvent(event);
|
|
463
|
-
}
|
|
464
|
+
const event = statusToEvent(newStatus);
|
|
465
|
+
if (!event || event === state.lastStatusEvent) {
|
|
466
|
+
return;
|
|
464
467
|
}
|
|
468
|
+
state.lastStatusEvent = event;
|
|
469
|
+
if (TERMINAL_STATES.has(newStatus)) {
|
|
470
|
+
await handleTerminalState(newStatus);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
emitEvent(event);
|
|
465
474
|
}
|
|
466
475
|
function handlePollError(error) {
|
|
467
476
|
const parsed = parseError(error);
|
|
468
477
|
if (parsed.category === "not_found") {
|
|
469
478
|
return;
|
|
470
479
|
}
|
|
471
|
-
emitEvent("error", parsed.message);
|
|
480
|
+
emitEvent("error", parsed.message, parsed.category, parsed.retryable);
|
|
472
481
|
state.resolved = true;
|
|
473
482
|
stopUpdates();
|
|
474
483
|
}
|
|
@@ -488,11 +497,17 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
488
497
|
return;
|
|
489
498
|
}
|
|
490
499
|
if (state.pollAttempts >= state.maxPollAttempts) {
|
|
491
|
-
resolveWithError(
|
|
500
|
+
resolveWithError(
|
|
501
|
+
"Timed out waiting for the transaction status to update",
|
|
502
|
+
"timeout"
|
|
503
|
+
);
|
|
492
504
|
return;
|
|
493
505
|
}
|
|
494
506
|
if (Date.now() - state.pollStartTime >= state.maxPollDuration) {
|
|
495
|
-
resolveWithError(
|
|
507
|
+
resolveWithError(
|
|
508
|
+
"Timed out waiting for the transaction status to update",
|
|
509
|
+
"timeout"
|
|
510
|
+
);
|
|
496
511
|
return;
|
|
497
512
|
}
|
|
498
513
|
state.pollAttempts += 1;
|
|
@@ -515,6 +530,15 @@ function createPaymentInstance(initialResponse, deps) {
|
|
|
515
530
|
}, 0);
|
|
516
531
|
return;
|
|
517
532
|
}
|
|
533
|
+
const initialEvent = statusToEvent(state.status);
|
|
534
|
+
if (initialEvent) {
|
|
535
|
+
state.lastStatusEvent = initialEvent;
|
|
536
|
+
setTimeout(() => {
|
|
537
|
+
if (!state.resolved) {
|
|
538
|
+
emitEvent(initialEvent);
|
|
539
|
+
}
|
|
540
|
+
}, 0);
|
|
541
|
+
}
|
|
518
542
|
scheduleNextPoll();
|
|
519
543
|
}
|
|
520
544
|
function stopUpdates() {
|
|
@@ -603,6 +627,9 @@ function normalizePhone(phone) {
|
|
|
603
627
|
}
|
|
604
628
|
return normalized;
|
|
605
629
|
}
|
|
630
|
+
function isValidPhoneFormat(normalizedPhone) {
|
|
631
|
+
return /^\d{9,15}$/.test(normalizedPhone);
|
|
632
|
+
}
|
|
606
633
|
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
607
634
|
function decodePayload(payload) {
|
|
608
635
|
return typeof payload === "string" ? payload : Buffer.from(payload).toString("utf8");
|
|
@@ -699,6 +726,56 @@ function validateNonEmpty(value, fieldName) {
|
|
|
699
726
|
throwValidation(`${fieldName} is required`);
|
|
700
727
|
}
|
|
701
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
|
+
}
|
|
702
779
|
function createSdkInstance(config) {
|
|
703
780
|
const transport = createTransport({
|
|
704
781
|
apiKey: config.apiKey,
|
|
@@ -722,23 +799,15 @@ function createSdkInstance(config) {
|
|
|
722
799
|
maxPollAttempts: config.maxPollAttempts
|
|
723
800
|
};
|
|
724
801
|
async function collectPayment(input) {
|
|
725
|
-
|
|
726
|
-
validateCollectionAmount(input.amount);
|
|
727
|
-
validateNonEmpty(input.customer.name, "customer.name");
|
|
728
|
-
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
729
|
-
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
730
|
-
validateNonEmpty(input.description, "description");
|
|
731
|
-
if (input.method === "bank" && !input.bank) {
|
|
732
|
-
throwValidation('bank details are required when method is "bank"');
|
|
733
|
-
}
|
|
734
|
-
let payload = {
|
|
735
|
-
...input,
|
|
736
|
-
reference,
|
|
737
|
-
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
738
|
-
};
|
|
802
|
+
let payload = prepareCollectPayload(input);
|
|
739
803
|
const mutated = await runHook(config.hooks?.beforeCollect, payload);
|
|
740
|
-
if (mutated != null)
|
|
741
|
-
payload =
|
|
804
|
+
if (mutated != null) {
|
|
805
|
+
payload = applyBeforeHookMutation(
|
|
806
|
+
mutated,
|
|
807
|
+
payload,
|
|
808
|
+
prepareCollectPayload
|
|
809
|
+
);
|
|
810
|
+
}
|
|
742
811
|
const result = await transport.send({
|
|
743
812
|
action: SDK_ACTIONS.collectPayment,
|
|
744
813
|
payload
|
|
@@ -746,35 +815,27 @@ function createSdkInstance(config) {
|
|
|
746
815
|
await runHook(
|
|
747
816
|
config.hooks?.afterCollect,
|
|
748
817
|
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
749
|
-
payload
|
|
818
|
+
{ ...payload, raw: input }
|
|
750
819
|
);
|
|
751
820
|
if (result.isErr) {
|
|
752
821
|
const sdkErr = parseError(result.error);
|
|
753
822
|
return createPaymentInstance(
|
|
754
|
-
{ reference, status: "pending" },
|
|
823
|
+
{ reference: payload.reference, status: "pending" },
|
|
755
824
|
{ ...commonDeps, initialError: sdkErr }
|
|
756
825
|
);
|
|
757
826
|
}
|
|
758
827
|
return createPaymentInstance(result.value, commonDeps);
|
|
759
828
|
}
|
|
760
829
|
async function collectPaymentAndResolve(input) {
|
|
761
|
-
|
|
762
|
-
validateCollectionAmount(input.amount);
|
|
763
|
-
validateNonEmpty(input.customer.name, "customer.name");
|
|
764
|
-
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
765
|
-
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
766
|
-
validateNonEmpty(input.description, "description");
|
|
767
|
-
if (input.method === "bank" && !input.bank) {
|
|
768
|
-
throwValidation('bank details are required when method is "bank"');
|
|
769
|
-
}
|
|
770
|
-
let payload = {
|
|
771
|
-
...input,
|
|
772
|
-
reference,
|
|
773
|
-
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
774
|
-
};
|
|
830
|
+
let payload = prepareCollectPayload(input);
|
|
775
831
|
const mutated = await runHook(config.hooks?.beforeCollect, payload);
|
|
776
|
-
if (mutated != null)
|
|
777
|
-
payload =
|
|
832
|
+
if (mutated != null) {
|
|
833
|
+
payload = applyBeforeHookMutation(
|
|
834
|
+
mutated,
|
|
835
|
+
payload,
|
|
836
|
+
prepareCollectPayload
|
|
837
|
+
);
|
|
838
|
+
}
|
|
778
839
|
const result = await transport.send({
|
|
779
840
|
action: SDK_ACTIONS.collectPaymentAndResolve,
|
|
780
841
|
payload
|
|
@@ -782,7 +843,7 @@ function createSdkInstance(config) {
|
|
|
782
843
|
await runHook(
|
|
783
844
|
config.hooks?.afterCollect,
|
|
784
845
|
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
785
|
-
payload
|
|
846
|
+
{ ...payload, raw: input }
|
|
786
847
|
);
|
|
787
848
|
if (result.isOk) {
|
|
788
849
|
return Ok(result.value);
|
|
@@ -790,28 +851,11 @@ function createSdkInstance(config) {
|
|
|
790
851
|
return Err(result.error);
|
|
791
852
|
}
|
|
792
853
|
async function makePayout(input) {
|
|
793
|
-
|
|
794
|
-
validatePayoutAmount(input.amount);
|
|
795
|
-
validateNonEmpty(input.customer.name, "customer.name");
|
|
796
|
-
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
797
|
-
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
798
|
-
validateNonEmpty(input.description, "description");
|
|
799
|
-
validateNonEmpty(
|
|
800
|
-
input.destination.accountHolderName,
|
|
801
|
-
"destination.accountHolderName"
|
|
802
|
-
);
|
|
803
|
-
validateNonEmpty(
|
|
804
|
-
input.destination.accountNumber,
|
|
805
|
-
"destination.accountNumber"
|
|
806
|
-
);
|
|
807
|
-
let payload = {
|
|
808
|
-
...input,
|
|
809
|
-
reference,
|
|
810
|
-
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
811
|
-
};
|
|
854
|
+
let payload = preparePayoutPayload(input);
|
|
812
855
|
const mutated = await runHook(config.hooks?.beforePayout, payload);
|
|
813
|
-
if (mutated != null)
|
|
814
|
-
payload =
|
|
856
|
+
if (mutated != null) {
|
|
857
|
+
payload = applyBeforeHookMutation(mutated, payload, preparePayoutPayload);
|
|
858
|
+
}
|
|
815
859
|
const result = await transport.send({
|
|
816
860
|
action: SDK_ACTIONS.makePayout,
|
|
817
861
|
payload
|
|
@@ -819,40 +863,23 @@ function createSdkInstance(config) {
|
|
|
819
863
|
await runHook(
|
|
820
864
|
config.hooks?.afterPayout,
|
|
821
865
|
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
822
|
-
payload
|
|
866
|
+
{ ...payload, raw: input }
|
|
823
867
|
);
|
|
824
868
|
if (result.isErr) {
|
|
825
869
|
const sdkErr = parseError(result.error);
|
|
826
870
|
return createPaymentInstance(
|
|
827
|
-
{ reference, status: "pending" },
|
|
871
|
+
{ reference: payload.reference, status: "pending" },
|
|
828
872
|
{ ...commonDeps, initialError: sdkErr }
|
|
829
873
|
);
|
|
830
874
|
}
|
|
831
875
|
return createPaymentInstance(result.value, commonDeps);
|
|
832
876
|
}
|
|
833
877
|
async function makePayoutAndResolve(input) {
|
|
834
|
-
|
|
835
|
-
validatePayoutAmount(input.amount);
|
|
836
|
-
validateNonEmpty(input.customer.name, "customer.name");
|
|
837
|
-
validateNonEmpty(input.customer.phoneNumber, "customer.phoneNumber");
|
|
838
|
-
const normalizedPhone = normalizePhone(input.customer.phoneNumber);
|
|
839
|
-
validateNonEmpty(input.description, "description");
|
|
840
|
-
validateNonEmpty(
|
|
841
|
-
input.destination.accountHolderName,
|
|
842
|
-
"destination.accountHolderName"
|
|
843
|
-
);
|
|
844
|
-
validateNonEmpty(
|
|
845
|
-
input.destination.accountNumber,
|
|
846
|
-
"destination.accountNumber"
|
|
847
|
-
);
|
|
848
|
-
let payload = {
|
|
849
|
-
...input,
|
|
850
|
-
reference,
|
|
851
|
-
customer: { ...input.customer, phoneNumber: normalizedPhone }
|
|
852
|
-
};
|
|
878
|
+
let payload = preparePayoutPayload(input);
|
|
853
879
|
const mutated = await runHook(config.hooks?.beforePayout, payload);
|
|
854
|
-
if (mutated != null)
|
|
855
|
-
payload =
|
|
880
|
+
if (mutated != null) {
|
|
881
|
+
payload = applyBeforeHookMutation(mutated, payload, preparePayoutPayload);
|
|
882
|
+
}
|
|
856
883
|
const result = await transport.send({
|
|
857
884
|
action: SDK_ACTIONS.makePayoutAndResolve,
|
|
858
885
|
payload
|
|
@@ -860,7 +887,7 @@ function createSdkInstance(config) {
|
|
|
860
887
|
await runHook(
|
|
861
888
|
config.hooks?.afterPayout,
|
|
862
889
|
result.isOk ? Ok({ reference: result.value.reference, status: result.value.status }) : Err(result.error),
|
|
863
|
-
payload
|
|
890
|
+
{ ...payload, raw: input }
|
|
864
891
|
);
|
|
865
892
|
if (result.isOk) {
|
|
866
893
|
return Ok(result.value);
|
|
@@ -894,6 +921,7 @@ function createSdkInstance(config) {
|
|
|
894
921
|
async function verifyPhone(input) {
|
|
895
922
|
validateNonEmpty(input.phoneNumber, "phoneNumber");
|
|
896
923
|
const normalizedPhone = normalizePhone(input.phoneNumber);
|
|
924
|
+
validatePhoneFormat(normalizedPhone, "phoneNumber");
|
|
897
925
|
const result = await transport.send({
|
|
898
926
|
action: SDK_ACTIONS.verifyPhone,
|
|
899
927
|
payload: { ...input, phoneNumber: normalizedPhone }
|