@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.
@@ -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": "16px",
21
- "font-family": "Helvetica, Arial, sans-serif",
21
+ "font-size": "15px",
22
+ "font-family": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
22
23
  "font-weight": "400",
23
- color: "#2f2f2f",
24
+ color: "#111827",
24
25
  padding: "0 14px",
25
- height: "44px",
26
- "border-radius": "4px",
27
- border: "1px solid #c7c7c7",
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: "#a0aec0",
34
+ color: "#9ca3af",
34
35
  },
35
-
36
36
  ".invalid": {
37
- color: "#b42318",
38
- border: "1px solid #d92d20",
37
+ color: "#dc2626",
38
+ border: "1px solid #fca5a5",
39
+ background: "#fff7f7",
39
40
  "box-shadow": "none",
40
41
  },
41
-
42
42
  ".valid": {
43
- color: "#2f2f2f",
44
- border: "1px solid #c7c7c7",
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 #c7c7c7",
51
- "box-shadow": "none",
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: 15,
58
- lineHeight: "22px",
59
- fontWeight: 700,
60
- color: "#4a4a4a",
61
- marginBottom: 10,
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={disabled || !cardFieldsForm}
80
+ disabled={isDisabled}
79
81
  onClick={() => {
80
82
  onSubmit()
81
83
  cardFieldsForm?.submit()
82
84
  }}
83
85
  style={{
84
86
  width: "100%",
85
- height: 52,
86
- padding: "0 16px",
87
- borderRadius: 4,
88
- border: "1px solid #c7c7c7",
89
- background: "#f5f5f5",
90
- color: "#2f363d",
91
- fontSize: 16,
92
- fontWeight: 700,
93
- fontFamily: "Helvetica, Arial, sans-serif",
94
- cursor: disabled ? "not-allowed" : "pointer",
95
- opacity: disabled ? 0.7 : 1,
96
- boxShadow: "none",
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: 12,
129
- border: "1px solid #ddd",
130
- borderRadius: 4,
152
+ padding: "12px 16px",
153
+ background: "#fefce8",
154
+ border: "1px solid #fde68a",
155
+ borderRadius: 8,
131
156
  fontSize: 13,
132
- color: "#3c434a",
157
+ color: "#92400e",
133
158
  }}
134
159
  >
135
- CardFields unavailable: missing client_token from backend.
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
- {/* ── ADDED: full overlay shown while card payment processes ─────────── */}
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.90)",
155
- borderRadius: 8,
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: 12,
161
- minHeight: 120,
184
+ gap: 14,
185
+ minHeight: 180,
162
186
  }}
163
187
  >
164
188
  <div
165
189
  style={{
166
- width: 32,
167
- height: 32,
190
+ width: 36,
191
+ height: 36,
168
192
  borderRadius: "50%",
169
- border: "2.5px solid #e5e7eb",
170
- borderTopColor: "#1877f2",
171
- animation: "_pp_spin .7s linear infinite",
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: 14, fontWeight: 500, color: "#111827" }}>
199
+ <div style={{ fontSize: 15, fontWeight: 600, color: "#111827" }}>
176
200
  Processing your payment…
177
201
  </div>
178
- <div style={{ fontSize: 12, color: "#6b7280", marginTop: 4 }}>
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: "grid",
223
- gap: 18,
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
- fontSize: 15,
232
- lineHeight: 1.5,
233
- color: "#4a4a4a",
234
- marginBottom: 2,
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
- Sandbox Mode Enabled.
238
- <br />
239
- Use test card <strong>4111 1111 1111 1111</strong> with any future
240
- expiration date and any CVV.
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
- <div style={{ width: "100%" }}>
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: "minmax(0, 1fr) minmax(0, 1fr)",
253
- columnGap: 18,
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
- <div style={{ marginTop: 2 }}>
269
- <SubmitButton
270
- disabled={submitting}
271
- label={submitting ? "Processing..." : "Pay by Card"}
272
- onSubmit={() => {
273
- setError(null)
274
- setSubmitting(true)
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
- {/* ── CHANGED: styled error box instead of plain colored text ──── */}
280
- {error ? (
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
- padding: "8px 12px",
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: 6,
343
+ borderRadius: 8,
287
344
  fontSize: 13,
288
345
  color: "#b91c1c",
346
+ lineHeight: 1.5,
289
347
  }}
290
348
  >
291
- {error}
349
+ <span style={{ flexShrink: 0, marginTop: 1 }}>⚠️</span>
350
+ <span>{error}</span>
292
351
  </div>
293
- ) : null}
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
- padding: "8px 12px",
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: 6,
168
+ borderRadius: 8,
129
169
  fontSize: 13,
130
170
  color: "#b91c1c",
171
+ lineHeight: 1.5,
131
172
  }}
132
173
  >
133
- {error}
174
+ <span style={{ flexShrink: 0, fontSize: 15 }}>⚠️</span>
175
+ <span>{error}</span>
134
176
  </div>
135
177
  ) : null}
136
178
  </div>