@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 +16 -1
- package/README.md +85 -52
- package/dist/index.cjs +1 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +1 -3
- package/dist/index.mjs.map +1 -1
- package/dist/order.cjs +1 -3
- package/dist/order.cjs.map +1 -1
- package/dist/order.d.cts +3 -1
- package/dist/order.d.ts +3 -1
- package/dist/order.mjs +1 -3
- package/dist/order.mjs.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +6 -4
- package/src/hooks/usePayPalConfig.ts +0 -1
- package/src/hooks/usePayPalPaymentMethods.ts +0 -1
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
|
|
40
|
-
|
|
41
|
-
| 🔵 **PayPal Smart Buttons**
|
|
42
|
-
| 💳 **Advanced Card Fields**
|
|
43
|
-
| 🛠 **Admin-driven config**
|
|
44
|
-
| ⚡ **Built-in UX**
|
|
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")
|
|
164
|
-
|
|
165
|
-
if (typeof config?.
|
|
166
|
-
|
|
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
|
-
? {
|
|
249
|
+
? {
|
|
250
|
+
[paymentMethod.id]: {
|
|
251
|
+
...(paymentInfoMap[paymentMethod.id] || {}),
|
|
252
|
+
title: paypalTitle,
|
|
253
|
+
},
|
|
254
|
+
}
|
|
246
255
|
: {}),
|
|
247
256
|
...(paymentMethod.id === PAYPAL_CARD_PROVIDER_ID
|
|
248
|
-
? {
|
|
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
|
-
{
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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)
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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")
|
|
498
|
-
|
|
499
|
-
if (typeof config?.
|
|
500
|
-
|
|
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(
|
|
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={
|
|
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 ===
|
|
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 ||
|
|
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
|
|
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
|