@nile-squad/nylonpay-ts 1.2.0 → 1.3.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/README.md CHANGED
@@ -41,7 +41,7 @@ Use your test keys to work in sandbox, or your production keys to go live. There
41
41
  |---|---|---|---|
42
42
  | `apiKey` | Yes | | Must start with `npk_` |
43
43
  | `apiSecret` | Yes | | Must start with `nps_` |
44
- | `baseUrl` | No | Default is used | Override only if self-hosting |
44
+ | `baseUrl` | No | Default is used | Override for a custom endpoint |
45
45
  | `timeoutMs` | No | `30000` | Request timeout in milliseconds |
46
46
  | `maxRetries` | No | `3` | Retry count for failed requests |
47
47
  | `maxPollIntervalMs` | No | `2000` | Polling interval for async payments |
@@ -231,16 +231,13 @@ if (!result.isOk) {
231
231
 
232
232
  `USD`, `EUR`, `GBP`, `KES`, `UGX`, `TZS`, `RWF`
233
233
 
234
- ## Development
234
+ ## Links
235
235
 
236
- Maintainer notes and pending work live in [`dev-note.md`](./dev-note.md).
237
-
238
- ```sh
239
- pnpm install
240
- pnpm test # vitest
241
- pnpm typecheck # tsc --noEmit
242
- pnpm build # tsup
243
- ```
236
+ - [Documentation](https://docs.nylonpay.nilesquad.com/docs)
237
+ - [SDK Spec](https://github.com/nile-squad/specs/blob/main/nylonpay-sdk-spec/spec.md)
238
+ - [GitHub Repository](https://github.com/nile-squad/nylonpay-ts)
239
+ - [Python SDK](https://github.com/nile-squad/nylonpay-py)
240
+ - [Nylon Pay](https://nylonpay.nilesquad.com)
244
241
 
245
242
  ## License
246
243
 
package/dist/index.cjs CHANGED
@@ -72,10 +72,12 @@ var SDK_ACTIONS = {
72
72
  makePayoutAndResolve: "sdk-make-payout-and-resolve",
73
73
  getStatus: "sdk-get-status",
74
74
  getTransaction: "sdk-get-transaction",
75
+ listTransactions: "sdk-list-transactions",
75
76
  verifyPhone: "sdk-verify-phone",
76
77
  createInvoice: "sdk-create-invoice"
77
78
  };
78
79
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
80
+ var MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
79
81
  function generateFingerprint() {
80
82
  const components = [
81
83
  `type:${os.type()}`,
@@ -270,6 +272,17 @@ function createTransport({
270
272
  body: bodyString,
271
273
  signal: controller.signal
272
274
  });
275
+ const contentLength = response.headers?.get("content-length");
276
+ if (contentLength && Number(contentLength) > MAX_RESPONSE_BYTES) {
277
+ cleanup();
278
+ return slangTs.Err(
279
+ JSON.stringify({
280
+ category: "internal",
281
+ message: "Received an invalid response from the server",
282
+ retryable: false
283
+ })
284
+ );
285
+ }
273
286
  if (!response.ok) {
274
287
  const statusCode = response.status;
275
288
  const retryable = RETRYABLE_STATUS_CODES.has(statusCode);
@@ -657,43 +670,44 @@ function extractSignedTimestampMs(payloadString) {
657
670
  return null;
658
671
  }
659
672
  function verifyWebhookSignature(input) {
660
- const payloadString = decodePayload(input.payload);
661
- const payloadBytes = Buffer.from(payloadString, "utf8");
662
- const expectedSignature = crypto.createHmac("sha256", input.secret).update(payloadBytes).digest("hex");
663
- const providedBuffer = Buffer.from(input.signature, "hex");
664
- const expectedBuffer = Buffer.from(expectedSignature, "hex");
665
- if (providedBuffer.length !== expectedBuffer.length) {
666
- return false;
667
- }
668
- if (!crypto.timingSafeEqual(providedBuffer, expectedBuffer)) {
669
- return false;
670
- }
671
- const toleranceSeconds = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
672
- if (toleranceSeconds <= 0) {
673
- return true;
674
- }
675
- const timestampMs = extractSignedTimestampMs(payloadString);
676
- if (timestampMs === null) {
673
+ try {
674
+ const payloadString = decodePayload(input.payload);
675
+ const payloadBytes = Buffer.from(payloadString, "utf8");
676
+ const expectedSignature = crypto.createHmac("sha256", input.secret).update(payloadBytes).digest("hex");
677
+ const providedBuffer = Buffer.from(input.signature, "hex");
678
+ const expectedBuffer = Buffer.from(expectedSignature, "hex");
679
+ if (providedBuffer.length !== expectedBuffer.length) {
680
+ return false;
681
+ }
682
+ if (!crypto.timingSafeEqual(providedBuffer, expectedBuffer)) {
683
+ return false;
684
+ }
685
+ const toleranceSeconds = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
686
+ if (toleranceSeconds === 0) {
687
+ return true;
688
+ }
689
+ if (toleranceSeconds < 0) {
690
+ return false;
691
+ }
692
+ const timestampMs = extractSignedTimestampMs(payloadString);
693
+ if (timestampMs === null) {
694
+ return false;
695
+ }
696
+ const ageMs = Math.abs(Date.now() - timestampMs);
697
+ return ageMs <= toleranceSeconds * 1e3;
698
+ } catch {
677
699
  return false;
678
700
  }
679
- const ageMs = Math.abs(Date.now() - timestampMs);
680
- return ageMs <= toleranceSeconds * 1e3;
681
701
  }
682
702
 
683
703
  // src/sdk.ts
684
- function generateReference() {
685
- return crypto.randomBytes(16).toString("hex").slice(0, 15);
686
- }
687
- var REFERENCE_MIN_LENGTH = 13;
688
- var REFERENCE_MAX_LENGTH = 15;
704
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
689
705
  function resolveReference(reference) {
690
706
  if (reference === void 0) {
691
- return generateReference();
707
+ return crypto.randomUUID();
692
708
  }
693
- if (reference.length < REFERENCE_MIN_LENGTH || reference.length > REFERENCE_MAX_LENGTH) {
694
- throwValidation(
695
- `reference must be ${REFERENCE_MIN_LENGTH}\u2013${REFERENCE_MAX_LENGTH} characters`
696
- );
709
+ if (!UUID_REGEX.test(reference)) {
710
+ throwValidation("reference must be a valid UUID");
697
711
  }
698
712
  return reference;
699
713
  }
@@ -960,6 +974,19 @@ function createSdkInstance(config) {
960
974
  }
961
975
  return slangTs.Err(result.error);
962
976
  }
977
+ async function listTransactions(input) {
978
+ const result = await transport.send({
979
+ action: SDK_ACTIONS.listTransactions,
980
+ payload: input ?? {}
981
+ });
982
+ if (result.isOk) {
983
+ return slangTs.Ok(result.value);
984
+ }
985
+ return slangTs.Err(result.error);
986
+ }
987
+ async function getTransactionsByTag(tag, options) {
988
+ return listTransactions({ ...options, tags: [tag] });
989
+ }
963
990
  function verifyWebhook(input) {
964
991
  return verifyWebhookSignature(input);
965
992
  }
@@ -970,6 +997,8 @@ function createSdkInstance(config) {
970
997
  makePayoutAndResolve,
971
998
  getStatus,
972
999
  getTransaction,
1000
+ listTransactions,
1001
+ getTransactionsByTag,
973
1002
  verifyPhone,
974
1003
  createInvoice,
975
1004
  verifyWebhookSignature: verifyWebhook