@easypayment/medusa-paypal-ui 1.0.37 → 1.0.39
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.cjs +296 -193
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +296 -193
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/PayPalAdvancedCard.tsx +150 -91
- package/src/components/PayPalSmartButtons.tsx +45 -3
|
@@ -15,52 +15,53 @@ import type { PayPalConfig } from "../client/types"
|
|
|
15
15
|
// ─── Spinner keyframes ────────────────────────────────────────────────────────
|
|
16
16
|
const SPIN_STYLE = `@keyframes _pp_spin { to { transform: rotate(360deg) } }`
|
|
17
17
|
|
|
18
|
+
// ─── PayPal hosted-field iframe styles ───────────────────────────────────────
|
|
18
19
|
const cardStyle = {
|
|
19
20
|
input: {
|
|
20
|
-
"font-size": "
|
|
21
|
-
"font-family": "
|
|
21
|
+
"font-size": "15px",
|
|
22
|
+
"font-family": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
22
23
|
"font-weight": "400",
|
|
23
|
-
color: "#
|
|
24
|
+
color: "#111827",
|
|
24
25
|
padding: "0 14px",
|
|
25
|
-
height: "
|
|
26
|
-
"border-radius": "
|
|
27
|
-
border: "1px solid #
|
|
26
|
+
height: "42px",
|
|
27
|
+
"border-radius": "8px",
|
|
28
|
+
border: "1px solid #d1d5db",
|
|
28
29
|
background: "#ffffff",
|
|
29
30
|
"box-shadow": "none",
|
|
31
|
+
transition: "border-color 0.15s ease",
|
|
30
32
|
},
|
|
31
|
-
|
|
32
33
|
"::placeholder": {
|
|
33
|
-
color: "#
|
|
34
|
+
color: "#9ca3af",
|
|
34
35
|
},
|
|
35
|
-
|
|
36
36
|
".invalid": {
|
|
37
|
-
color: "#
|
|
38
|
-
border: "1px solid #
|
|
37
|
+
color: "#dc2626",
|
|
38
|
+
border: "1px solid #fca5a5",
|
|
39
|
+
background: "#fff7f7",
|
|
39
40
|
"box-shadow": "none",
|
|
40
41
|
},
|
|
41
|
-
|
|
42
42
|
".valid": {
|
|
43
|
-
color: "#
|
|
44
|
-
border: "1px solid #
|
|
43
|
+
color: "#111827",
|
|
44
|
+
border: "1px solid #d1d5db",
|
|
45
45
|
"box-shadow": "none",
|
|
46
46
|
},
|
|
47
|
-
|
|
48
47
|
"input:focus": {
|
|
49
48
|
outline: "none",
|
|
50
|
-
border: "1px solid #
|
|
51
|
-
"box-shadow": "
|
|
49
|
+
border: "1px solid #2563eb",
|
|
50
|
+
"box-shadow": "0 0 0 3px rgba(37,99,235,0.12)",
|
|
52
51
|
},
|
|
53
52
|
}
|
|
54
53
|
|
|
54
|
+
// ─── Label style ─────────────────────────────────────────────────────────────
|
|
55
55
|
const labelStyle: React.CSSProperties = {
|
|
56
56
|
display: "block",
|
|
57
|
-
fontSize:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
fontSize: 13,
|
|
58
|
+
fontWeight: 500,
|
|
59
|
+
color: "#374151",
|
|
60
|
+
marginBottom: 6,
|
|
61
|
+
letterSpacing: "0.01em",
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// ─── Submit button ────────────────────────────────────────────────────────────
|
|
64
65
|
function SubmitButton({
|
|
65
66
|
disabled,
|
|
66
67
|
label,
|
|
@@ -71,29 +72,51 @@ function SubmitButton({
|
|
|
71
72
|
onSubmit: () => void
|
|
72
73
|
}) {
|
|
73
74
|
const { cardFieldsForm } = usePayPalCardFields()
|
|
75
|
+
const isDisabled = disabled || !cardFieldsForm
|
|
74
76
|
|
|
75
77
|
return (
|
|
76
78
|
<button
|
|
77
79
|
type="button"
|
|
78
|
-
disabled={
|
|
80
|
+
disabled={isDisabled}
|
|
79
81
|
onClick={() => {
|
|
80
82
|
onSubmit()
|
|
81
83
|
cardFieldsForm?.submit()
|
|
82
84
|
}}
|
|
83
85
|
style={{
|
|
84
86
|
width: "100%",
|
|
85
|
-
height:
|
|
86
|
-
padding: "0
|
|
87
|
-
borderRadius:
|
|
88
|
-
border: "
|
|
89
|
-
background:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
height: 48,
|
|
88
|
+
padding: "0 20px",
|
|
89
|
+
borderRadius: 8,
|
|
90
|
+
border: "none",
|
|
91
|
+
background: isDisabled
|
|
92
|
+
? "#e5e7eb"
|
|
93
|
+
: "linear-gradient(180deg, #2563eb 0%, #1d4ed8 100%)",
|
|
94
|
+
color: isDisabled ? "#9ca3af" : "#ffffff",
|
|
95
|
+
fontSize: 15,
|
|
96
|
+
fontWeight: 600,
|
|
97
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
98
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
99
|
+
letterSpacing: "0.01em",
|
|
100
|
+
boxShadow: isDisabled
|
|
101
|
+
? "none"
|
|
102
|
+
: "0 1px 3px rgba(37,99,235,0.3), 0 1px 2px rgba(0,0,0,0.06)",
|
|
103
|
+
transition: "all 0.15s ease",
|
|
104
|
+
}}
|
|
105
|
+
onMouseEnter={(e) => {
|
|
106
|
+
if (!isDisabled) {
|
|
107
|
+
;(e.target as HTMLButtonElement).style.background =
|
|
108
|
+
"linear-gradient(180deg, #1d4ed8 0%, #1e40af 100%)"
|
|
109
|
+
;(e.target as HTMLButtonElement).style.boxShadow =
|
|
110
|
+
"0 4px 6px rgba(37,99,235,0.35), 0 2px 4px rgba(0,0,0,0.06)"
|
|
111
|
+
}
|
|
112
|
+
}}
|
|
113
|
+
onMouseLeave={(e) => {
|
|
114
|
+
if (!isDisabled) {
|
|
115
|
+
;(e.target as HTMLButtonElement).style.background =
|
|
116
|
+
"linear-gradient(180deg, #2563eb 0%, #1d4ed8 100%)"
|
|
117
|
+
;(e.target as HTMLButtonElement).style.boxShadow =
|
|
118
|
+
"0 1px 3px rgba(37,99,235,0.3), 0 1px 2px rgba(0,0,0,0.06)"
|
|
119
|
+
}
|
|
97
120
|
}}
|
|
98
121
|
>
|
|
99
122
|
{label}
|
|
@@ -101,6 +124,7 @@ function SubmitButton({
|
|
|
101
124
|
)
|
|
102
125
|
}
|
|
103
126
|
|
|
127
|
+
// ─── Main component ───────────────────────────────────────────────────────────
|
|
104
128
|
export function PayPalAdvancedCard(props: {
|
|
105
129
|
baseUrl: string
|
|
106
130
|
publishableApiKey?: string
|
|
@@ -125,14 +149,15 @@ export function PayPalAdvancedCard(props: {
|
|
|
125
149
|
return (
|
|
126
150
|
<div
|
|
127
151
|
style={{
|
|
128
|
-
padding:
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
padding: "12px 16px",
|
|
153
|
+
background: "#fefce8",
|
|
154
|
+
border: "1px solid #fde68a",
|
|
155
|
+
borderRadius: 8,
|
|
131
156
|
fontSize: 13,
|
|
132
|
-
color: "#
|
|
157
|
+
color: "#92400e",
|
|
133
158
|
}}
|
|
134
159
|
>
|
|
135
|
-
|
|
160
|
+
Card fields unavailable — missing client token from backend.
|
|
136
161
|
</div>
|
|
137
162
|
)
|
|
138
163
|
}
|
|
@@ -140,49 +165,47 @@ export function PayPalAdvancedCard(props: {
|
|
|
140
165
|
const isSandbox = config.environment === "sandbox"
|
|
141
166
|
|
|
142
167
|
return (
|
|
143
|
-
// ── ADDED: wrapper div with position relative so overlay works ────────────
|
|
144
168
|
<div style={{ position: "relative" }}>
|
|
145
169
|
<style>{SPIN_STYLE}</style>
|
|
146
170
|
|
|
147
|
-
{/* ──
|
|
171
|
+
{/* ── Processing overlay ──────────────────────────────────────────────── */}
|
|
148
172
|
{submitting && (
|
|
149
173
|
<div
|
|
150
174
|
style={{
|
|
151
175
|
position: "absolute",
|
|
152
176
|
inset: 0,
|
|
153
177
|
zIndex: 20,
|
|
154
|
-
background: "rgba(255,255,255,0.
|
|
155
|
-
borderRadius:
|
|
178
|
+
background: "rgba(255,255,255,0.92)",
|
|
179
|
+
borderRadius: 12,
|
|
156
180
|
display: "flex",
|
|
157
181
|
flexDirection: "column",
|
|
158
182
|
alignItems: "center",
|
|
159
183
|
justifyContent: "center",
|
|
160
|
-
gap:
|
|
161
|
-
minHeight:
|
|
184
|
+
gap: 14,
|
|
185
|
+
minHeight: 180,
|
|
162
186
|
}}
|
|
163
187
|
>
|
|
164
188
|
<div
|
|
165
189
|
style={{
|
|
166
|
-
width:
|
|
167
|
-
height:
|
|
190
|
+
width: 36,
|
|
191
|
+
height: 36,
|
|
168
192
|
borderRadius: "50%",
|
|
169
|
-
border: "
|
|
170
|
-
borderTopColor: "#
|
|
171
|
-
animation: "_pp_spin .
|
|
193
|
+
border: "3px solid #dbeafe",
|
|
194
|
+
borderTopColor: "#2563eb",
|
|
195
|
+
animation: "_pp_spin .75s linear infinite",
|
|
172
196
|
}}
|
|
173
197
|
/>
|
|
174
198
|
<div style={{ textAlign: "center" }}>
|
|
175
|
-
<div style={{ fontSize:
|
|
199
|
+
<div style={{ fontSize: 15, fontWeight: 600, color: "#111827" }}>
|
|
176
200
|
Processing your payment…
|
|
177
201
|
</div>
|
|
178
|
-
<div style={{ fontSize:
|
|
202
|
+
<div style={{ fontSize: 13, color: "#6b7280", marginTop: 4 }}>
|
|
179
203
|
Please do not close or refresh this page
|
|
180
204
|
</div>
|
|
181
205
|
</div>
|
|
182
206
|
</div>
|
|
183
207
|
)}
|
|
184
208
|
|
|
185
|
-
{/* ── UNCHANGED: PayPalCardFieldsProvider and all its children are exactly as before ── */}
|
|
186
209
|
<PayPalCardFieldsProvider
|
|
187
210
|
style={cardStyle}
|
|
188
211
|
createOrder={async () => {
|
|
@@ -193,10 +216,8 @@ export function PayPalAdvancedCard(props: {
|
|
|
193
216
|
onApprove={async (data: { orderID?: string }) => {
|
|
194
217
|
try {
|
|
195
218
|
setError(null)
|
|
196
|
-
|
|
197
219
|
const orderId = String(data?.orderID || "")
|
|
198
220
|
const result = await api.captureOrder(cartId, orderId)
|
|
199
|
-
|
|
200
221
|
onPaid?.(result)
|
|
201
222
|
} catch (e: unknown) {
|
|
202
223
|
const msg = e instanceof Error ? e.message : "Card payment failed"
|
|
@@ -209,9 +230,7 @@ export function PayPalAdvancedCard(props: {
|
|
|
209
230
|
setSubmitting(false)
|
|
210
231
|
}}
|
|
211
232
|
onError={(e: Error | { message?: string }) => {
|
|
212
|
-
const msg =
|
|
213
|
-
e instanceof Error ? e.message : e?.message || "CardFields error"
|
|
214
|
-
|
|
233
|
+
const msg = e instanceof Error ? e.message : e?.message || "CardFields error"
|
|
215
234
|
setError(msg)
|
|
216
235
|
onError?.(msg)
|
|
217
236
|
setSubmitting(false)
|
|
@@ -219,80 +238,120 @@ export function PayPalAdvancedCard(props: {
|
|
|
219
238
|
>
|
|
220
239
|
<div
|
|
221
240
|
style={{
|
|
222
|
-
display: "
|
|
223
|
-
|
|
241
|
+
display: "flex",
|
|
242
|
+
flexDirection: "column",
|
|
243
|
+
gap: 16,
|
|
224
244
|
width: "100%",
|
|
225
|
-
maxWidth: 516,
|
|
226
245
|
}}
|
|
227
246
|
>
|
|
247
|
+
{/* ── Sandbox notice ──────────────────────────────────────────────── */}
|
|
228
248
|
{isSandbox && (
|
|
229
249
|
<div
|
|
230
250
|
style={{
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
251
|
+
display: "flex",
|
|
252
|
+
alignItems: "flex-start",
|
|
253
|
+
gap: 10,
|
|
254
|
+
padding: "10px 14px",
|
|
255
|
+
background: "#eff6ff",
|
|
256
|
+
border: "1px solid #bfdbfe",
|
|
257
|
+
borderRadius: 8,
|
|
258
|
+
fontSize: 13,
|
|
259
|
+
color: "#1e40af",
|
|
260
|
+
lineHeight: 1.55,
|
|
235
261
|
}}
|
|
236
262
|
>
|
|
237
|
-
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
263
|
+
<span style={{ fontSize: 15, flexShrink: 0, marginTop: 1 }}>🧪</span>
|
|
264
|
+
<span>
|
|
265
|
+
Sandbox mode — use test card{" "}
|
|
266
|
+
<strong style={{ fontFamily: "monospace", letterSpacing: "0.05em" }}>
|
|
267
|
+
4111 1111 1111 1111
|
|
268
|
+
</strong>{" "}
|
|
269
|
+
with any future date and any CVV.
|
|
270
|
+
</span>
|
|
241
271
|
</div>
|
|
242
272
|
)}
|
|
243
273
|
|
|
244
|
-
|
|
274
|
+
{/* ── Card number ─────────────────────────────────────────────────── */}
|
|
275
|
+
<div>
|
|
245
276
|
<label style={labelStyle}>Card number</label>
|
|
246
277
|
<PayPalNumberField />
|
|
247
278
|
</div>
|
|
248
279
|
|
|
280
|
+
{/* ── Expiry + CVV ────────────────────────────────────────────────── */}
|
|
249
281
|
<div
|
|
250
282
|
style={{
|
|
251
283
|
display: "grid",
|
|
252
|
-
gridTemplateColumns: "
|
|
253
|
-
|
|
254
|
-
alignItems: "start",
|
|
284
|
+
gridTemplateColumns: "1fr 1fr",
|
|
285
|
+
gap: 16,
|
|
255
286
|
}}
|
|
256
287
|
>
|
|
257
288
|
<div>
|
|
258
289
|
<label style={labelStyle}>Expiration date</label>
|
|
259
290
|
<PayPalExpiryField />
|
|
260
291
|
</div>
|
|
261
|
-
|
|
262
292
|
<div>
|
|
263
293
|
<label style={labelStyle}>Security code</label>
|
|
264
294
|
<PayPalCVVField />
|
|
265
295
|
</div>
|
|
266
296
|
</div>
|
|
267
297
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
298
|
+
{/* ── Security badge ──────────────────────────────────────────────── */}
|
|
299
|
+
<div
|
|
300
|
+
style={{
|
|
301
|
+
display: "flex",
|
|
302
|
+
alignItems: "center",
|
|
303
|
+
gap: 6,
|
|
304
|
+
fontSize: 12,
|
|
305
|
+
color: "#6b7280",
|
|
306
|
+
}}
|
|
307
|
+
>
|
|
308
|
+
<svg
|
|
309
|
+
width="14"
|
|
310
|
+
height="14"
|
|
311
|
+
viewBox="0 0 24 24"
|
|
312
|
+
fill="none"
|
|
313
|
+
stroke="#10b981"
|
|
314
|
+
strokeWidth="2.5"
|
|
315
|
+
strokeLinecap="round"
|
|
316
|
+
strokeLinejoin="round"
|
|
317
|
+
>
|
|
318
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
|
|
319
|
+
</svg>
|
|
320
|
+
<span>Your payment is secured with 256-bit SSL encryption</span>
|
|
277
321
|
</div>
|
|
278
322
|
|
|
279
|
-
{/* ──
|
|
280
|
-
|
|
323
|
+
{/* ── Submit button ────────────────────────────────────────────────── */}
|
|
324
|
+
<SubmitButton
|
|
325
|
+
disabled={submitting}
|
|
326
|
+
label={submitting ? "Processing…" : "Pay by Card"}
|
|
327
|
+
onSubmit={() => {
|
|
328
|
+
setError(null)
|
|
329
|
+
setSubmitting(true)
|
|
330
|
+
}}
|
|
331
|
+
/>
|
|
332
|
+
|
|
333
|
+
{/* ── Error box ───────────────────────────────────────────────────── */}
|
|
334
|
+
{error && (
|
|
281
335
|
<div
|
|
282
336
|
style={{
|
|
283
|
-
|
|
337
|
+
display: "flex",
|
|
338
|
+
alignItems: "flex-start",
|
|
339
|
+
gap: 10,
|
|
340
|
+
padding: "10px 14px",
|
|
284
341
|
background: "#fef2f2",
|
|
285
342
|
border: "1px solid #fecaca",
|
|
286
|
-
borderRadius:
|
|
343
|
+
borderRadius: 8,
|
|
287
344
|
fontSize: 13,
|
|
288
345
|
color: "#b91c1c",
|
|
346
|
+
lineHeight: 1.5,
|
|
289
347
|
}}
|
|
290
348
|
>
|
|
291
|
-
{
|
|
349
|
+
<span style={{ flexShrink: 0, marginTop: 1 }}>⚠️</span>
|
|
350
|
+
<span>{error}</span>
|
|
292
351
|
</div>
|
|
293
|
-
)
|
|
352
|
+
)}
|
|
294
353
|
</div>
|
|
295
354
|
</PayPalCardFieldsProvider>
|
|
296
355
|
</div>
|
|
297
356
|
)
|
|
298
|
-
}
|
|
357
|
+
}
|
|
@@ -79,6 +79,43 @@ export function PayPalSmartButtons(props: {
|
|
|
79
79
|
</div>
|
|
80
80
|
)}
|
|
81
81
|
|
|
82
|
+
{/* ── Sandbox notice ─────────────────────────────────────────────────── */}
|
|
83
|
+
{config.environment === "sandbox" && (
|
|
84
|
+
<div
|
|
85
|
+
style={{
|
|
86
|
+
display: "flex",
|
|
87
|
+
alignItems: "flex-start",
|
|
88
|
+
gap: 8,
|
|
89
|
+
padding: "10px 14px",
|
|
90
|
+
background: "#eff6ff",
|
|
91
|
+
border: "1px solid #bfdbfe",
|
|
92
|
+
borderRadius: 8,
|
|
93
|
+
fontSize: 13,
|
|
94
|
+
color: "#1e40af",
|
|
95
|
+
lineHeight: 1.5,
|
|
96
|
+
marginBottom: 10,
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
<svg
|
|
100
|
+
width="16"
|
|
101
|
+
height="16"
|
|
102
|
+
viewBox="0 0 24 24"
|
|
103
|
+
fill="none"
|
|
104
|
+
stroke="#1e40af"
|
|
105
|
+
strokeWidth="2"
|
|
106
|
+
strokeLinecap="round"
|
|
107
|
+
style={{ flexShrink: 0, marginTop: 1 }}
|
|
108
|
+
>
|
|
109
|
+
<circle cx="12" cy="12" r="10" />
|
|
110
|
+
<path d="M12 8v4M12 16h.01" />
|
|
111
|
+
</svg>
|
|
112
|
+
<span>
|
|
113
|
+
Sandbox mode — you will not be charged. Use your PayPal sandbox
|
|
114
|
+
account to complete the payment.
|
|
115
|
+
</span>
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
|
|
82
119
|
<PayPalButtons
|
|
83
120
|
style={{
|
|
84
121
|
layout: "vertical",
|
|
@@ -122,15 +159,20 @@ export function PayPalSmartButtons(props: {
|
|
|
122
159
|
<div
|
|
123
160
|
style={{
|
|
124
161
|
marginTop: 10,
|
|
125
|
-
|
|
162
|
+
display: "flex",
|
|
163
|
+
alignItems: "flex-start",
|
|
164
|
+
gap: 8,
|
|
165
|
+
padding: "10px 14px",
|
|
126
166
|
background: "#fef2f2",
|
|
127
167
|
border: "1px solid #fecaca",
|
|
128
|
-
borderRadius:
|
|
168
|
+
borderRadius: 8,
|
|
129
169
|
fontSize: 13,
|
|
130
170
|
color: "#b91c1c",
|
|
171
|
+
lineHeight: 1.5,
|
|
131
172
|
}}
|
|
132
173
|
>
|
|
133
|
-
{
|
|
174
|
+
<span style={{ flexShrink: 0, fontSize: 15 }}>⚠️</span>
|
|
175
|
+
<span>{error}</span>
|
|
134
176
|
</div>
|
|
135
177
|
) : null}
|
|
136
178
|
</div>
|