@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,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,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
|
+
};
|