@12-apps/payments-frontend 1.16.0 → 1.18.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.
@@ -0,0 +1,175 @@
1
+ 'use client';
2
+
3
+ import { Alert, Box, Button, Stack, Typography } from '@mui/material';
4
+ import { useState, type ReactNode } from 'react';
5
+
6
+ import type { ConnectApplicationStatus, PaymentEnvironment } from '@12-apps/payments-backend';
7
+
8
+ /**
9
+ * One environment's Connect application (FUT-479, packaged by FUT-573).
10
+ * Sandbox and produção are SEPARATE applications with separate id/secret
11
+ * pairs, so each card stands on its own: configured or not, what PagBank says
12
+ * is registered, and whether the registered redirect URI matches the
13
+ * deployment's callback.
14
+ */
15
+
16
+ const ENV_LABEL: Record<PaymentEnvironment, string> = {
17
+ SANDBOX: 'Sandbox',
18
+ PRODUCTION: 'Produção',
19
+ };
20
+
21
+ /** The bordered-card look every block of these screens shares. */
22
+ export const CARD_SX = {
23
+ border: 1,
24
+ borderColor: 'divider',
25
+ borderRadius: 2,
26
+ p: 2,
27
+ } as const;
28
+
29
+ function Field({ label, children }: { label: string; children: ReactNode }): ReactNode {
30
+ return (
31
+ <Box
32
+ sx={{ display: 'flex', flexDirection: 'column', gap: 0.25, minWidth: 180, wordBreak: 'break-all' }}
33
+ >
34
+ <Typography variant="caption" color="text.secondary" fontWeight={600}>
35
+ {label}
36
+ </Typography>
37
+ <Typography component="span" variant="body2">
38
+ {children}
39
+ </Typography>
40
+ </Box>
41
+ );
42
+ }
43
+
44
+ /** The verdict the screen exists for: does the registered callback match ours? */
45
+ function MismatchAlert({ status }: { status: ConnectApplicationStatus }): ReactNode {
46
+ if (status.application === null) return null;
47
+ if (status.redirectUriMismatch === true) {
48
+ return (
49
+ <Alert severity="error" data-testid={`connect-mismatch-${status.environment}`}>
50
+ A redirect_uri registrada no PagBank é diferente do callback desta instalação. O fluxo
51
+ de autorização OAuth falha silenciosamente até o cadastro ser corrigido no PagBank.
52
+ </Alert>
53
+ );
54
+ }
55
+ if (status.redirectUriMismatch === false) {
56
+ return (
57
+ <Alert severity="success" data-testid={`connect-match-${status.environment}`}>
58
+ A redirect_uri registrada confere com o callback desta instalação.
59
+ </Alert>
60
+ );
61
+ }
62
+ return (
63
+ <Alert severity="warning" data-testid={`connect-unknown-${status.environment}`}>
64
+ A resposta do PagBank não informou a redirect_uri — não foi possível comparar com o
65
+ callback desta instalação.
66
+ </Alert>
67
+ );
68
+ }
69
+
70
+ /** What PagBank reports as registered, plus whatever extra keys came back. */
71
+ function ApplicationFields({ status }: { status: ConnectApplicationStatus }): ReactNode {
72
+ const app = status.application;
73
+ if (app === null) return null;
74
+ const extraKeys = Object.keys(app.extra);
75
+ return (
76
+ <Stack spacing={1.5}>
77
+ <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2.5 }}>
78
+ <Field label="Nome (exibido ao lojista)">{app.name ?? '—'}</Field>
79
+ <Field label="Site">{app.site ?? '—'}</Field>
80
+ <Field label="Descrição">{app.description ?? '—'}</Field>
81
+ <Field label="Logo">{app.logo ?? '—'}</Field>
82
+ <Field label="redirect_uri registrada">{app.redirectUri ?? 'não informada'}</Field>
83
+ </Box>
84
+ {extraKeys.length > 0 ? (
85
+ <Box data-testid={`connect-extra-${status.environment}`}>
86
+ <Typography variant="caption" color="text.secondary" fontWeight={600}>
87
+ Outros campos retornados (schema não documentado)
88
+ </Typography>
89
+ <Box component="pre" sx={{ m: 0, fontSize: 12, overflowX: 'auto' }}>
90
+ {JSON.stringify(app.extra, null, 2)}
91
+ </Box>
92
+ </Box>
93
+ ) : null}
94
+ </Stack>
95
+ );
96
+ }
97
+
98
+ /**
99
+ * The collapsible "what feeds this environment" help. The variable names are
100
+ * the HOST's own configuration surface, so they arrive via `configVars`; with
101
+ * none provided the toggle is omitted entirely rather than opening on nothing.
102
+ */
103
+ function ConfigHelp({
104
+ environment,
105
+ configVars,
106
+ }: {
107
+ environment: PaymentEnvironment;
108
+ configVars?: string[];
109
+ }): ReactNode {
110
+ const [open, setOpen] = useState(false);
111
+ if (!configVars || configVars.length === 0) return null;
112
+ return (
113
+ <Stack spacing={1} alignItems="flex-start">
114
+ <Button
115
+ variant="outlined"
116
+ size="small"
117
+ onClick={() => setOpen((value) => !value)}
118
+ data-testid={`connect-config-toggle-${environment}`}
119
+ >
120
+ {open ? 'Ocultar variáveis de ambiente' : 'Ver variáveis de ambiente'}
121
+ </Button>
122
+ {open ? (
123
+ <Box data-testid={`connect-config-details-${environment}`}>
124
+ <Typography variant="caption" color="text.secondary" component="p">
125
+ A aplicação deste ambiente é resolvida estritamente por estas variáveis (sem
126
+ fallback entre ambientes):
127
+ </Typography>
128
+ <Box component="ul" sx={{ m: 0, pl: 2.5 }}>
129
+ {configVars.map((name) => (
130
+ <Box component="li" key={name}>
131
+ <Box component="code" sx={{ fontFamily: 'monospace', fontSize: 12 }}>
132
+ {name}
133
+ </Box>
134
+ </Box>
135
+ ))}
136
+ </Box>
137
+ </Box>
138
+ ) : null}
139
+ </Stack>
140
+ );
141
+ }
142
+
143
+ export function ConnectEnvironmentCard({
144
+ status,
145
+ configVars,
146
+ }: {
147
+ status: ConnectApplicationStatus;
148
+ configVars?: string[];
149
+ }): ReactNode {
150
+ return (
151
+ <Stack spacing={1.5} data-testid={`connect-env-${status.environment}`} sx={CARD_SX}>
152
+ <Box sx={{ display: 'flex', alignItems: 'baseline', gap: 1.5, wordBreak: 'break-all' }}>
153
+ <Typography variant="body2" fontWeight={600}>
154
+ {ENV_LABEL[status.environment]}
155
+ </Typography>
156
+ <Typography component="span" variant="caption" color="text.secondary">
157
+ {status.clientId !== null ? `client_id: ${status.clientId}` : ''}
158
+ </Typography>
159
+ </Box>
160
+ {!status.configured ? (
161
+ <Typography variant="body2" color="text.secondary">
162
+ Nenhuma aplicação configurada neste ambiente.
163
+ </Typography>
164
+ ) : null}
165
+ {status.error !== null ? (
166
+ <Alert severity="warning" data-testid={`connect-error-${status.environment}`}>
167
+ {status.error}
168
+ </Alert>
169
+ ) : null}
170
+ <MismatchAlert status={status} />
171
+ <ApplicationFields status={status} />
172
+ <ConfigHelp environment={status.environment} configVars={configVars} />
173
+ </Stack>
174
+ );
175
+ }
@@ -0,0 +1,86 @@
1
+ 'use client';
2
+
3
+ import { Box, Link, Stack, Typography } from '@mui/material';
4
+ import type { ReactNode } from 'react';
5
+
6
+ import type { HomologacaoGuide } from '@12-apps/payments-backend';
7
+
8
+ import { CARD_SX } from './ConnectEnvironmentCard';
9
+
10
+ /**
11
+ * The paste-ready homologação answers (FUT-483, packaged by FUT-573) — the
12
+ * Pipefy form with every deployment-specific value already computed, and the
13
+ * services list naming BOTH Order and Connect.
14
+ */
15
+
16
+ /** One paste-ready value, in a copyable code block. */
17
+ function Answer({ label, children }: { label: string; children: ReactNode }): ReactNode {
18
+ return (
19
+ <Stack spacing={0.25}>
20
+ <Typography variant="caption" color="text.secondary" fontWeight={600}>
21
+ {label}
22
+ </Typography>
23
+ <Box
24
+ component="code"
25
+ sx={{
26
+ fontFamily: 'monospace',
27
+ fontSize: 12,
28
+ p: 1,
29
+ borderRadius: 1,
30
+ bgcolor: 'action.hover',
31
+ wordBreak: 'break-word',
32
+ whiteSpace: 'pre-wrap',
33
+ }}
34
+ >
35
+ {children}
36
+ </Box>
37
+ </Stack>
38
+ );
39
+ }
40
+
41
+ export function HomologacaoGuideCard({ guide }: { guide: HomologacaoGuide }): ReactNode {
42
+ return (
43
+ <Stack spacing={1.5} data-testid="homologacao-guide-card" sx={CARD_SX}>
44
+ <Typography variant="body2" fontWeight={600}>
45
+ Formulário de homologação — respostas prontas
46
+ </Typography>
47
+ <Typography variant="body2" color="text.secondary" component="p">
48
+ Abra o{' '}
49
+ <Link
50
+ href={guide.formUrl}
51
+ target="_blank"
52
+ rel="noreferrer"
53
+ data-testid="homologacao-form-link"
54
+ >
55
+ formulário oficial de homologação
56
+ </Link>{' '}
57
+ e preencha com os valores abaixo. Em paralelo, abra um chamado no{' '}
58
+ <Link href={guide.supportFormUrl} target="_blank" rel="noreferrer">
59
+ SIP — Suporte Integração PagBank
60
+ </Link>{' '}
61
+ citando o 403 ACCESS_DENIED — o que responder primeiro resolve a dúvida de o
62
+ formulário cobrir ou não o Connect. Documentação:{' '}
63
+ <Link href={guide.docsUrl} target="_blank" rel="noreferrer">
64
+ solicitar homologação
65
+ </Link>
66
+ .
67
+ </Typography>
68
+ <Answer label="Selecione o tipo de integração">{guide.integrationType}</Answer>
69
+ <Box data-testid="homologacao-services">
70
+ <Answer label="Selecione qual serviço você integrou (marque OS DOIS)">
71
+ {guide.services.join('\n')}
72
+ </Answer>
73
+ </Box>
74
+ <Answer label="Instruções de acesso ao seu ambiente (limite de 255 caracteres)">
75
+ {guide.accessInstructions}
76
+ </Answer>
77
+ <Answer label="URL do site">{guide.siteUrl}</Answer>
78
+ <Answer label="Detalhe quais produtos/serviços serão comercializados">
79
+ {guide.productsDescription}
80
+ </Answer>
81
+ <Typography variant="caption" color="text.secondary" component="p">
82
+ {guide.slaText}
83
+ </Typography>
84
+ </Stack>
85
+ );
86
+ }
@@ -0,0 +1,170 @@
1
+ 'use client';
2
+
3
+ import { Alert, Box, Button, Chip, Stack, TextField, Typography } from '@mui/material';
4
+ import { useState, type ReactNode } from 'react';
5
+
6
+ import type { PlatformHomologationStatus } from '@12-apps/payments-backend';
7
+
8
+ import { CARD_SX } from './ConnectEnvironmentCard';
9
+
10
+ /**
11
+ * The homologação outcome record (FUT-483, packaged by FUT-573) — the durable
12
+ * answer to "is the platform homologated?". Absence of the record is the
13
+ * honest fourth state ("não solicitada"), which is why it is displayed but
14
+ * never offered as a choice.
15
+ */
16
+
17
+ /**
18
+ * One provider's recorded outcome as the wire carries it — the backend's
19
+ * `PlatformHomologationRecord` after JSON serialization (dates as ISO
20
+ * strings).
21
+ */
22
+ export interface PlatformHomologationRecordView {
23
+ provider: string;
24
+ status: PlatformHomologationStatus;
25
+ protocol: string | null;
26
+ notes: string | null;
27
+ submittedAt: string | null;
28
+ decidedAt: string | null;
29
+ updatedBy: string | null;
30
+ updatedAt: string;
31
+ }
32
+
33
+ /** What the outcome form submits — the whole record, replaced deliberately. */
34
+ export interface HomologacaoSaveInput {
35
+ status: PlatformHomologationStatus;
36
+ protocol: string;
37
+ notes: string;
38
+ }
39
+
40
+ /** The host's save-mutation state, whatever machinery produces it. */
41
+ export interface HomologacaoSaveState {
42
+ pending: boolean;
43
+ error: string | null;
44
+ success: boolean;
45
+ }
46
+
47
+ const STATUS_LABEL: Record<PlatformHomologationStatus, string> = {
48
+ SUBMITTED: 'Solicitada',
49
+ APPROVED: 'Aprovada',
50
+ REJECTED: 'Recusada',
51
+ };
52
+
53
+ const STATUS_COLOR: Record<PlatformHomologationStatus, 'warning' | 'success' | 'error'> = {
54
+ SUBMITTED: 'warning',
55
+ APPROVED: 'success',
56
+ REJECTED: 'error',
57
+ };
58
+
59
+ function StatusChip({ record }: { record: PlatformHomologationRecordView | null }): ReactNode {
60
+ if (record === null) {
61
+ return (
62
+ <Chip
63
+ label="Não solicitada"
64
+ size="small"
65
+ variant="outlined"
66
+ data-testid="homologacao-status-chip"
67
+ />
68
+ );
69
+ }
70
+ return (
71
+ <Chip
72
+ label={STATUS_LABEL[record.status]}
73
+ size="small"
74
+ color={STATUS_COLOR[record.status]}
75
+ data-testid="homologacao-status-chip"
76
+ />
77
+ );
78
+ }
79
+
80
+ const formatDateTime = (iso: string): string => new Date(iso).toLocaleString('pt-BR');
81
+
82
+ function RecordTrail({ record }: { record: PlatformHomologationRecordView }): ReactNode {
83
+ return (
84
+ <Typography variant="caption" color="text.secondary" component="p">
85
+ {record.submittedAt ? `Solicitada em ${formatDateTime(record.submittedAt)}. ` : ''}
86
+ {record.decidedAt ? `Decidida em ${formatDateTime(record.decidedAt)}. ` : ''}
87
+ {record.updatedBy ? `Registrado por ${record.updatedBy}.` : ''}
88
+ </Typography>
89
+ );
90
+ }
91
+
92
+ interface HomologacaoOutcomeCardProps {
93
+ record: PlatformHomologationRecordView | null;
94
+ /** Record the outcome — the host PUTs it and refreshes `record`. */
95
+ onSave: (input: HomologacaoSaveInput) => void;
96
+ save: HomologacaoSaveState;
97
+ }
98
+
99
+ export function HomologacaoOutcomeCard(props: HomologacaoOutcomeCardProps): ReactNode {
100
+ const { record, onSave, save } = props;
101
+ const [status, setStatus] = useState<PlatformHomologationStatus>(record?.status ?? 'SUBMITTED');
102
+ const [protocol, setProtocol] = useState(record?.protocol ?? '');
103
+ const [notes, setNotes] = useState(record?.notes ?? '');
104
+
105
+ return (
106
+ <Stack spacing={1.5} data-testid="homologacao-outcome-card" sx={CARD_SX}>
107
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
108
+ <Typography variant="body2" fontWeight={600}>
109
+ Situação da homologação
110
+ </Typography>
111
+ <StatusChip record={record} />
112
+ </Box>
113
+ {record ? <RecordTrail record={record} /> : null}
114
+ <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1.5, alignItems: 'flex-start' }}>
115
+ <TextField
116
+ select
117
+ size="small"
118
+ label="Situação"
119
+ value={status}
120
+ onChange={(event) => setStatus(event.target.value as PlatformHomologationStatus)}
121
+ slotProps={{
122
+ select: { native: true },
123
+ htmlInput: { 'data-testid': 'homologacao-status-select' },
124
+ }}
125
+ >
126
+ {(Object.keys(STATUS_LABEL) as PlatformHomologationStatus[]).map((key) => (
127
+ <option key={key} value={key}>
128
+ {STATUS_LABEL[key]}
129
+ </option>
130
+ ))}
131
+ </TextField>
132
+ <TextField
133
+ size="small"
134
+ aria-label="Protocolo"
135
+ placeholder="Protocolo (cartão do Pipefy / chamado)"
136
+ value={protocol}
137
+ onChange={(event) => setProtocol(event.target.value)}
138
+ slotProps={{ htmlInput: { 'data-testid': 'homologacao-protocol' } }}
139
+ />
140
+ <TextField
141
+ size="small"
142
+ aria-label="Observações"
143
+ placeholder="Observações (resposta do PagBank, contexto…)"
144
+ value={notes}
145
+ onChange={(event) => setNotes(event.target.value)}
146
+ slotProps={{ htmlInput: { 'data-testid': 'homologacao-notes' } }}
147
+ />
148
+ <Button
149
+ variant="contained"
150
+ size="small"
151
+ disabled={save.pending}
152
+ onClick={() => onSave({ status, protocol, notes })}
153
+ data-testid="homologacao-save"
154
+ >
155
+ Registrar
156
+ </Button>
157
+ </Box>
158
+ {save.error !== null ? (
159
+ <Alert severity="error" data-testid="homologacao-save-error">
160
+ {save.error}
161
+ </Alert>
162
+ ) : null}
163
+ {save.success ? (
164
+ <Alert severity="success" data-testid="homologacao-save-ok">
165
+ Registro atualizado.
166
+ </Alert>
167
+ ) : null}
168
+ </Stack>
169
+ );
170
+ }
@@ -0,0 +1,107 @@
1
+ 'use client';
2
+
3
+ import { Alert, Box, Button, Stack, Typography } from '@mui/material';
4
+ import { useState, type ReactNode } from 'react';
5
+
6
+ import type { HomologacaoGuide } from '@12-apps/payments-backend';
7
+
8
+ import { CARD_SX } from './ConnectEnvironmentCard';
9
+ import { HomologacaoGuideCard } from './HomologacaoGuideCard';
10
+ import {
11
+ HomologacaoOutcomeCard,
12
+ type HomologacaoSaveInput,
13
+ type HomologacaoSaveState,
14
+ type PlatformHomologationRecordView,
15
+ } from './HomologacaoOutcomeCard';
16
+
17
+ /**
18
+ * The PLATFORM's PagBank homologação screen (FUT-483, packaged by FUT-573).
19
+ *
20
+ * The platform is the direct integrator, so the homologação is the
21
+ * platform's, once — store owners are platform users and are exempt. The
22
+ * screen carries the three halves: the recorded outcome (so "is the platform
23
+ * homologated?" stops being a question for a person), the Pipefy form with
24
+ * paste-ready answers (BOTH services — Order and Connect), and the evidence
25
+ * generator running on the platform's own sandbox credentials.
26
+ *
27
+ * Dumb by design: data and mutations arrive via props from the host's own
28
+ * mounted routes (`platformHomologacaoGuide`, `createHomologationRecordService`
29
+ * and `buildPlatformHomologacaoAnexo` in `@12-apps/payments-backend`), so the
30
+ * host page is a thin mount.
31
+ */
32
+ export interface PlatformHomologacaoProps {
33
+ /** The recorded outcome; null renders the honest "não solicitada". */
34
+ record: PlatformHomologationRecordView | null;
35
+ /** The paste-ready answers, computed by the host's backend. */
36
+ guide: HomologacaoGuide;
37
+ /** Record the outcome — the host PUTs it and refreshes `record`. */
38
+ onSaveRecord: (input: HomologacaoSaveInput) => void;
39
+ /** The host's save-mutation state. */
40
+ save: HomologacaoSaveState;
41
+ /**
42
+ * Generate AND deliver the evidence file (the host downloads what its anexo
43
+ * route answers). Reject with an Error whose message names the reason —
44
+ * e.g. the missing platform sandbox token and where to fix it — and the
45
+ * card shows it verbatim.
46
+ */
47
+ onGenerateAnexo: () => Promise<void>;
48
+ }
49
+
50
+ /** The evidence-file half: real sandbox calls, downloaded as a text file. */
51
+ function AnexoCard({ onGenerate }: { onGenerate: () => Promise<void> }): ReactNode {
52
+ const [error, setError] = useState<string | null>(null);
53
+ const [busy, setBusy] = useState(false);
54
+
55
+ const generate = async (): Promise<void> => {
56
+ setBusy(true);
57
+ setError(null);
58
+ try {
59
+ await onGenerate();
60
+ } catch (cause) {
61
+ setError(cause instanceof Error ? cause.message : 'Não foi possível gerar o anexo.');
62
+ } finally {
63
+ setBusy(false);
64
+ }
65
+ };
66
+
67
+ return (
68
+ <Stack spacing={1.5} data-testid="homologacao-anexo-card" sx={CARD_SX}>
69
+ <Typography variant="body2" fontWeight={600}>
70
+ Anexo de evidências
71
+ </Typography>
72
+ <Typography variant="body2" color="text.secondary" component="p">
73
+ O formulário exige os requests e responses das requisições enviadas às APIs do
74
+ PagBank. O botão abaixo faz as chamadas reais no ambiente de testes (Sandbox) com o
75
+ token da própria plataforma — nada é cobrado de verdade — e baixa o arquivo pronto
76
+ para anexar, com o token redigido.
77
+ </Typography>
78
+ <Box>
79
+ <Button
80
+ variant="outlined"
81
+ size="small"
82
+ disabled={busy}
83
+ onClick={() => void generate()}
84
+ data-testid="homologacao-anexo-button"
85
+ >
86
+ Gerar anexo
87
+ </Button>
88
+ </Box>
89
+ {error !== null ? (
90
+ <Alert severity="error" data-testid="homologacao-anexo-error">
91
+ {error}
92
+ </Alert>
93
+ ) : null}
94
+ </Stack>
95
+ );
96
+ }
97
+
98
+ export function PlatformHomologacao(props: PlatformHomologacaoProps): ReactNode {
99
+ const { record, guide, onSaveRecord, save, onGenerateAnexo } = props;
100
+ return (
101
+ <Stack spacing={2} data-testid="platform-homologacao">
102
+ <HomologacaoOutcomeCard record={record} onSave={onSaveRecord} save={save} />
103
+ <HomologacaoGuideCard guide={guide} />
104
+ <AnexoCard onGenerate={onGenerateAnexo} />
105
+ </Stack>
106
+ );
107
+ }
@@ -96,6 +96,7 @@ function buildCheckout(
96
96
  providerConfig={config}
97
97
  tenantSlug={tenantSlug}
98
98
  confirmationExtra={runtime.config.confirmation?.extra}
99
+ validateApplePayMerchant={ports.validateApplePayMerchant}
99
100
  />
100
101
  );
