@gopaycz/gopay-js-sdk 1.6.6 → 1.7.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 CHANGED
@@ -23,7 +23,9 @@ __export(src_exports, {
23
23
  GoPayErrorCodes: () => GoPayErrorCodes,
24
24
  GoPayHTTPError: () => GoPayHTTPError,
25
25
  GoPaySDKError: () => GoPaySDKError,
26
+ GoPayScopes: () => GoPayScopes,
26
27
  SDK_VERSION: () => SDK_VERSION,
28
+ combineScopes: () => combineScopes,
27
29
  createGoPaySDK: () => createGoPaySDK
28
30
  });
29
31
  module.exports = __toCommonJS(src_exports);
@@ -78,6 +80,7 @@ var GoPaySDKError = class extends Error {
78
80
  this.cause = options.cause;
79
81
  }
80
82
  this.errorCode = options?.errorCode;
83
+ this.chargeState = options?.chargeState;
81
84
  }
82
85
  };
83
86
  var GoPayHTTPError = class extends Error {
@@ -88,6 +91,43 @@ var GoPayHTTPError = class extends Error {
88
91
  this.name = "GoPayHTTPError";
89
92
  }
90
93
  };
94
+ function runPollLoop(poll, intervalMs, signal, reject, onTick, onAbort, extraCleanup) {
95
+ let stopped = false;
96
+ let pollHandle;
97
+ const stop = (fn) => {
98
+ if (stopped) {
99
+ return;
100
+ }
101
+ stopped = true;
102
+ clearTimeout(pollHandle);
103
+ extraCleanup?.();
104
+ signal?.removeEventListener("abort", abortListener);
105
+ fn();
106
+ };
107
+ const abortListener = () => onAbort(stop);
108
+ signal?.addEventListener("abort", abortListener, { once: true });
109
+ const doPoll = () => {
110
+ if (stopped) {
111
+ return;
112
+ }
113
+ poll().then((state) => {
114
+ if (stopped) {
115
+ return;
116
+ }
117
+ onTick(state, stop);
118
+ if (!stopped) {
119
+ pollHandle = setTimeout(doPoll, intervalMs);
120
+ }
121
+ }).catch((err) => {
122
+ if (stopped) {
123
+ return;
124
+ }
125
+ stop(() => reject(err));
126
+ });
127
+ };
128
+ doPoll();
129
+ return stop;
130
+ }
91
131
  function awaitCharge(poll, options) {
92
132
  const intervalMs = options?.intervalMs ?? 2e3;
93
133
  const initialTimeoutMs = options?.initialTimeoutMs ?? 3e4;
@@ -99,56 +139,25 @@ function awaitCharge(poll, options) {
99
139
  );
100
140
  }
