@12-apps/payments-frontend 1.0.0
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/eslint.config.js +34 -0
- package/package.json +63 -0
- package/src/__tests__/checkout-confirmation.test.tsx +177 -0
- package/src/__tests__/connection-state.test.tsx +84 -0
- package/src/__tests__/context.test.tsx +88 -0
- package/src/__tests__/controlled-provider.test.tsx +116 -0
- package/src/__tests__/credential-confirm.test.tsx +193 -0
- package/src/__tests__/initial-provider.test.tsx +81 -0
- package/src/__tests__/provider-priority-list.test.tsx +159 -0
- package/src/__tests__/provider-status-bar.test.tsx +152 -0
- package/src/__tests__/verification-slot.test.tsx +125 -0
- package/src/client.ts +200 -0
- package/src/components/CheckoutFlow.tsx +169 -0
- package/src/components/CheckoutPayment.tsx +379 -0
- package/src/components/ConfirmCredentialSave.tsx +106 -0
- package/src/components/CredentialFields.tsx +158 -0
- package/src/components/CredentialFormAlerts.tsx +144 -0
- package/src/components/EnvironmentTabs.tsx +109 -0
- package/src/components/PaymentProviderSettings.tsx +267 -0
- package/src/components/ProviderConnection.tsx +251 -0
- package/src/components/ProviderCredentialForm.tsx +387 -0
- package/src/components/ProviderList.tsx +126 -0
- package/src/components/ProviderPanel.tsx +300 -0
- package/src/components/ProviderPriorityList.tsx +293 -0
- package/src/components/ProviderSetupGuide.tsx +263 -0
- package/src/components/ProviderStatusBar.tsx +192 -0
- package/src/components/SetupGuideSection.tsx +190 -0
- package/src/components/checkout-ack.ts +106 -0
- package/src/components/connection-state.ts +66 -0
- package/src/components/credential-rules.ts +120 -0
- package/src/components/rich-text.tsx +27 -0
- package/src/components/settings-state.ts +211 -0
- package/src/context.tsx +144 -0
- package/src/index.ts +69 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Fragment, type ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `**bold**`, and nothing else.
|
|
7
|
+
*
|
|
8
|
+
* Adapters author their walkthrough as plain strings — the content crosses an
|
|
9
|
+
* HTTP boundary, so it cannot carry markup — but the emphasis is load-bearing
|
|
10
|
+
* copy rather than decoration: `alterar` and `qual conta` are the words that
|
|
11
|
+
* say what the sentence is warning ABOUT. Shouting them in capitals was the
|
|
12
|
+
* workaround, and it reads as a raised voice while being harder to scan.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately not a markdown library. Two asterisks is the entire grammar, an
|
|
15
|
+
* unmatched pair renders verbatim, and nothing here can emit HTML — the text is
|
|
16
|
+
* provider-authored, and a renderer that interpreted more would be an injection
|
|
17
|
+
* surface on a payments screen.
|
|
18
|
+
*/
|
|
19
|
+
export function richText(text: string): ReactNode {
|
|
20
|
+
return text.split(/\*\*(.+?)\*\*/g).map((chunk, index) =>
|
|
21
|
+
index % 2 === 1 ? (
|
|
22
|
+
<strong key={index}>{chunk}</strong>
|
|
23
|
+
) : (
|
|
24
|
+
<Fragment key={index}>{chunk}</Fragment>
|
|
25
|
+
),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
MaskedProviderConfig,
|
|
7
|
+
MerchantSettingsView,
|
|
8
|
+
ProviderDescriptor,
|
|
9
|
+
ProviderSetupGuide as Guide,
|
|
10
|
+
} from '@12-apps/payments-backend';
|
|
11
|
+
|
|
12
|
+
import type { PaymentsSettingsClient } from '../client';
|
|
13
|
+
import { useCheckoutAck } from './checkout-ack';
|
|
14
|
+
import { CHECKOUT_CONFIRM_ACTION } from './SetupGuideSection';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Everything the settings screen KNOWS, separated from what it draws.
|
|
18
|
+
*
|
|
19
|
+
* The component had accumulated five hooks and three derivations above its
|
|
20
|
+
* early returns — the position where a React component is least readable,
|
|
21
|
+
* because none of it can be moved and all of it has to be understood before
|
|
22
|
+
* the first line of markup. Split out, that file is about layout and this one
|
|
23
|
+
* is about state.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Fetch the selected provider's walkthrough.
|
|
28
|
+
*
|
|
29
|
+
* `progressKey` exists because a guide is no longer a constant per provider:
|
|
30
|
+
* the server decides which step to show from what the store has already done,
|
|
31
|
+
* so the walkthrough goes stale the moment the connection changes. Keyed on
|
|
32
|
+
* the provider alone, saving a credential left the owner reading the step they
|
|
33
|
+
* had just completed — the screen said "informe sua InfiniteTag" underneath
|
|
34
|
+
* the tag they had saved a second earlier.
|
|
35
|
+
*
|
|
36
|
+
* A derived STRING rather than the config object: the settings view is
|
|
37
|
+
* replaced on every reload, so an object dependency would refetch the guide
|
|
38
|
+
* after any action at all, including ones that cannot change it.
|
|
39
|
+
*/
|
|
40
|
+
export function useSetupGuide(
|
|
41
|
+
client: PaymentsSettingsClient,
|
|
42
|
+
provider: string | null,
|
|
43
|
+
progressKey: string,
|
|
44
|
+
): { guide: Guide | null; loaded: boolean } {
|
|
45
|
+
const [state, setState] = useState<{ guide: Guide | null; loaded: boolean }>({
|
|
46
|
+
guide: null,
|
|
47
|
+
loaded: false,
|
|
48
|
+
});
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setState({ guide: null, loaded: false });
|
|
51
|
+
if (!provider) return;
|
|
52
|
+
client
|
|
53
|
+
.getSetupGuide(provider)
|
|
54
|
+
// `loaded` is not cosmetic. "No guide yet" and "this provider ships no
|
|
55
|
+
// guide" are the same `null`, and the difference decides whether a REAL
|
|
56
|
+
// payment button is on screen — so for one frame after every load, a
|
|
57
|
+
// store mid-setup was offered the charge before anything knew which step
|
|
58
|
+
// it was on.
|
|
59
|
+
.then((guide) => setState({ guide, loaded: true }))
|
|
60
|
+
.catch(() => setState({ guide: null, loaded: true }));
|
|
61
|
+
}, [client, provider, progressKey]);
|
|
62
|
+
return state;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The facts a guide branches on, flattened to a comparable string: which
|
|
67
|
+
* credential fields are filled in the active environment, whether the probe
|
|
68
|
+
* passed, and whether a charge has proved it.
|
|
69
|
+
*/
|
|
70
|
+
export function progressKeyOf(config: MaskedProviderConfig | null): string {
|
|
71
|
+
if (!config) return 'none';
|
|
72
|
+
const configured = Object.entries(config.environments[config.environment] ?? {})
|
|
73
|
+
.filter(([, field]) => field.configured)
|
|
74
|
+
.map(([key]) => key)
|
|
75
|
+
.sort()
|
|
76
|
+
.join(',');
|
|
77
|
+
return `${config.environment}|${config.status}|${config.chargeVerifiedAt ?? ''}|${configured}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Is the walkthrough still waiting on the owner to confirm something?
|
|
82
|
+
*
|
|
83
|
+
* The one input to `blocked`. It lives with the guide rather than inside the
|
|
84
|
+
* activation step because the guide is what knows a confirmable section is
|
|
85
|
+
* still on screen; the step only knows about charges.
|
|
86
|
+
*/
|
|
87
|
+
export function guideAwaitsConfirmation(
|
|
88
|
+
guide: Guide | null,
|
|
89
|
+
confirmed: boolean,
|
|
90
|
+
loaded: boolean,
|
|
91
|
+
): boolean {
|
|
92
|
+
if (confirmed) return false;
|
|
93
|
+
// Withheld until we know. Erring towards blocked costs at most a moment's
|
|
94
|
+
// wait on a button; erring the other way puts "Pagar e ativar" in front of an
|
|
95
|
+
// owner whose setup we have not read yet, and that one is paid for in money.
|
|
96
|
+
if (!loaded) return true;
|
|
97
|
+
if (!guide) return false;
|
|
98
|
+
return guide.sections.some((section) =>
|
|
99
|
+
section.steps.some((step) => step.action === CHECKOUT_CONFIRM_ACTION),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Has the store settled the setup step that only it can see?
|
|
105
|
+
*
|
|
106
|
+
* Two sources, in order of authority. A charge that LANDED proves the
|
|
107
|
+
* provider-side switch is on far better than any answer could, so a store that
|
|
108
|
+
* reconnects from a machine holding no stored confirmation is never sent back
|
|
109
|
+
* through step 2. Failing that, the owner's own word — scoped to this client's
|
|
110
|
+
* tenant-specific base URL, so an owner who runs two stores answers for each.
|
|
111
|
+
*/
|
|
112
|
+
export function useSetupConfirmation(
|
|
113
|
+
client: PaymentsSettingsClient,
|
|
114
|
+
active: ProviderDescriptor | null,
|
|
115
|
+
config: MaskedProviderConfig | null,
|
|
116
|
+
): { confirmed: boolean; confirm: () => void; withdraw: () => void } {
|
|
117
|
+
const ack = useCheckoutAck(client.baseUrl, active ? active.name : null);
|
|
118
|
+
const proven = Boolean(config?.chargeVerifiedAt);
|
|
119
|
+
return { ...ack, confirmed: proven ? true : ack.confirmed };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The provider the owner has open, and the config row that belongs to it.
|
|
124
|
+
*
|
|
125
|
+
* One hook rather than two `useMemo`s inline, because both have to be resolved
|
|
126
|
+
* BEFORE the loading and error returns — the guide hook depends on them — and
|
|
127
|
+
* that is exactly the position where a component accumulates the branches that
|
|
128
|
+
* make it unreadable.
|
|
129
|
+
*/
|
|
130
|
+
export function useOpenProvider(
|
|
131
|
+
view: MerchantSettingsView | null,
|
|
132
|
+
selected: string | null,
|
|
133
|
+
): { active: ProviderDescriptor | null; activeConfig: MaskedProviderConfig | null } {
|
|
134
|
+
// Nothing is open until the owner picks: the landing view is the list of
|
|
135
|
+
// providers, not one arbitrary provider's configuration.
|
|
136
|
+
const active = useMemo(() => {
|
|
137
|
+
if (!view || !selected) return null;
|
|
138
|
+
return view.providers.find((p) => p.name === selected) ?? null;
|
|
139
|
+
}, [view, selected]);
|
|
140
|
+
|
|
141
|
+
// A stored connection is what makes the manual walkthrough steps redundant —
|
|
142
|
+
// and, since the guide branches on it, what decides which step is shown.
|
|
143
|
+
const activeConfig = useMemo(() => {
|
|
144
|
+
if (!view || !active) return null;
|
|
145
|
+
return view.configs.find((c) => c.provider === active.name) ?? null;
|
|
146
|
+
}, [view, active]);
|
|
147
|
+
|
|
148
|
+
return { active, activeConfig };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function useSettingsState(client: PaymentsSettingsClient) {
|
|
152
|
+
const [view, setView] = useState<MerchantSettingsView | null>(null);
|
|
153
|
+
const [error, setError] = useState<string | null>(null);
|
|
154
|
+
const reload = useCallback(async () => {
|
|
155
|
+
try {
|
|
156
|
+
setView(await client.getSettings());
|
|
157
|
+
setError(null);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
160
|
+
}
|
|
161
|
+
}, [client]);
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
void reload();
|
|
164
|
+
}, [reload]);
|
|
165
|
+
return { view, error, reload };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Which provider is open, and the once-only handover from the host.
|
|
170
|
+
*
|
|
171
|
+
* Seeding `useState` with `initialProvider` is not enough: the host reads the
|
|
172
|
+
* provider from the OAuth callback's query string in an EFFECT, so on first
|
|
173
|
+
* render it is still null — and `useState` keeps that null forever. The owner
|
|
174
|
+
* therefore landed on the list after authorizing, which is the bug this fixed
|
|
175
|
+
* twice.
|
|
176
|
+
*
|
|
177
|
+
* Applied ONCE. The host holds the outcome for the page's lifetime, so
|
|
178
|
+
* re-applying it would make "Voltar aos provedores" bounce straight back into
|
|
179
|
+
* the provider and trap the owner on one screen.
|
|
180
|
+
*
|
|
181
|
+
* None of that applies when the host is CONTROLLING the selection: there is no
|
|
182
|
+
* handover to make, because the host's value already is the answer on every
|
|
183
|
+
* render. `controlled === undefined` is the only test for that — `null` is a
|
|
184
|
+
* controlled host legitimately saying "the list", and treating it as absent
|
|
185
|
+
* would strand the owner in whichever provider they last opened.
|
|
186
|
+
*/
|
|
187
|
+
export function useSelectedProvider(
|
|
188
|
+
initialProvider: string | null,
|
|
189
|
+
controlled: string | null | undefined,
|
|
190
|
+
onProviderChange: ((provider: string | null) => void) | undefined,
|
|
191
|
+
) {
|
|
192
|
+
const isControlled = controlled !== undefined;
|
|
193
|
+
const [internal, setInternal] = useState<string | null>(initialProvider);
|
|
194
|
+
const honoredInitial = useRef(false);
|
|
195
|
+
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (isControlled || honoredInitial.current || !initialProvider) return;
|
|
198
|
+
honoredInitial.current = true;
|
|
199
|
+
setInternal(initialProvider);
|
|
200
|
+
}, [initialProvider, isControlled]);
|
|
201
|
+
|
|
202
|
+
const setSelected = useCallback(
|
|
203
|
+
(provider: string | null) => {
|
|
204
|
+
if (!isControlled) setInternal(provider);
|
|
205
|
+
onProviderChange?.(provider);
|
|
206
|
+
},
|
|
207
|
+
[isControlled, onProviderChange],
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return { selected: isControlled ? controlled : internal, setSelected };
|
|
211
|
+
}
|
package/src/context.tsx
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
type ReactNode,
|
|
11
|
+
} from 'react';
|
|
12
|
+
|
|
13
|
+
import type { ClientChargeView } from '@12-apps/payments-backend';
|
|
14
|
+
|
|
15
|
+
import { isSettled, type ClientChargeRequest, type PaymentsClient } from './client';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Headless React bindings — state and flow only, ZERO UI. Visual checkout
|
|
19
|
+
* components belong to each host's design system (`@12-apps/ui` here); these
|
|
20
|
+
* hooks make every such UI provider-agnostic: the same `useCreateCharge` +
|
|
21
|
+
* `useChargeStatus` pair drives a PIX QR screen, a card form, or an
|
|
22
|
+
* InfinitePay redirect, switching on `charge.method` / `hostedCheckoutUrl`.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const PaymentsContext = createContext<PaymentsClient | null>(null);
|
|
26
|
+
|
|
27
|
+
export interface PaymentsProviderProps {
|
|
28
|
+
client: PaymentsClient;
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function PaymentsProvider({ client, children }: PaymentsProviderProps): ReactNode {
|
|
33
|
+
return <PaymentsContext.Provider value={client}>{children}</PaymentsContext.Provider>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function usePaymentsClient(): PaymentsClient {
|
|
37
|
+
const client = useContext(PaymentsContext);
|
|
38
|
+
if (!client) {
|
|
39
|
+
throw new Error('usePaymentsClient must be used inside a <PaymentsProvider>');
|
|
40
|
+
}
|
|
41
|
+
return client;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface CreateChargeState {
|
|
45
|
+
charge: ClientChargeView | null;
|
|
46
|
+
loading: boolean;
|
|
47
|
+
error: Error | null;
|
|
48
|
+
create(request: ClientChargeRequest): Promise<ClientChargeView | null>;
|
|
49
|
+
reset(): void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Imperative charge creation with loading/error state. */
|
|
53
|
+
export function useCreateCharge(): CreateChargeState {
|
|
54
|
+
const client = usePaymentsClient();
|
|
55
|
+
const [charge, setCharge] = useState<ClientChargeView | null>(null);
|
|
56
|
+
const [loading, setLoading] = useState(false);
|
|
57
|
+
const [error, setError] = useState<Error | null>(null);
|
|
58
|
+
|
|
59
|
+
const create = useCallback(
|
|
60
|
+
async (request: ClientChargeRequest) => {
|
|
61
|
+
setLoading(true);
|
|
62
|
+
setError(null);
|
|
63
|
+
try {
|
|
64
|
+
const created = await client.createCharge(request);
|
|
65
|
+
setCharge(created);
|
|
66
|
+
return created;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
69
|
+
return null;
|
|
70
|
+
} finally {
|
|
71
|
+
setLoading(false);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
[client],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const reset = useCallback(() => {
|
|
78
|
+
setCharge(null);
|
|
79
|
+
setError(null);
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
return { charge, loading, error, create, reset };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ChargeStatusOptions {
|
|
86
|
+
/** Poll interval; default 4000ms. Polling stops on settled states. */
|
|
87
|
+
intervalMs?: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ChargeStatusState {
|
|
91
|
+
charge: ClientChargeView | null;
|
|
92
|
+
/** True once the charge reached a state that will not change by waiting. */
|
|
93
|
+
settled: boolean;
|
|
94
|
+
error: Error | null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Identifies one charge to poll: which provider, which provider-side id. */
|
|
98
|
+
export interface ChargeRef {
|
|
99
|
+
provider: string;
|
|
100
|
+
providerChargeId: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Polls the host for a charge until it settles (webhooks update the server;
|
|
105
|
+
* the browser polls the server — never the provider). Pass `null` to idle.
|
|
106
|
+
*/
|
|
107
|
+
export function useChargeStatus(
|
|
108
|
+
ref: ChargeRef | null,
|
|
109
|
+
options: ChargeStatusOptions = {},
|
|
110
|
+
): ChargeStatusState {
|
|
111
|
+
const client = usePaymentsClient();
|
|
112
|
+
const intervalMs = options.intervalMs ?? 4000;
|
|
113
|
+
const [charge, setCharge] = useState<ClientChargeView | null>(null);
|
|
114
|
+
const [error, setError] = useState<Error | null>(null);
|
|
115
|
+
const settled = charge !== null && isSettled(charge.status);
|
|
116
|
+
const settledRef = useRef(settled);
|
|
117
|
+
settledRef.current = settled;
|
|
118
|
+
const provider = ref?.provider ?? null;
|
|
119
|
+
const providerChargeId = ref?.providerChargeId ?? null;
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (!provider || !providerChargeId) return undefined;
|
|
123
|
+
let disposed = false;
|
|
124
|
+
|
|
125
|
+
const tick = async (): Promise<void> => {
|
|
126
|
+
if (disposed || settledRef.current) return;
|
|
127
|
+
try {
|
|
128
|
+
const fresh = await client.getCharge(provider, providerChargeId);
|
|
129
|
+
if (!disposed) setCharge(fresh);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
if (!disposed) setError(err instanceof Error ? err : new Error(String(err)));
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
void tick();
|
|
136
|
+
const timer = setInterval(() => void tick(), intervalMs);
|
|
137
|
+
return () => {
|
|
138
|
+
disposed = true;
|
|
139
|
+
clearInterval(timer);
|
|
140
|
+
};
|
|
141
|
+
}, [client, provider, providerChargeId, intervalMs]);
|
|
142
|
+
|
|
143
|
+
return { charge, settled, error };
|
|
144
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@12-apps/payments-frontend` — the browser half of the payments platform.
|
|
3
|
+
*
|
|
4
|
+
* Plug-and-play MUI components for the two payment surfaces (per-provider
|
|
5
|
+
* settings page, checkout payment step), plus the headless hooks and fetch
|
|
6
|
+
* clients they are built on for hosts that want their own pixels. Imports
|
|
7
|
+
* only TYPES from `@12-apps/payments-backend`; talks exclusively to the host's
|
|
8
|
+
* mounted payments HTTP surface.
|
|
9
|
+
*/
|
|
10
|
+
export {
|
|
11
|
+
createPaymentsClient,
|
|
12
|
+
createPaymentsSettingsClient,
|
|
13
|
+
isSettled,
|
|
14
|
+
PaymentsClientError,
|
|
15
|
+
type ClientChargeRequest,
|
|
16
|
+
type ClientPaymentsConfig,
|
|
17
|
+
type PaymentsClient,
|
|
18
|
+
type PaymentsClientOptions,
|
|
19
|
+
type PaymentsSettingsClient,
|
|
20
|
+
} from './client';
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
PaymentsProvider,
|
|
24
|
+
useChargeStatus,
|
|
25
|
+
useCreateCharge,
|
|
26
|
+
usePaymentsClient,
|
|
27
|
+
type ChargeRef,
|
|
28
|
+
type ChargeStatusOptions,
|
|
29
|
+
type ChargeStatusState,
|
|
30
|
+
type CreateChargeState,
|
|
31
|
+
type PaymentsProviderProps,
|
|
32
|
+
} from './context';
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
CheckoutPayment,
|
|
36
|
+
type CardFormValues,
|
|
37
|
+
type CheckoutPaymentProps,
|
|
38
|
+
type SavedCardOption,
|
|
39
|
+
} from './components/CheckoutPayment';
|
|
40
|
+
export { CheckoutFlow, type CheckoutFlowProps } from './components/CheckoutFlow';
|
|
41
|
+
export {
|
|
42
|
+
ProviderConnection,
|
|
43
|
+
type ProviderConnectionProps,
|
|
44
|
+
} from './components/ProviderConnection';
|
|
45
|
+
export {
|
|
46
|
+
ProviderPriorityList,
|
|
47
|
+
type ProviderPriorityListProps,
|
|
48
|
+
} from './components/ProviderPriorityList';
|
|
49
|
+
export {
|
|
50
|
+
ProviderSetupGuide,
|
|
51
|
+
type ProviderSetupGuideProps,
|
|
52
|
+
} from './components/ProviderSetupGuide';
|
|
53
|
+
export {
|
|
54
|
+
CHECKOUT_CONFIRM_ACTION,
|
|
55
|
+
SetupGuideSection,
|
|
56
|
+
type SetupGuideSectionProps,
|
|
57
|
+
} from './components/SetupGuideSection';
|
|
58
|
+
export { ProviderStatusBar, statusBadge } from './components/ProviderStatusBar';
|
|
59
|
+
export {
|
|
60
|
+
PaymentProviderSettings,
|
|
61
|
+
type PaymentProviderSettingsProps,
|
|
62
|
+
} from './components/PaymentProviderSettings';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Re-exported because it appears in the `prepareConnect` prop a host must
|
|
66
|
+
* implement: without it the host could not type its own callback without
|
|
67
|
+
* taking a direct dependency on the backend package.
|
|
68
|
+
*/
|
|
69
|
+
export type { PaymentEnvironment } from '@12-apps/payments-backend';
|