@easypayment/medusa-paypal-ui 1.2.8 → 1.3.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/CHANGELOG.md +82 -0
- package/LICENSE +21 -1
- package/README.md +8 -2
- package/dist/index.cjs +605 -676
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +101 -28
- package/dist/index.d.ts +101 -28
- package/dist/index.mjs +601 -676
- package/dist/index.mjs.map +1 -1
- package/dist/order.cjs +11 -8
- package/dist/order.cjs.map +1 -1
- package/dist/order.d.cts +1 -1
- package/dist/order.d.ts +1 -1
- package/dist/order.mjs +10 -8
- package/dist/order.mjs.map +1 -1
- package/package.json +43 -4
- package/src/adapters/MedusaNextPayPalAdapter.tsx +48 -10
- package/src/client/config-cache.ts +96 -0
- package/src/client/http.ts +24 -5
- package/src/client/paypal.ts +28 -6
- package/src/components/PayPalAdvancedCard.tsx +88 -214
- package/src/components/PayPalPaymentSection.tsx +17 -6
- package/src/components/PayPalProvider.tsx +4 -1
- package/src/components/PayPalSmartButtons.tsx +92 -224
- package/src/components/internal/ui.tsx +221 -0
- package/src/hooks/usePayPalConfig.ts +43 -32
- package/src/hooks/usePayPalPaymentMethods.ts +59 -54
- package/src/index.ts +4 -0
- package/src/order.ts +16 -8
- package/src/utils/processing-overlay.ts +25 -21
|
@@ -22,11 +22,13 @@ import {
|
|
|
22
22
|
showProcessingOverlay,
|
|
23
23
|
hideProcessingOverlay,
|
|
24
24
|
} from "../utils/processing-overlay"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
import {
|
|
26
|
+
AnimationStyles,
|
|
27
|
+
ErrorBanner,
|
|
28
|
+
LoadingCard,
|
|
29
|
+
ProcessingOverlayView,
|
|
30
|
+
ShieldIcon,
|
|
31
|
+
} from "./internal/ui"
|
|
30
32
|
|
|
31
33
|
const cardStyle = {
|
|
32
34
|
input: {
|
|
@@ -75,11 +77,13 @@ const labelStyle: React.CSSProperties = {
|
|
|
75
77
|
|
|
76
78
|
function SubmitButton({
|
|
77
79
|
disabled,
|
|
80
|
+
busy,
|
|
78
81
|
label,
|
|
79
82
|
onSubmit,
|
|
80
83
|
onSubmitError,
|
|
81
84
|
}: {
|
|
82
85
|
disabled: boolean
|
|
86
|
+
busy: boolean
|
|
83
87
|
label: string
|
|
84
88
|
onSubmit: () => void
|
|
85
89
|
onSubmitError: (message: string) => void
|
|
@@ -91,7 +95,7 @@ function SubmitButton({
|
|
|
91
95
|
<button
|
|
92
96
|
type="button"
|
|
93
97
|
disabled={isDisabled}
|
|
94
|
-
aria-busy={
|
|
98
|
+
aria-busy={busy}
|
|
95
99
|
onClick={() => {
|
|
96
100
|
onSubmit()
|
|
97
101
|
// submit() returns a promise and its rejection is NOT routed to the
|
|
@@ -153,30 +157,49 @@ function SubmitButton({
|
|
|
153
157
|
export function PayPalAdvancedCard(props: {
|
|
154
158
|
baseUrl: string
|
|
155
159
|
publishableApiKey?: string
|
|
160
|
+
/** Forwarded to every API call — see `HttpOptions.credentials`. */
|
|
161
|
+
credentials?: RequestCredentials
|
|
156
162
|
cartId: string
|
|
157
163
|
config: PayPalConfig
|
|
158
|
-
onPaid?: (result: Record<string, unknown>) => void
|
|
164
|
+
onPaid?: (result: Record<string, unknown>) => void | Promise<void>
|
|
159
165
|
onError?: (message: string) => void
|
|
160
166
|
}) {
|
|
161
|
-
const {
|
|
167
|
+
const {
|
|
168
|
+
baseUrl,
|
|
169
|
+
publishableApiKey,
|
|
170
|
+
credentials,
|
|
171
|
+
cartId,
|
|
172
|
+
config,
|
|
173
|
+
onPaid,
|
|
174
|
+
onError,
|
|
175
|
+
} = props
|
|
162
176
|
|
|
163
177
|
const api = useMemo(
|
|
164
|
-
() => createPayPalStoreApi({ baseUrl, publishableApiKey }),
|
|
165
|
-
[baseUrl, publishableApiKey]
|
|
178
|
+
() => createPayPalStoreApi({ baseUrl, publishableApiKey, credentials }),
|
|
179
|
+
[baseUrl, publishableApiKey, credentials]
|
|
166
180
|
)
|
|
167
181
|
|
|
168
182
|
const [error, setError] = useState<string | null>(null)
|
|
169
183
|
const [submitting, setSubmitting] = useState(false)
|
|
170
184
|
const submittingRef = useRef(false)
|
|
171
185
|
const creatingOrderRef = useRef(false)
|
|
186
|
+
// True while the shared full-page DOM overlay is mounted, so the component
|
|
187
|
+
// doesn't also render its own copy on top of it.
|
|
188
|
+
const [domOverlayUp, setDomOverlayUp] = useState(false)
|
|
172
189
|
// Once a capture succeeded the buyer's money is taken: any later failure
|
|
173
190
|
// must retry ONLY the order finalization, never a fresh order/capture.
|
|
174
191
|
const capturedRef = useRef<Record<string, unknown> | null>(null)
|
|
175
192
|
const [completionPending, setCompletionPending] = useState(false)
|
|
176
193
|
const [{ isPending, isResolved, isRejected }] = usePayPalScriptReducer()
|
|
177
194
|
|
|
195
|
+
const showOverlay = React.useCallback(() => {
|
|
196
|
+
showProcessingOverlay()
|
|
197
|
+
setDomOverlayUp(true)
|
|
198
|
+
}, [])
|
|
199
|
+
|
|
178
200
|
const resetSubmitState = React.useCallback(() => {
|
|
179
201
|
hideProcessingOverlay()
|
|
202
|
+
setDomOverlayUp(false)
|
|
180
203
|
creatingOrderRef.current = false
|
|
181
204
|
submittingRef.current = false
|
|
182
205
|
setSubmitting(false)
|
|
@@ -191,6 +214,7 @@ export function PayPalAdvancedCard(props: {
|
|
|
191
214
|
creatingOrderRef.current = false
|
|
192
215
|
submittingRef.current = false
|
|
193
216
|
setSubmitting(false)
|
|
217
|
+
setDomOverlayUp(false)
|
|
194
218
|
}
|
|
195
219
|
}
|
|
196
220
|
window.addEventListener("pageshow", onPageShow)
|
|
@@ -214,14 +238,13 @@ export function PayPalAdvancedCard(props: {
|
|
|
214
238
|
const captured = capturedRef.current
|
|
215
239
|
if (!captured) return
|
|
216
240
|
setSubmitting(true)
|
|
217
|
-
|
|
241
|
+
showOverlay()
|
|
218
242
|
setError(null)
|
|
219
243
|
try {
|
|
220
|
-
const completeResult = await markPaymentComplete(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
)
|
|
244
|
+
const completeResult = await markPaymentComplete(baseUrl, cartId, {
|
|
245
|
+
publishableApiKey,
|
|
246
|
+
credentials,
|
|
247
|
+
})
|
|
225
248
|
// The order is finalized — the persisted "captured" flag has served its
|
|
226
249
|
// purpose. Clear it before the consumer callback (which may redirect).
|
|
227
250
|
clearCartCaptured(cartId)
|
|
@@ -236,33 +259,25 @@ export function PayPalAdvancedCard(props: {
|
|
|
236
259
|
setError(msg)
|
|
237
260
|
onError?.(msg)
|
|
238
261
|
}
|
|
239
|
-
}, [
|
|
262
|
+
}, [
|
|
263
|
+
baseUrl,
|
|
264
|
+
cartId,
|
|
265
|
+
publishableApiKey,
|
|
266
|
+
credentials,
|
|
267
|
+
onPaid,
|
|
268
|
+
onError,
|
|
269
|
+
showOverlay,
|
|
270
|
+
resetSubmitState,
|
|
271
|
+
])
|
|
240
272
|
|
|
241
273
|
if (!config.currency_supported) return null
|
|
242
274
|
|
|
243
275
|
if (isRejected) {
|
|
244
276
|
return (
|
|
245
|
-
<
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
alignItems: "flex-start",
|
|
250
|
-
gap: 8,
|
|
251
|
-
padding: "10px 14px",
|
|
252
|
-
background: "#fef2f2",
|
|
253
|
-
border: "1px solid #fecaca",
|
|
254
|
-
borderRadius: 8,
|
|
255
|
-
fontSize: 13,
|
|
256
|
-
color: "#b91c1c",
|
|
257
|
-
lineHeight: 1.5,
|
|
258
|
-
}}
|
|
259
|
-
>
|
|
260
|
-
<span style={{ flexShrink: 0, fontSize: 15 }}>⚠️</span>
|
|
261
|
-
<span>
|
|
262
|
-
PayPal failed to load. Please refresh the page or try a different
|
|
263
|
-
payment method.
|
|
264
|
-
</span>
|
|
265
|
-
</div>
|
|
277
|
+
<ErrorBanner>
|
|
278
|
+
PayPal failed to load. Please refresh the page or try a different
|
|
279
|
+
payment method.
|
|
280
|
+
</ErrorBanner>
|
|
266
281
|
)
|
|
267
282
|
}
|
|
268
283
|
|
|
@@ -287,36 +302,9 @@ export function PayPalAdvancedCard(props: {
|
|
|
287
302
|
|
|
288
303
|
if (isPending) {
|
|
289
304
|
return (
|
|
290
|
-
<div
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
style={{
|
|
294
|
-
display: "flex",
|
|
295
|
-
alignItems: "center",
|
|
296
|
-
justifyContent: "center",
|
|
297
|
-
gap: 10,
|
|
298
|
-
padding: "18px 16px",
|
|
299
|
-
background: "#f9fafb",
|
|
300
|
-
border: "1px solid #e5e7eb",
|
|
301
|
-
borderRadius: 8,
|
|
302
|
-
minHeight: 55,
|
|
303
|
-
}}
|
|
304
|
-
>
|
|
305
|
-
<style>{SPIN_STYLE}</style>
|
|
306
|
-
<div
|
|
307
|
-
style={{
|
|
308
|
-
width: 22,
|
|
309
|
-
height: 22,
|
|
310
|
-
borderRadius: "50%",
|
|
311
|
-
border: "2.5px solid #e5e7eb",
|
|
312
|
-
borderTopColor: "#0070ba",
|
|
313
|
-
animation: "_pp_spin .7s linear infinite",
|
|
314
|
-
flexShrink: 0,
|
|
315
|
-
}}
|
|
316
|
-
/>
|
|
317
|
-
<div style={{ fontSize: 13, fontWeight: 500, color: "#6b7280" }}>
|
|
318
|
-
Loading card fields…
|
|
319
|
-
</div>
|
|
305
|
+
<div>
|
|
306
|
+
<AnimationStyles />
|
|
307
|
+
<LoadingCard label="Loading card fields…" />
|
|
320
308
|
</div>
|
|
321
309
|
)
|
|
322
310
|
}
|
|
@@ -325,107 +313,10 @@ export function PayPalAdvancedCard(props: {
|
|
|
325
313
|
|
|
326
314
|
return (
|
|
327
315
|
<div style={{ position: "relative" }}>
|
|
328
|
-
<
|
|
316
|
+
<AnimationStyles />
|
|
329
317
|
|
|
330
|
-
{submitting && (
|
|
331
|
-
<
|
|
332
|
-
role="status"
|
|
333
|
-
aria-label="Processing payment"
|
|
334
|
-
style={{
|
|
335
|
-
position: "fixed",
|
|
336
|
-
inset: 0,
|
|
337
|
-
zIndex: 9999,
|
|
338
|
-
background: "rgba(255,255,255,0.96)",
|
|
339
|
-
display: "flex",
|
|
340
|
-
flexDirection: "column",
|
|
341
|
-
alignItems: "center",
|
|
342
|
-
justifyContent: "center",
|
|
343
|
-
gap: 20,
|
|
344
|
-
}}
|
|
345
|
-
>
|
|
346
|
-
<div style={{ position: "relative", width: 56, height: 56 }}>
|
|
347
|
-
<div
|
|
348
|
-
style={{
|
|
349
|
-
position: "absolute",
|
|
350
|
-
inset: 0,
|
|
351
|
-
borderRadius: "50%",
|
|
352
|
-
border: "3px solid #e5e7eb",
|
|
353
|
-
}}
|
|
354
|
-
/>
|
|
355
|
-
<div
|
|
356
|
-
style={{
|
|
357
|
-
position: "absolute",
|
|
358
|
-
inset: 0,
|
|
359
|
-
borderRadius: "50%",
|
|
360
|
-
border: "3px solid transparent",
|
|
361
|
-
borderTopColor: "#2563eb",
|
|
362
|
-
animation: "_pp_spin .8s linear infinite",
|
|
363
|
-
}}
|
|
364
|
-
/>
|
|
365
|
-
<svg
|
|
366
|
-
viewBox="0 0 24 24"
|
|
367
|
-
fill="none"
|
|
368
|
-
stroke="#2563eb"
|
|
369
|
-
strokeWidth="1.8"
|
|
370
|
-
strokeLinecap="round"
|
|
371
|
-
strokeLinejoin="round"
|
|
372
|
-
style={{
|
|
373
|
-
position: "absolute",
|
|
374
|
-
top: "50%",
|
|
375
|
-
left: "50%",
|
|
376
|
-
transform: "translate(-50%, -50%)",
|
|
377
|
-
width: 24,
|
|
378
|
-
height: 24,
|
|
379
|
-
}}
|
|
380
|
-
>
|
|
381
|
-
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
|
|
382
|
-
</svg>
|
|
383
|
-
</div>
|
|
384
|
-
<div style={{ textAlign: "center" }}>
|
|
385
|
-
<div
|
|
386
|
-
style={{
|
|
387
|
-
fontSize: 18,
|
|
388
|
-
fontWeight: 600,
|
|
389
|
-
color: "#111827",
|
|
390
|
-
letterSpacing: "-0.01em",
|
|
391
|
-
}}
|
|
392
|
-
>
|
|
393
|
-
Processing your payment
|
|
394
|
-
</div>
|
|
395
|
-
<div
|
|
396
|
-
style={{
|
|
397
|
-
fontSize: 14,
|
|
398
|
-
color: "#6b7280",
|
|
399
|
-
marginTop: 6,
|
|
400
|
-
lineHeight: 1.5,
|
|
401
|
-
}}
|
|
402
|
-
>
|
|
403
|
-
This may take a few moments. Please do not close
|
|
404
|
-
<br />
|
|
405
|
-
or refresh this page.
|
|
406
|
-
</div>
|
|
407
|
-
</div>
|
|
408
|
-
<div
|
|
409
|
-
style={{
|
|
410
|
-
width: 200,
|
|
411
|
-
height: 3,
|
|
412
|
-
background: "#e5e7eb",
|
|
413
|
-
borderRadius: 3,
|
|
414
|
-
overflow: "hidden",
|
|
415
|
-
marginTop: 4,
|
|
416
|
-
}}
|
|
417
|
-
>
|
|
418
|
-
<div
|
|
419
|
-
style={{
|
|
420
|
-
width: "40%",
|
|
421
|
-
height: "100%",
|
|
422
|
-
background: "linear-gradient(90deg, #2563eb, #1d4ed8)",
|
|
423
|
-
borderRadius: 3,
|
|
424
|
-
animation: "_pp_progress 1.5s ease-in-out infinite",
|
|
425
|
-
}}
|
|
426
|
-
/>
|
|
427
|
-
</div>
|
|
428
|
-
</div>
|
|
318
|
+
{submitting && !domOverlayUp && (
|
|
319
|
+
<ProcessingOverlayView accent="#2563eb" />
|
|
429
320
|
)}
|
|
430
321
|
|
|
431
322
|
<PayPalCardFieldsProvider
|
|
@@ -454,7 +345,7 @@ export function PayPalAdvancedCard(props: {
|
|
|
454
345
|
onApprove={async (data: { orderID?: string }) => {
|
|
455
346
|
try {
|
|
456
347
|
setError(null)
|
|
457
|
-
|
|
348
|
+
showOverlay()
|
|
458
349
|
const orderId = String(data?.orderID || "")
|
|
459
350
|
if (!orderId)
|
|
460
351
|
throw new Error(
|
|
@@ -478,20 +369,14 @@ export function PayPalAdvancedCard(props: {
|
|
|
478
369
|
await finalizeCapturedPayment()
|
|
479
370
|
}}
|
|
480
371
|
onCancel={() => {
|
|
481
|
-
|
|
482
|
-
creatingOrderRef.current = false
|
|
483
|
-
submittingRef.current = false
|
|
484
|
-
setSubmitting(false)
|
|
372
|
+
resetSubmitState()
|
|
485
373
|
}}
|
|
486
374
|
onError={(e: Error | { message?: string }) => {
|
|
487
375
|
const msg =
|
|
488
376
|
e instanceof Error ? e.message : e?.message || "CardFields error"
|
|
489
377
|
setError(msg)
|
|
490
378
|
onError?.(msg)
|
|
491
|
-
|
|
492
|
-
creatingOrderRef.current = false
|
|
493
|
-
submittingRef.current = false
|
|
494
|
-
setSubmitting(false)
|
|
379
|
+
resetSubmitState()
|
|
495
380
|
}}
|
|
496
381
|
>
|
|
497
382
|
<div
|
|
@@ -517,7 +402,10 @@ export function PayPalAdvancedCard(props: {
|
|
|
517
402
|
lineHeight: 1.55,
|
|
518
403
|
}}
|
|
519
404
|
>
|
|
520
|
-
<span
|
|
405
|
+
<span
|
|
406
|
+
aria-hidden="true"
|
|
407
|
+
style={{ fontSize: 15, flexShrink: 0, marginTop: 1 }}
|
|
408
|
+
>
|
|
521
409
|
🧪
|
|
522
410
|
</span>
|
|
523
411
|
<span>
|
|
@@ -533,7 +421,12 @@ export function PayPalAdvancedCard(props: {
|
|
|
533
421
|
)}
|
|
534
422
|
|
|
535
423
|
<div>
|
|
536
|
-
|
|
424
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control --
|
|
425
|
+
the PayPal hosted field below is a cross-origin iframe; it cannot be
|
|
426
|
+
associated via htmlFor, and PayPal provides the accessible name inside */}
|
|
427
|
+
<label style={labelStyle} id="_pp-label-card-number">
|
|
428
|
+
Card number
|
|
429
|
+
</label>
|
|
537
430
|
<PayPalNumberField />
|
|
538
431
|
</div>
|
|
539
432
|
|
|
@@ -545,11 +438,21 @@ export function PayPalAdvancedCard(props: {
|
|
|
545
438
|
}}
|
|
546
439
|
>
|
|
547
440
|
<div>
|
|
548
|
-
|
|
441
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control --
|
|
442
|
+
the PayPal hosted field below is a cross-origin iframe; it cannot be
|
|
443
|
+
associated via htmlFor, and PayPal provides the accessible name inside */}
|
|
444
|
+
<label style={labelStyle} id="_pp-label-expiry">
|
|
445
|
+
Expiration date
|
|
446
|
+
</label>
|
|
549
447
|
<PayPalExpiryField />
|
|
550
448
|
</div>
|
|
551
449
|
<div>
|
|
552
|
-
|
|
450
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control --
|
|
451
|
+
the PayPal hosted field below is a cross-origin iframe; it cannot be
|
|
452
|
+
associated via htmlFor, and PayPal provides the accessible name inside */}
|
|
453
|
+
<label style={labelStyle} id="_pp-label-cvv">
|
|
454
|
+
Security code
|
|
455
|
+
</label>
|
|
553
456
|
<PayPalCVVField />
|
|
554
457
|
</div>
|
|
555
458
|
</div>
|
|
@@ -563,18 +466,7 @@ export function PayPalAdvancedCard(props: {
|
|
|
563
466
|
color: "#6b7280",
|
|
564
467
|
}}
|
|
565
468
|
>
|
|
566
|
-
<
|
|
567
|
-
width="14"
|
|
568
|
-
height="14"
|
|
569
|
-
viewBox="0 0 24 24"
|
|
570
|
-
fill="none"
|
|
571
|
-
stroke="#10b981"
|
|
572
|
-
strokeWidth="2.5"
|
|
573
|
-
strokeLinecap="round"
|
|
574
|
-
strokeLinejoin="round"
|
|
575
|
-
>
|
|
576
|
-
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
|
|
577
|
-
</svg>
|
|
469
|
+
<ShieldIcon color="#10b981" size={14} />
|
|
578
470
|
<span>Your payment is secured with 256-bit SSL encryption</span>
|
|
579
471
|
</div>
|
|
580
472
|
|
|
@@ -603,13 +495,14 @@ export function PayPalAdvancedCard(props: {
|
|
|
603
495
|
) : (
|
|
604
496
|
<SubmitButton
|
|
605
497
|
disabled={submitting}
|
|
498
|
+
busy={submitting}
|
|
606
499
|
label={submitting ? "Processing…" : "Pay by Card"}
|
|
607
500
|
onSubmit={() => {
|
|
608
501
|
if (submittingRef.current) return
|
|
609
502
|
submittingRef.current = true
|
|
610
503
|
setError(null)
|
|
611
504
|
setSubmitting(true)
|
|
612
|
-
|
|
505
|
+
showOverlay()
|
|
613
506
|
}}
|
|
614
507
|
onSubmitError={(msg) => {
|
|
615
508
|
resetSubmitState()
|
|
@@ -619,26 +512,7 @@ export function PayPalAdvancedCard(props: {
|
|
|
619
512
|
/>
|
|
620
513
|
)}
|
|
621
514
|
|
|
622
|
-
{error &&
|
|
623
|
-
<div
|
|
624
|
-
role="alert"
|
|
625
|
-
style={{
|
|
626
|
-
display: "flex",
|
|
627
|
-
alignItems: "flex-start",
|
|
628
|
-
gap: 10,
|
|
629
|
-
padding: "10px 14px",
|
|
630
|
-
background: "#fef2f2",
|
|
631
|
-
border: "1px solid #fecaca",
|
|
632
|
-
borderRadius: 8,
|
|
633
|
-
fontSize: 13,
|
|
634
|
-
color: "#b91c1c",
|
|
635
|
-
lineHeight: 1.5,
|
|
636
|
-
}}
|
|
637
|
-
>
|
|
638
|
-
<span style={{ flexShrink: 0, marginTop: 1 }}>⚠️</span>
|
|
639
|
-
<span>{error}</span>
|
|
640
|
-
</div>
|
|
641
|
-
)}
|
|
515
|
+
{error && <ErrorBanner>{error}</ErrorBanner>}
|
|
642
516
|
</div>
|
|
643
517
|
</PayPalCardFieldsProvider>
|
|
644
518
|
</div>
|
|
@@ -16,13 +16,12 @@ export {
|
|
|
16
16
|
isPayPalProviderId,
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
import { AnimationStyles } from "./internal/ui"
|
|
20
20
|
|
|
21
21
|
function SessionInitCard() {
|
|
22
22
|
return (
|
|
23
23
|
<div
|
|
24
24
|
role="status"
|
|
25
|
-
aria-label="Setting up payment"
|
|
26
25
|
style={{
|
|
27
26
|
display: "flex",
|
|
28
27
|
alignItems: "center",
|
|
@@ -34,15 +33,16 @@ function SessionInitCard() {
|
|
|
34
33
|
borderRadius: 10,
|
|
35
34
|
}}
|
|
36
35
|
>
|
|
37
|
-
<
|
|
36
|
+
<AnimationStyles />
|
|
38
37
|
<div
|
|
38
|
+
className="_pp-spin-sm"
|
|
39
|
+
aria-hidden="true"
|
|
39
40
|
style={{
|
|
40
41
|
width: 20,
|
|
41
42
|
height: 20,
|
|
42
43
|
borderRadius: "50%",
|
|
43
44
|
border: "2.5px solid #e5e7eb",
|
|
44
45
|
borderTopColor: "#0070ba",
|
|
45
|
-
animation: "_pp_section_spin .7s linear infinite",
|
|
46
46
|
flexShrink: 0,
|
|
47
47
|
}}
|
|
48
48
|
/>
|
|
@@ -58,10 +58,17 @@ export type PayPalPaymentSectionProps = {
|
|
|
58
58
|
selectedProviderId: string | null | undefined
|
|
59
59
|
baseUrl: string
|
|
60
60
|
publishableApiKey?: string
|
|
61
|
+
/** Forwarded to every API call — see `HttpOptions.credentials`. */
|
|
62
|
+
credentials?: RequestCredentials
|
|
63
|
+
/**
|
|
64
|
+
* The cart's currency code (e.g. "usd"). Recommended: busts the config
|
|
65
|
+
* cache when the buyer switches region/currency on the same cart.
|
|
66
|
+
*/
|
|
67
|
+
currencyCode?: string
|
|
61
68
|
sessionLoading?: boolean
|
|
62
|
-
onSuccess?: (cartId: string) => void
|
|
69
|
+
onSuccess?: (cartId: string) => void | Promise<void>
|
|
63
70
|
onError?: (message: string) => void
|
|
64
|
-
onPaid?: (result: unknown) => void
|
|
71
|
+
onPaid?: (result: unknown) => void | Promise<void>
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
/**
|
|
@@ -77,6 +84,8 @@ export function PayPalPaymentSection({
|
|
|
77
84
|
selectedProviderId,
|
|
78
85
|
baseUrl,
|
|
79
86
|
publishableApiKey,
|
|
87
|
+
credentials,
|
|
88
|
+
currencyCode,
|
|
80
89
|
sessionLoading = false,
|
|
81
90
|
onSuccess,
|
|
82
91
|
onError,
|
|
@@ -91,6 +100,8 @@ export function PayPalPaymentSection({
|
|
|
91
100
|
selectedProviderId={selectedProviderId}
|
|
92
101
|
baseUrl={baseUrl}
|
|
93
102
|
publishableApiKey={publishableApiKey}
|
|
103
|
+
credentials={credentials}
|
|
104
|
+
currencyCode={currencyCode}
|
|
94
105
|
onSuccess={onSuccess}
|
|
95
106
|
onError={onError}
|
|
96
107
|
onPaid={onPaid}
|
|
@@ -5,7 +5,10 @@ import { PayPalScriptProvider } from "@paypal/react-paypal-js"
|
|
|
5
5
|
import type { ReactPayPalScriptOptions } from "@paypal/react-paypal-js"
|
|
6
6
|
import type { PayPalConfig } from "../client/types"
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// "MBJ_Medusa_SI" is the Medusa-platform BN code PayPal assigned on 2026-10-06
|
|
9
|
+
// (replacing "MBJTechnolabs_SI_SPB"). Must stay in sync with the backend's
|
|
10
|
+
// PAYPAL_PARTNER_ATTRIBUTION_ID so SDK and REST attribution match.
|
|
11
|
+
const BN_CODE = "MBJ_Medusa_SI"
|
|
9
12
|
|
|
10
13
|
export function PayPalProvider(props: {
|
|
11
14
|
config: PayPalConfig
|