@easypayment/medusa-paypal-ui 1.2.3 → 1.2.8

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/CHANGELOG.md CHANGED
@@ -2,9 +2,18 @@
2
2
 
3
3
  All notable changes to `@easypayment/medusa-paypal-ui` are documented here.
4
4
 
5
+ ## 1.2.7 — 2026-09-23
6
+
7
+ ### Changed
8
+
9
+ - Code formatting only, no behavior changes. Version aligned with
10
+ `@easypayment/medusa-payment-paypal` 1.2.7 — both packages now share the
11
+ same version number. (1.2.4–1.2.6 were skipped to match.)
12
+
5
13
  ## 1.2.3 — 2026-09-21
6
14
 
7
15
  ### Changed
16
+
8
17
  - Code formatting only: applied the repo's Prettier style (no semicolons,
9
18
  double quotes, 80-column width) across `src/` and added `.prettierrc` so the
10
19
  package shares the plugin's formatting config. No behavior changes.
@@ -12,6 +21,7 @@ All notable changes to `@easypayment/medusa-paypal-ui` are documented here.
12
21
  ## 1.2.0 — 2026-09-02
13
22
 
14
23
  ### Fixed
24
+
15
25
  - **A reload between capture and finalization no longer strands the buyer.**
16
26
  The "money was captured" state is now persisted per cart in
17
27
  `sessionStorage`; after a reload/crash the components restore the
@@ -26,6 +36,7 @@ All notable changes to `@easypayment/medusa-paypal-ui` are documented here.
26
36
  idempotency scope there.)
27
37
 
28
38
  ### Added
39
+
29
40
  - `credentials` option on the HTTP client (`HttpOptions.credentials`). Defaults
30
41
  to the historical `"include"`; set `"omit"`/`"same-origin"` for backends
31
42
  answering with `Access-Control-Allow-Origin: *`, which browsers reject for
@@ -37,6 +48,7 @@ All notable changes to `@easypayment/medusa-paypal-ui` are documented here.
37
48
  so storefronts can stop advertising methods that will fail once selected.
38
49
 
39
50
  ### Changed
51
+
40
52
  - A payment method the merchant disabled in admin now renders a neutral
41
53
  "currently unavailable" notice instead of blank space under the selected
42
54
  option.
@@ -48,6 +60,7 @@ All notable changes to `@easypayment/medusa-paypal-ui` are documented here.
48
60
  ## 1.1.0
49
61
 
50
62
  ### Fixed (re-audit)
63
+
51
64
  - **Card submit rejections no longer strand the buyer under the processing
52
65
  overlay.** `cardFieldsForm.submit()` returns a promise whose rejection is not
53
66
  routed to the provider's `onError`; it is now caught, the overlay/state reset,
@@ -82,8 +95,8 @@ All notable changes to `@easypayment/medusa-paypal-ui` are documented here.
82
95
  (`paymentAction`, `advancedCardEnabled`); the old `data` envelope shape is
83
96
  kept as deprecated so existing code compiles.
84
97
 
85
-
86
98
  ### Added
99
+
87
100
  - **Transient-failure retry** in the HTTP client (opt-in `retries`, jittered
88
101
  backoff on network errors / timeouts / 429 / 5xx). Wired into the idempotent
89
102
  `markPaymentComplete` and `captureOrder` calls so a network blip right after
@@ -92,12 +105,14 @@ All notable changes to `@easypayment/medusa-paypal-ui` are documented here.
92
105
  - Tests for the retry logic and the capture/complete retry paths.
93
106
 
94
107
  ### Changed
108
+
95
109
  - Test environment switched from `node` to `jsdom` so component/hook tests can
96
110
  run against a DOM (the client tests continue to pass under it).
97
111
 
98
112
  ## 1.0.56
99
113
 
100
114
  ### Added
115
+
101
116
  - Unit test suite (vitest) for the framework-agnostic client logic: the HTTP
102
117
  client (base-url/path normalization, publishable-key header, 401/403 handling,
103
118
  HTML-tag stripping, empty-body and non-JSON guards, timeout surfacing) and the
package/README.md CHANGED
@@ -36,13 +36,13 @@
36
36
 
37
37
  `@easypayment/medusa-paypal-ui` is the **storefront UI package** that connects your Next.js (App Router) storefront to the `@easypayment/medusa-paypal` backend plugin. It ships the PayPal adapter used inside your checkout payment step — your storefront adds the adapter, provider filtering, and backend config handling to the existing Medusa payment UI.
38
38
 
