@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.d.ts CHANGED
@@ -149,6 +149,14 @@ type VerifyWebhookInput = {
149
149
  payload: string | Uint8Array;
150
150
  signature: string;
151
151
  secret: string;
152
+ /**
153
+ * Replay-protection window in seconds. After the signature is verified, the
154
+ * timestamp carried inside the signed body must be within this many seconds of
155
+ * now, or verification fails. Defaults to 300 (5 minutes). Set to `0` to
156
+ * disable the freshness check (not recommended — a captured webhook then
157
+ * verifies forever).
158
+ */
159
+ toleranceSeconds?: number;
152
160
  };
153
161
  /**
154
162
  * Full transaction record returned by lookups, event handlers, and the
@@ -245,16 +253,39 @@ type AfterPayoutHook = (result: Result<{
245
253
  reference: string;
246
254
  status: string;
247
255
  }, string>, input: MakePayoutInput) => void | Promise<void>;
256
+ /**
257
+ * Wrapper applied to every lifecycle hook. The SDK runs `fn` inside `safeTry`,
258
+ * so a throw or rejection in merchant code never bubbles into the payment flow —
259
+ * it is routed to `onError` instead.
260
+ *
261
+ * WHY `onError` is required: an unhandled hook failure in a payments SDK is the
262
+ * worst kind of silent bug (the payment "succeeds" while a wallet credit or
263
+ * fulfillment side-effect was lost). Forcing the merchant to declare what
264
+ * happens on failure replaces both the old "throw and maybe crash" behaviour and
265
+ * a silent `catch {}` with an explicit, type-enforced decision.
266
+ */
267
+ type SdkHook<TFn> = {
268
+ /** Set `false` to disable this hook without removing its config. Default: true. */
269
+ enabled?: boolean;
270
+ /** The hook implementation. */
271
+ fn: TFn;
272
+ /**
273
+ * Required. Receives the thrown/rejected value if `fn` fails. Runs inside
274
+ * `safeTry` too, so an error here is contained as well.
275
+ */
276
+ onError: (error: unknown) => void | Promise<void>;
277
+ };
248
278
  /**
249
279
  * Lifecycle hooks registered once at SDK creation. Each hook fires on every
250
280
  * matching operation — use them for cross-cutting concerns like logging,
251
- * audit trails, and payload enrichment.
281
+ * audit trails, and payload enrichment. Every hook is wrapped in {@link SdkHook}
282
+ * so merchant code can never crash the payment flow.
252
283
  */
253
284
  type SdkHooks = {
254
- beforeCollect?: BeforeCollectHook;
255
- afterCollect?: AfterCollectHook;
256
- beforePayout?: BeforePayoutHook;
257
- afterPayout?: AfterPayoutHook;
285
+ beforeCollect?: SdkHook<BeforeCollectHook>;
286
+ afterCollect?: SdkHook<AfterCollectHook>;
287
+ beforePayout?: SdkHook<BeforePayoutHook>;
288
+ afterPayout?: SdkHook<AfterPayoutHook>;
258
289
  };
259
290
  /**
260
291
  * SDK configuration supplied by the merchant at initialization.
@@ -273,12 +304,6 @@ type NylonPayConfig = {
273
304
  maxPollIntervalMs?: number;
274
305
  maxPollDurationMs?: number;
275
306
  maxPollAttempts?: number;
276
- /**
277
- * Receive status updates over an SSE stream (default `true`), falling back to
278
- * polling automatically when the stream is unavailable. Set `false` to poll
279
- * only.
280
- */
281
- streaming?: boolean;
282
307
  fetch?: typeof globalThis.fetch;
283
308
  /** Force a new instance even if one already exists for this key+url pair. */
284
309
  force?: boolean;
@@ -595,9 +620,9 @@ interface PaymentInstance {
595
620
  * Factory function to create a Nylon Pay SDK instance.
596
621
  * This is the main entry point for merchants.
597
622
  *
598
- * Calling createNylonPay with the same apiKey and baseUrl returns the same
599
- * instance (singleton per key+url pair). Pass { force: true } to create a
600
- * fresh instance and replace the cached one.
623
+ * Calling createNylonPay with the same apiKey, apiSecret and baseUrl returns the
624
+ * same instance (singleton per key+secret+url). Rotating the secret yields a
625
+ * fresh instance. Pass { force: true } to force a new instance regardless.
601
626
  *
602
627
  * @example
603
628
  * ```ts
@@ -613,9 +638,9 @@ interface PaymentInstance {
613
638
  /**
614
639
  * Create a Nylon Pay SDK instance.
615
640
  *
616
- * Returns the same instance for the same apiKey + baseUrl combination unless
617
- * { force: true } is passed. Use your test keys for sandbox, production keys
618
- * for live.
641
+ * Returns the same instance for the same apiKey + apiSecret + baseUrl
642
+ * combination unless { force: true } is passed. Use your test keys for sandbox,
643
+ * production keys for live.
619
644
  *
620
645
  * @param config - SDK configuration with apiKey and apiSecret
621
646
  * @returns SDK instance with all payment operations
@@ -663,13 +688,19 @@ declare function parseError(error: string): SdkError;
663
688
  */
664
689
 
665
690
  /**
666
- * Verify that a webhook payload was signed by Nylon Pay.
667
- * Operates on raw payload bytes, NOT parsed JSON (spec invariant #8).
691
+ * Verify that a webhook payload was genuinely sent by Nylon Pay.
692
+ *
693
+ * Two checks, both must pass:
694
+ * 1. **Authenticity** — HMAC-SHA256 over the raw payload bytes (NOT parsed
695
+ * JSON, spec invariant #8) matches the provided signature.
696
+ * 2. **Freshness** — the `timestamp` carried inside the signed body is within
697
+ * `toleranceSeconds` of now (default 300s). This is what stops a replay: a
698
+ * captured `(body, signature)` pair stays cryptographically valid forever,
699
+ * but its embedded timestamp goes stale. Every genuine delivery, including
700
+ * retries hours later, is re-stamped and re-signed, so this never rejects
701
+ * legitimate traffic. Pass `toleranceSeconds: 0` to skip this check.
668
702
  *
669
- * @param input.payload - Raw request body as string or Uint8Array
670
- * @param input.signature - Signature from the webhook header
671
- * @param input.secret - Merchant's webhook secret
672
- * @returns True when the signature is valid
703
+ * @returns True when the signature is valid and (when enforced) the webhook is fresh
673
704
  */
674
705
  declare function verifyWebhookSignature(input: VerifyWebhookInput): boolean;
675
706