@easypayment/medusa-paypal-ui 1.1.1 → 1.2.2

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.cts CHANGED
@@ -45,8 +45,27 @@ type PayPalSettingsResponse = {
45
45
  type HttpOptions = {
46
46
  baseUrl: string;
47
47
  publishableApiKey?: string;
48
+ /**
49
+ * `credentials` mode for every request. Defaults to "include" (the
50
+ * historical behavior, required when the backend sits behind cookie-based
51
+ * gateways like Cloudflare Access). Set to "omit" or "same-origin" when the
52
+ * backend answers with `Access-Control-Allow-Origin: *` — browsers reject
53
+ * credentialed requests against a wildcard CORS origin outright.
54
+ */
55
+ credentials?: RequestCredentials;
48
56
  };
49
57
 
58
+ /**
59
+ * Fresh idempotency key per create-order ATTEMPT (i.e. per buyer click).
60
+ *
61
+ * The backend derives the PayPal-Request-Id from this header. It must be
62
+ * unique per attempt: PayPal replays the cached response for a reused request
63
+ * id, so a key stable across cart changes would hand back the ORIGINAL order —
64
+ * at the original total — after the buyer edits the cart, charging a stale
65
+ * amount. Reuse/dedup of an unchanged order is handled server-side by the
66
+ * stored-order staleness check, not by this key.
67
+ */
68
+ declare function generateIdempotencyKey(): string;
50
69
  declare function markPaymentComplete(baseUrl: string, cartId: string, publishableApiKey?: string): Promise<Record<string, unknown>>;
51
70
  declare function createPayPalStoreApi(opts: HttpOptions): {
52
71
  getConfig(cartId?: string, signal?: AbortSignal): Promise<PayPalConfig>;
@@ -113,9 +132,18 @@ type MedusaNextPayPalAdapterProps = {
113
132
  };
114
133
  declare function MedusaNextPayPalAdapter(props: MedusaNextPayPalAdapterProps): React.JSX.Element | null;
115
134
 
135
+ /**
136
+ * Provider ids registered by `@easypayment/medusa-payment-paypal`.
137
+ *
138
+ * Single source of truth — `PayPalPaymentSection`, `MedusaNextPayPalAdapter`,
139
+ * and the server-safe `order` entrypoint all re-export from here so the ids
140
+ * can never drift between entrypoints.
141
+ */
116
142
  declare const PAYPAL_WALLET_PROVIDER_ID: "pp_paypal_paypal";
117
143
  declare const PAYPAL_CARD_PROVIDER_ID: "pp_paypal_card_paypal_card";
118
- declare function isPayPalProviderId(id?: string | null): boolean;
144
+ /** True when the provider id belongs to this PayPal plugin. */
145
+ declare function isPayPalProviderId(providerId?: string | null): boolean;
146
+
119
147
  type PayPalPaymentSectionProps = {
120
148
  cartId: string;
121
149
  selectedProviderId: string | null | undefined;
@@ -126,6 +154,14 @@ type PayPalPaymentSectionProps = {
126
154
  onError?: (message: string) => void;
127
155
  onPaid?: (result: unknown) => void;
128
156
  };
157
+ /**
158
+ * Drop-in PayPal payment section for a Medusa checkout.
159
+ *
160
+ * A thin wrapper over {@link MedusaNextPayPalAdapter} (they previously carried
161
+ * two ~90%-identical render paths that had to be edited in lockstep): this
162
+ * component pins the default provider ids and adds the `sessionLoading` gate
163
+ * shown while the storefront is still creating the payment session.
164
+ */
129
165
  declare function PayPalPaymentSection({ cartId, selectedProviderId, baseUrl, publishableApiKey, sessionLoading, onSuccess, onError, onPaid, }: PayPalPaymentSectionProps): React.JSX.Element | null;
130
166
 
131
167
  type Args = {
@@ -140,10 +176,36 @@ type Result = {
140
176
  cardEnabled: boolean;
141
177
  cardTitle: string;
142
178
  loading: boolean;
179
+ /**
180
+ * Non-null when the config fetch failed and the returned flags are the
181
+ * optimistic defaults rather than the merchant's real settings. Storefronts
182
+ * can use this to hide or annotate the PayPal options instead of advertising
183
+ * methods that will fail once selected.
184
+ */
185
+ error: string | null;
143
186
  };
144
187
  declare function usePayPalPaymentMethods({ baseUrl, publishableApiKey, cartId, enabled, }: Args): Result;
145
188
 
146
189
  declare function showProcessingOverlay(): void;
147
190
  declare function hideProcessingOverlay(): void;
148
191
 
149
- export { MedusaNextPayPalAdapter, type MedusaNextPayPalAdapterProps, PAYPAL_CARD_PROVIDER_ID, PAYPAL_WALLET_PROVIDER_ID, PayPalAdvancedCard, type PayPalConfig, PayPalCurrencyNotice, PayPalPaymentSection, type PayPalPaymentSectionProps, PayPalProvider, type PayPalSettingsResponse, PayPalSmartButtons, createPayPalStoreApi, hideProcessingOverlay, isPayPalProviderId, markPaymentComplete, showProcessingOverlay, usePayPalConfig, usePayPalPaymentMethods };
192
+ /**
193
+ * Per-cart "money was captured" flag, persisted in sessionStorage.
194
+ *
195
+ * The in-memory `capturedRef` protects a buyer within one page lifetime, but a
196
+ * reload/crash between the capture and the order finalization loses it — the
197
+ * buyer would then see fresh payment buttons for a cart whose money is already
198
+ * taken. Persisting the flag lets the components restore the finalize-only
199
+ * Retry path after a reload instead of offering a second payment.
200
+ *
201
+ * sessionStorage is deliberately chosen over localStorage: it is scoped to the
202
+ * tab/session, so an abandoned flag can't leak into a future visit after the
203
+ * cart id is reused, and it needs no expiry logic. Every access is guarded —
204
+ * private browsing modes and storage-blocking settings throw on access, and a
205
+ * storage failure must never break the payment flow.
206
+ */
207
+ declare function markCartCaptured(cartId: string): void;
208
+ declare function wasCartCaptured(cartId: string): boolean;
209
+ declare function clearCartCaptured(cartId: string): void;
210
+
211
+ export { MedusaNextPayPalAdapter, type MedusaNextPayPalAdapterProps, PAYPAL_CARD_PROVIDER_ID, PAYPAL_WALLET_PROVIDER_ID, PayPalAdvancedCard, type PayPalConfig, PayPalCurrencyNotice, PayPalPaymentSection, type PayPalPaymentSectionProps, PayPalProvider, type PayPalSettingsResponse, PayPalSmartButtons, clearCartCaptured, createPayPalStoreApi, generateIdempotencyKey, hideProcessingOverlay, isPayPalProviderId, markCartCaptured, markPaymentComplete, showProcessingOverlay, usePayPalConfig, usePayPalPaymentMethods, wasCartCaptured };
package/dist/index.d.ts CHANGED
@@ -45,8 +45,27 @@ type PayPalSettingsResponse = {
45
45
  type HttpOptions = {
46
46
  baseUrl: string;
47
47
  publishableApiKey?: string;
48
+ /**
49
+ * `credentials` mode for every request. Defaults to "include" (the
50
+ * historical behavior, required when the backend sits behind cookie-based
51
+ * gateways like Cloudflare Access). Set to "omit" or "same-origin" when the
52
+ * backend answers with `Access-Control-Allow-Origin: *` — browsers reject
53
+ * credentialed requests against a wildcard CORS origin outright.
54
+ */
55
+ credentials?: RequestCredentials;
48
56
  };
49
57
 
58
+ /**
59
+ * Fresh idempotency key per create-order ATTEMPT (i.e. per buyer click).
60
+ *
61
+ * The backend derives the PayPal-Request-Id from this header. It must be
62
+ * unique per attempt: PayPal replays the cached response for a reused request
63
+ * id, so a key stable across cart changes would hand back the ORIGINAL order —
64
+ * at the original total — after the buyer edits the cart, charging a stale
65
+ * amount. Reuse/dedup of an unchanged order is handled server-side by the
66
+ * stored-order staleness check, not by this key.
67
+ */
68
+ declare function generateIdempotencyKey(): string;
50
69
  declare function markPaymentComplete(baseUrl: string, cartId: string, publishableApiKey?: string): Promise<Record<string, unknown>>;
51
70
  declare function createPayPalStoreApi(opts: HttpOptions): {
52
71
  getConfig(cartId?: string, signal?: AbortSignal): Promise<PayPalConfig>;
@@ -113,9 +132,18 @@ type MedusaNextPayPalAdapterProps = {
113
132
  };
114
133
  declare function MedusaNextPayPalAdapter(props: MedusaNextPayPalAdapterProps): React.JSX.Element | null;
115
134
 
135
+ /**
136
+ * Provider ids registered by `@easypayment/medusa-payment-paypal`.
137
+ *
138
+ * Single source of truth — `PayPalPaymentSection`, `MedusaNextPayPalAdapter`,
139
+ * and the server-safe `order` entrypoint all re-export from here so the ids
140
+ * can never drift between entrypoints.
141
+ */
116
142
  declare const PAYPAL_WALLET_PROVIDER_ID: "pp_paypal_paypal";
117
143
  declare const PAYPAL_CARD_PROVIDER_ID: "pp_paypal_card_paypal_card";
118
- declare function isPayPalProviderId(id?: string | null): boolean;
144
+ /** True when the provider id belongs to this PayPal plugin. */
145
+ declare function isPayPalProviderId(providerId?: string | null): boolean;
146
+
119
147
  type PayPalPaymentSectionProps = {
120
148
  cartId: string;
121
149
  selectedProviderId: string | null | undefined;
@@ -126,6 +154,14 @@ type PayPalPaymentSectionProps = {
126
154
  onError?: (message: string) => void;
127
155
  onPaid?: (result: unknown) => void;
128
156
  };
157
+ /**
158
+ * Drop-in PayPal payment section for a Medusa checkout.
159
+ *
160
+ * A thin wrapper over {@link MedusaNextPayPalAdapter} (they previously carried
161
+ * two ~90%-identical render paths that had to be edited in lockstep): this
162
+ * component pins the default provider ids and adds the `sessionLoading` gate
163
+ * shown while the storefront is still creating the payment session.
164
+ */
129
165
  declare function PayPalPaymentSection({ cartId, selectedProviderId, baseUrl, publishableApiKey, sessionLoading, onSuccess, onError, onPaid, }: PayPalPaymentSectionProps): React.JSX.Element | null;
130
166
 
131
167
  type Args = {
@@ -140,10 +176,36 @@ type Result = {
140
176
  cardEnabled: boolean;
141
177
  cardTitle: string;
142
178
  loading: boolean;
179
+ /**
180
+ * Non-null when the config fetch failed and the returned flags are the
181
+ * optimistic defaults rather than the merchant's real settings. Storefronts
182
+ * can use this to hide or annotate the PayPal options instead of advertising
183
+ * methods that will fail once selected.
184
+ */
185
+ error: string | null;
143
186
  };
144
187
  declare function usePayPalPaymentMethods({ baseUrl, publishableApiKey, cartId, enabled, }: Args): Result;
145
188
 
146
189
  declare function showProcessingOverlay(): void;
147
190
  declare function hideProcessingOverlay(): void;
148
191
 
149
- export { MedusaNextPayPalAdapter, type MedusaNextPayPalAdapterProps, PAYPAL_CARD_PROVIDER_ID, PAYPAL_WALLET_PROVIDER_ID, PayPalAdvancedCard, type PayPalConfig, PayPalCurrencyNotice, PayPalPaymentSection, type PayPalPaymentSectionProps, PayPalProvider, type PayPalSettingsResponse, PayPalSmartButtons, createPayPalStoreApi, hideProcessingOverlay, isPayPalProviderId, markPaymentComplete, showProcessingOverlay, usePayPalConfig, usePayPalPaymentMethods };
192
+ /**
193
+ * Per-cart "money was captured" flag, persisted in sessionStorage.
194
+ *
195
+ * The in-memory `capturedRef` protects a buyer within one page lifetime, but a
196
+ * reload/crash between the capture and the order finalization loses it — the
197
+ * buyer would then see fresh payment buttons for a cart whose money is already
198
+ * taken. Persisting the flag lets the components restore the finalize-only
199
+ * Retry path after a reload instead of offering a second payment.
200
+ *
201
+ * sessionStorage is deliberately chosen over localStorage: it is scoped to the
202
+ * tab/session, so an abandoned flag can't leak into a future visit after the
203
+ * cart id is reused, and it needs no expiry logic. Every access is guarded —
204
+ * private browsing modes and storage-blocking settings throw on access, and a
205
+ * storage failure must never break the payment flow.
206
+ */
207
+ declare function markCartCaptured(cartId: string): void;
208
+ declare function wasCartCaptured(cartId: string): boolean;
209
+ declare function clearCartCaptured(cartId: string): void;
210
+
211
+ export { MedusaNextPayPalAdapter, type MedusaNextPayPalAdapterProps, PAYPAL_CARD_PROVIDER_ID, PAYPAL_WALLET_PROVIDER_ID, PayPalAdvancedCard, type PayPalConfig, PayPalCurrencyNotice, PayPalPaymentSection, type PayPalPaymentSectionProps, PayPalProvider, type PayPalSettingsResponse, PayPalSmartButtons, clearCartCaptured, createPayPalStoreApi, generateIdempotencyKey, hideProcessingOverlay, isPayPalProviderId, markCartCaptured, markPaymentComplete, showProcessingOverlay, usePayPalConfig, usePayPalPaymentMethods, wasCartCaptured };
package/dist/index.mjs CHANGED
@@ -66,7 +66,7 @@ function createHttpClient(opts) {
66
66
  res = await fetch(url, {
67
67
  ...init,
68
68
  headers,
69
- credentials: "include",
69
+ credentials: opts.credentials ?? "include",
70
70
  signal: controller.signal
71
71
  });
72
72
  text = await res.text().catch(() => "");
@@ -135,6 +135,14 @@ function createHttpClient(opts) {
135
135
  }
136
136
 
137
137
  // src/client/paypal.ts
138
+ function generateIdempotencyKey() {
139
+ try {
140
+ const c = globalThis.crypto;
141
+ if (c?.randomUUID) return c.randomUUID();
142
+ } catch {
143
+ }
144
+ return `pp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
145
+ }
138
146
  async function markPaymentComplete(baseUrl, cartId, publishableApiKey) {
139
147
  const http = createHttpClient({ baseUrl, publishableApiKey });
140
148
  return http.request(
@@ -160,7 +168,13 @@ function createPayPalStoreApi(opts) {
160
168
  createOrder(cartId, isCardPayment = false) {
161
169
  return http.request(`/store/paypal/create-order`, {
162
170
  method: "POST",
163
- headers: { "Content-Type": "application/json" },
171
+ headers: {
172
+ "Content-Type": "application/json",
173
+ // One key per attempt — see generateIdempotencyKey. Deliberately NOT
174
+ // sent for capture-order: there the server's deterministic
175
+ // per-order-id fallback is the correct idempotency scope.
176
+ "Idempotency-Key": generateIdempotencyKey()
177
+ },
164
178
  body: JSON.stringify({ cart_id: cartId, is_card_payment: isCardPayment })
165
179
  });
166
180
  },
@@ -293,6 +307,31 @@ function PayPalCurrencyNotice({ config }) {
293
307
  import { useCallback, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef2, useState as useState2 } from "react";
294
308
  import { PayPalButtons, usePayPalScriptReducer } from "@paypal/react-paypal-js";
295
309
 
310
+ // src/utils/captured-state.ts
311
+ var KEY_PREFIX = "__pp_captured::";
312
+ function markCartCaptured(cartId) {
313
+ if (!cartId) return;
314
+ try {
315
+ window.sessionStorage.setItem(`${KEY_PREFIX}${cartId}`, String(Date.now()));
316
+ } catch {
317
+ }
318
+ }
319
+ function wasCartCaptured(cartId) {
320
+ if (!cartId) return false;
321
+ try {
322
+ return window.sessionStorage.getItem(`${KEY_PREFIX}${cartId}`) !== null;
323
+ } catch {
324
+ return false;
325
+ }
326
+ }
327
+ function clearCartCaptured(cartId) {
328
+ if (!cartId) return;
329
+ try {
330
+ window.sessionStorage.removeItem(`${KEY_PREFIX}${cartId}`);
331
+ } catch {
332
+ }
333
+ }
334
+
296
335
  // src/utils/next-errors.ts
297
336
  function isNextRouterError(e) {
298
337
  if (typeof e !== "object" || e === null || !("digest" in e)) return false;
@@ -416,6 +455,13 @@ function PayPalSmartButtons(props) {
416
455
  window.addEventListener("pageshow", onPageShow);
417
456
  return () => window.removeEventListener("pageshow", onPageShow);
418
457
  }, []);
458
+ useEffect2(() => {
459
+ if (capturedRef.current || !wasCartCaptured(cartId)) return;
460
+ capturedRef.current = {};
461
+ setCompletionPending(true);
462
+ const msg = "Your payment was already received for this cart. Please use the button below to finish placing your order \u2014 do not pay again.";
463
+ setError(msg);
464
+ }, [cartId]);
419
465
  useEffect2(() => {
420
466
  if (!isResolved || buttonsReady) {
421
467
  return;
@@ -431,6 +477,7 @@ function PayPalSmartButtons(props) {
431
477
  setError(null);
432
478
  try {
433
479
  const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
480
+ clearCartCaptured(cartId);
434
481
  await onPaid?.({ ...captured, ...completeResult });
435
482
  setCompletionPending(false);
436
483
  } catch (e) {
@@ -701,6 +748,7 @@ function PayPalSmartButtons(props) {
701
748
  if (!orderId) throw new Error("PayPal order ID is missing from approval response");
702
749
  const result = await api.captureOrder(cartId, orderId);
703
750
  capturedRef.current = result || {};
751
+ markCartCaptured(cartId);
704
752
  } catch (e) {
705
753
  if (isNextRouterError(e)) return;
706
754
  hideProcessingOverlay();
@@ -916,6 +964,14 @@ function PayPalAdvancedCard(props) {
916
964
  window.addEventListener("pageshow", onPageShow);
917
965
  return () => window.removeEventListener("pageshow", onPageShow);
918
966
  }, []);
967
+ React3.useEffect(() => {
968
+ if (capturedRef.current || !wasCartCaptured(cartId)) return;
969
+ capturedRef.current = {};
970
+ setCompletionPending(true);
971
+ setError(
972
+ "Your payment was already received for this cart. Please use the button below to finish placing your order \u2014 do not pay again."
973
+ );
974
+ }, [cartId]);
919
975
  const finalizeCapturedPayment = React3.useCallback(async () => {
920
976
  const captured = capturedRef.current;
921
977
  if (!captured) return;
@@ -924,6 +980,7 @@ function PayPalAdvancedCard(props) {
924
980
  setError(null);
925
981
  try {
926
982
  const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
983
+ clearCartCaptured(cartId);
927
984
  await onPaid?.({ ...captured, ...completeResult });
928
985
  setCompletionPending(false);
929
986
  } catch (e) {
@@ -1145,6 +1202,7 @@ function PayPalAdvancedCard(props) {
1145
1202
  if (!orderId) throw new Error("PayPal order ID is missing from approval response");
1146
1203
  const result = await api.captureOrder(cartId, orderId);
1147
1204
  capturedRef.current = result || {};
1205
+ markCartCaptured(cartId);
1148
1206
  } catch (e) {
1149
1207
  if (isNextRouterError(e)) return;
1150
1208
  resetSubmitState();
@@ -1333,9 +1391,23 @@ function PayPalAdvancedCard(props) {
1333
1391
 
1334
1392
  // src/adapters/MedusaNextPayPalAdapter.tsx
1335
1393
  import { useCallback as useCallback2 } from "react";
1394
+
1395
+ // src/constants.ts
1396
+ var PAYPAL_WALLET_PROVIDER_ID = "pp_paypal_paypal";
1397
+ var PAYPAL_CARD_PROVIDER_ID = "pp_paypal_card_paypal_card";
1398
+ var PAYPAL_PROVIDER_IDS = [
1399
+ PAYPAL_WALLET_PROVIDER_ID,
1400
+ PAYPAL_CARD_PROVIDER_ID
1401
+ ];
1402
+ function isPayPalProviderId(providerId) {
1403
+ if (!providerId) return false;
1404
+ return PAYPAL_PROVIDER_IDS.includes(
1405
+ providerId
1406
+ );
1407
+ }
1408
+
1409
+ // src/adapters/MedusaNextPayPalAdapter.tsx
1336
1410
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1337
- var DEFAULT_PAYPAL_PROVIDER_ID = "pp_paypal_paypal";
1338
- var DEFAULT_PAYPAL_CARD_PROVIDER_ID = "pp_paypal_card_paypal_card";
1339
1411
  var SPIN_STYLE3 = `@keyframes _pp_spin { to { transform: rotate(360deg) } }`;
1340
1412
  function PayPalLoadingCard() {
1341
1413
  return /* @__PURE__ */ jsxs4(
@@ -1393,6 +1465,26 @@ function PayPalErrorCard({ message }) {
1393
1465
  }
1394
1466
  );
1395
1467
  }
1468
+ function PayPalUnavailableCard({ label }) {
1469
+ return /* @__PURE__ */ jsxs4(
1470
+ "div",
1471
+ {
1472
+ role: "status",
1473
+ style: {
1474
+ padding: "12px 16px",
1475
+ background: "#f9fafb",
1476
+ border: "1px solid #e5e7eb",
1477
+ borderRadius: 10,
1478
+ fontSize: 13,
1479
+ color: "#6b7280"
1480
+ },
1481
+ children: [
1482
+ label,
1483
+ " is currently unavailable. Please choose a different payment method."
1484
+ ]
1485
+ }
1486
+ );
1487
+ }
1396
1488
  function MedusaNextPayPalAdapter(props) {
1397
1489
  const {
1398
1490
  cartId,
@@ -1404,8 +1496,8 @@ function MedusaNextPayPalAdapter(props) {
1404
1496
  onError,
1405
1497
  onPaid
1406
1498
  } = props;
1407
- const paypalProviderId = providerIds?.paypal || DEFAULT_PAYPAL_PROVIDER_ID;
1408
- const paypalCardProviderId = providerIds?.paypalCard || DEFAULT_PAYPAL_CARD_PROVIDER_ID;
1499
+ const paypalProviderId = providerIds?.paypal || PAYPAL_WALLET_PROVIDER_ID;
1500
+ const paypalCardProviderId = providerIds?.paypalCard || PAYPAL_CARD_PROVIDER_ID;
1409
1501
  const shouldRender = selectedProviderId === paypalProviderId || selectedProviderId === paypalCardProviderId;
1410
1502
  const { config, loading, error } = usePayPalConfig({
1411
1503
  baseUrl,
@@ -1425,8 +1517,12 @@ function MedusaNextPayPalAdapter(props) {
1425
1517
  if (error) return /* @__PURE__ */ jsx5(PayPalErrorCard, { message: error });
1426
1518
  if (!config) return null;
1427
1519
  const isCardProvider = selectedProviderId === paypalCardProviderId;
1428
- if (config.paypal_enabled === false && !isCardProvider) return null;
1429
- if (isCardProvider && config.card_enabled === false) return null;
1520
+ if (config.paypal_enabled === false && !isCardProvider) {
1521
+ return /* @__PURE__ */ jsx5(PayPalUnavailableCard, { label: config.paypal_title || "PayPal" });
1522
+ }
1523
+ if (isCardProvider && config.card_enabled === false) {
1524
+ return /* @__PURE__ */ jsx5(PayPalUnavailableCard, { label: config.card_title || "Card payment" });
1525
+ }
1430
1526
  const disableFunding = Array.isArray(config.disable_buttons) ? config.disable_buttons.join(",") : void 0;
1431
1527
  return /* @__PURE__ */ jsxs4("div", { style: { display: "grid", gap: 12 }, children: [
1432
1528
  /* @__PURE__ */ jsx5(PayPalCurrencyNotice, { config }),
@@ -1463,18 +1559,7 @@ function MedusaNextPayPalAdapter(props) {
1463
1559
  }
1464
1560
 
1465
1561
  // src/components/PayPalPaymentSection.tsx
1466
- import { useCallback as useCallback3 } from "react";
1467
1562
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1468
- var PAYPAL_WALLET_PROVIDER_ID = "pp_paypal_paypal";
1469
- var PAYPAL_CARD_PROVIDER_ID = "pp_paypal_card_paypal_card";
1470
- var PAYPAL_PROVIDER_IDS = [
1471
- PAYPAL_WALLET_PROVIDER_ID,
1472
- PAYPAL_CARD_PROVIDER_ID
1473
- ];
1474
- function isPayPalProviderId(id) {
1475
- if (!id) return false;
1476
- return PAYPAL_PROVIDER_IDS.includes(id);
1477
- }
1478
1563
  var SPIN_STYLE4 = `@keyframes _pp_section_spin { to { transform: rotate(360deg) } }`;
1479
1564
  function SessionInitCard() {
1480
1565
  return /* @__PURE__ */ jsxs5(
@@ -1513,62 +1598,6 @@ function SessionInitCard() {
1513
1598
  }
1514
1599
  );
1515
1600
  }
1516
- function ConfigLoadingCard() {
1517
- return /* @__PURE__ */ jsxs5(
1518
- "div",
1519
- {
1520
- role: "status",
1521
- "aria-label": "Connecting to PayPal",
1522
- style: {
1523
- display: "flex",
1524
- alignItems: "center",
1525
- gap: 12,
1526
- padding: "14px 16px",
1527
- background: "#f9fafb",
1528
- border: "1px solid #e5e7eb",
1529
- borderRadius: 10
1530
- },
1531
- children: [
1532
- /* @__PURE__ */ jsx6("style", { children: SPIN_STYLE4 }),
1533
- /* @__PURE__ */ jsx6(
1534
- "div",
1535
- {
1536
- style: {
1537
- width: 22,
1538
- height: 22,
1539
- borderRadius: "50%",
1540
- border: "2.5px solid #e5e7eb",
1541
- borderTopColor: "#0070ba",
1542
- animation: "_pp_section_spin .7s linear infinite",
1543
- flexShrink: 0
1544
- }
1545
- }
1546
- ),
1547
- /* @__PURE__ */ jsxs5("div", { children: [
1548
- /* @__PURE__ */ jsx6("div", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Connecting to PayPal\u2026" }),
1549
- /* @__PURE__ */ jsx6("div", { style: { fontSize: 12, color: "#6b7280", marginTop: 2 }, children: "Setting up secure payment" })
1550
- ] })
1551
- ]
1552
- }
1553
- );
1554
- }
1555
- function ErrorCard({ message }) {
1556
- return /* @__PURE__ */ jsx6(
1557
- "div",
1558
- {
1559
- role: "alert",
1560
- style: {
1561
- padding: "12px 16px",
1562
- background: "#fef2f2",
1563
- border: "1px solid #fecaca",
1564
- borderRadius: 10,
1565
- fontSize: 13,
1566
- color: "#b91c1c"
1567
- },
1568
- children: message
1569
- }
1570
- );
1571
- }
1572
1601
  function PayPalPaymentSection({
1573
1602
  cartId,
1574
1603
  selectedProviderId,
@@ -1579,63 +1608,20 @@ function PayPalPaymentSection({
1579
1608
  onError,
1580
1609
  onPaid
1581
1610
  }) {
1582
- const shouldRender = isPayPalProviderId(selectedProviderId);
1583
- const { config, loading, error } = usePayPalConfig({
1584
- baseUrl,
1585
- publishableApiKey,
1586
- cartId,
1587
- enabled: shouldRender
1588
- });
1589
- const handlePaid = useCallback3(
1590
- async (captureResult) => {
1591
- onPaid?.(captureResult);
1592
- await onSuccess?.(cartId);
1593
- },
1594
- [cartId, onPaid, onSuccess]
1595
- );
1596
- if (!shouldRender) return null;
1611
+ if (!isPayPalProviderId(selectedProviderId)) return null;
1597
1612
  if (sessionLoading) return /* @__PURE__ */ jsx6(SessionInitCard, {});
1598
- if (loading) return /* @__PURE__ */ jsx6(ConfigLoadingCard, {});
1599
- if (error) return /* @__PURE__ */ jsx6(ErrorCard, { message: error });
1600
- if (!config) return null;
1601
- if (config.paypal_enabled === false && selectedProviderId === PAYPAL_WALLET_PROVIDER_ID) {
1602
- return null;
1603
- }
1604
- const isCardProvider = selectedProviderId === PAYPAL_CARD_PROVIDER_ID;
1605
- if (isCardProvider && config.card_enabled === false) return null;
1606
- const disableFunding = Array.isArray(config.disable_buttons) ? config.disable_buttons.join(",") : void 0;
1607
- return /* @__PURE__ */ jsxs5("div", { style: { display: "grid", gap: 12 }, children: [
1608
- /* @__PURE__ */ jsx6(PayPalCurrencyNotice, { config }),
1609
- /* @__PURE__ */ jsx6(
1610
- PayPalProvider,
1611
- {
1612
- config,
1613
- intent: config.intent === "authorize" ? "authorize" : "capture",
1614
- disableFunding,
1615
- children: isCardProvider ? /* @__PURE__ */ jsx6(
1616
- PayPalAdvancedCard,
1617
- {
1618
- baseUrl,
1619
- publishableApiKey,
1620
- cartId,
1621
- config,
1622
- onPaid: handlePaid,
1623
- onError
1624
- }
1625
- ) : /* @__PURE__ */ jsx6(
1626
- PayPalSmartButtons,
1627
- {
1628
- baseUrl,
1629
- publishableApiKey,
1630
- cartId,
1631
- config,
1632
- onPaid: handlePaid,
1633
- onError
1634
- }
1635
- )
1636
- }
1637
- )
1638
- ] }, selectedProviderId);
1613
+ return /* @__PURE__ */ jsx6(
1614
+ MedusaNextPayPalAdapter,
1615
+ {
1616
+ cartId,
1617
+ selectedProviderId,
1618
+ baseUrl,
1619
+ publishableApiKey,
1620
+ onSuccess,
1621
+ onError,
1622
+ onPaid
1623
+ }
1624
+ );
1639
1625
  }
1640
1626
 
1641
1627
  // src/hooks/usePayPalPaymentMethods.ts
@@ -1658,7 +1644,8 @@ var DEFAULT_RESULT = {
1658
1644
  paypalTitle: "PayPal",
1659
1645
  cardEnabled: true,
1660
1646
  cardTitle: "Credit or Debit Card",
1661
- loading: false
1647
+ loading: false,
1648
+ error: null
1662
1649
  };
1663
1650
  function usePayPalPaymentMethods({
1664
1651
  baseUrl,
@@ -1699,7 +1686,8 @@ function usePayPalPaymentMethods({
1699
1686
  paypalTitle: typeof cfg.paypal_title === "string" && cfg.paypal_title ? cfg.paypal_title : "PayPal",
1700
1687
  cardEnabled: cfg.card_enabled !== false,
1701
1688
  cardTitle: typeof cfg.card_title === "string" && cfg.card_title ? cfg.card_title : "Credit or Debit Card",
1702
- loading: false
1689
+ loading: false,
1690
+ error: null
1703
1691
  };
1704
1692
  cacheSet2(k, { result: next, at: Date.now() });
1705
1693
  setResult(next);
@@ -1712,12 +1700,17 @@ function usePayPalPaymentMethods({
1712
1700
  ...DEFAULT_RESULT,
1713
1701
  paypalEnabled: false,
1714
1702
  cardEnabled: false,
1715
- loading: false
1703
+ loading: false,
1704
+ error: msg || "PayPal is disabled"
1716
1705
  };
1717
1706
  setResult(disabled);
1718
1707
  return;
1719
1708
  }
1720
- setResult({ ...DEFAULT_RESULT, loading: false });
1709
+ setResult({
1710
+ ...DEFAULT_RESULT,
1711
+ loading: false,
1712
+ error: msg || "Failed to load PayPal payment methods"
1713
+ });
1721
1714
  }
1722
1715
  })();
1723
1716
  return () => {
@@ -1736,12 +1729,16 @@ export {
1736
1729
  PayPalPaymentSection,
1737
1730
  PayPalProvider,
1738
1731
  PayPalSmartButtons,
1732
+ clearCartCaptured,
1739
1733
  createPayPalStoreApi,
1734
+ generateIdempotencyKey,
1740
1735
  hideProcessingOverlay,
1741
1736
  isPayPalProviderId,
1737
+ markCartCaptured,
1742
1738
  markPaymentComplete,
1743
1739
  showProcessingOverlay,
1744
1740
  usePayPalConfig,
1745
- usePayPalPaymentMethods
1741
+ usePayPalPaymentMethods,
1742
+ wasCartCaptured
1746
1743
  };
1747
1744
  //# sourceMappingURL=index.mjs.map