101
102
  }
@@ -74,6 +74,13 @@ export interface CheckoutPorts {
74
74
  * some hosts must log or confirm the departure.
75
75
  */
76
76
  navigate?(url: string): void;
77
+ /**
78
+ * Apple Pay merchant validation (FUT-472): exchange the session's
79
+ * `validationURL` for an Apple merchant session, SERVER-SIDE — the merchant
80
+ * identity certificate must never reach a browser. Optional; without it the
81
+ * Apple Pay sheet cannot start and the card form remains the way to pay.
82
+ */
83
+ validateApplePayMerchant?(validationURL: string): Promise<unknown>;
77
84
  /** The remedy shown on the no-provider screen, AND the host's veto. */
78
85
  useAvailability?(): CheckoutAvailability;
79
86
  }
package/src/index.ts CHANGED
@@ -84,6 +84,29 @@ export {
84
84
  export { type CheckoutHostPorts } from './components/checkout/use-checkout-controller';
85
85
  export { PaymentsUnavailable } from './components/checkout/payments-unavailable';
86
86
  export { fetchCheckoutConfig } from './components/checkout/client';
87
+ // ---------------------------------------------------------------------------
88
+ // Digital wallets (FUT-471/472) — the Google-branded button and the capability
89
+ // read it is gated on. `CheckoutFlow` wires these automatically; they are
90
+ // exported for hosts composing their own pixels.
91
+ // ---------------------------------------------------------------------------
92
+ export {
93
+ GooglePayButton,
94
+ type GooglePayApi,
95
+ type GooglePayButtonProps,
96
+ type GooglePaymentData,
97
+ type GooglePaymentsClient,
98
+ type GooglePayGatewayParams,
99
+ } from './components/checkout/google-pay-button';
100
+ export {
101
+ ApplePayButton,
102
+ applePaySupported,
103
+ APPLE_PAY_SUPPORTED_NETWORKS,
104
+ type ApplePayButtonProps,
105
+ type ApplePayPaymentRequest,
106
+ type ApplePaySessionClass,
107
+ type ApplePaySessionLike,
108
+ } from './components/checkout/apple-pay-button';
109
+ export { applePayDeclared, googlePayConfig } from './components/checkout/method-capability';
87
110
  export {
88
111
  CheckoutComponentsProvider,
89
112
  type CheckoutActionBarProps,
@@ -103,11 +126,13 @@ export {
103
126
  type BuyerContact,
104
127
  type BuyerField,
105
128
  type BuyerInfo,
129
+ type ChargeWalletInput,
106
130
  type CheckoutChainLink,
107
131
  type CheckoutCustomerField,
108
132
  type CheckoutError,
109
133
  type CheckoutOrder,
110
134
  type CheckoutProviderConfig,
135
+ type CheckoutWalletType,
111
136
  type ComandaCheckout,
112
137
  type CreateOrderRequest,
113
138
  type CreateOrderResult,
@@ -169,6 +194,28 @@ export {
169
194
  type PaymentProviderSettingsProps,
170
195
  } from './components/PaymentProviderSettings';
171
196
 
197
+ // ---------------------------------------------------------------------------
198
+ // The PLATFORM operations screens (FUT-479 / FUT-483, packaged by FUT-573) —
199
+ // the Connect-application consult and the homologação, as dumb components a
200
+ // host page mounts with data + callbacks from its own routes. Their backend
201
+ // halves live in `@12-apps/payments-backend` (`consultConnectApplications`,
202
+ // `platformHomologacaoGuide`, `createHomologationRecordService`,
203
+ // `buildPlatformHomologacaoAnexo`).
204
+ // ---------------------------------------------------------------------------
205
+ export {
206
+ ConnectApplicationPanel,
207
+ type ConnectApplicationPanelProps,
208
+ } from './components/platform/ConnectApplicationPanel';
209
+ export {
210
+ PlatformHomologacao,
211
+ type PlatformHomologacaoProps,
212
+ } from './components/platform/PlatformHomologacao';
213
+ export {
214
+ type HomologacaoSaveInput,
215
+ type HomologacaoSaveState,
216
+ type PlatformHomologationRecordView,
217
+ } from './components/platform/HomologacaoOutcomeCard';
218
+
172
219
  /**
173
220
  * Re-exported because it appears in the `prepareConnect` prop a host must
174
221
  * implement: without it the host could not type its own callback without