@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.
@@ -0,0 +1,160 @@
1
+ import * as vue from 'vue';
2
+ import { PropType } from 'vue';
3
+ import { ThemeId, VaultCardThemeColors, FloPayAppearance, ButtonsLayoutStyles } from '@flopay/shared';
4
+ import { a as FloPayCardSetupCompleteEvent, F as FloPayCardSetupCancelEvent, b as FloPayCardSetupDeclineEvent, c as FloPayCardSetupError } from './hosted-setup-jpaaXDFA.cjs';
5
+
6
+ /** Props for the browser-only secure card-setup surface (callbacks are emits). */
7
+ interface FloPayCardSetupProps {
8
+ /** Opaque id returned by the merchant-authenticated setup-session endpoint. */
9
+ sessionId: string;
10
+ /** Session-bound token returned alongside `sessionId`. */
11
+ nonce: string;
12
+ billingApiUrl?: string;
13
+ /** Flo-owned privacy-safe telemetry is enabled by default; set false to opt out. */
14
+ telemetry?: boolean;
15
+ /**
16
+ * Theme for FloPay's hosted, PCI-compliant card widget. Accepts the same
17
+ * high-level `ThemeId` as `FloPayCheckout` (resolved through the same
18
+ * bundle, so one value styles both surfaces identically) or raw
19
+ * `VaultCardThemeColors` for full manual control. Defaults to
20
+ * `'modern-light'`, which an unrecognized preset id also falls back to; pass
21
+ * `'classic'` for the historic FloPay look.
22
+ */
23
+ theme?: ThemeId | VaultCardThemeColors;
24
+ /**
25
+ * Inline styles for the hosted-widget container. A named preset first gives
26
+ * that container the checkout credit-card pane's surface, with any stored
27
+ * `buttonsStyles.cardFormContainer` merged over it; each `containerStyle`
28
+ * property then wins over that surface, and any `background*` member
29
+ * replaces the surface background entirely.
30
+ */
31
+ containerStyle?: Record<string, string | number>;
32
+ /**
33
+ * Appearance override, the same prop and type as `FloPayCheckout`'s (for
34
+ * example a client's saved embed theme `appearance`). It replaces the
35
+ * preset's appearance outright, as checkout does: its variables drive the
36
+ * hosted card fields, the hosted submit button and the outcome overlay.
37
+ * Ignored with raw `VaultCardThemeColors`; an empty `{}` is the same as
38
+ * omitting it. Changes restyle in place.
39
+ */
40
+ appearance?: FloPayAppearance;
41
+ /**
42
+ * Buttons-layout style overrides, the same prop and type as
43
+ * `FloPayCheckout`'s (for example a client's saved embed theme
44
+ * `buttonsStyles`), merged over the preset's buttons layout the way checkout
45
+ * merges them. `submitButton` and the `cardInput*` members style the hosted
46
+ * card form, and `cardFormContainer` the pane around it and the outcome
47
+ * overlay's panel. Ignored with raw `VaultCardThemeColors`; an empty `{}` is
48
+ * the same as omitting it. Changes restyle in place.
49
+ */
50
+ buttonsStyles?: ButtonsLayoutStyles;
51
+ }
52
+ /**
53
+ * Mounts FloPay's hosted card capture (the SDK's `cardCapture` adapter) for a
54
+ * setup session the merchant's server created. It takes only `sessionId` +
55
+ * `nonce`, never creates a session, never loads Stripe.js, and leaves the
56
+ * verification and its 3DS to the hosted widget. Emits `ready`, `complete`,
57
+ * `decline`, `cancel` (unmounted before an outcome), `validation` and `error`.
58
+ *
59
+ * Like React's, it draws the full-page processing / success / failure overlay
60
+ * over the hosted form ("SAVING CARD...", "CARD SAVED", "CARD NOT SAVED"),
61
+ * themed with the preset's credit-card pane. A client's stored `appearance`
62
+ * and `buttonsStyles` resolve over the preset exactly as in React and
63
+ * `FloPayCheckout`, and restyle the mounted form in place.
64
+ */
65
+ declare const FloPayCardSetup: vue.DefineComponent<vue.ExtractPropTypes<{
66
+ sessionId: {
67
+ type: StringConstructor;
68
+ default: undefined;
69
+ };
70
+ nonce: {
71
+ type: StringConstructor;
72
+ default: undefined;
73
+ };
74
+ billingApiUrl: {
75
+ type: StringConstructor;
76
+ default: undefined;
77
+ };
78
+ telemetry: {
79
+ type: BooleanConstructor;
80
+ default: undefined;
81
+ };
82
+ theme: {
83
+ type: PropType<ThemeId | VaultCardThemeColors>;
84
+ default: string;
85
+ };
86
+ containerStyle: {
87
+ type: PropType<Record<string, string | number>>;
88
+ default: undefined;
89
+ };
90
+ appearance: {
91
+ type: PropType<FloPayAppearance>;
92
+ default: undefined;
93
+ };
94
+ buttonsStyles: {
95
+ type: PropType<ButtonsLayoutStyles>;
96
+ default: undefined;
97
+ };
98
+ }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
99
+ [key: string]: any;
100
+ }> | (vue.VNode<vue.RendererNode, vue.RendererElement, {
101
+ [key: string]: any;
102
+ }> | null)[] | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
103
+ ready: () => true;
104
+ complete: (_event: FloPayCardSetupCompleteEvent) => true;
105
+ cancel: (_event: FloPayCardSetupCancelEvent) => true;
106
+ decline: (_event: FloPayCardSetupDeclineEvent) => true;
107
+ validation: (_message: string | null) => true;
108
+ error: (_error: FloPayCardSetupError) => true;
109
+ }, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
110
+ sessionId: {
111
+ type: StringConstructor;
112
+ default: undefined;
113
+ };
114
+ nonce: {
115
+ type: StringConstructor;
116
+ default: undefined;
117
+ };
118
+ billingApiUrl: {
119
+ type: StringConstructor;
120
+ default: undefined;
121
+ };
122
+ telemetry: {
123
+ type: BooleanConstructor;
124
+ default: undefined;
125
+ };
126
+ theme: {
127
+ type: PropType<ThemeId | VaultCardThemeColors>;
128
+ default: string;
129
+ };
130
+ containerStyle: {
131
+ type: PropType<Record<string, string | number>>;
132
+ default: undefined;
133
+ };
134
+ appearance: {
135
+ type: PropType<FloPayAppearance>;
136
+ default: undefined;
137
+ };
138
+ buttonsStyles: {
139
+ type: PropType<ButtonsLayoutStyles>;
140
+ default: undefined;
141
+ };
142
+ }>> & Readonly<{
143
+ onError?: ((_error: FloPayCardSetupError) => any) | undefined;
144
+ onCancel?: ((_event: FloPayCardSetupCancelEvent) => any) | undefined;
145
+ onReady?: (() => any) | undefined;
146
+ onComplete?: ((_event: FloPayCardSetupCompleteEvent) => any) | undefined;
147
+ onDecline?: ((_event: FloPayCardSetupDeclineEvent) => any) | undefined;
148
+ onValidation?: ((_message: string | null) => any) | undefined;
149
+ }>, {
150
+ theme: ThemeId | VaultCardThemeColors;
151
+ telemetry: boolean;
152
+ sessionId: string;
153
+ nonce: string;
154
+ containerStyle: Record<string, string | number>;
155
+ billingApiUrl: string;
156
+ appearance: FloPayAppearance;
157
+ buttonsStyles: ButtonsLayoutStyles;
158
+ }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
159
+
160
+ export { FloPayCardSetup, FloPayCardSetupCancelEvent, FloPayCardSetupCompleteEvent, FloPayCardSetupDeclineEvent, FloPayCardSetupError, type FloPayCardSetupProps };
@@ -0,0 +1,160 @@
1
+ import * as vue from 'vue';
2
+ import { PropType } from 'vue';
3
+ import { ThemeId, VaultCardThemeColors, FloPayAppearance, ButtonsLayoutStyles } from '@flopay/shared';
4
+ import { a as FloPayCardSetupCompleteEvent, F as FloPayCardSetupCancelEvent, b as FloPayCardSetupDeclineEvent, c as FloPayCardSetupError } from './hosted-setup-jpaaXDFA.js';
5
+
6
+ /** Props for the browser-only secure card-setup surface (callbacks are emits). */
7
+ interface FloPayCardSetupProps {
8
+ /** Opaque id returned by the merchant-authenticated setup-session endpoint. */
9
+ sessionId: string;
10
+ /** Session-bound token returned alongside `sessionId`. */
11
+ nonce: string;
12
+ billingApiUrl?: string;
13
+ /** Flo-owned privacy-safe telemetry is enabled by default; set false to opt out. */
14
+ telemetry?: boolean;
15
+ /**
16
+ * Theme for FloPay's hosted, PCI-compliant card widget. Accepts the same
17
+ * high-level `ThemeId` as `FloPayCheckout` (resolved through the same
18
+ * bundle, so one value styles both surfaces identically) or raw
19
+ * `VaultCardThemeColors` for full manual control. Defaults to
20
+ * `'modern-light'`, which an unrecognized preset id also falls back to; pass
21
+ * `'classic'` for the historic FloPay look.
22
+ */
23
+ theme?: ThemeId | VaultCardThemeColors;
24
+ /**
25
+ * Inline styles for the hosted-widget container. A named preset first gives
26
+ * that container the checkout credit-card pane's surface, with any stored
27
+ * `buttonsStyles.cardFormContainer` merged over it; each `containerStyle`
28
+ * property then wins over that surface, and any `background*` member
29
+ * replaces the surface background entirely.
30
+ */
31
+ containerStyle?: Record<string, string | number>;
32
+ /**
33
+ * Appearance override, the same prop and type as `FloPayCheckout`'s (for
34
+ * example a client's saved embed theme `appearance`). It replaces the
35
+ * preset's appearance outright, as checkout does: its variables drive the
36
+ * hosted card fields, the hosted submit button and the outcome overlay.
37
+ * Ignored with raw `VaultCardThemeColors`; an empty `{}` is the same as
38
+ * omitting it. Changes restyle in place.
39
+ */
40
+ appearance?: FloPayAppearance;
41
+ /**
42
+ * Buttons-layout style overrides, the same prop and type as
43
+ * `FloPayCheckout`'s (for example a client's saved embed theme
44
+ * `buttonsStyles`), merged over the preset's buttons layout the way checkout
45
+ * merges them. `submitButton` and the `cardInput*` members style the hosted
46
+ * card form, and `cardFormContainer` the pane around it and the outcome
47
+ * overlay's panel. Ignored with raw `VaultCardThemeColors`; an empty `{}` is
48
+ * the same as omitting it. Changes restyle in place.
49
+ */
50
+ buttonsStyles?: ButtonsLayoutStyles;
51
+ }
52
+ /**
53
+ * Mounts FloPay's hosted card capture (the SDK's `cardCapture` adapter) for a
54
+ * setup session the merchant's server created. It takes only `sessionId` +
55
+ * `nonce`, never creates a session, never loads Stripe.js, and leaves the
56
+ * verification and its 3DS to the hosted widget. Emits `ready`, `complete`,
57
+ * `decline`, `cancel` (unmounted before an outcome), `validation` and `error`.
58
+ *
59
+ * Like React's, it draws the full-page processing / success / failure overlay
60
+ * over the hosted form ("SAVING CARD...", "CARD SAVED", "CARD NOT SAVED"),
61
+ * themed with the preset's credit-card pane. A client's stored `appearance`
62
+ * and `buttonsStyles` resolve over the preset exactly as in React and
63
+ * `FloPayCheckout`, and restyle the mounted form in place.
64
+ */
65
+ declare const FloPayCardSetup: vue.DefineComponent<vue.ExtractPropTypes<{
66
+ sessionId: {
67
+ type: StringConstructor;
68
+ default: undefined;
69
+ };
70
+ nonce: {
71
+ type: StringConstructor;
72
+ default: undefined;
73
+ };
74
+ billingApiUrl: {
75
+ type: StringConstructor;
76
+ default: undefined;
77
+ };
78
+ telemetry: {
79
+ type: BooleanConstructor;
80
+ default: undefined;
81
+ };
82
+ theme: {
83
+ type: PropType<ThemeId | VaultCardThemeColors>;
84
+ default: string;
85
+ };
86
+ containerStyle: {
87
+ type: PropType<Record<string, string | number>>;
88
+ default: undefined;
89
+ };
90
+ appearance: {
91
+ type: PropType<FloPayAppearance>;
92
+ default: undefined;
93
+ };
94
+ buttonsStyles: {
95
+ type: PropType<ButtonsLayoutStyles>;
96
+ default: undefined;
97
+ };
98
+ }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
99
+ [key: string]: any;
100
+ }> | (vue.VNode<vue.RendererNode, vue.RendererElement, {
101
+ [key: string]: any;
102
+ }> | null)[] | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
103
+ ready: () => true;
104
+ complete: (_event: FloPayCardSetupCompleteEvent) => true;
105
+ cancel: (_event: FloPayCardSetupCancelEvent) => true;
106
+ decline: (_event: FloPayCardSetupDeclineEvent) => true;
107
+ validation: (_message: string | null) => true;
108
+ error: (_error: FloPayCardSetupError) => true;
109
+ }, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
110
+ sessionId: {
111
+ type: StringConstructor;
112
+ default: undefined;
113
+ };
114
+ nonce: {
115
+ type: StringConstructor;
116
+ default: undefined;
117
+ };
118
+ billingApiUrl: {
119
+ type: StringConstructor;
120
+ default: undefined;
121
+ };
122
+ telemetry: {
123
+ type: BooleanConstructor;
124
+ default: undefined;
125
+ };
126
+ theme: {
127
+ type: PropType<ThemeId | VaultCardThemeColors>;
128
+ default: string;
129
+ };
130
+ containerStyle: {
131
+ type: PropType<Record<string, string | number>>;
132
+ default: undefined;
133
+ };
134
+ appearance: {
135
+ type: PropType<FloPayAppearance>;
136
+ default: undefined;
137
+ };
138
+ buttonsStyles: {
139
+ type: PropType<ButtonsLayoutStyles>;
140
+ default: undefined;
141
+ };
142
+ }>> & Readonly<{
143
+ onError?: ((_error: FloPayCardSetupError) => any) | undefined;
144
+ onCancel?: ((_event: FloPayCardSetupCancelEvent) => any) | undefined;
145
+ onReady?: (() => any) | undefined;
146
+ onComplete?: ((_event: FloPayCardSetupCompleteEvent) => any) | undefined;
147
+ onDecline?: ((_event: FloPayCardSetupDeclineEvent) => any) | undefined;
148
+ onValidation?: ((_message: string | null) => any) | undefined;
149
+ }>, {
150
+ theme: ThemeId | VaultCardThemeColors;
151
+ telemetry: boolean;
152
+ sessionId: string;
153
+ nonce: string;
154
+ containerStyle: Record<string, string | number>;
155
+ billingApiUrl: string;
156
+ appearance: FloPayAppearance;
157
+ buttonsStyles: ButtonsLayoutStyles;
158
+ }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
159
+
160
+ export { FloPayCardSetup, FloPayCardSetupCancelEvent, FloPayCardSetupCompleteEvent, FloPayCardSetupDeclineEvent, FloPayCardSetupError, type FloPayCardSetupProps };
@@ -0,0 +1,10 @@
1
+ import {
2
+ FloPayCardSetup
3
+ } from "./chunk-DUU6SMKP.mjs";
4
+ import {
5
+ FloPayCardSetupError
6
+ } from "./chunk-W52CQTJI.mjs";
7
+ export {
8
+ FloPayCardSetup,
9
+ FloPayCardSetupError
10
+ };
@@ -0,0 +1,332 @@
1
+ import {
2
+ PROCESSING_OVERLAY_ERROR_DELAY_MS,
3
+ PROCESSING_OVERLAY_SUCCESS_DELAY_MS,
4
+ ProcessingOverlay,
5
+ VaultCardFields,
6
+ asCardSetupError,
7
+ useCardSetupTheme,
8
+ useHostedSetup
9
+ } from "./chunk-W52CQTJI.mjs";
10
+
11
+ // src/flopay-payment-method-setup-main.ts
12
+ import {
13
+ clearSetupRedirectParams,
14
+ PaymentAPI,
15
+ readSetupRedirectReturn
16
+ } from "@flopay/js/card-setup";
17
+ import {
18
+ FloPayError,
19
+ invokeMerchantCallback,
20
+ readSetupAdvertisement,
21
+ resolveBillingApiUrl,
22
+ setupOverlayLabels,
23
+ stripeMethodCategory
24
+ } from "@flopay/shared";
25
+ import {
26
+ defineAsyncComponent,
27
+ defineComponent,
28
+ h,
29
+ onBeforeUnmount,
30
+ onMounted,
31
+ ref,
32
+ shallowRef,
33
+ watch
34
+ } from "vue";
35
+ var VERIFY_ATTEMPTS = 15;
36
+ var VERIFY_INTERVAL_MS = 2e3;
37
+ function delay(ms) {
38
+ return new Promise((resolve) => setTimeout(resolve, ms));
39
+ }
40
+ var StripeSetupMethods = defineAsyncComponent(
41
+ () => import("./stripe-elements-QGT45VIE.mjs").then((module) => module.StripePaymentMethods)
42
+ );
43
+ var PayPalSetupMethods = defineAsyncComponent(
44
+ () => import("./payment-method-setup-paypal-XPSU4CHA.mjs").then((module) => module.PayPalSetupMethod)
45
+ );
46
+ var FloPayPaymentMethodSetup = defineComponent({
47
+ name: "FloPayPaymentMethodSetup",
48
+ props: {
49
+ sessionId: { type: String, default: void 0 },
50
+ nonce: { type: String, default: void 0 },
51
+ billingApiUrl: { type: String, default: void 0 },
52
+ telemetry: { type: Boolean, default: void 0 },
53
+ theme: {
54
+ type: [String, Object],
55
+ default: "modern-light"
56
+ },
57
+ containerStyle: {
58
+ type: Object,
59
+ default: void 0
60
+ },
61
+ appearance: { type: Object, default: void 0 },
62
+ buttonsStyles: { type: Object, default: void 0 }
63
+ },
64
+ emits: {
65
+ ready: () => true,
66
+ complete: (_event) => true,
67
+ cancel: (_event) => true,
68
+ decline: (_event) => true,
69
+ validation: (_message) => true,
70
+ error: (_error) => true
71
+ },
72
+ setup(props, { emit, slots }) {
73
+ const overlay = shallowRef(null);
74
+ const setup = useHostedSetup(
75
+ props,
76
+ {
77
+ complete: (event) => emit("complete", event),
78
+ decline: (event) => emit("decline", event),
79
+ cancel: (event) => emit("cancel", event),
80
+ error: (error) => emit("error", error),
81
+ // The hosted card's outcome overlay, in card wording (React's `'card'`).
82
+ overlay: (state) => {
83
+ showOverlay(state ? { ...state, wording: "card" } : null);
84
+ }
85
+ },
86
+ { requireVault: (session) => readSetupAdvertisement(session).card }
87
+ );
88
+ const theme = useCardSetupTheme(props);
89
+ const verified = ref(false);
90
+ let mounted = false;
91
+ let submissionInFlight = false;
92
+ let completionEmitted = false;
93
+ let verificationGeneration = 0;
94
+ let overlayTimer = null;
95
+ const sessionId = () => props.sessionId ?? "";
96
+ const showOverlay = (next) => {
97
+ if (overlayTimer) clearTimeout(overlayTimer);
98
+ overlayTimer = null;
99
+ overlay.value = next;
100
+ };
101
+ const clearOverlayLater = () => {
102
+ overlayTimer = setTimeout(() => {
103
+ overlayTimer = null;
104
+ if (mounted) overlay.value = null;
105
+ }, PROCESSING_OVERLAY_ERROR_DELAY_MS);
106
+ };
107
+ onMounted(() => {
108
+ mounted = true;
109
+ });
110
+ onBeforeUnmount(() => {
111
+ mounted = false;
112
+ verificationGeneration += 1;
113
+ if (overlayTimer) clearTimeout(overlayTimer);
114
+ overlayTimer = null;
115
+ });
116
+ const reportVerified = () => {
117
+ setup.settle();
118
+ submissionInFlight = false;
119
+ verified.value = true;
120
+ showOverlay({ status: "success", wording: "payment_method", message: null });
121
+ setTimeout(() => {
122
+ if (completionEmitted) return;
123
+ completionEmitted = true;
124
+ invokeMerchantCallback(
125
+ () => emit("complete", { status: "succeeded", sessionId: sessionId() })
126
+ );
127
+ if (mounted) overlay.value = null;
128
+ }, PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
129
+ };
130
+ const reportDecline = (event) => {
131
+ setup.settle();
132
+ submissionInFlight = false;
133
+ showOverlay({ status: "error", wording: "payment_method", message: event.message ?? null });
134
+ invokeMerchantCallback(
135
+ () => emit("decline", {
136
+ status: "declined",
137
+ sessionId: sessionId(),
138
+ ...event.reason ? { reason: event.reason } : {},
139
+ ...event.message ? { message: event.message } : {}
140
+ })
141
+ );
142
+ clearOverlayLater();
143
+ };
144
+ const reportFailure = (error) => {
145
+ if (submissionInFlight) {
146
+ submissionInFlight = false;
147
+ showOverlay({ status: "error", wording: "payment_method", message: error.message });
148
+ clearOverlayLater();
149
+ } else {
150
+ showOverlay(null);
151
+ }
152
+ invokeMerchantCallback(() => emit("error", error));
153
+ };
154
+ const verifySetup = async (options = {}) => {
155
+ verificationGeneration += 1;
156
+ const generation = verificationGeneration;
157
+ const stale = () => !mounted || generation !== verificationGeneration;
158
+ submissionInFlight = true;
159
+ showOverlay({ status: "processing", wording: "payment_method", message: null });
160
+ const api = new PaymentAPI(resolveBillingApiUrl(props.billingApiUrl), {
161
+ telemetry: props.telemetry
162
+ });
163
+ try {
164
+ for (let attempt = 0; attempt < VERIFY_ATTEMPTS; attempt += 1) {
165
+ if (attempt > 0 || options.waitFirst) await delay(VERIFY_INTERVAL_MS);
166
+ if (stale()) return;
167
+ const result = await api.getUnifiedCheckoutSession(sessionId(), props.nonce);
168
+ if (stale()) return;
169
+ const status = result.data.session?.status;
170
+ if (status === "complete") {
171
+ reportVerified();
172
+ return;
173
+ }
174
+ if (status === "expired") {
175
+ throw new FloPayError(
176
+ "This setup session expired before the payment method was saved.",
177
+ "validation_error",
178
+ { code: "payment_method_setup_expired" }
179
+ );
180
+ }
181
+ }
182
+ throw new FloPayError(
183
+ "The payment method could not be confirmed yet. Please try again.",
184
+ "api_error",
185
+ { code: "payment_method_setup_unverified" }
186
+ );
187
+ } catch (error) {
188
+ if (stale()) return;
189
+ const unverified = error instanceof FloPayError && error.code === "payment_method_setup_unverified";
190
+ reportFailure(asCardSetupError(error, unverified ? { retryable: true } : {}));
191
+ } finally {
192
+ api.destroy();
193
+ }
194
+ };
195
+ const redirectReturn = readSetupRedirectReturn();
196
+ if (redirectReturn) {
197
+ const stopRedirectWatch = watch(
198
+ () => setup.session.value,
199
+ (normalized) => {
200
+ if (!normalized) return;
201
+ stopRedirectWatch();
202
+ clearSetupRedirectParams();
203
+ if (normalized.data.session?.status === "complete") {
204
+ reportVerified();
205
+ return;
206
+ }
207
+ if (redirectReturn.failed) {
208
+ reportDecline({ reason: "redirect_failed" });
209
+ return;
210
+ }
211
+ void verifySetup({ waitFirst: true });
212
+ }
213
+ );
214
+ }
215
+ const overlayNode = () => {
216
+ const state = overlay.value;
217
+ if (!state) return null;
218
+ return h(ProcessingOverlay, {
219
+ status: state.status,
220
+ errorMessage: state.message,
221
+ ...setupOverlayLabels(state.wording),
222
+ successNote: null,
223
+ ...theme.overlayTheme.value ? { theme: theme.overlayTheme.value } : {}
224
+ });
225
+ };
226
+ return () => {
227
+ if (setup.loadError.value) {
228
+ return h("div", { role: "alert" }, setup.loadError.value.message);
229
+ }
230
+ const normalized = setup.session.value;
231
+ if (!normalized) return slots.loading?.() ?? null;
232
+ if (verified.value) {
233
+ return h("div", { "data-testid": "flopay-payment-method-setup" }, [overlayNode()]);
234
+ }
235
+ const advertisement = readSetupAdvertisement(normalized);
236
+ const session = normalized.data.session ?? null;
237
+ const billingApiUrl = resolveBillingApiUrl(props.billingApiUrl);
238
+ const stripeBranch = (entry) => h("div", { key: entry.method, "data-flopay-payment-method": entry.method }, [
239
+ h(StripeSetupMethods, {
240
+ session,
241
+ sessionId: sessionId(),
242
+ nonce: props.nonce,
243
+ billingApiUrl,
244
+ publishableKey: entry.publishableKey,
245
+ methods: [entry.method],
246
+ intentKind: "setup",
247
+ currency: entry.currency,
248
+ ...theme.appearance.value ? { appearance: theme.appearance.value } : {},
249
+ // The provider said yes; wait for the backend to agree.
250
+ onComplete: () => {
251
+ void verifySetup();
252
+ },
253
+ onDecline: (decline) => reportDecline({
254
+ ...decline.code ? { reason: decline.code } : {},
255
+ ...decline.message ? { message: decline.message } : {}
256
+ }),
257
+ onError: (error) => reportFailure(asCardSetupError(error))
258
+ })
259
+ ]);
260
+ const paypal = advertisement.paypal;
261
+ const paypalBranch = paypal ? h("div", { key: "paypal", "data-flopay-payment-method": "paypal" }, [
262
+ h(PayPalSetupMethods, {
263
+ clientId: paypal.clientId,
264
+ ...paypal.environment ? { environment: paypal.environment } : {},
265
+ ...advertisement.currency ? { currency: advertisement.currency } : {},
266
+ sessionId: sessionId(),
267
+ nonce: props.nonce ?? "",
268
+ billingApiUrl,
269
+ telemetry: props.telemetry,
270
+ customer: {
271
+ userId: session?.customer?.id ?? "",
272
+ email: session?.customer?.email ?? ""
273
+ },
274
+ onSubmitting: () => {
275
+ submissionInFlight = true;
276
+ showOverlay({ status: "processing", wording: "payment_method", message: null });
277
+ },
278
+ onConfirmed: () => {
279
+ void verifySetup();
280
+ },
281
+ onFailed: (error, options) => reportFailure(asCardSetupError(error, options)),
282
+ // Closing PayPal's window is not an outcome: no cancel, decline or error.
283
+ onAbandoned: () => {
284
+ submissionInFlight = false;
285
+ showOverlay(null);
286
+ }
287
+ })
288
+ ]) : null;
289
+ const capture = setup.capture.value;
290
+ const vault = setup.vault.value;
291
+ const card = advertisement.card && capture && vault?.html ? h("div", { key: "card", "data-flopay-payment-method": "card" }, [
292
+ h(VaultCardFields, {
293
+ capture,
294
+ html: vault.html,
295
+ messageToken: vault.messageToken,
296
+ expectedOrigin: vault.expectedOrigin,
297
+ verificationRequired: vault.verificationRequired,
298
+ theme: theme.vaultTheme.value,
299
+ containerStyle: theme.surfaceStyle.value,
300
+ onReady: () => invokeMerchantCallback(() => emit("ready")),
301
+ onValidation: (message) => invokeMerchantCallback(() => emit("validation", message)),
302
+ onError: (message) => {
303
+ if (!message) return;
304
+ setup.widgetFailed(message);
305
+ const error = asCardSetupError(new Error(message), {
306
+ code: "card_setup_widget_failed",
307
+ retryable: true
308
+ });
309
+ invokeMerchantCallback(() => emit("error", error));
310
+ }
311
+ })
312
+ ]) : null;
313
+ const wallets = advertisement.stripe.filter(
314
+ ({ method }) => stripeMethodCategory(method) === "wallet"
315
+ );
316
+ const tiles = advertisement.stripe.filter(
317
+ ({ method }) => stripeMethodCategory(method) !== "wallet"
318
+ );
319
+ return h("div", { "data-testid": "flopay-payment-method-setup" }, [
320
+ overlayNode(),
321
+ ...wallets.map(stripeBranch),
322
+ paypalBranch,
323
+ ...tiles.map(stripeBranch),
324
+ card
325
+ ]);
326
+ };
327
+ }
328
+ });
329
+
330
+ export {
331
+ FloPayPaymentMethodSetup
332
+ };