@superlogic/spree-pay 0.4.12 → 0.5.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.
package/build/index.css CHANGED
@@ -555,6 +555,11 @@
555
555
  .sl-spreepay .gap-6 {
556
556
  gap: calc(var(--spacing) * 6);
557
557
  }
558
+ .sl-spreepay .truncate {
559
+ overflow: hidden;
560
+ text-overflow: ellipsis;
561
+ white-space: nowrap;
562
+ }
558
563
  .sl-spreepay .overflow-hidden {
559
564
  overflow: hidden;
560
565
  }
package/build/index.d.cts CHANGED
@@ -20,7 +20,9 @@ type SpreePayProps = Partial<{
20
20
 
21
21
  declare const SpreePay: (props: SpreePayProps) => react_jsx_runtime.JSX.Element;
22
22
 
23
- declare const useCapture3DS: (searchParams: Record<string, string | null>) => void;
23
+ declare const useCapture3DS: ({ paymentIntent }: {
24
+ paymentIntent?: string | null;
25
+ }) => void;
24
26
 
25
27
  type Environment = 'dev' | 'stg' | 'prod';
26
28
  type ENV = {
@@ -114,7 +116,7 @@ declare enum PaymentType {
114
116
  CREDIT_CARD_SPLIT = "SPLIT",
115
117
  POINTS = "POINTS"
116
118
  }
117
- declare enum SlapiPaymentStatus {
119
+ declare enum PaymentStatus {
118
120
  NOT_INITIALIZED = "NOT_INITIALIZED",// back-end only
119
121
  PENDING = "PENDING",// back-end only
120
122
  AUTHORIZED = "AUTHORIZED",// success
@@ -124,7 +126,7 @@ declare enum SlapiPaymentStatus {
124
126
  CAPTURE_FAILED = "CAPTURE_FAILED"
125
127
  }
126
128
  type PaymentMethodResult = {
127
- status: SlapiPaymentStatus;
129
+ status: PaymentStatus;
128
130
  paymentId: string;
129
131
  txHash: string | null;
130
132
  txId: string | null;
@@ -133,7 +135,6 @@ type PaymentMethodResult = {
133
135
  type ProcessFnParams = {
134
136
  hash: string;
135
137
  capture?: boolean;
136
- metadata?: Record<string, unknown>;
137
138
  };
138
139
  type ProcessFn = (data: ProcessFnParams) => Promise<PaymentMethodResult>;
139
140
 
@@ -160,6 +161,50 @@ declare global {
160
161
  }
161
162
  }
162
163
 
164
+ /**
165
+ * Thrown on a non-ok HTTP response. Keeps the status code and raw body on
166
+ * dedicated fields so callers can branch on 401/422/5xx without parsing the
167
+ * message string.
168
+ */
169
+ declare class SpreePayHttpError extends Error {
170
+ status: number;
171
+ body: string;
172
+ url: string;
173
+ constructor(status: number, body: string, url: string);
174
+ }
175
+ /**
176
+ * Discriminates *why* a payment failed so consumers can show tailored UI
177
+ * (e.g. "your card was declined, try another" vs. a generic "try again").
178
+ */
179
+ declare enum PaymentErrorCode {
180
+ /** The backend rejected the card (a 4xx decline, or a terminal FAILED status on a card payment). */
181
+ CARD_ERROR = "CARD_ERROR",
182
+ /** Anything else: network/server error, timeout, cancelled flow, crypto failure, … */
183
+ PAYMENT_ERROR = "PAYMENT_ERROR"
184
+ }
185
+ /**
186
+ * Wraps a payment failure with a category `code` and payment `status`; the original
187
+ * error is kept on `cause` (logs only). The widget throws this — detect it with
188
+ * `isPaymentError`. Not meant to be constructed by consumers.
189
+ */
190
+ declare class PaymentError extends Error {
191
+ status: PaymentStatus;
192
+ code: PaymentErrorCode;
193
+ /**
194
+ * Optional stable code from the backend (e.g. `INSUFFICIENT_FUNDS`) for branching
195
+ * on specific cases without changing the `code` enum. Safe to expose (an
196
+ * identifier, not free-text); absent until the backend supplies one.
197
+ */
198
+ backendCode?: string;
199
+ constructor(message: string, status: PaymentStatus, options?: {
200
+ code?: PaymentErrorCode;
201
+ backendCode?: string;
202
+ cause?: unknown;
203
+ });
204
+ }
205
+ /** Type guard so consumers can branch on PaymentError without duck-typing `.status`. */
206
+ declare const isPaymentError: (e: unknown) => e is PaymentError;
207
+
163
208
  declare enum LogLevel {
164
209
  DEBUG = 0,
165
210
  INFO = 1,
@@ -234,4 +279,4 @@ declare const configureLogger: (config: Partial<LoggerConfig> & {
234
279
  environment?: Environment;
235
280
  }) => void;
236
281
 
237
- export { type ENV, LogLevel, type PaymentMethodResult, PaymentType, type ProcessFn, type ProcessFnParams, type SelectedPaymentMethod, SlapiPaymentStatus, SpreePay, type SpreePayProps, SpreePayProvider, configureLogger, logger, useCapture3DS, useSpreePay };
282
+ export { type ENV, type Environment, type LogContext, LogLevel, type LoggerConfig, PaymentError, PaymentErrorCode, type PaymentMethodResult, PaymentStatus, PaymentType, type ProcessFn, type ProcessFnParams, type SelectedPaymentMethod, SpreePay, SpreePayHttpError, type SpreePayProps, SpreePayProvider, configureLogger, isPaymentError, logger, useCapture3DS, useSpreePay };
package/build/index.d.ts CHANGED
@@ -20,7 +20,9 @@ type SpreePayProps = Partial<{
20
20
 
21
21
  declare const SpreePay: (props: SpreePayProps) => react_jsx_runtime.JSX.Element;
22
22
 
23
- declare const useCapture3DS: (searchParams: Record<string, string | null>) => void;
23
+ declare const useCapture3DS: ({ paymentIntent }: {
24
+ paymentIntent?: string | null;
25
+ }) => void;
24
26
 
25
27
  type Environment = 'dev' | 'stg' | 'prod';
26
28
  type ENV = {
@@ -114,7 +116,7 @@ declare enum PaymentType {
114
116
  CREDIT_CARD_SPLIT = "SPLIT",
115
117
  POINTS = "POINTS"
116
118
  }
117
- declare enum SlapiPaymentStatus {
119
+ declare enum PaymentStatus {
118
120
  NOT_INITIALIZED = "NOT_INITIALIZED",// back-end only
119
121
  PENDING = "PENDING",// back-end only
120
122
  AUTHORIZED = "AUTHORIZED",// success
@@ -124,7 +126,7 @@ declare enum SlapiPaymentStatus {
124
126
  CAPTURE_FAILED = "CAPTURE_FAILED"
125
127
  }
126
128
  type PaymentMethodResult = {
127
- status: SlapiPaymentStatus;
129
+ status: PaymentStatus;
128
130
  paymentId: string;
129
131
  txHash: string | null;
130
132
  txId: string | null;
@@ -133,7 +135,6 @@ type PaymentMethodResult = {
133
135
  type ProcessFnParams = {
134
136
  hash: string;
135
137
  capture?: boolean;
136
- metadata?: Record<string, unknown>;
137
138
  };
138
139
  type ProcessFn = (data: ProcessFnParams) => Promise<PaymentMethodResult>;
139
140
 
@@ -160,6 +161,50 @@ declare global {
160
161
  }
161
162
  }
162
163
 
164
+ /**
165
+ * Thrown on a non-ok HTTP response. Keeps the status code and raw body on
166
+ * dedicated fields so callers can branch on 401/422/5xx without parsing the
167
+ * message string.
168
+ */
169
+ declare class SpreePayHttpError extends Error {
170
+ status: number;
171
+ body: string;
172
+ url: string;
173
+ constructor(status: number, body: string, url: string);
174
+ }
175
+ /**
176
+ * Discriminates *why* a payment failed so consumers can show tailored UI
177
+ * (e.g. "your card was declined, try another" vs. a generic "try again").
178
+ */
179
+ declare enum PaymentErrorCode {
180
+ /** The backend rejected the card (a 4xx decline, or a terminal FAILED status on a card payment). */
181
+ CARD_ERROR = "CARD_ERROR",
182
+ /** Anything else: network/server error, timeout, cancelled flow, crypto failure, … */
183
+ PAYMENT_ERROR = "PAYMENT_ERROR"
184
+ }
185
+ /**
186
+ * Wraps a payment failure with a category `code` and payment `status`; the original
187
+ * error is kept on `cause` (logs only). The widget throws this — detect it with
188
+ * `isPaymentError`. Not meant to be constructed by consumers.
189
+ */
190
+ declare class PaymentError extends Error {
191
+ status: PaymentStatus;
192
+ code: PaymentErrorCode;
193
+ /**
194
+ * Optional stable code from the backend (e.g. `INSUFFICIENT_FUNDS`) for branching
195
+ * on specific cases without changing the `code` enum. Safe to expose (an
196
+ * identifier, not free-text); absent until the backend supplies one.
197
+ */
198
+ backendCode?: string;
199
+ constructor(message: string, status: PaymentStatus, options?: {
200
+ code?: PaymentErrorCode;
201
+ backendCode?: string;
202
+ cause?: unknown;
203
+ });
204
+ }
205
+ /** Type guard so consumers can branch on PaymentError without duck-typing `.status`. */
206
+ declare const isPaymentError: (e: unknown) => e is PaymentError;
207
+
163
208
  declare enum LogLevel {
164
209
  DEBUG = 0,
165
210
  INFO = 1,
@@ -234,4 +279,4 @@ declare const configureLogger: (config: Partial<LoggerConfig> & {
234
279
  environment?: Environment;
235
280
  }) => void;
236
281
 
237
- export { type ENV, LogLevel, type PaymentMethodResult, PaymentType, type ProcessFn, type ProcessFnParams, type SelectedPaymentMethod, SlapiPaymentStatus, SpreePay, type SpreePayProps, SpreePayProvider, configureLogger, logger, useCapture3DS, useSpreePay };
282
+ export { type ENV, type Environment, type LogContext, LogLevel, type LoggerConfig, PaymentError, PaymentErrorCode, type PaymentMethodResult, PaymentStatus, PaymentType, type ProcessFn, type ProcessFnParams, type SelectedPaymentMethod, SpreePay, SpreePayHttpError, type SpreePayProps, SpreePayProvider, configureLogger, isPaymentError, logger, useCapture3DS, useSpreePay };
package/build/index.js CHANGED
@@ -8,39 +8,49 @@ import {
8
8
  getSplitAmount,
9
9
  getTransactionFee,
10
10
  useSlapiBalance
11
- } from "./chunk-RORSHUSF.js";
11
+ } from "./chunk-4BM6VTMT.js";
12
12
  import {
13
13
  Iframe3ds
14
- } from "./chunk-JXZZJ7NS.js";
14
+ } from "./chunk-AXNTOSBU.js";
15
15
  import {
16
16
  InfoBanner,
17
17
  LogLevel,
18
+ LoginStatusProvider,
18
19
  PaymentError,
20
+ PaymentErrorCode,
21
+ PaymentStatus,
19
22
  PaymentType,
20
23
  PortalContainerProvider,
21
24
  SlapiPaymentService,
25
+ SpreePayHttpError,
22
26
  SpreePayProvider,
23
27
  StaticConfigProvider,
24
28
  cn,
25
29
  configureLogger,
30
+ isDeclineStatus,
26
31
  isNewCard,
32
+ isPaymentError,
33
+ isSuccessStatus,
27
34
  logger,
28
35
  registerApi,
36
+ settlePaymentResult,
37
+ toPaymentError,
38
+ useIsLoggedIn,
29
39
  useSpreePay,
30
40
  useSpreePayConfig,
31
41
  useSpreePayEnv,
32
42
  useSpreePayRegister,
33
43
  useSpreePaymentMethod,
34
44
  useStaticConfig
35
- } from "./chunk-WRFG6YIS.js";
45
+ } from "./chunk-PTEQVWLJ.js";
36
46
 
37
47
  // src/SpreePay.tsx
38
- import { useCallback as useCallback8, useEffect as useEffect7, useMemo as useMemo9, useState as useState12 } from "react";
48
+ import { useCallback as useCallback8, useEffect as useEffect8, useMemo as useMemo9, useState as useState12 } from "react";
39
49
  import NiceModal4 from "@ebay/nice-modal-react";
40
50
  import { SWRConfig } from "swr";
41
51
 
42
52
  // src/SpreePayContent.tsx
43
- import { Suspense, lazy } from "react";
53
+ import { Suspense, lazy, useEffect as useEffect6 } from "react";
44
54
 
45
55
  // src/components/CreditCardTab/CreditCardTab.tsx
46
56
  import { useCallback as useCallback6, useEffect as useEffect5, useState as useState10 } from "react";
@@ -61,12 +71,8 @@ var useCardPayment = () => {
61
71
  });
62
72
  throw error;
63
73
  }
64
- const { hash, capture, metadata } = params;
65
- cardPaymentLogger.info("Starting card payment", {
66
- hash,
67
- capture,
68
- hasMetadata: Boolean(metadata)
69
- });
74
+ const { hash, capture } = params;
75
+ cardPaymentLogger.info("Starting card payment", { hash, capture });
70
76
  const card = selectedPaymentMethod.method;
71
77
  let cardId;
72
78
  if ("token" in card) {
@@ -97,7 +103,6 @@ var useCardPayment = () => {
97
103
  const { data: paymentResData } = await SlapiPaymentService.createPayment({
98
104
  hash,
99
105
  capture,
100
- metadata,
101
106
  type: "CREDIT_CARD" /* CREDIT_CARD */,
102
107
  card: {
103
108
  cardId,
@@ -260,10 +265,13 @@ async function longPollPoints(paymentId) {
260
265
  let retries = 0;
261
266
  while (retries < MAX_RETRIES) {
262
267
  const { detail } = await SlapiPaymentService.getStatus(paymentId);
263
- if (detail.status === "FAILED" /* FAILED */) {
264
- throw new Error("Something went wrong with the payment");
268
+ if (isDeclineStatus(detail.status)) {
269
+ throw new PaymentError("Something went wrong with the points payment.", detail.status, {
270
+ code: "PAYMENT_ERROR" /* PAYMENT_ERROR */,
271
+ backendCode: detail.errorCode ?? void 0
272
+ });
265
273
  }
266
- if (["AUTHORIZED" /* AUTHORIZED */, "CAPTURED" /* CAPTURED */].includes(detail.status)) {
274
+ if (isSuccessStatus(detail.status)) {
267
275
  return detail.status;
268
276
  }
269
277
  await new Promise((res) => setTimeout(res, REFRESH_INTERVAL));
@@ -276,13 +284,21 @@ async function longPollCardStatus(paymentId) {
276
284
  let shown3ds = false;
277
285
  while (retries < MAX_RETRIES) {
278
286
  const { detail } = await SlapiPaymentService.getStatus(paymentId);
279
- if (detail.status === "FAILED" /* FAILED */) {
280
- throw new Error(`Something went wrong with the ${detail.validationType} payment`);
287
+ if (isDeclineStatus(detail.status)) {
288
+ const isPointsLeg = detail.validationType === "POINTS";
289
+ throw new PaymentError(
290
+ isPointsLeg ? "Something went wrong with the points payment." : "Your card payment could not be completed.",
291
+ detail.status,
292
+ {
293
+ code: isPointsLeg ? "PAYMENT_ERROR" /* PAYMENT_ERROR */ : "CARD_ERROR" /* CARD_ERROR */,
294
+ backendCode: detail.errorCode ?? void 0
295
+ }
296
+ );
281
297
  }
282
298
  if (
283
299
  // Process to points payment
284
300
  detail.validationType === "POINTS" || // early card payment completion
285
- ["AUTHORIZED" /* AUTHORIZED */, "CAPTURED" /* CAPTURED */].includes(detail.status)
301
+ isSuccessStatus(detail.status)
286
302
  ) {
287
303
  return detail.status;
288
304
  }
@@ -312,7 +328,7 @@ var usePointsPayment = (mode = "web2") => {
312
328
  });
313
329
  throw error;
314
330
  }
315
- const { hash, capture, metadata } = params;
331
+ const { hash, capture } = params;
316
332
  pointsPaymentLogger.info("Starting points payment", {
317
333
  hash,
318
334
  capture,
@@ -327,7 +343,6 @@ var usePointsPayment = (mode = "web2") => {
327
343
  hash,
328
344
  // capture should be always true for web2 points payments
329
345
  capture: mode === "web2" ? true : capture,
330
- metadata,
331
346
  type: "POINTS" /* POINTS */
332
347
  });
333
348
  pointsPaymentLogger.info("Points payment created", {
@@ -420,7 +435,7 @@ var useSplitCardPayments = (mode = "web2") => {
420
435
  if (selectedPaymentMethod.type !== "CREDIT_CARD" /* CREDIT_CARD */ || !selectedPaymentMethod.method || !params.points) {
421
436
  throw new Error("Unsupported payment method");
422
437
  }
423
- const { hash, capture, metadata, points } = params;
438
+ const { hash, capture, points } = params;
424
439
  splitPaymentLogger.info("Starting split card+points payment", { hash, mode, points, capture });
425
440
  const card = selectedPaymentMethod.method;
426
441
  let cardId;
@@ -448,7 +463,6 @@ var useSplitCardPayments = (mode = "web2") => {
448
463
  const { data: paymentResData } = await SlapiPaymentService.createPayment({
449
464
  hash,
450
465
  capture,
451
- metadata,
452
466
  type: "SPLIT" /* CREDIT_CARD_SPLIT */,
453
467
  card: {
454
468
  cardId,
@@ -871,6 +885,7 @@ var StripeWrapper = ({ onCancel, saveNewCard, publicKey }) => {
871
885
  };
872
886
  var CreditCard = ({ newCards, setNewCards }) => {
873
887
  const [showForm, setShowForm] = useState3(false);
888
+ const isLoggedIn = useIsLoggedIn();
874
889
  const { selectedPaymentMethod, setSelectedPaymentMethod } = useSpreePaymentMethod();
875
890
  const { spreePayConfig } = useSpreePayConfig();
876
891
  const setCard = (card) => {
@@ -892,6 +907,14 @@ var CreditCard = ({ newCards, setNewCards }) => {
892
907
  },
893
908
  [setNewCards]
894
909
  );
910
+ const handleAddNewCard = () => {
911
+ setSelectedPaymentMethod({
912
+ ...selectedPaymentMethod,
913
+ type: "CREDIT_CARD" /* CREDIT_CARD */,
914
+ method: null
915
+ });
916
+ setShowForm(true);
917
+ };
895
918
  const handleCancel = () => {
896
919
  setShowForm(false);
897
920
  };
@@ -911,14 +934,14 @@ var CreditCard = ({ newCards, setNewCards }) => {
911
934
  /* @__PURE__ */ jsx5(
912
935
  "button",
913
936
  {
914
- onClick: () => setShowForm(true),
915
- disabled: !spreePayConfig?.stripePublicKey,
937
+ onClick: handleAddNewCard,
938
+ disabled: !isLoggedIn || !spreePayConfig?.stripePublicKey,
916
939
  className: "text-md text-(--brand-primary) hover:underline disabled:cursor-not-allowed disabled:no-underline disabled:opacity-50",
917
940
  children: "Add new card"
918
941
  }
919
942
  )
920
943
  ] }),
921
- spreePayConfig?.stripePublicKey && showForm && /* @__PURE__ */ jsx5(StripeWrapper, { onCancel: handleCancel, saveNewCard, publicKey: spreePayConfig.stripePublicKey })
944
+ isLoggedIn && spreePayConfig?.stripePublicKey && showForm && /* @__PURE__ */ jsx5(StripeWrapper, { onCancel: handleCancel, saveNewCard, publicKey: spreePayConfig.stripePublicKey })
922
945
  ] });
923
946
  };
924
947
 
@@ -2081,6 +2104,7 @@ var SplitBlock = (props) => {
2081
2104
  // src/components/CreditCardTab/Points/Points.tsx
2082
2105
  import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
2083
2106
  var Points = () => {
2107
+ const isLoggedIn = useIsLoggedIn();
2084
2108
  const [usePoints, setUsePoints] = useState9(false);
2085
2109
  const [selectedPointsType, setSelectedPointsType] = useState9(null);
2086
2110
  const { setSelectedPaymentMethod, selectedPaymentMethod } = useSpreePaymentMethod();
@@ -2100,7 +2124,7 @@ var Points = () => {
2100
2124
  value: usePoints,
2101
2125
  onChange: handleTogglePoints,
2102
2126
  message: spreePayConfig?.creditCard.pointsInfoMessage,
2103
- disabled: !spreePayConfig?.creditCard.enabled || !spreePayConfig?.creditCard.points || appProps.disabledPoints
2127
+ disabled: !isLoggedIn || !spreePayConfig?.creditCard.enabled || !spreePayConfig?.creditCard.points || appProps.disabledPoints
2104
2128
  }
2105
2129
  ),
2106
2130
  usePoints && /* @__PURE__ */ jsx15(
@@ -2116,7 +2140,7 @@ var Points = () => {
2116
2140
 
2117
2141
  // src/components/CreditCardTab/CreditCardTab.tsx
2118
2142
  import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
2119
- var CreditCardTab = ({ isLoggedIn }) => {
2143
+ var CreditCardTab = () => {
2120
2144
  const { selectedPaymentMethod, setSelectedPaymentMethod } = useSpreePaymentMethod();
2121
2145
  const { useWeb3Points } = useSpreePayEnv();
2122
2146
  const { appProps } = useStaticConfig();
@@ -2131,23 +2155,23 @@ var CreditCardTab = ({ isLoggedIn }) => {
2131
2155
  const { pointsPayment } = usePointsPayment(isWeb3Enabled ? "web3" : "web2");
2132
2156
  const handlePay = useCallback6(
2133
2157
  async (data) => {
2158
+ const pointsAmount = selectedPaymentMethod.pointsAmount ?? 0;
2159
+ const usdAmount = getSplitAmount(appProps.amount ?? 0, pointsAmount, spreePayConfig?.pointsConversionRatio);
2160
+ const isPurePoints = !usdAmount && pointsAmount > 0;
2161
+ const failureMessage = isPurePoints ? "Points payment failed" : "Card payment failed";
2162
+ const failureCode = isPurePoints ? "PAYMENT_ERROR" /* PAYMENT_ERROR */ : "CARD_ERROR" /* CARD_ERROR */;
2134
2163
  try {
2135
- let res = null;
2136
- const pointsAmount = selectedPaymentMethod.pointsAmount ?? 0;
2137
- const usdAmount = getSplitAmount(appProps.amount ?? 0, pointsAmount, spreePayConfig?.pointsConversionRatio);
2164
+ let res;
2138
2165
  if (usdAmount && pointsAmount) {
2139
2166
  res = await splitPayment({ ...data, points: pointsAmount });
2140
- } else if (!usdAmount && pointsAmount) {
2167
+ } else if (isPurePoints) {
2141
2168
  res = await pointsPayment({ ...data, points: pointsAmount });
2142
2169
  } else {
2143
2170
  res = await cardPayment(data);
2144
2171
  }
2145
- if (["AUTHORIZED" /* AUTHORIZED */, "CAPTURED" /* CAPTURED */].includes(res.status)) {
2146
- return Promise.resolve(res);
2147
- }
2148
- return Promise.reject(new PaymentError("Card payment failed", res.status));
2149
- } catch (_) {
2150
- return Promise.reject(new PaymentError("Payment failed", "FAILED" /* FAILED */));
2172
+ return settlePaymentResult(res, failureMessage, failureCode);
2173
+ } catch (e) {
2174
+ return Promise.reject(toPaymentError(e, failureMessage, failureCode));
2151
2175
  } finally {
2152
2176
  mutateBalance();
2153
2177
  if (selectedPaymentMethod.type === "CREDIT_CARD" /* CREDIT_CARD */ && selectedPaymentMethod.method && isNewCard(selectedPaymentMethod.method)) {
@@ -2184,7 +2208,7 @@ var CreditCardTab = ({ isLoggedIn }) => {
2184
2208
  return /* @__PURE__ */ jsxs9("div", { children: [
2185
2209
  /* @__PURE__ */ jsx16("div", { className: "border-b border-b-(--border-component-specific-card) px-5 py-5 md:px-7 md:py-6", children: /* @__PURE__ */ jsx16(CreditCard, { newCards, setNewCards }) }),
2186
2210
  !spreePayConfig?.creditCard.hidePoints && /* @__PURE__ */ jsx16("div", { className: "flex flex-col gap-4 border-b border-b-(--border-component-specific-card) px-5 pt-5 pb-5 md:px-7 md:pt-6 md:pb-7", children: /* @__PURE__ */ jsx16(Points, {}) }),
2187
- /* @__PURE__ */ jsx16(CheckoutButton, { isLoggedIn })
2211
+ /* @__PURE__ */ jsx16(CheckoutButton, {})
2188
2212
  ] });
2189
2213
  };
2190
2214
 
@@ -2260,24 +2284,30 @@ var TabButtons = (props) => {
2260
2284
 
2261
2285
  // src/SpreePayContent.tsx
2262
2286
  import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
2263
- var CryptoTab = lazy(() => import("./CryptoTab-57MMM4AG.js").then((module) => ({ default: module.CryptoTab })));
2287
+ var CryptoTab = lazy(() => import("./CryptoTab-632DRL2D.js").then((module) => ({ default: module.CryptoTab })));
2264
2288
  var CryptoComTab = lazy(
2265
- () => import("./CryptoComTab-DAKL7IZF.js").then((module) => ({ default: module.CryptoComTab }))
2289
+ () => import("./CryptoComTab-ZAWBJUCM.js").then((module) => ({ default: module.CryptoComTab }))
2266
2290
  );
2267
2291
  var TabLoadingFallback = () => /* @__PURE__ */ jsx18("div", { className: "flex items-center justify-center px-5 py-8 md:px-7", children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-3", children: [
2268
2292
  /* @__PURE__ */ jsx18("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-(--border-component-specific-card) border-t-(--brand-primary)" }),
2269
2293
  /* @__PURE__ */ jsx18("p", { className: "text-sm text-(--text-tertiary)", children: "Loading payment method..." })
2270
2294
  ] }) });
2271
- var SpreePayContent = ({ isLoggedIn }) => {
2295
+ var SpreePayContent = () => {
2296
+ const isLoggedIn = useIsLoggedIn();
2272
2297
  const { selectedPaymentMethod, setSelectedPaymentMethod } = useSpreePaymentMethod();
2298
+ useEffect6(() => {
2299
+ if (!isLoggedIn) {
2300
+ setSelectedPaymentMethod({ type: "CREDIT_CARD" /* CREDIT_CARD */, method: null });
2301
+ }
2302
+ }, [isLoggedIn, setSelectedPaymentMethod]);
2273
2303
  return /* @__PURE__ */ jsxs11("div", { className: "w-full overflow-hidden rounded-3xl border border-(--border-component-specific-card) bg-(--surface-component-specific-card-default-card) shadow-[0_6.25px_25px_0_var(--shadow-component-specific-card)]", children: [
2274
2304
  /* @__PURE__ */ jsxs11("div", { className: "flex w-full flex-col gap-2 border-b border-b-(--border-component-specific-card) pt-5 pb-3 md:pt-6 md:pb-5", children: [
2275
2305
  /* @__PURE__ */ jsx18("h2", { className: "px-5 text-[28px] leading-8 font-medium text-(--brand-primary) md:px-7", children: "Choose a payment method" }),
2276
2306
  /* @__PURE__ */ jsx18(TabButtons, { value: selectedPaymentMethod.type, onChange: setSelectedPaymentMethod })
2277
2307
  ] }),
2278
- selectedPaymentMethod.type === "CREDIT_CARD" /* CREDIT_CARD */ && /* @__PURE__ */ jsx18(CreditCardTab, { isLoggedIn }),
2308
+ selectedPaymentMethod.type === "CREDIT_CARD" /* CREDIT_CARD */ && /* @__PURE__ */ jsx18(CreditCardTab, {}),
2279
2309
  /* @__PURE__ */ jsxs11(Suspense, { fallback: /* @__PURE__ */ jsx18(TabLoadingFallback, {}), children: [
2280
- selectedPaymentMethod.type === "CRYPTO" /* CRYPTO */ && /* @__PURE__ */ jsx18(CryptoTab, { isLoggedIn }),
2310
+ selectedPaymentMethod.type === "CRYPTO" /* CRYPTO */ && /* @__PURE__ */ jsx18(CryptoTab, {}),
2281
2311
  selectedPaymentMethod.type === "CDC" /* CDC */ && /* @__PURE__ */ jsx18(CryptoComTab, {})
2282
2312
  ] })
2283
2313
  ] });
@@ -2332,7 +2362,7 @@ var ErrorBoundary = class extends Component {
2332
2362
  };
2333
2363
 
2334
2364
  // src/hooks/useKeycloakSSO.ts
2335
- import { useCallback as useCallback7, useEffect as useEffect6, useRef as useRef6, useState as useState11 } from "react";
2365
+ import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef6, useState as useState11 } from "react";
2336
2366
  import Keycloak from "keycloak-js";
2337
2367
  var refreshAheadSeconds = 60;
2338
2368
  var keycloakLogger = logger.child("keycloak");
@@ -2370,10 +2400,10 @@ function useKeycloakSSO(config) {
2370
2400
  });
2371
2401
  }, delayMs);
2372
2402
  }, []);
2373
- useEffect6(() => {
2403
+ useEffect7(() => {
2374
2404
  scheduleRefreshRef.current = scheduleRefresh;
2375
2405
  }, [scheduleRefresh]);
2376
- useEffect6(() => {
2406
+ useEffect7(() => {
2377
2407
  if (initRef.current || !enabled) return;
2378
2408
  initRef.current = true;
2379
2409
  const kc = new Keycloak({ url, realm, clientId });
@@ -2419,6 +2449,7 @@ var isTokenExpired = (token) => {
2419
2449
 
2420
2450
  // src/SpreePay.tsx
2421
2451
  import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
2452
+ var unauthenticatedFetcher = () => Promise.resolve(null);
2422
2453
  var SpreePayInner = () => {
2423
2454
  const [portalEl, setPortalEl] = useState12(null);
2424
2455
  const rootRef = useCallback8((node) => {
@@ -2427,7 +2458,7 @@ var SpreePayInner = () => {
2427
2458
  setPortalEl(el ?? null);
2428
2459
  }, []);
2429
2460
  const { environment, tenantId, keycloakClientId, accessToken: envAccessToken, ssoPageURI } = useSpreePayEnv();
2430
- useEffect7(() => {
2461
+ useEffect8(() => {
2431
2462
  configureLogger({ environment });
2432
2463
  logger.logVersion();
2433
2464
  }, [environment]);
@@ -2441,7 +2472,6 @@ var SpreePayInner = () => {
2441
2472
  enabled: !envTokenValid
2442
2473
  });
2443
2474
  const _accessToken = envTokenValid ? envAccessToken : accessToken;
2444
- const unauthenticatedFetcher = useCallback8(() => Promise.resolve(null), []);
2445
2475
  const slapiFetcher = useMemo9(() => {
2446
2476
  if (_accessToken) {
2447
2477
  return registerApi({
@@ -2451,7 +2481,7 @@ var SpreePayInner = () => {
2451
2481
  });
2452
2482
  }
2453
2483
  return unauthenticatedFetcher;
2454
- }, [_accessToken, staticConfig, tenantId, unauthenticatedFetcher]);
2484
+ }, [_accessToken, staticConfig, tenantId]);
2455
2485
  const getContent = () => {
2456
2486
  if (isChecking) {
2457
2487
  return /* @__PURE__ */ jsxs13("div", { className: "flex w-full flex-col", children: [
@@ -2468,7 +2498,7 @@ var SpreePayInner = () => {
2468
2498
  revalidateOnFocus: false,
2469
2499
  revalidateIfStale: false
2470
2500
  },
2471
- children: /* @__PURE__ */ jsx20(PortalContainerProvider, { container: portalEl, children: /* @__PURE__ */ jsx20(NiceModal4.Provider, { children: /* @__PURE__ */ jsx20(SpreePayContent, { isLoggedIn: Boolean(_accessToken) }) }) })
2501
+ children: /* @__PURE__ */ jsx20(PortalContainerProvider, { container: portalEl, children: /* @__PURE__ */ jsx20(NiceModal4.Provider, { children: /* @__PURE__ */ jsx20(LoginStatusProvider, { isLoggedIn: Boolean(_accessToken), children: /* @__PURE__ */ jsx20(SpreePayContent, {}) }) }) })
2472
2502
  }
2473
2503
  );
2474
2504
  };
@@ -2482,20 +2512,25 @@ var SpreePay = (props) => {
2482
2512
  };
2483
2513
 
2484
2514
  // src/hooks/useCapture3DS.ts
2485
- import { useEffect as useEffect8 } from "react";
2486
- var useCapture3DS = (searchParams) => {
2487
- useEffect8(() => {
2488
- if (typeof window !== "undefined" && window.parent && searchParams?.paymentIntent) {
2489
- window.parent.SP_EVENT_BUS?.emit("paymentIntent", { paymentIntent: searchParams.paymentIntent });
2515
+ import { useEffect as useEffect9 } from "react";
2516
+ var useCapture3DS = ({ paymentIntent }) => {
2517
+ useEffect9(() => {
2518
+ if (typeof window !== "undefined" && window.parent && paymentIntent) {
2519
+ window.parent.SP_EVENT_BUS?.emit("paymentIntent", { paymentIntent });
2490
2520
  }
2491
- }, [searchParams]);
2521
+ }, [paymentIntent]);
2492
2522
  };
2493
2523
  export {
2494
2524
  LogLevel,
2525
+ PaymentError,
2526
+ PaymentErrorCode,
2527
+ PaymentStatus,
2495
2528
  PaymentType,
2496
2529
  SpreePay,
2530
+ SpreePayHttpError,
2497
2531
  SpreePayProvider,
2498
2532
  configureLogger,
2533
+ isPaymentError,
2499
2534
  logger,
2500
2535
  useCapture3DS,
2501
2536
  useSpreePay
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superlogic/spree-pay",
3
- "version": "0.4.12",
3
+ "version": "0.5.1",
4
4
  "description": "Spree-pay React component and utilities",
5
5
  "private": false,
6
6
  "type": "module",
@@ -30,6 +30,8 @@
30
30
  "dev": "tsup --watch",
31
31
  "lint": "eslint . --max-warnings 0",
32
32
  "check-ts": "tsc --noEmit",
33
+ "test": "vitest run",
34
+ "test:watch": "vitest",
33
35
  "publish": "npm run build"
34
36
  },
35
37
  "publishConfig": {
@@ -71,7 +73,8 @@
71
73
  "tailwindcss": "^4.3.0",
72
74
  "tsup": "^8.5.1",
73
75
  "tw-animate-css": "^1.4.0",
74
- "typescript": "^6.0.3"
76
+ "typescript": "^6.0.3",
77
+ "vitest": "^4.1.6"
75
78
  },
76
79
  "tsup": {
77
80
  "entry": [