39
- | Feature | Details |
40
- |---|---|
41
- | 🔵 **PayPal Smart Buttons** | Wallet-based checkout via `pp_paypal_paypal` |
42
- | 💳 **Advanced Card Fields** | Hosted PCI-compliant advanced credit card inputs via `pp_paypal_card_paypal_card` |
43
- | 🛠 **Admin-driven config** | Enable/disable providers and set labels from Medusa Admin |
44
- | ⚡ **Built-in UX** | Smart Buttons and Advanced Card UI rendered by `MedusaNextPayPalAdapter` |
45
- | 🔄 **Storefront-controlled flow** | Your payment step controls session creation, loading states, and `placeOrder` |
39
+ | Feature | Details |
40
+ | --------------------------------- | --------------------------------------------------------------------------------- |
41
+ | 🔵 **PayPal Smart Buttons** | Wallet-based checkout via `pp_paypal_paypal` |
42
+ | 💳 **Advanced Card Fields** | Hosted PCI-compliant advanced credit card inputs via `pp_paypal_card_paypal_card` |
43
+ | 🛠 **Admin-driven config** | Enable/disable providers and set labels from Medusa Admin |
44
+ | ⚡ **Built-in UX** | Smart Buttons and Advanced Card UI rendered by `MedusaNextPayPalAdapter` |
45
+ | 🔄 **Storefront-controlled flow** | Your payment step controls session creation, loading states, and `placeOrder` |
46
46
 
47
47
  ---
48
48
 
@@ -160,10 +160,14 @@ useEffect(() => {
160
160
 
161
161
  const config = await response.json()
162
162
 
163
- if (typeof config?.paypal_enabled === "boolean") setPaypalEnabled(config.paypal_enabled)
164
- if (typeof config?.paypal_title === "string" && config.paypal_title) setPaypalTitle(config.paypal_title)
165
- if (typeof config?.card_enabled === "boolean") setCardEnabled(config.card_enabled)
166
- if (typeof config?.card_title === "string" && config.card_title) setCardTitle(config.card_title)
163
+ if (typeof config?.paypal_enabled === "boolean")
164
+ setPaypalEnabled(config.paypal_enabled)
165
+ if (typeof config?.paypal_title === "string" && config.paypal_title)
166
+ setPaypalTitle(config.paypal_title)
167
+ if (typeof config?.card_enabled === "boolean")
168
+ setCardEnabled(config.card_enabled)
169
+ if (typeof config?.card_title === "string" && config.card_title)
170
+ setCardTitle(config.card_title)
167
171
  } catch (err) {
168
172
  if ((err as Error).name !== "AbortError") setPaypalLoading(false)
169
173
  }
@@ -215,7 +219,7 @@ const filteredPaymentMethods = useMemo(
215
219
  if (paymentMethod.id === PAYPAL_CARD_PROVIDER_ID) return cardEnabled
216
220
  return true
217
221
  }),
218
- [availablePaymentMethods, cardEnabled, paypalEnabled],
222
+ [availablePaymentMethods, cardEnabled, paypalEnabled]
219
223
  )
220
224
  ```
221
225
 
@@ -242,10 +246,20 @@ This makes the radio button labels show the titles configured in Medusa Admin in
242
246
  paymentInfoMap={{
243
247
  ...paymentInfoMap,
244
248
  ...(paymentMethod.id === PAYPAL_PROVIDER_ID
245
- ? { [paymentMethod.id]: { ...(paymentInfoMap[paymentMethod.id] || {}), title: paypalTitle } }
249
+ ? {
250
+ [paymentMethod.id]: {
251
+ ...(paymentInfoMap[paymentMethod.id] || {}),
252
+ title: paypalTitle,
253
+ },
254
+ }
246
255
  : {}),
247
256
  ...(paymentMethod.id === PAYPAL_CARD_PROVIDER_ID
248
- ? { [paymentMethod.id]: { ...(paymentInfoMap[paymentMethod.id] || {}), title: cardTitle } }
257
+ ? {
258
+ [paymentMethod.id]: {
259
+ ...(paymentInfoMap[paymentMethod.id] || {}),
260
+ title: cardTitle,
261
+ },
262
+ }
249
263
  : {}),
250
264
  }}
251
265
  paymentProviderId={paymentMethod.id}
@@ -262,24 +276,32 @@ This makes the radio button labels show the titles configured in Medusa Admin in
262
276
  The first block shows a loading spinner while the session is being set up. The second block renders the PayPal buttons or card fields once the session is ready.
263
277
 
264
278
  ```tsx
