@flopay/vue 0.0.0-stage → 1.13.1
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/README.md +102 -2
- package/dist/card-setup-entry.cjs +614 -0
- package/dist/card-setup-entry.d.cts +160 -0
- package/dist/card-setup-entry.d.ts +160 -0
- package/dist/card-setup-entry.mjs +10 -0
- package/dist/chunk-2PLBNCM6.mjs +332 -0
- package/dist/chunk-DUU6SMKP.mjs +97 -0
- package/dist/chunk-VXYNYMHI.mjs +833 -0
- package/dist/chunk-W52CQTJI.mjs +527 -0
- package/dist/hosted-setup-jpaaXDFA.d.cts +33 -0
- package/dist/hosted-setup-jpaaXDFA.d.ts +33 -0
- package/dist/index.cjs +4987 -0
- package/dist/index.d.cts +1940 -0
- package/dist/index.d.ts +1940 -0
- package/dist/index.mjs +3196 -0
- package/dist/payment-method-setup-entry.cjs +1809 -0
- package/dist/payment-method-setup-entry.d.cts +156 -0
- package/dist/payment-method-setup-entry.d.ts +156 -0
- package/dist/payment-method-setup-entry.mjs +7 -0
- package/dist/payment-method-setup-paypal-XPSU4CHA.mjs +148 -0
- package/dist/stripe-elements-QGT45VIE.mjs +6 -0
- package/package.json +60 -3
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
// src/hosted-setup.ts
|
|
2
|
+
import { PaymentAPI, PciVaultCardCapture } from "@flopay/js/card-setup";
|
|
3
|
+
import {
|
|
4
|
+
FloPayError,
|
|
5
|
+
invokeMerchantCallback,
|
|
6
|
+
isCardSetupCheckoutSession,
|
|
7
|
+
isMountableVaultBlock,
|
|
8
|
+
mergeCardSetupSurface,
|
|
9
|
+
resolveBillingApiUrl,
|
|
10
|
+
resolveCardSetupTheme,
|
|
11
|
+
resolveVaultMountOptions
|
|
12
|
+
} from "@flopay/shared";
|
|
13
|
+
import {
|
|
14
|
+
computed,
|
|
15
|
+
onBeforeUnmount,
|
|
16
|
+
onMounted as onMounted2,
|
|
17
|
+
ref,
|
|
18
|
+
shallowRef
|
|
19
|
+
} from "vue";
|
|
20
|
+
|
|
21
|
+
// src/processing-overlay.ts
|
|
22
|
+
import { defineComponent, h, onMounted } from "vue";
|
|
23
|
+
var PROCESSING_OVERLAY_SUCCESS_DELAY_MS = 600;
|
|
24
|
+
var PROCESSING_OVERLAY_ERROR_DELAY_MS = 400;
|
|
25
|
+
var NEUTRAL_THEME = {
|
|
26
|
+
panel: { background: "white" },
|
|
27
|
+
spinnerTrackColor: "#e5e7eb",
|
|
28
|
+
spinnerAccentColor: "#4A49FF",
|
|
29
|
+
labelColors: { processing: "#374151", success: "#16a34a", error: "#dc2626" },
|
|
30
|
+
noteColor: "#6b7280"
|
|
31
|
+
};
|
|
32
|
+
var KEYFRAMES_ID = "flopay-processing-overlay-keyframes";
|
|
33
|
+
var KEYFRAMES = [
|
|
34
|
+
"@keyframes flopay-spin { to { transform: rotate(360deg); } }",
|
|
35
|
+
"@keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }",
|
|
36
|
+
"@keyframes flopay-draw { to { stroke-dashoffset: 0; } }",
|
|
37
|
+
"@keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }"
|
|
38
|
+
].join("\n");
|
|
39
|
+
function ensureKeyframes() {
|
|
40
|
+
if (typeof document === "undefined" || document.getElementById(KEYFRAMES_ID)) return;
|
|
41
|
+
const style = document.createElement("style");
|
|
42
|
+
style.id = KEYFRAMES_ID;
|
|
43
|
+
style.textContent = KEYFRAMES;
|
|
44
|
+
document.head.appendChild(style);
|
|
45
|
+
}
|
|
46
|
+
var SVG = {
|
|
47
|
+
"aria-hidden": "true",
|
|
48
|
+
width: "48",
|
|
49
|
+
height: "48",
|
|
50
|
+
viewBox: "0 0 24 24",
|
|
51
|
+
fill: "none"
|
|
52
|
+
};
|
|
53
|
+
function statusIcon(status, theme) {
|
|
54
|
+
if (status === "processing") {
|
|
55
|
+
return h("svg", { ...SVG, style: { animation: "flopay-spin 0.8s linear infinite" } }, [
|
|
56
|
+
h("circle", {
|
|
57
|
+
cx: "12",
|
|
58
|
+
cy: "12",
|
|
59
|
+
r: "10",
|
|
60
|
+
stroke: theme.spinnerTrackColor,
|
|
61
|
+
"stroke-width": "3"
|
|
62
|
+
}),
|
|
63
|
+
h("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: theme.spinnerAccentColor })
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
if (status === "success") {
|
|
67
|
+
return h("div", { style: { animation: "flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)" } }, [
|
|
68
|
+
h("svg", SVG, [
|
|
69
|
+
h("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
|
|
70
|
+
h("path", {
|
|
71
|
+
d: "M7 12.5l3 3 7-7",
|
|
72
|
+
stroke: "white",
|
|
73
|
+
"stroke-width": "2.5",
|
|
74
|
+
"stroke-linecap": "round",
|
|
75
|
+
"stroke-linejoin": "round",
|
|
76
|
+
style: {
|
|
77
|
+
strokeDasharray: 20,
|
|
78
|
+
strokeDashoffset: 20,
|
|
79
|
+
animation: "flopay-draw 0.4s 0.15s ease forwards"
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
])
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
return h("div", { style: { animation: "flopay-shake 0.4s ease" } }, [
|
|
86
|
+
h("svg", SVG, [
|
|
87
|
+
h("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
|
|
88
|
+
h("path", {
|
|
89
|
+
d: "M8 8l8 8M16 8l-8 8",
|
|
90
|
+
stroke: "white",
|
|
91
|
+
"stroke-width": "2.5",
|
|
92
|
+
"stroke-linecap": "round",
|
|
93
|
+
style: {
|
|
94
|
+
strokeDasharray: 12,
|
|
95
|
+
strokeDashoffset: 12,
|
|
96
|
+
animation: "flopay-draw 0.3s 0.1s ease forwards"
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
])
|
|
100
|
+
]);
|
|
101
|
+
}
|
|
102
|
+
var ProcessingOverlay = defineComponent({
|
|
103
|
+
name: "FloPayProcessingOverlay",
|
|
104
|
+
props: {
|
|
105
|
+
status: { type: String, required: true },
|
|
106
|
+
errorMessage: { type: String, default: null },
|
|
107
|
+
processingLabel: { type: String, default: "PROCESSING..." },
|
|
108
|
+
successLabel: { type: String, default: "PAYMENT SUCCESSFUL" },
|
|
109
|
+
errorLabel: { type: String, default: "PAYMENT FAILED" },
|
|
110
|
+
/** Success sub-note; `null` omits it (surfaces that do not redirect). */
|
|
111
|
+
successNote: {
|
|
112
|
+
type: String,
|
|
113
|
+
default: "You will be automatically redirected, do not close or navigate away from this window."
|
|
114
|
+
},
|
|
115
|
+
theme: { type: Object, default: void 0 }
|
|
116
|
+
},
|
|
117
|
+
setup(props) {
|
|
118
|
+
onMounted(ensureKeyframes);
|
|
119
|
+
return () => {
|
|
120
|
+
const theme = props.theme ?? NEUTRAL_THEME;
|
|
121
|
+
const noteStyle = {
|
|
122
|
+
fontSize: "13px",
|
|
123
|
+
color: theme.noteColor,
|
|
124
|
+
fontWeight: 400,
|
|
125
|
+
maxWidth: "260px",
|
|
126
|
+
lineHeight: 1.4,
|
|
127
|
+
margin: 0
|
|
128
|
+
};
|
|
129
|
+
const label = {
|
|
130
|
+
processing: props.processingLabel,
|
|
131
|
+
success: props.successLabel,
|
|
132
|
+
error: props.errorLabel
|
|
133
|
+
}[props.status];
|
|
134
|
+
const note = { processing: null, success: props.successNote, error: props.errorMessage }[props.status];
|
|
135
|
+
return h(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
"data-testid": "flopay-processing-overlay",
|
|
139
|
+
"data-status": props.status,
|
|
140
|
+
// The attribute the Vue setup surfaces' overlay already carried.
|
|
141
|
+
"data-state": props.status,
|
|
142
|
+
role: "dialog",
|
|
143
|
+
"aria-modal": "true",
|
|
144
|
+
style: {
|
|
145
|
+
position: "fixed",
|
|
146
|
+
inset: 0,
|
|
147
|
+
background: "rgba(0,0,0,0.35)",
|
|
148
|
+
display: "flex",
|
|
149
|
+
alignItems: "center",
|
|
150
|
+
justifyContent: "center",
|
|
151
|
+
zIndex: 1e3,
|
|
152
|
+
backdropFilter: "blur(2px)"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
[
|
|
156
|
+
h(
|
|
157
|
+
"div",
|
|
158
|
+
{
|
|
159
|
+
"data-testid": "flopay-processing-overlay-panel",
|
|
160
|
+
style: {
|
|
161
|
+
borderRadius: "12px",
|
|
162
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
|
|
163
|
+
...theme.panel,
|
|
164
|
+
padding: "2rem 2.5rem",
|
|
165
|
+
textAlign: "center",
|
|
166
|
+
minWidth: "240px",
|
|
167
|
+
display: "flex",
|
|
168
|
+
flexDirection: "column",
|
|
169
|
+
alignItems: "center",
|
|
170
|
+
gap: "16px"
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
[
|
|
174
|
+
h("div", { style: { width: "48px", height: "48px", position: "relative" } }, [
|
|
175
|
+
statusIcon(props.status, theme)
|
|
176
|
+
]),
|
|
177
|
+
h(
|
|
178
|
+
"span",
|
|
179
|
+
{
|
|
180
|
+
"data-testid": "flopay-processing-overlay-label",
|
|
181
|
+
style: {
|
|
182
|
+
fontSize: "14px",
|
|
183
|
+
fontWeight: 600,
|
|
184
|
+
letterSpacing: "0.05em",
|
|
185
|
+
color: theme.labelColors[props.status]
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
label
|
|
189
|
+
),
|
|
190
|
+
note ? h("p", { style: noteStyle }, note) : null
|
|
191
|
+
]
|
|
192
|
+
)
|
|
193
|
+
]
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// src/hosted-setup.ts
|
|
200
|
+
var FloPayCardSetupError = class extends FloPayError {
|
|
201
|
+
constructor(message, type, options) {
|
|
202
|
+
super(message, type, options);
|
|
203
|
+
this.name = "FloPayCardSetupError";
|
|
204
|
+
this.retryable = options.retryable;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
function asCardSetupError(error, overrides = {}) {
|
|
208
|
+
const source = error instanceof FloPayError ? error : void 0;
|
|
209
|
+
const statusCode = source?.statusCode;
|
|
210
|
+
const retryable = overrides.retryable ?? (source?.type === "network_error" || source?.type === "rate_limit_error" || typeof statusCode === "number" && statusCode >= 500 || source?.code === "vault_capture_timeout" || !source);
|
|
211
|
+
return new FloPayCardSetupError(
|
|
212
|
+
source?.message ?? (error instanceof Error ? error.message : "Failed to load the secure card form."),
|
|
213
|
+
source?.type ?? "api_error",
|
|
214
|
+
{
|
|
215
|
+
code: overrides.code ?? source?.code ?? "card_setup_load_failed",
|
|
216
|
+
retryable,
|
|
217
|
+
...source?.param ? { param: source.param } : {},
|
|
218
|
+
...statusCode === void 0 ? {} : { statusCode }
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
function useCardSetupTheme(source) {
|
|
223
|
+
const resolved = computed(
|
|
224
|
+
() => resolveCardSetupTheme({
|
|
225
|
+
theme: source.theme ?? "modern-light",
|
|
226
|
+
appearance: source.appearance,
|
|
227
|
+
buttonsStyles: source.buttonsStyles
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
const vaultTheme = computed(() => resolved.value.vaultTheme);
|
|
231
|
+
const surfaceStyle = computed(
|
|
232
|
+
() => mergeCardSetupSurface(resolved.value.surface, source.containerStyle)
|
|
233
|
+
);
|
|
234
|
+
const overlayTheme = computed(() => resolved.value.overlayTheme);
|
|
235
|
+
const appearance = computed(() => resolved.value.appearance);
|
|
236
|
+
return { vaultTheme, surfaceStyle, overlayTheme, appearance };
|
|
237
|
+
}
|
|
238
|
+
function useHostedSetup(props, emitters, options) {
|
|
239
|
+
const capture = shallowRef(null);
|
|
240
|
+
const vault = shallowRef(null);
|
|
241
|
+
const session = shallowRef(null);
|
|
242
|
+
const loadError = ref(null);
|
|
243
|
+
let active = true;
|
|
244
|
+
let terminal = false;
|
|
245
|
+
let completed = false;
|
|
246
|
+
let submissionInFlight = false;
|
|
247
|
+
let clearTimer = null;
|
|
248
|
+
const offs = [];
|
|
249
|
+
const sessionId = () => props.sessionId ?? "";
|
|
250
|
+
const showOverlay = (state) => {
|
|
251
|
+
if (clearTimer) clearTimeout(clearTimer);
|
|
252
|
+
clearTimer = null;
|
|
253
|
+
emitters.overlay(state);
|
|
254
|
+
};
|
|
255
|
+
const holdFailure = (message) => {
|
|
256
|
+
showOverlay({ status: "error", message });
|
|
257
|
+
clearTimer = setTimeout(() => {
|
|
258
|
+
clearTimer = null;
|
|
259
|
+
if (active) emitters.overlay(null);
|
|
260
|
+
}, PROCESSING_OVERLAY_ERROR_DELAY_MS);
|
|
261
|
+
};
|
|
262
|
+
const bind = (adapter) => {
|
|
263
|
+
submissionInFlight = false;
|
|
264
|
+
offs.push(
|
|
265
|
+
adapter.on("submitting", () => {
|
|
266
|
+
submissionInFlight = true;
|
|
267
|
+
showOverlay({ status: "processing", message: null });
|
|
268
|
+
}),
|
|
269
|
+
adapter.on("complete", (event) => {
|
|
270
|
+
terminal = true;
|
|
271
|
+
submissionInFlight = false;
|
|
272
|
+
showOverlay({ status: "success", message: null });
|
|
273
|
+
setTimeout(() => {
|
|
274
|
+
if (completed) return;
|
|
275
|
+
completed = true;
|
|
276
|
+
invokeMerchantCallback(
|
|
277
|
+
() => emitters.complete({
|
|
278
|
+
status: "succeeded",
|
|
279
|
+
sessionId: sessionId(),
|
|
280
|
+
...event.paymentMethod ? { paymentMethod: event.paymentMethod } : {}
|
|
281
|
+
})
|
|
282
|
+
);
|
|
283
|
+
if (active) emitters.overlay(null);
|
|
284
|
+
}, PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
285
|
+
}),
|
|
286
|
+
adapter.on("decline", (event) => {
|
|
287
|
+
terminal = true;
|
|
288
|
+
submissionInFlight = false;
|
|
289
|
+
holdFailure(event.message ?? null);
|
|
290
|
+
invokeMerchantCallback(
|
|
291
|
+
() => emitters.decline({
|
|
292
|
+
status: "declined",
|
|
293
|
+
sessionId: sessionId(),
|
|
294
|
+
...event.declineReason ? { reason: event.declineReason } : {},
|
|
295
|
+
...event.message ? { message: event.message } : {}
|
|
296
|
+
})
|
|
297
|
+
);
|
|
298
|
+
}),
|
|
299
|
+
// Closing the bank's authentication window is not an outcome: the hosted
|
|
300
|
+
// form keeps polling and may still report one. Drop the overlay but keep
|
|
301
|
+
// the submission in flight, so a late failure still shows "CARD NOT SAVED".
|
|
302
|
+
adapter.on("challenge_dismissed", () => {
|
|
303
|
+
showOverlay(null);
|
|
304
|
+
})
|
|
305
|
+
);
|
|
306
|
+
};
|
|
307
|
+
onMounted2(async () => {
|
|
308
|
+
const billingApiUrl = resolveBillingApiUrl(props.billingApiUrl);
|
|
309
|
+
const api = new PaymentAPI(billingApiUrl, { telemetry: props.telemetry });
|
|
310
|
+
try {
|
|
311
|
+
if (!sessionId().trim()) {
|
|
312
|
+
throw new FloPayError("A setup surface requires a sessionId.", "validation_error", {
|
|
313
|
+
code: "MissingCheckoutSessionId",
|
|
314
|
+
param: "sessionId"
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
if (!props.nonce?.trim()) {
|
|
318
|
+
throw new FloPayError("A setup surface requires a session nonce.", "validation_error", {
|
|
319
|
+
code: "MissingCheckoutSessionToken",
|
|
320
|
+
param: "nonce"
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
const result = await api.getUnifiedCheckoutSession(sessionId(), props.nonce);
|
|
324
|
+
const read = result.data.session;
|
|
325
|
+
if (!read || !isCardSetupCheckoutSession(read)) {
|
|
326
|
+
throw new FloPayError(
|
|
327
|
+
"Only a zero-amount, productless setup session is accepted.",
|
|
328
|
+
"validation_error",
|
|
329
|
+
{ code: "InvalidCardSetupSession", param: "sessionId" }
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
if (!active) return;
|
|
333
|
+
session.value = result;
|
|
334
|
+
if (!options.requireVault(result)) return;
|
|
335
|
+
let block = read.vault;
|
|
336
|
+
if (!isMountableVaultBlock(block))
|
|
337
|
+
block = await api.getVaultCapture(sessionId(), props.nonce);
|
|
338
|
+
const mount = resolveVaultMountOptions(block, {
|
|
339
|
+
billingApiUrl,
|
|
340
|
+
sessionId: sessionId(),
|
|
341
|
+
nonce: props.nonce
|
|
342
|
+
});
|
|
343
|
+
if (!mount) {
|
|
344
|
+
throw new FloPayError(
|
|
345
|
+
"FloPay: this setup session has no vault card capability. Upgrade the billing backend integration.",
|
|
346
|
+
"api_error",
|
|
347
|
+
{ code: "UnsupportedBackendVaultCapability" }
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
if (!active) return;
|
|
351
|
+
const adapter = new PciVaultCardCapture({
|
|
352
|
+
sessionId: sessionId(),
|
|
353
|
+
operation: "card_setup",
|
|
354
|
+
telemetry: props.telemetry
|
|
355
|
+
});
|
|
356
|
+
bind(adapter);
|
|
357
|
+
vault.value = mount;
|
|
358
|
+
capture.value = adapter;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
if (!active) return;
|
|
361
|
+
terminal = true;
|
|
362
|
+
const failure = asCardSetupError(error);
|
|
363
|
+
loadError.value = failure;
|
|
364
|
+
invokeMerchantCallback(() => emitters.error(failure));
|
|
365
|
+
} finally {
|
|
366
|
+
api.destroy();
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
onBeforeUnmount(() => {
|
|
370
|
+
active = false;
|
|
371
|
+
if (clearTimer) clearTimeout(clearTimer);
|
|
372
|
+
clearTimer = null;
|
|
373
|
+
for (const off of offs.splice(0)) off();
|
|
374
|
+
if (!terminal && sessionId()) {
|
|
375
|
+
invokeMerchantCallback(
|
|
376
|
+
() => emitters.cancel({ status: "cancelled", sessionId: sessionId() })
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
return {
|
|
381
|
+
capture,
|
|
382
|
+
vault,
|
|
383
|
+
session,
|
|
384
|
+
loadError,
|
|
385
|
+
settle: () => {
|
|
386
|
+
terminal = true;
|
|
387
|
+
},
|
|
388
|
+
widgetFailed: (message) => {
|
|
389
|
+
if (!submissionInFlight) return;
|
|
390
|
+
submissionInFlight = false;
|
|
391
|
+
holdFailure(message);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/vault-card-fields.ts
|
|
397
|
+
import { invokeMerchantCallback as invokeMerchantCallback2 } from "@flopay/shared";
|
|
398
|
+
import {
|
|
399
|
+
defineComponent as defineComponent2,
|
|
400
|
+
h as h2,
|
|
401
|
+
onBeforeUnmount as onBeforeUnmount2,
|
|
402
|
+
onMounted as onMounted3,
|
|
403
|
+
ref as ref2,
|
|
404
|
+
watch
|
|
405
|
+
} from "vue";
|
|
406
|
+
var labelledCaptures = /* @__PURE__ */ new WeakSet();
|
|
407
|
+
var VaultCardFields = defineComponent2({
|
|
408
|
+
name: "VaultCardFields",
|
|
409
|
+
props: {
|
|
410
|
+
capture: { type: Object, default: void 0 },
|
|
411
|
+
html: { type: String, default: void 0 },
|
|
412
|
+
messageToken: { type: String, default: void 0 },
|
|
413
|
+
expectedOrigin: { type: String, default: void 0 },
|
|
414
|
+
verificationRequired: { type: Boolean, default: void 0 },
|
|
415
|
+
theme: { type: Object, default: void 0 },
|
|
416
|
+
submitLabel: { type: String, default: void 0 },
|
|
417
|
+
containerStyle: { type: Object, default: void 0 }
|
|
418
|
+
},
|
|
419
|
+
emits: {
|
|
420
|
+
ready: () => true,
|
|
421
|
+
error: (_message) => true,
|
|
422
|
+
validation: (_message) => true
|
|
423
|
+
},
|
|
424
|
+
setup(props, { emit }) {
|
|
425
|
+
const container = ref2(null);
|
|
426
|
+
let teardown = null;
|
|
427
|
+
let appliedLabel;
|
|
428
|
+
let appliedTheme = null;
|
|
429
|
+
const mountWidget = () => {
|
|
430
|
+
teardown?.();
|
|
431
|
+
teardown = null;
|
|
432
|
+
const el = container.value;
|
|
433
|
+
const { capture, html } = props;
|
|
434
|
+
if (!el || !capture || !html) return;
|
|
435
|
+
let active = true;
|
|
436
|
+
let readyEmitted = false;
|
|
437
|
+
const emitReadyOnce = () => {
|
|
438
|
+
if (!active || readyEmitted) return;
|
|
439
|
+
readyEmitted = true;
|
|
440
|
+
invokeMerchantCallback2(() => emit("ready"));
|
|
441
|
+
};
|
|
442
|
+
const offReady = capture.on("ready", emitReadyOnce);
|
|
443
|
+
const offError = capture.on("error", (event) => {
|
|
444
|
+
const message = event.message ?? "There was a problem loading the secure card form.";
|
|
445
|
+
invokeMerchantCallback2(() => emit("error", message));
|
|
446
|
+
});
|
|
447
|
+
const offValidation = capture.on("validation", (event) => {
|
|
448
|
+
invokeMerchantCallback2(() => emit("validation", event.message ?? null));
|
|
449
|
+
});
|
|
450
|
+
const label = props.submitLabel ?? void 0;
|
|
451
|
+
const labelDirective = label ?? (labelledCaptures.has(capture) ? null : void 0);
|
|
452
|
+
appliedLabel = label;
|
|
453
|
+
appliedTheme = props.theme ?? null;
|
|
454
|
+
if (label !== void 0) labelledCaptures.add(capture);
|
|
455
|
+
capture.mount(el, {
|
|
456
|
+
html,
|
|
457
|
+
...props.messageToken ? { messageToken: props.messageToken } : {},
|
|
458
|
+
...props.expectedOrigin ? { expectedOrigin: props.expectedOrigin } : {},
|
|
459
|
+
...props.verificationRequired ? { verificationRequired: true } : {},
|
|
460
|
+
...props.theme ? { theme: props.theme } : {},
|
|
461
|
+
...labelDirective === void 0 ? {} : { submitLabel: labelDirective }
|
|
462
|
+
}).then(emitReadyOnce).catch((error) => {
|
|
463
|
+
if (!active) return;
|
|
464
|
+
const message = error instanceof Error ? error.message : "Failed to load the secure card form.";
|
|
465
|
+
invokeMerchantCallback2(() => emit("error", message));
|
|
466
|
+
});
|
|
467
|
+
teardown = () => {
|
|
468
|
+
active = false;
|
|
469
|
+
offReady();
|
|
470
|
+
offError();
|
|
471
|
+
offValidation();
|
|
472
|
+
capture.unmount();
|
|
473
|
+
};
|
|
474
|
+
};
|
|
475
|
+
onMounted3(() => {
|
|
476
|
+
mountWidget();
|
|
477
|
+
watch(
|
|
478
|
+
() => [
|
|
479
|
+
props.capture,
|
|
480
|
+
props.html,
|
|
481
|
+
props.messageToken,
|
|
482
|
+
props.expectedOrigin,
|
|
483
|
+
props.verificationRequired
|
|
484
|
+
],
|
|
485
|
+
mountWidget
|
|
486
|
+
);
|
|
487
|
+
watch(
|
|
488
|
+
() => props.theme,
|
|
489
|
+
(theme) => {
|
|
490
|
+
if (theme === void 0) return;
|
|
491
|
+
if (theme === null && appliedTheme === null) return;
|
|
492
|
+
appliedTheme = theme;
|
|
493
|
+
props.capture?.applyTheme?.(theme);
|
|
494
|
+
}
|
|
495
|
+
);
|
|
496
|
+
watch(
|
|
497
|
+
() => props.submitLabel ?? void 0,
|
|
498
|
+
(label) => {
|
|
499
|
+
if (!props.capture || label === appliedLabel) return;
|
|
500
|
+
appliedLabel = label;
|
|
501
|
+
if (label !== void 0) labelledCaptures.add(props.capture);
|
|
502
|
+
props.capture.setSubmitLabel?.(label ?? null);
|
|
503
|
+
}
|
|
504
|
+
);
|
|
505
|
+
});
|
|
506
|
+
onBeforeUnmount2(() => {
|
|
507
|
+
teardown?.();
|
|
508
|
+
teardown = null;
|
|
509
|
+
});
|
|
510
|
+
return () => h2("div", {
|
|
511
|
+
ref: container,
|
|
512
|
+
"data-testid": "flopay-vault-card-fields",
|
|
513
|
+
style: props.containerStyle
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
export {
|
|
519
|
+
PROCESSING_OVERLAY_SUCCESS_DELAY_MS,
|
|
520
|
+
PROCESSING_OVERLAY_ERROR_DELAY_MS,
|
|
521
|
+
ProcessingOverlay,
|
|
522
|
+
FloPayCardSetupError,
|
|
523
|
+
asCardSetupError,
|
|
524
|
+
useCardSetupTheme,
|
|
525
|
+
useHostedSetup,
|
|
526
|
+
VaultCardFields
|
|
527
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SavedCardDisplay, FloPayError, FloPayErrorType } from '@flopay/shared';
|
|
2
|
+
|
|
3
|
+
/** Verified terminal result from a no-charge setup. */
|
|
4
|
+
interface FloPayCardSetupCompleteEvent {
|
|
5
|
+
status: 'succeeded';
|
|
6
|
+
sessionId: string;
|
|
7
|
+
/** Present when the backend terminal message includes the saved-card display DTO. */
|
|
8
|
+
paymentMethod?: SavedCardDisplay;
|
|
9
|
+
}
|
|
10
|
+
/** Provider-neutral terminal decline from verification. */
|
|
11
|
+
interface FloPayCardSetupDeclineEvent {
|
|
12
|
+
status: 'declined';
|
|
13
|
+
sessionId: string;
|
|
14
|
+
reason?: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Abandoned setup result emitted when the component unmounts pre-terminally. */
|
|
18
|
+
interface FloPayCardSetupCancelEvent {
|
|
19
|
+
status: 'cancelled';
|
|
20
|
+
sessionId: string;
|
|
21
|
+
}
|
|
22
|
+
/** Structured technical/validation failure from a setup surface. */
|
|
23
|
+
declare class FloPayCardSetupError extends FloPayError {
|
|
24
|
+
readonly retryable: boolean;
|
|
25
|
+
constructor(message: string, type: FloPayErrorType, options: {
|
|
26
|
+
code: string;
|
|
27
|
+
retryable: boolean;
|
|
28
|
+
param?: string;
|
|
29
|
+
statusCode?: number;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { type FloPayCardSetupCancelEvent as F, type FloPayCardSetupCompleteEvent as a, type FloPayCardSetupDeclineEvent as b, FloPayCardSetupError as c };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SavedCardDisplay, FloPayError, FloPayErrorType } from '@flopay/shared';
|
|
2
|
+
|
|
3
|
+
/** Verified terminal result from a no-charge setup. */
|
|
4
|
+
interface FloPayCardSetupCompleteEvent {
|
|
5
|
+
status: 'succeeded';
|
|
6
|
+
sessionId: string;
|
|
7
|
+
/** Present when the backend terminal message includes the saved-card display DTO. */
|
|
8
|
+
paymentMethod?: SavedCardDisplay;
|
|
9
|
+
}
|
|
10
|
+
/** Provider-neutral terminal decline from verification. */
|
|
11
|
+
interface FloPayCardSetupDeclineEvent {
|
|
12
|
+
status: 'declined';
|
|
13
|
+
sessionId: string;
|
|
14
|
+
reason?: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Abandoned setup result emitted when the component unmounts pre-terminally. */
|
|
18
|
+
interface FloPayCardSetupCancelEvent {
|
|
19
|
+
status: 'cancelled';
|
|
20
|
+
sessionId: string;
|
|
21
|
+
}
|
|
22
|
+
/** Structured technical/validation failure from a setup surface. */
|
|
23
|
+
declare class FloPayCardSetupError extends FloPayError {
|
|
24
|
+
readonly retryable: boolean;
|
|
25
|
+
constructor(message: string, type: FloPayErrorType, options: {
|
|
26
|
+
code: string;
|
|
27
|
+
retryable: boolean;
|
|
28
|
+
param?: string;
|
|
29
|
+
statusCode?: number;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { type FloPayCardSetupCancelEvent as F, type FloPayCardSetupCompleteEvent as a, type FloPayCardSetupDeclineEvent as b, FloPayCardSetupError as c };
|