@t402/core 2.5.0 → 2.6.1

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.
@@ -23,7 +23,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
23
23
  var http_exports = {};
24
24
  __export(http_exports, {
25
25
  HTTPFacilitatorClient: () => HTTPFacilitatorClient,
26
+ IntentClient: () => IntentClient,
26
27
  RouteConfigurationError: () => RouteConfigurationError,
28
+ StreamingClient: () => StreamingClient,
27
29
  decodePaymentRequiredHeader: () => decodePaymentRequiredHeader,
28
30
  decodePaymentResponseHeader: () => decodePaymentResponseHeader,
29
31
  decodePaymentSignatureHeader: () => decodePaymentSignatureHeader,
@@ -115,6 +117,38 @@ function safeBase64Decode(data) {
115
117
  return Buffer.from(data, "base64").toString("utf-8");
116
118
  }
117
119
 
120
+ // src/errors.ts
121
+ var T402PaymentError = class _T402PaymentError extends Error {
122
+ constructor(message, options) {
123
+ super(message);
124
+ __publicField(this, "cause");
125
+ __publicField(this, "phase");
126
+ __publicField(this, "retryable");
127
+ __publicField(this, "code");
128
+ this.name = "T402PaymentError";
129
+ this.cause = options?.cause;
130
+ this.phase = options?.phase ?? "unknown";
131
+ this.retryable = options?.retryable ?? false;
132
+ this.code = options?.code;
133
+ if (Error.captureStackTrace) {
134
+ Error.captureStackTrace(this, _T402PaymentError);
135
+ }
136
+ }
137
+ isRetryable() {
138
+ return this.retryable;
139
+ }
140
+ toJSON() {
141
+ return {
142
+ name: this.name,
143
+ message: this.message,
144
+ phase: this.phase,
145
+ retryable: this.retryable,
146
+ code: this.code,
147
+ cause: this.cause?.message
148
+ };
149
+ }
150
+ };
151
+
118
152
  // src/index.ts
119
153
  var t402Version = 2;
120
154
 
@@ -627,7 +661,11 @@ var HTTPFacilitatorClient = class {
627
661
  });
628
662
  if (!response.ok) {
629
663
  const errorText = await response.text().catch(() => response.statusText);
630
- throw new Error(`Facilitator verify failed (${response.status}): ${errorText}`);
664
+ throw new T402PaymentError(`Facilitator verify failed (${response.status}): ${errorText}`, {
665
+ phase: "verification",
666
+ code: response.status,
667
+ retryable: response.status >= 500 || response.status === 429
668
+ });
631
669
  }
632
670
  return await response.json();
633
671
  }
@@ -636,12 +674,16 @@ var HTTPFacilitatorClient = class {
636
674
  *
637
675
  * @param paymentPayload - The payment to settle
638
676
  * @param paymentRequirements - The requirements for settlement
677
+ * @param options - Optional settings including idempotency key
639
678
  * @returns Settlement response
640
679
  */
641
- async settle(paymentPayload, paymentRequirements) {
680
+ async settle(paymentPayload, paymentRequirements, options) {
642
681
  let headers = {
643
682
  "Content-Type": "application/json"
644
683
  };
684
+ if (options?.idempotencyKey) {
685
+ headers["Idempotency-Key"] = options.idempotencyKey;
686
+ }
645
687
  if (this._createAuthHeaders) {
646
688
  const authHeaders = await this.createAuthHeaders("settle");
647
689
  headers = { ...headers, ...authHeaders.headers };
@@ -657,7 +699,11 @@ var HTTPFacilitatorClient = class {
657
699
  });
658
700
  if (!response.ok) {
659
701
  const errorText = await response.text().catch(() => response.statusText);
660
- throw new Error(`Facilitator settle failed (${response.status}): ${errorText}`);
702
+ throw new T402PaymentError(`Facilitator settle failed (${response.status}): ${errorText}`, {
703
+ phase: "settlement",
704
+ code: response.status,
705
+ retryable: response.status >= 500 || response.status === 429
706
+ });
661
707
  }
662
708
  return await response.json();
663
709
  }
@@ -680,7 +726,14 @@ var HTTPFacilitatorClient = class {
680
726
  });
681
727
  if (!response.ok) {
682
728
  const errorText = await response.text().catch(() => response.statusText);
683
- throw new Error(`Facilitator getSupported failed (${response.status}): ${errorText}`);
729
+ throw new T402PaymentError(
730
+ `Facilitator getSupported failed (${response.status}): ${errorText}`,
731
+ {
732
+ phase: "unknown",
733
+ code: response.status,
734
+ retryable: response.status >= 500 || response.status === 429
735
+ }
736
+ );
684
737
  }
685
738
  return await response.json();
686
739
  }