265
- {/* Loading state while PayPal session is being created */}
266
- {isPayPal(selectedPaymentMethod) && paypalLoading && (
267
- <div>Setting up payment...</div>
268
- )}
269
-
270
- {/* PayPal buttons or card fields */}
271
- {isPayPal(selectedPaymentMethod) && !paypalLoading && (
272
- <MedusaNextPayPalAdapter
273
- cartId={cart.id}
274
- selectedProviderId={selectedPaymentMethod}
275
- baseUrl={process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!}
276
- publishableApiKey={process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY}
277
- onSuccess={async () => {
278
- await placeOrder(cart.id)
279
- }}
280
- onError={(message) => setError(message)}
281
- />
282
- )}
279
+ {
280
+ /* Loading state while PayPal session is being created */
281
+ }
282
+ {
283
+ isPayPal(selectedPaymentMethod) && paypalLoading && (
284
+ <div>Setting up payment...</div>
285
+ )
286
+ }
287
+
288
+ {
289
+ /* PayPal buttons or card fields */
290
+ }
291
+ {
292
+ isPayPal(selectedPaymentMethod) && !paypalLoading && (
293
+ <MedusaNextPayPalAdapter
294
+ cartId={cart.id}
295
+ selectedProviderId={selectedPaymentMethod}
296
+ baseUrl={process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!}
297
+ publishableApiKey={process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY}
298
+ onSuccess={async () => {
299
+ await placeOrder(cart.id)
300
+ }}
301
+ onError={(message) => setError(message)}
302
+ />
303
+ )
304
+ }
283
305
  ```
284
306
 
285
307
  ---
@@ -299,7 +321,7 @@ PayPal handles its own checkout action, so the "Continue to review" button must
299
321
  disabled={
300
322
  (isStripeLike(selectedPaymentMethod) && !cardComplete) ||
301
323
  (!selectedPaymentMethod && !paidByGiftcard) ||
302
- isPayPal(selectedPaymentMethod) // 👈 add this line
324
+ isPayPal(selectedPaymentMethod) // 👈 add this line
303
325
  }
304
326
  data-testid="submit-payment-button"
305
327
  >
@@ -325,9 +347,9 @@ This shows the admin-configured title instead of a hardcoded or missing label.
325
347
  {activeSession?.provider_id === "pp_paypal_paypal"
326
348
  ? paypalTitle
327
349
  : activeSession?.provider_id === "pp_paypal_card_paypal_card"
328
- ? cardTitle
329
- : paymentInfoMap[activeSession?.provider_id]?.title ||
330
- activeSession?.provider_id}
350
+ ? cardTitle
351
+ : paymentInfoMap[activeSession?.provider_id]?.title ||
352
+ activeSession?.provider_id}
331
353
  </Text>
332
354
  ```
333
355
 