101
141
  return new Promise((resolve, reject) => {
102
- let stopped = false;
103
142
  let lastActionRequiredUrl = null;
104
- const stop = (fn) => {
105
- if (stopped) {
106
- return;
107
- }
108
- stopped = true;
109
- clearTimeout(initialTimer);
110
- options?.signal?.removeEventListener("abort", onAbort);
111
- fn();
112
- };
113
- let onAbort;
114
- onAbort = () => {
115
- stop(
116
- () => reject(
117
- new GoPaySDKError("[GoPaySDK] Charge polling aborted.", {
118
- errorCode: GoPayErrorCodes.CHARGE_FAILED
119
- })
120
- )
121
- );
122
- };
123
- options?.signal?.addEventListener("abort", onAbort, { once: true });
124
- const initialTimer = setTimeout(() => {
125
- stop(
126
- () => reject(
127
- new GoPaySDKError(
128
- "[GoPaySDK] Charge did not progress within the initial timeout",
129
- { errorCode: GoPayErrorCodes.CHARGE_TIMEOUT }
130
- )
131
- )
132
- );
133
- }, initialTimeoutMs);
134
- const doPoll = () => {
135
- if (stopped) {
136
- return;
137
- }
138
- poll().then((state) => {
139
- if (stopped) {
140
- return;
141
- }
143
+ let initialTimer;
144
+ const stop = runPollLoop(
145
+ poll,
146
+ intervalMs,
147
+ options?.signal,
148
+ reject,
149
+ (state, stop2) => {
142
150
  options?.onStateChange?.(state);
143
151
  if (state.state === "SUCCEEDED") {
144
- stop(() => resolve(state));
152
+ stop2(() => resolve(state));
145
153
  return;
146
154
  }
147
155
  if (state.state === "FAILED") {
148
- stop(
156
+ stop2(
149
157
  () => reject(
150
158
  new GoPaySDKError("[GoPaySDK] Charge failed", {
151
- errorCode: GoPayErrorCodes.CHARGE_FAILED
159
+ errorCode: GoPayErrorCodes.CHARGE_FAILED,
160
+ chargeState: state
152
161
  })
153
162
  )
154
163
  );
@@ -158,20 +167,32 @@ function awaitCharge(poll, options) {
158
167
  clearTimeout(initialTimer);
159
168
  if (state.action?.redirect_url && state.action.redirect_url !== lastActionRequiredUrl) {
160
169
  lastActionRequiredUrl = state.action.redirect_url;
161
- options?.onActionRequired?.(
162
- state.action.redirect_url
163
- );
170
+ options?.onActionRequired?.(state.action.redirect_url);
164
171
  }
165
172
  }
166
- setTimeout(doPoll, intervalMs);
167
- }).catch((err) => {
168
- if (stopped) {
169
- return;
170
- }
171
- stop(() => reject(err));
172
- });
173
- };
174
- doPoll();
173
+ },
174
+ (stop2) => stop2(
175
+ () => reject(
176
+ new GoPaySDKError(
177
+ "[GoPaySDK] Charge polling aborted.",
178
+ {
179
+ errorCode: GoPayErrorCodes.CHARGE_FAILED
180
+ }
181
+ )
182
+ )
183
+ ),
184
+ () => clearTimeout(initialTimer)
185
+ );
186
+ initialTimer = setTimeout(() => {
187
+ stop(
188
+ () => reject(
189
+ new GoPaySDKError(
190
+ "[GoPaySDK] Charge did not progress within the initial timeout",
191
+ { errorCode: GoPayErrorCodes.CHARGE_TIMEOUT }
192
+ )
193
+ )
194
+ );
195
+ }, initialTimeoutMs);
175
196
  });
176
197
  }
177
198
  var BASE_URLS = {
@@ -209,7 +230,7 @@ function buildUrl(baseUrl, path) {
209
230
  var MAX_RETRIES = 2;
210
231
  var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS", "PUT", "DELETE"]);
211
232
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
212
- async function fetchWithRetry(url, init, timeoutMs) {
233
+ async function fetchWithRetry(url, init, signal) {
213
234
  const method = (init?.method ?? "GET").toUpperCase();
214
235
  const isIdempotent = IDEMPOTENT_METHODS.has(method);
215
236
  let lastResponse;
@@ -222,7 +243,7 @@ async function fetchWithRetry(url, init, timeoutMs) {
222
243
  const response = await fetch(
223
244
  new Request(url, {
224
245
  ...init,
225
- signal: AbortSignal.timeout(timeoutMs)
246
+ signal
226
247
  })
227
248
  );
228
249
  if (response.status < 500) {
@@ -233,7 +254,7 @@ async function fetchWithRetry(url, init, timeoutMs) {
233
254
  }
234
255
  lastResponse = response;
235
256
  } catch (err) {
236
- if (err instanceof Error && err.name === "TimeoutError") {
257
+ if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
237
258
  throw err;
238
259
  }
239
260
  if (!isIdempotent) {
@@ -300,10 +321,12 @@ function createAuthHandler(deps) {
300
321
  }
301
322
  headers.set("Authorization", `Bearer ${tokens.access_token}`);
302
323
  }
303
- async function fetchAndHandle401(url, init) {
324
+ async function fetchAndHandle401(url, init, signal) {
304
325
  const { getTimeoutMs, debugLogResponse, store, emitError } = deps;
305
326
  const timeoutMs = getTimeoutMs();
306
- let response = await fetchWithRetry(url, init, timeoutMs);
327
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
328
+ const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
329
+ let response = await fetchWithRetry(url, init, combinedSignal);
307
330
  debugLogResponse(response);
308
331
  if (response.status !== 401 || url.includes(AUTH_PATH)) {
309
332
  return response;
@@ -319,7 +342,7 @@ function createAuthHandler(deps) {
319
342
  new Request(url, {
320
343
  ...init,
321
344
  headers: retryHeaders,
322
- signal: AbortSignal.timeout(timeoutMs)
345
+ signal: combinedSignal
323
346
  })
324
347
  );
325
348
  debugLogResponse(response);
@@ -487,7 +510,10 @@ function createHttpClient(config, reAuthAction) {
487
510
  }
488
511
  }
489
512
  function emitError(error) {
490
- config.onError?.(error);
513
+ try {
514
+ config.onError?.(error);
515
+ } catch {
516
+ }
491
517
  throw error;
492
518
  }
493
519
  function handleError(err) {
@@ -569,10 +595,11 @@ function createHttpClient(config, reAuthAction) {
569
595
  const headers = new Headers({ Accept: "application/json" });
570
596
  await auth.injectAuth(headers, url, options);
571
597
  debugLogRequest("GET", url);
572
- const response = await auth.fetchAndHandle401(url, {
573
- method: "GET",
574
- headers
575
- });
598
+ const response = await auth.fetchAndHandle401(
599
+ url,
600
+ { method: "GET", headers },
601
+ options?.signal
602
+ );
576
603
  await throwIfNotOk(response);
577
604
  return await response.json();
578
605
  } catch (err) {
@@ -588,11 +615,11 @@ function createHttpClient(config, reAuthAction) {
588
615
  });
589
616
  await auth.injectAuth(headers, url, options);
590
617
  debugLogRequest("POST", url);
591
- const response = await auth.fetchAndHandle401(url, {
592
- method: "POST",
593
- headers,
594
- body: JSON.stringify(body)
595
- });
618
+ const response = await auth.fetchAndHandle401(
619
+ url,
620
+ { method: "POST", headers, body: JSON.stringify(body) },
621
+ options?.signal
622
+ );
596
623
  await throwIfNotOk(response);
597
624
  return await response.json();
598
625
  } catch (err) {
@@ -605,10 +632,11 @@ function createHttpClient(config, reAuthAction) {
605
632
  const headers = new Headers();
606
633
  await auth.injectAuth(headers, url, options);
607
634
  debugLogRequest("DELETE", url);
608
- const response = await auth.fetchAndHandle401(url, {
609
- method: "DELETE",
610
- headers
611
- });
635
+ const response = await auth.fetchAndHandle401(
636
+ url,
637
+ { method: "DELETE", headers },
638
+ options?.signal
639
+ );
612
640
  await throwIfNotOk(response);
613
641
  } catch (err) {
614
642
  return handleError(err);
@@ -634,7 +662,10 @@ function createHttpClient(config, reAuthAction) {
634
662
  method: "POST",
635
663
  headers,
636
664
  body: bodyStr,
637
- signal: AbortSignal.timeout(timeoutMs())
665
+ signal: options?.signal ? AbortSignal.any([
666
+ options.signal,
667
+ AbortSignal.timeout(timeoutMs())
668
+ ]) : AbortSignal.timeout(timeoutMs())
638
669
  })
639
670
  );
640
671
  debugLogResponse(response);
@@ -646,6 +677,75 @@ function createHttpClient(config, reAuthAction) {
646
677
  }
647
678
  };
648
679
  }
680
+ var DEFAULT_TERMINAL_STATES = /* @__PURE__ */ new Set([
681
+ "PAID",
682
+ "CANCELED",
683
+ "TIMEOUTED",
684
+ "REFUNDED",
685
+ "PARTIALLY_REFUNDED"
686
+ ]);
687
+ function awaitPaymentStatus(poll, options) {
688
+ const intervalMs = options?.intervalMs ?? 3e3;
689
+ const terminal = options?.terminalStates ? new Set(options.terminalStates) : DEFAULT_TERMINAL_STATES;
690
+ if (options?.signal?.aborted) {
691
+ return Promise.reject(
692
+ new GoPaySDKError("[GoPaySDK] Payment status polling aborted.", {
693
+ errorCode: GoPayErrorCodes.CHARGE_FAILED
694
+ })
695
+ );
696
+ }
697
+ return new Promise((resolve, reject) => {
698
+ let timeoutHandle;
699
+ const stop = runPollLoop(
700
+ poll,
701
+ intervalMs,
702
+ options?.signal,
703
+ reject,
704
+ (status, stop2) => {
705
+ options?.onStateChange?.(status);
706
+ if (terminal.has(status.state)) {
707
+ stop2(() => resolve(status));
708
+ }
709
+ },
710
+ (stop2) => stop2(
711
+ () => reject(
712
+ new GoPaySDKError(
713
+ "[GoPaySDK] Payment status polling aborted.",
714
+ { errorCode: GoPayErrorCodes.CHARGE_FAILED }
715
+ )
716
+ )
717
+ ),
718
+ () => clearTimeout(timeoutHandle)
719
+ );
720
+ if (options?.timeoutMs !== void 0) {
721
+ timeoutHandle = setTimeout(() => {
722
+ stop(
723
+ () => reject(
724
+ new GoPaySDKError(
725
+ "[GoPaySDK] Payment did not reach a terminal state within the timeout.",
726
+ { errorCode: GoPayErrorCodes.CHARGE_TIMEOUT }
727
+ )
728
+ )
729
+ );
730
+ }, options.timeoutMs);
731
+ }
732
+ });
733
+ }
734
+ var GoPayScopes = {
735
+ /** Create payments, charge, refund, manage recurrences and payment links. */
736
+ PAYMENT_WRITE: "payment:write",
737
+ /** Read payment status, charge state, recurrences, refunds. */
738
+ PAYMENT_READ: "payment:read",
739
+ /** Charge a single payment (browser/payment-credentials grant only). */
740
+ PAYMENT_CHARGE: "payment:charge",
741
+ /** Tokenize card data via the hosted card form. */
742
+ CARD_WRITE: "card:write",
743
+ /** Read and delete stored card tokens. */
744
+ CARD_READ: "card:read"
745
+ };
746
+ function combineScopes(...scopes) {
747
+ return scopes.join(" ");
748
+ }
649
749
  function requireNonEmptyString(value, field) {
650
750
  if (typeof value !== "string" || !value.trim()) {
651
751
  throw new GoPaySDKError(`[GoPaySDK] ${field} is required`, {
@@ -861,9 +961,9 @@ function createPaymentsApi(client) {
861
961
  *
862
962
  * @param paymentId - Payment session ID returned by {@link createPayment}
863
963
  */
864
- async getPaymentStatus(paymentId) {
964
+ async getPaymentStatus(paymentId, options) {
865
965
  const pid = requireNonEmptyString(paymentId, "paymentId");
866
- return client.get(`/payments/${pid}`);
966
+ return client.get(`/payments/${pid}`, options);
867
967
  },
868
968
  /**
869
969
  * Create a new payment session.
@@ -877,10 +977,11 @@ function createPaymentsApi(client) {
877
977
  * @param goid - Merchant's GoPay ID (eshop identifier)
878
978
  * @param params - Payment creation parameters
879
979
  */
880
- async createPayment(goid, params) {
980
+ async createPayment(goid, params, options) {
881
981
  return client.post(
882
982
  `/eshops/${goid}/payments`,
883
- params
983
+ params,
984
+ options
884
985
  );
885
986
  },
886
987
  /**
@@ -891,11 +992,12 @@ function createPaymentsApi(client) {
891
992
  * @param paymentId - Payment session ID returned by {@link createPayment}
892
993
  * @param params - Charge parameters including payment instrument details
893
994
  */
894
- async chargePayment(paymentId, params) {
995
+ async chargePayment(paymentId, params, options) {
895
996
  const pid = requireNonEmptyString(paymentId, "paymentId");
896
997
  return client.post(
897
998
  `/payments/${pid}/charge`,
898
- params
999
+ params,
1000
+ options
899
1001
  );
900
1002
  },
901
1003
  /**
@@ -905,10 +1007,11 @@ function createPaymentsApi(client) {
905
1007
  *
906
1008
  * @param paymentId - Payment session ID returned by {@link createPayment}
907
1009
  */
908
- async getChargeState(paymentId) {
1010
+ async getChargeState(paymentId, options) {
909
1011
  const pid = requireNonEmptyString(paymentId, "paymentId");
910
1012
  return client.get(
911
- `/payments/${pid}/charge`
1013
+ `/payments/${pid}/charge`,
1014
+ options
912
1015
  );
913
1016
  },
914
1017
  /**
@@ -1006,10 +1109,33 @@ function createPaymentsApi(client) {
1006
1109
  const pid = requireNonEmptyString(paymentId, "paymentId");
1007
1110
  return awaitCharge(
1008
1111
  () => client.get(
1009
- `/payments/${pid}/charge`
1112
+ `/payments/${pid}/charge`,
1113
+ { signal: options?.signal }
1010
1114
  ),
1011
1115
  options
1012
1116
  );
1117
+ },
1118
+ /**
1119
+ * Poll the payment status until it reaches a terminal state.
1120
+ *
1121
+ * Resolves with the terminal `PaymentDetails` response.
1122
+ * Use for QR and bank-transfer payments where completion is confirmed at
1123
+ * the payment level (`state === 'PAID'`) rather than the charge level.
1124
+ *
1125
+ * No client-side timeout by default — the server cancels the payment on
1126
+ * its own schedule. Pass `options.timeoutMs` for a client-side ceiling.
1127
+ *
1128
+ * @param paymentId - Payment session ID
1129
+ * @param options - Polling configuration and callbacks
1130
+ */
1131
+ awaitPaymentStatus(paymentId, options) {
1132
+ const pid = requireNonEmptyString(paymentId, "paymentId");
1133
+ return awaitPaymentStatus(
1134
+ () => client.get(`/payments/${pid}`, {
1135
+ signal: options?.signal
1136
+ }),
1137
+ options
1138
+ );
1013
1139
  }
1014
1140
  };
1015
1141
  }
@@ -1140,7 +1266,7 @@ function createRefundsApi(client) {
1140
1266
  }
1141
1267
 
1142
1268
  // src/version.ts
1143
- var SDK_VERSION = "1.6.6";
1269
+ var SDK_VERSION = "1.7.0";
1144
1270
 
1145
1271
  // src/gopay-sdk.ts
1146
1272
  function createGoPaySDK(config = {}) {
@@ -1160,7 +1286,9 @@ function createGoPaySDK(config = {}) {
1160
1286
  GoPayErrorCodes,
1161
1287
  GoPayHTTPError,
1162
1288
  GoPaySDKError,
1289
+ GoPayScopes,
1163
1290
  SDK_VERSION,
1291
+ combineScopes,
1164
1292
  createGoPaySDK
1165
1293
  });
1166
1294
  //# sourceMappingURL=index.cjs.map