@@ -742,8 +795,9 @@ var t402HTTPClient = class {
742
795
  "X-PAYMENT": encodePaymentSignatureHeader(paymentPayload)
743
796
  };
744
797
  default:
745
- throw new Error(
746
- `Unsupported t402 version: ${paymentPayload.t402Version}`
798
+ throw new T402PaymentError(
799
+ `Unsupported t402 version: ${paymentPayload.t402Version}`,
800
+ { phase: "submission" }
747
801
  );
748
802
  }
749
803
  }
@@ -762,7 +816,9 @@ var t402HTTPClient = class {
762
816
  if (body && body instanceof Object && "t402Version" in body && body.t402Version === 1) {
763
817
  return body;
764
818
  }
765
- throw new Error("Invalid payment required response");
819
+ throw new T402PaymentError("Invalid payment required response", {
820
+ phase: "submission"
821
+ });
766
822
  }
767
823
  /**
768
824
  * Extracts payment settlement response from HTTP headers.
@@ -779,7 +835,9 @@ var t402HTTPClient = class {
779
835
  if (xPaymentResponse) {
780
836
  return decodePaymentResponseHeader(xPaymentResponse);
781
837
  }
782
- throw new Error("Payment response header not found");
838
+ throw new T402PaymentError("Payment response header not found", {
839
+ phase: "settlement"
840
+ });
783
841
  }
784
842
  /**
785
843
  * Creates a payment payload for the given payment requirements.
@@ -793,6 +851,247 @@ var t402HTTPClient = class {
793
851
  }
794
852
  };
795
853
 
854
+ // src/http/streamingClient.ts
855
+ var DEFAULT_FACILITATOR_URL2 = "https://facilitator.t402.io";
856
+ var StreamingClient = class {
857
+ constructor(config) {
858
+ __publicField(this, "url");
859
+ __publicField(this, "apiKey");
860
+ __publicField(this, "requesterAddress");
861
+ this.url = config?.url || DEFAULT_FACILITATOR_URL2;
862
+ this.apiKey = config?.apiKey;
863
+ this.requesterAddress = config?.requesterAddress;
864
+ }
865
+ /**
866
+ * Open a new payment stream
867
+ */
868
+ async openStream(params) {
869
+ return this.post("/v1/stream/open", params);
870
+ }
871
+ /**
872
+ * Update a stream with a new cumulative amount
873
+ */
874
+ async updateStream(params) {
875
+ return this.post("/v1/stream/update", params);
876
+ }
877
+ /**
878
+ * Close a stream and settle the final amount
879
+ */
880
+ async closeStream(params) {
881
+ return this.post("/v1/stream/close", params);
882
+ }
883
+ /**
884
+ * Get stream details by ID
885
+ */
886
+ async getStream(id, options) {
887
+ const params = new URLSearchParams();
888
+ if (options?.includeUpdates) params.set("include_updates", "true");
889
+ if (options?.includeStats) params.set("include_stats", "true");
890
+ const query = params.toString();
891
+ const path = `/v1/stream/${encodeURIComponent(id)}${query ? `?${query}` : ""}`;
892
+ return this.get(path);
893
+ }
894
+ /**
895
+ * Pause an active stream
896
+ */
897
+ async pauseStream(id) {
898
+ return this.post(`/v1/stream/${encodeURIComponent(id)}/pause`, {});
899
+ }
900
+ /**
901
+ * Resume a paused stream
902
+ */
903
+ async resumeStream(id) {
904
+ return this.post(`/v1/stream/${encodeURIComponent(id)}/resume`, {});
905
+ }
906
+ /**
907
+ * List streams with optional filters
908
+ */
909
+ async listStreams(filters) {
910
+ const params = new URLSearchParams();
911
+ if (filters?.network) params.set("network", filters.network);
912
+ if (filters?.payer) params.set("payer", filters.payer);
913
+ if (filters?.payee) params.set("payee", filters.payee);
914
+ if (filters?.status) {
915
+ for (const s of filters.status) params.append("status", s);
916
+ }
917
+ if (filters?.limit !== void 0) params.set("limit", String(filters.limit));
918
+ if (filters?.offset !== void 0) params.set("offset", String(filters.offset));
919
+ if (filters?.orderBy) params.set("orderBy", filters.orderBy);
920
+ if (filters?.orderDesc) params.set("orderDesc", "true");
921
+ const query = params.toString();
922
+ return this.get(`/v1/stream${query ? `?${query}` : ""}`);
923
+ }
924
+ // ============================================================================
925
+ // Internal HTTP helpers
926
+ // ============================================================================
927
+ buildHeaders() {
928
+ const headers = {
929
+ "Content-Type": "application/json"
930
+ };
931
+ if (this.apiKey) {
932
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
933
+ }
934
+ if (this.requesterAddress) {
935
+ headers["X-Requester-Address"] = this.requesterAddress;
936
+ }
937
+ return headers;
938
+ }
939
+ async post(path, body) {
940
+ const response = await fetch(`${this.url}${path}`, {
941
+ method: "POST",
942
+ headers: this.buildHeaders(),
943
+ body: JSON.stringify(body)
944
+ });
945
+ if (!response.ok) {
946
+ const errorText = await response.text().catch(() => response.statusText);
947
+ throw new T402PaymentError(`Streaming API ${path} failed (${response.status}): ${errorText}`, {
948
+ phase: "unknown",
949
+ code: response.status,
950
+ retryable: response.status >= 500 || response.status === 429
951
+ });
952
+ }
953
+ return await response.json();
954
+ }
955
+ async get(path) {
956
+ const response = await fetch(`${this.url}${path}`, {
957
+ method: "GET",
958
+ headers: this.buildHeaders()
959
+ });
960
+ if (!response.ok) {
961
+ const errorText = await response.text().catch(() => response.statusText);
962
+ throw new T402PaymentError(`Streaming API ${path} failed (${response.status}): ${errorText}`, {
963
+ phase: "unknown",
964
+ code: response.status,
965
+ retryable: response.status >= 500 || response.status === 429
966
+ });
967
+ }
968
+ return await response.json();
969
+ }
970
+ };
971
+
972
+ // src/http/intentClient.ts
973
+ var DEFAULT_FACILITATOR_URL3 = "https://facilitator.t402.io";
974
+ var IntentClient = class {
975
+ constructor(config) {
976
+ __publicField(this, "url");
977
+ __publicField(this, "apiKey");
978
+ this.url = config?.url || DEFAULT_FACILITATOR_URL3;
979
+ this.apiKey = config?.apiKey;
980
+ }
981
+ /**
982
+ * Create a new payment intent and get available routes
983
+ */
984
+ async createIntent(params) {
985
+ return this.post("/v1/intent", params);
986
+ }
987
+ /**
988
+ * Get intent details by ID
989
+ */
990
+ async getIntent(id) {
991
+ return this.get(`/v1/intent/${encodeURIComponent(id)}`);
992
+ }
993
+ /**
994
+ * Select a route for an intent
995
+ */
996
+ async selectRoute(id, params) {
997
+ return this.post(
998
+ `/v1/intent/${encodeURIComponent(id)}/route`,
999
+ params
1000
+ );
1001
+ }
1002
+ /**
1003
+ * Execute an intent with a signed authorization
1004
+ */
1005
+ async executeIntent(id, params) {
1006
+ return this.post(
1007
+ `/v1/intent/${encodeURIComponent(id)}/execute`,
1008
+ params
1009
+ );
1010
+ }
1011
+ /**
1012
+ * Cancel a pending intent
1013
+ */
1014
+ async cancelIntent(id, params) {
1015
+ return this.post(
1016
+ `/v1/intent/${encodeURIComponent(id)}/cancel`,
1017
+ params ?? {}
1018
+ );
1019
+ }
1020
+ /**
1021
+ * Refresh available routes for an intent
1022
+ */
1023
+ async refreshRoutes(id) {
1024
+ return this.post(
1025
+ `/v1/intent/${encodeURIComponent(id)}/refresh`,
1026
+ {}
1027
+ );
1028
+ }
1029
+ /**
1030
+ * List intents with optional filters
1031
+ */
1032
+ async listIntents(filters) {
1033
+ const params = new URLSearchParams();
1034
+ if (filters?.payer) params.set("payer", filters.payer);
1035
+ if (filters?.payee) params.set("payee", filters.payee);
1036
+ if (filters?.status) {
1037
+ for (const s of filters.status) params.append("status", s);
1038
+ }
1039
+ if (filters?.limit !== void 0) params.set("limit", String(filters.limit));
1040
+ if (filters?.offset !== void 0) params.set("offset", String(filters.offset));
1041
+ const query = params.toString();
1042
+ return this.get(`/v1/intent${query ? `?${query}` : ""}`);
1043
+ }
1044
+ /**
1045
+ * Get intent statistics (counts by status)
1046
+ */
1047
+ async getIntentStats() {
1048
+ return this.get("/v1/intent/stats");
1049
+ }
1050
+ // ============================================================================
1051
+ // Internal HTTP helpers
1052
+ // ============================================================================
1053
+ buildHeaders() {
1054
+ const headers = {
1055
+ "Content-Type": "application/json"
1056
+ };
1057
+ if (this.apiKey) {
1058
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
1059
+ }
1060
+ return headers;
1061
+ }
1062
+ async post(path, body) {
1063
+ const response = await fetch(`${this.url}${path}`, {
1064
+ method: "POST",
1065
+ headers: this.buildHeaders(),
1066
+ body: JSON.stringify(body)
1067
+ });
1068
+ if (!response.ok) {
1069
+ const errorText = await response.text().catch(() => response.statusText);
1070
+ throw new T402PaymentError(`Intent API ${path} failed (${response.status}): ${errorText}`, {
1071
+ phase: "unknown",
1072
+ code: response.status,
1073
+ retryable: response.status >= 500 || response.status === 429
1074
+ });
1075
+ }
1076
+ return await response.json();
1077
+ }
1078
+ async get(path) {
1079
+ const response = await fetch(`${this.url}${path}`, {
1080
+ method: "GET",
1081
+ headers: this.buildHeaders()
1082
+ });
1083
+ if (!response.ok) {
1084
+ const errorText = await response.text().catch(() => response.statusText);
1085
+ throw new T402PaymentError(`Intent API ${path} failed (${response.status}): ${errorText}`, {
1086
+ phase: "unknown",
1087
+ code: response.status,
1088
+ retryable: response.status >= 500 || response.status === 429
1089
+ });
1090
+ }
1091
+ return await response.json();
1092
+ }
1093
+ };
1094
+
796
1095
  // src/http/index.ts
797
1096
  function encodePaymentSignatureHeader(paymentPayload) {
798
1097
  return safeBase64Encode(JSON.stringify(paymentPayload));
@@ -830,7 +1129,9 @@ function decodePaymentResponseHeader(paymentResponseHeader) {
830
1129
  // Annotate the CommonJS export names for ESM import in node:
831
1130
  0 && (module.exports = {
832
1131
  HTTPFacilitatorClient,
1132
+ IntentClient,
833
1133
  RouteConfigurationError,
1134
+ StreamingClient,
834
1135
  decodePaymentRequiredHeader,
835
1136
  decodePaymentResponseHeader,
836
1137
  decodePaymentSignatureHeader,