@@ -368,7 +390,7 @@ const Payment = ({
368
390
  availablePaymentMethods: any[]
369
391
  }) => {
370
392
  const activeSession = cart.payment_collection?.payment_sessions?.find(
371
- (paymentSession: any) => paymentSession.status === "pending",
393
+ (paymentSession: any) => paymentSession.status === "pending"
372
394
  )
373
395
 
374
396
  const [isLoading, setIsLoading] = useState(false)
@@ -376,7 +398,7 @@ const Payment = ({
376
398
  const [cardBrand, setCardBrand] = useState<string | null>(null)
377
399
  const [cardComplete, setCardComplete] = useState(false)
378
400
  const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(
379
- activeSession?.provider_id ?? "",
401
+ activeSession?.provider_id ?? ""
380
402
  )
381
403
  const [paypalEnabled, setPaypalEnabled] = useState(true)
382
404
  const [paypalTitle, setPaypalTitle] = useState("PayPal")
@@ -397,7 +419,7 @@ const Payment = ({
397
419
  if (paymentMethod.id === PAYPAL_CARD_PROVIDER_ID) return cardEnabled
398
420
  return true
399
421
  }),
400
- [availablePaymentMethods, cardEnabled, paypalEnabled],
422
+ [availablePaymentMethods, cardEnabled, paypalEnabled]
401
423
  )
402
424
 
403
425
  const setPaymentMethod = async (method: string) => {
@@ -427,7 +449,7 @@ const Payment = ({
427
449
  params.set(name, value)
428
450
  return params.toString()
429
451
  },
430
- [searchParams],
452
+ [searchParams]
431
453
  )
432
454
 
433
455
  const handleEdit = () => {
@@ -454,7 +476,7 @@ const Payment = ({
454
476
  if (!shouldInputCard) {
455
477
  return router.push(
456
478
  pathname + "?" + createQueryString("step", "review"),
457
- { scroll: false },
479
+ { scroll: false }
458
480
  )
459
481
  }
460
482
  } catch (err: any) {
@@ -494,10 +516,14 @@ const Payment = ({
494
516
 
495
517
  const config = await response.json()
496
518
 
497
- if (typeof config?.paypal_enabled === "boolean") setPaypalEnabled(config.paypal_enabled)
498
- if (typeof config?.paypal_title === "string" && config.paypal_title) setPaypalTitle(config.paypal_title)
499
- if (typeof config?.card_enabled === "boolean") setCardEnabled(config.card_enabled)
500
- if (typeof config?.card_title === "string" && config.card_title) setCardTitle(config.card_title)
519
+ if (typeof config?.paypal_enabled === "boolean")
520
+ setPaypalEnabled(config.paypal_enabled)
521
+ if (typeof config?.paypal_title === "string" && config.paypal_title)
522
+ setPaypalTitle(config.paypal_title)
523
+ if (typeof config?.card_enabled === "boolean")
524
+ setCardEnabled(config.card_enabled)
525
+ if (typeof config?.card_title === "string" && config.card_title)
526
+ setCardTitle(config.card_title)
501
527
  } catch (err) {
502
528
  if ((err as Error).name !== "AbortError") setPaypalLoading(false)
503
529
  }
@@ -517,7 +543,7 @@ const Payment = ({
517
543
  {
518
544
  "opacity-50 pointer-events-none select-none":
519
545
  !isOpen && !paymentReady,
520
- },
546
+ }
521
547
  )}
522
548
  >
523
549
  Payment
@@ -542,7 +568,9 @@ const Payment = ({
542
568
  filteredPaymentMethods.length > 0 &&
543
569
  (paypalEnabled ||
544
570
  cardEnabled ||
545
- availablePaymentMethods.some((method) => !isPayPal(method.id))) && (
571
+ availablePaymentMethods.some(
572
+ (method) => !isPayPal(method.id)
573
+ )) && (
546
574
  <>
547
575
  <RadioGroup
548
576
  value={selectedPaymentMethod}
@@ -630,7 +658,9 @@ const Payment = ({
630
658
  cartId={cart.id}
631
659
  selectedProviderId={selectedPaymentMethod}
632
660
  baseUrl={process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!}
633
- publishableApiKey={process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY}
661
+ publishableApiKey={
662
+ process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
663
+ }
634
664
  onSuccess={async () => {
635
665
  await placeOrder(cart.id)
636
666
  }}
@@ -690,7 +720,8 @@ const Payment = ({
690
720
  >
691
721
  {activeSession?.provider_id === "pp_paypal_paypal"
692
722
  ? paypalTitle
693
- : activeSession?.provider_id === "pp_paypal_card_paypal_card"
723
+ : activeSession?.provider_id ===
724
+ "pp_paypal_card_paypal_card"
694
725
  ? cardTitle
695
726
  : paymentInfoMap[activeSession?.provider_id]?.title ||
696
727
  activeSession?.provider_id}
@@ -705,7 +736,9 @@ const Payment = ({
705
736
  data-testid="payment-details-summary"
706
737
  >
707
738
  <Container className="flex items-center h-7 w-fit p-2 bg-ui-button-neutral-hover">
708
- {paymentInfoMap[selectedPaymentMethod]?.icon || <CreditCard />}
739
+ {paymentInfoMap[selectedPaymentMethod]?.icon || (
740
+ <CreditCard />
741
+ )}
709
742
  </Container>
710
743
  <Text>
711
744
  {isStripeLike(selectedPaymentMethod) && cardBrand
@@ -744,7 +777,7 @@ export default Payment
744
777
 
745
778
  You don't need to do anything for these — they're automatic:
746
779
 
747
- - **No double charges.** Once a customer has paid, the checkout only ever offers a *"Retry — finish placing my order"* button — even if the page is reloaded or the browser crashes mid-checkout.
780
+ - **No double charges.** Once a customer has paid, the checkout only ever offers a _"Retry — finish placing my order"_ button — even if the page is reloaded or the browser crashes mid-checkout.
748
781
  - **Always the right amount.** If the customer changes their cart during checkout, a fresh PayPal order is created at the new total.
749
782
  - **Clear messages.** Customers see friendly, actionable messages instead of technical errors, and a payment method you've disabled in Medusa Admin shows a polite "currently unavailable" note.
750
783
 
@@ -779,4 +812,4 @@ CVV Any 3 digits
779
812
 
780
813
  ## 📄 License
781
814
 
782
- MIT © [Easy Payment](https://www.npmjs.com/package/@easypayment/medusa-paypal-ui)
815
+ MIT © [Easy Payment](https://www.npmjs.com/package/@easypayment/medusa-paypal-ui)
package/dist/index.cjs CHANGED
@@ -1554,9 +1554,7 @@ var PAYPAL_PROVIDER_IDS = [
1554
1554
  ];
1555
1555
  function isPayPalProviderId(providerId) {
1556
1556
  if (!providerId) return false;
1557
- return PAYPAL_PROVIDER_IDS.includes(
1558
- providerId
1559
- );
1557
+ return PAYPAL_PROVIDER_IDS.includes(providerId);
1560
1558
  }
1561
1559
 
1562
1560
  // src/adapters/MedusaNextPayPalAdapter.tsx