@12-apps/payments-frontend 3.9.0 → 3.11.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/package.json +2 -2
- package/src/activation/charge-copy.ts +10 -0
- package/src/activation/use-activation-charge.ts +10 -8
- package/src/card/copy.ts +117 -0
- package/src/card/cpf.ts +12 -5
- package/src/card/fields.tsx +17 -12
- package/src/card/format.ts +33 -17
- package/src/card/index.ts +3 -1
- package/src/card/pt-BR.ts +52 -0
- package/src/card/stripe-token.ts +5 -6
- package/src/card/tokenize.ts +28 -32
- package/src/components/CheckoutPayment.tsx +37 -201
- package/src/components/ConfirmCredentialSave.tsx +8 -10
- package/src/components/ConnectionCard.tsx +24 -19
- package/src/components/ConnectionProbe.tsx +3 -1
- package/src/components/CredentialFieldStack.tsx +8 -2
- package/src/components/CredentialFields.tsx +13 -6
- package/src/components/CredentialFormAlerts.tsx +28 -19
- package/src/components/EnvironmentTabs.tsx +23 -15
- package/src/components/OAuthPanel.tsx +5 -6
- package/src/components/PaymentProviderSettings.tsx +45 -13
- package/src/components/ProviderConnection.tsx +37 -28
- package/src/components/ProviderList.tsx +6 -3
- package/src/components/ProviderPriorityList.tsx +18 -16
- package/src/components/ProviderSetupGuide.tsx +3 -1
- package/src/components/ProviderStatusBar.tsx +24 -24
- package/src/components/SetupGuideSection.tsx +15 -6
- package/src/components/checkout/apple-pay-button.tsx +19 -9
- package/src/components/checkout/buyer-fields.ts +5 -3
- package/src/components/checkout/buyer-gate.ts +68 -0
- package/src/components/checkout/buyer-info-form.tsx +14 -15
- package/src/components/checkout/card-instruments.ts +31 -12
- package/src/components/checkout/card-view.tsx +11 -7
- package/src/components/checkout/checkout-flow.tsx +4 -1
- package/src/components/checkout/checkout-steps.tsx +3 -1
- package/src/components/checkout/client-context.tsx +28 -16
- package/src/components/checkout/client.ts +43 -16
- package/src/components/checkout/copy-context.tsx +83 -0
- package/src/components/checkout/google-pay-button.tsx +19 -7
- package/src/components/checkout/method-picker.tsx +34 -10
- package/src/components/checkout/payer-summary.tsx +8 -6
- package/src/components/checkout/payment-error-panel.tsx +8 -5
- package/src/components/checkout/pix-view.tsx +16 -9
- package/src/components/checkout/providers/hosted-link.tsx +26 -16
- package/src/components/checkout/pt-BR.ts +27 -0
- package/src/components/checkout/screens-copy.ts +201 -0
- package/src/components/checkout/screens-pt-BR.ts +101 -0
- package/src/components/checkout/transport.ts +34 -8
- package/src/components/checkout/use-card-checkout.ts +13 -5
- package/src/components/checkout/use-checkout-controller.ts +5 -48
- package/src/components/checkout/view-copy.ts +13 -0
- package/src/components/checkout/wallet-pane.tsx +13 -8
- package/src/components/checkout-payment-copy.ts +88 -0
- package/src/components/checkout-payment-pt-BR.ts +50 -0
- package/src/components/checkout-payment-types.ts +25 -0
- package/src/components/checkout-payment-views.tsx +225 -0
- package/src/components/connection-state.ts +22 -10
- package/src/components/credential-rules.ts +8 -3
- package/src/components/platform/ConnectApplicationPanel.tsx +6 -2
- package/src/components/platform/ConnectEnvironmentCard.tsx +19 -15
- package/src/components/platform/HomologacaoGuideCard.tsx +23 -19
- package/src/components/platform/HomologacaoOutcomeCard.tsx +24 -16
- package/src/components/platform/PlatformHomologacao.tsx +12 -8
- package/src/components/settings-copy-context.tsx +50 -0
- package/src/components/settings-copy.ts +312 -0
- package/src/components/settings-pt-BR.ts +172 -0
- package/src/flows/copy.ts +12 -0
- package/src/flows/create-payment-flows.tsx +8 -1
- package/src/flows/runtime.tsx +12 -4
- package/src/flows/screens-vault.tsx +10 -10
- package/src/flows/types.ts +5 -2
- package/src/flows/use-add-card.ts +10 -5
- package/src/index.ts +63 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { PaymentsSettingsCopy } from './settings-copy';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The pt-BR pack for the payments settings surface — a NAMED constant a host
|
|
5
|
+
* passes by hand, never a default.
|
|
6
|
+
*
|
|
7
|
+
* The filename is what exempts this file from the copy-portability gate:
|
|
8
|
+
* Portuguese may ship, it may not be silent. Every sentence is VERBATIM what
|
|
9
|
+
* the screen used to render, so a host adopting it sees no change — what
|
|
10
|
+
* changes is that the words are chosen in a diff.
|
|
11
|
+
*/
|
|
12
|
+
export const PT_BR_PAYMENTS_SETTINGS_COPY: PaymentsSettingsCopy = {
|
|
13
|
+
status: {
|
|
14
|
+
verified: 'VERIFICADO',
|
|
15
|
+
unverified: 'NÃO VERIFICADO',
|
|
16
|
+
connectionOk: 'CONEXÃO OK',
|
|
17
|
+
reconnectRequired: 'RECONECTAR',
|
|
18
|
+
notConnected: 'Não conectado',
|
|
19
|
+
threeStepsAhead: 'Este provedor só passa a receber vendas depois dos 3 passos abaixo.',
|
|
20
|
+
connectAndVerifyFirst: 'Conecte e verifique o provedor antes de ativar as vendas.',
|
|
21
|
+
receiving: {
|
|
22
|
+
state: 'Recebendo vendas',
|
|
23
|
+
sub: 'Sua loja está recebendo por este provedor.',
|
|
24
|
+
},
|
|
25
|
+
pausedByOwner: {
|
|
26
|
+
state: 'Pausado',
|
|
27
|
+
sub: 'Conexão pronta e pausada por você — nenhum pedido novo é cobrado aqui.',
|
|
28
|
+
},
|
|
29
|
+
pausedChip: 'Pausado',
|
|
30
|
+
readyNotReceiving: {
|
|
31
|
+
state: 'Ainda não está recebendo',
|
|
32
|
+
sub: 'Tudo pronto — ligue a chave para começar a receber.',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
listBadge: {
|
|
36
|
+
active: 'Ativo',
|
|
37
|
+
connected: 'Conectado',
|
|
38
|
+
reconnect: 'Reconectar',
|
|
39
|
+
notConnected: 'Não conectado',
|
|
40
|
+
},
|
|
41
|
+
environment: {
|
|
42
|
+
production: 'Produção',
|
|
43
|
+
sandbox: 'Sandbox',
|
|
44
|
+
groupLabel: 'Ambiente desta conexão',
|
|
45
|
+
productionMeaning: 'Produção — dinheiro real.',
|
|
46
|
+
sandboxMeaning: 'Sandbox — ambiente de teste.',
|
|
47
|
+
productionConsequence: 'Tudo o que você fizer aqui vale para as vendas da loja.',
|
|
48
|
+
sandboxConsequence: 'Nenhum valor sai ou entra de verdade.',
|
|
49
|
+
storeIsUsing: (environmentName) => ` Hoje a loja está usando ${environmentName}.`,
|
|
50
|
+
},
|
|
51
|
+
oauth: {
|
|
52
|
+
connectAction: (displayName) => `Conectar com ${displayName}`,
|
|
53
|
+
reconnectAction: 'Reconectar',
|
|
54
|
+
removeAction: 'Remover conexão',
|
|
55
|
+
invitation: (displayName) =>
|
|
56
|
+
`Conecte sua conta ${displayName} autorizando o acesso no site do provedor. Nenhuma chave precisa ser copiada.`,
|
|
57
|
+
roundTripNote: 'Você sai para o provedor e volta para cá — leva menos de um minuto.',
|
|
58
|
+
connectedExplainer:
|
|
59
|
+
'Sua conta está conectada. As cobranças são criadas em seu nome — nenhuma chave precisa ser copiada.',
|
|
60
|
+
connectedNote: (displayName) =>
|
|
61
|
+
`Conta ${displayName} conectada. Revogue quando quiser, aqui ou no painel do provedor.`,
|
|
62
|
+
connectedAt: (when) => `Conectada em ${when}`,
|
|
63
|
+
validUntil: (when) => `Autorização válida até ${when}`,
|
|
64
|
+
expiredAt: (when) =>
|
|
65
|
+
`A autorização expirou em ${when}. Reconecte para voltar a receber pagamentos.`,
|
|
66
|
+
expiresAt: (when) =>
|
|
67
|
+
`A autorização expira em ${when}. Se o aviso continuar, reconecte a conta.`,
|
|
68
|
+
revoked: 'A autorização expirou ou foi revogada. Reconecte para voltar a receber pagamentos.',
|
|
69
|
+
notAvailableHere: (displayName) =>
|
|
70
|
+
`A conexão automática com ${displayName} não está disponível nesta instalação — ` +
|
|
71
|
+
'o aplicativo de autorização não foi cadastrado. Para conectar agora, abra ' +
|
|
72
|
+
'“Prefiro informar as credenciais manualmente” abaixo e cole as suas próprias chaves.',
|
|
73
|
+
connectUnavailable:
|
|
74
|
+
'Este provedor conecta por autorização, mas o botão de conexão não está disponível nesta ' +
|
|
75
|
+
'instalação. Você ainda pode conectar informando as credenciais manualmente.',
|
|
76
|
+
preferOAuth: 'Prefiro conectar por autorização',
|
|
77
|
+
preferCredentials: 'Prefiro informar as credenciais manualmente',
|
|
78
|
+
scopes: {
|
|
79
|
+
read: 'Consultar pagamentos',
|
|
80
|
+
create: 'Criar cobranças',
|
|
81
|
+
refund: 'Estornar pagamentos',
|
|
82
|
+
account: 'Consultar dados da conta',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
card: {
|
|
86
|
+
accountHeading: (displayName) => `Conta ${displayName}`,
|
|
87
|
+
accountLabel: 'Conta',
|
|
88
|
+
connectionLabel: 'Conexão',
|
|
89
|
+
authorizedAt: (displayName) => `Autorizada no ${displayName}`,
|
|
90
|
+
environmentLabel: 'Ambiente',
|
|
91
|
+
connectedAtLabel: 'Conectada em',
|
|
92
|
+
sandboxWithNote: 'Sandbox (testes)',
|
|
93
|
+
steps: {
|
|
94
|
+
signIn: (displayName) =>
|
|
95
|
+
`Entre na sua conta ${displayName} — dá para criar uma na hora, se ainda não tiver.`,
|
|
96
|
+
authorize: 'Autorize o acesso na tela do provedor.',
|
|
97
|
+
comeBack: 'Você volta para cá com a conta conectada.',
|
|
98
|
+
},
|
|
99
|
+
removeAction: 'Remover conexão',
|
|
100
|
+
removeQuestion: (displayName) => `Remover a conexão com ${displayName}?`,
|
|
101
|
+
removeConsequenceLive:
|
|
102
|
+
'A loja para de receber na hora e fica sem provedor ativo — pedidos novos não conseguem ser pagos até você conectar outro.',
|
|
103
|
+
removeConsequenceIdle: 'Esta conexão sai da loja. Você pode conectar de novo depois.',
|
|
104
|
+
removeStopsChargingNow: 'Pedidos novos deixam de ser cobrados imediatamente.',
|
|
105
|
+
removeRevokes: (displayName) =>
|
|
106
|
+
`A autorização é revogada no ${displayName}. Sua conta e seu histórico continuam lá, intactos.`,
|
|
107
|
+
removeKeepsSettled: (displayName) =>
|
|
108
|
+
`Pagamentos já aprovados e estornos em andamento seguem normalmente pelo ${displayName}.`,
|
|
109
|
+
removeRestartsSetup: 'Para reconectar, os passos recomeçam do zero.',
|
|
110
|
+
pauseInstead: 'Só pausar o recebimento',
|
|
111
|
+
cancel: 'Cancelar',
|
|
112
|
+
},
|
|
113
|
+
credentials: {
|
|
114
|
+
configuredKeepBlank: 'Configurado — deixe em branco para manter o valor atual.',
|
|
115
|
+
advancedSuffix: ' · só para plataformas Connect',
|
|
116
|
+
probeAction: 'Testar conexão',
|
|
117
|
+
reverifyWarning: (displayName) =>
|
|
118
|
+
`Esta loja já está verificada. Trocar as credenciais **exige uma nova verificação** ` +
|
|
119
|
+
`e a loja **para de receber** pelo ${displayName} até que ela termine.`,
|
|
120
|
+
probeRunning: 'Testando a conexão…',
|
|
121
|
+
probeSaveNote: 'Salvamos e testamos as chaves no provedor antes de seguir.',
|
|
122
|
+
probeIncompleteNote: 'Guardamos o que você já preencheu. Complete os campos para testar.',
|
|
123
|
+
probeFailed: (environmentName) =>
|
|
124
|
+
`Não foi possível conectar em ${environmentName}. Confira as credenciais deste ambiente.`,
|
|
125
|
+
checkPass: 'Verificado',
|
|
126
|
+
checkFail: 'Corrigir',
|
|
127
|
+
uncheckable: 'Não verificável',
|
|
128
|
+
saveAndTest: 'Salvar e testar conexão',
|
|
129
|
+
save: 'Salvar',
|
|
130
|
+
saveOnly: (fieldLabel) => `Salvar ${fieldLabel}`,
|
|
131
|
+
changeAction: 'Alterar',
|
|
132
|
+
},
|
|
133
|
+
priority: {
|
|
134
|
+
moveUp: (label) => `Mover ${label} para cima`,
|
|
135
|
+
moveDown: (label) => `Mover ${label} para baixo`,
|
|
136
|
+
saveFailed: 'Não foi possível salvar a ordem.',
|
|
137
|
+
firstInChain: 'primeiro',
|
|
138
|
+
orderHeading: 'Ordem de tentativa',
|
|
139
|
+
chainExplainer: (firstProviderLabel) =>
|
|
140
|
+
`O checkout tenta **${firstProviderLabel}** primeiro. Se uma cobrança falhar por motivo ` +
|
|
141
|
+
'técnico, ele tenta o próximo da lista — mas só quando é possível comprovar que a ' +
|
|
142
|
+
'tentativa anterior não gerou cobrança.',
|
|
143
|
+
noneActive:
|
|
144
|
+
'Nenhum provedor está ativo. O checkout não conseguirá cobrar até que você ative ao menos um.',
|
|
145
|
+
retryDeclinedLabel: 'Tentar cartão **recusado** no próximo provedor',
|
|
146
|
+
retryDeclinedOn:
|
|
147
|
+
'Uma recusa passa para o próximo da lista. Isso pode aumentar custos de transação e sinais de fraude.',
|
|
148
|
+
retryDeclinedOff:
|
|
149
|
+
'Padrão: uma recusa encerra a cobrança. Só falhas técnicas passam para o próximo.',
|
|
150
|
+
},
|
|
151
|
+
providerList: {
|
|
152
|
+
heading: 'Escolha o provedor de pagamento',
|
|
153
|
+
subheading: 'Selecione onde sua loja recebe — a configuração é feita sob medida para ele.',
|
|
154
|
+
},
|
|
155
|
+
confirmSave: {
|
|
156
|
+
title: 'Confirme para onde vai o dinheiro',
|
|
157
|
+
body: (fieldLabel) =>
|
|
158
|
+
`Todo pagamento recebido por esta loja será depositado na conta desta ${fieldLabel}:`,
|
|
159
|
+
fieldFallback: 'credencial',
|
|
160
|
+
warning:
|
|
161
|
+
'Um valor errado envia os pagamentos desta loja para outra pessoa, e não há como reverter.',
|
|
162
|
+
cancelAction: 'Voltar e revisar',
|
|
163
|
+
confirmAction: 'É essa, salvar',
|
|
164
|
+
},
|
|
165
|
+
setupGuide: {
|
|
166
|
+
defaultConfirmLabel: 'Já habilitei o Checkout Integrado',
|
|
167
|
+
confirmedByYou: 'Confirmado por você',
|
|
168
|
+
reviewAction: 'Revisar',
|
|
169
|
+
copyValue: (fieldLabel) => `Copiar ${fieldLabel}`,
|
|
170
|
+
copied: 'Copiado',
|
|
171
|
+
},
|
|
172
|
+
};
|
package/src/flows/copy.ts
CHANGED
|
@@ -89,4 +89,16 @@ export interface CheckoutCopyFE {
|
|
|
89
89
|
manageCardsTitle: string;
|
|
90
90
|
manageCardsEmpty: string;
|
|
91
91
|
manageCardsAdd: string;
|
|
92
|
+
/**
|
|
93
|
+
* How a card on file READS — the network's name when the provider shared
|
|
94
|
+
* none, and the expiry line beside it.
|
|
95
|
+
*
|
|
96
|
+
* Both were Portuguese literals inside this screen ("Cartão", "Validade
|
|
97
|
+
* MM/AAAA"), which is the whole of what a buyer sees of a saved card when
|
|
98
|
+
* the provider is stingy with metadata. `cardExpiry` is a function because
|
|
99
|
+
* the order of month and year, and whether the word leads or trails, is the
|
|
100
|
+
* translator's call — the two numbers arrive already padded.
|
|
101
|
+
*/
|
|
102
|
+
cardUnknownBrand: string;
|
|
103
|
+
cardExpiry(month: string, year: number): string;
|
|
92
104
|
}
|
|
@@ -40,7 +40,14 @@ const ALWAYS_PAYABLE: CheckoutAvailability = { payable: true };
|
|
|
40
40
|
|
|
41
41
|
/** Build the runtime every screen closes over. */
|
|
42
42
|
function buildRuntime(config: PaymentFlowsConfig): FlowsRuntime {
|
|
43
|
-
|
|
43
|
+
// The transport's own failure sentences come from the SAME copy pack the
|
|
44
|
+
// screens read (FUT-760), so a host answers the question once. A `transport`
|
|
45
|
+
// that names its own `copy` still wins — a second mount may talk to a
|
|
46
|
+
// different surface with a different voice.
|
|
47
|
+
const client = createCheckoutClient({
|
|
48
|
+
copy: config.copy.views.screens.screens.transport,
|
|
49
|
+
...config.transport,
|
|
50
|
+
});
|
|
44
51
|
const navigate =
|
|
45
52
|
config.ports.navigate ??
|
|
46
53
|
((url: string) => {
|
package/src/flows/runtime.tsx
CHANGED
|
@@ -27,6 +27,7 @@ import { CheckoutClientProvider } from "../components/checkout/client-context";
|
|
|
27
27
|
import { CheckoutNavigateProvider } from "../components/checkout/navigate-context";
|
|
28
28
|
import type { CheckoutClient } from "../components/checkout/transport";
|
|
29
29
|
import type { CheckoutProviderConfig } from "../components/checkout/types";
|
|
30
|
+
import { CheckoutCopyProvider } from "../components/checkout/copy-context";
|
|
30
31
|
import { CheckoutComponentsProvider } from "../components/checkout/ui";
|
|
31
32
|
|
|
32
33
|
import type { CheckoutCopyFE } from "./copy";
|
|
@@ -102,7 +103,7 @@ export function useResolvedConfig(runtime: FlowsRuntime): CheckoutConfigState {
|
|
|
102
103
|
return provided ?? own;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
/** Slots, transport and the navigate port — what a screen cannot work without. */
|
|
106
|
+
/** Slots, WORDS, transport and the navigate port — what a screen cannot work without. */
|
|
106
107
|
export function FlowsShell({
|
|
107
108
|
runtime,
|
|
108
109
|
children,
|
|
@@ -112,9 +113,16 @@ export function FlowsShell({
|
|
|
112
113
|
}): JSX.Element {
|
|
113
114
|
return (
|
|
114
115
|
<CheckoutComponentsProvider components={runtime.config.components}>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
{/* The factory's STANDALONE screens (add-card, manage-cards) render the
|
|
117
|
+
same card fields the checkout does, so they need the same words —
|
|
118
|
+
and they are mounted here rather than under `CheckoutFlow`. */}
|
|
119
|
+
<CheckoutCopyProvider copy={runtime.config.copy.views.screens}>
|
|
120
|
+
<CheckoutClientProvider client={runtime.client}>
|
|
121
|
+
<CheckoutNavigateProvider navigate={runtime.navigate}>
|
|
122
|
+
{children}
|
|
123
|
+
</CheckoutNavigateProvider>
|
|
124
|
+
</CheckoutClientProvider>
|
|
125
|
+
</CheckoutCopyProvider>
|
|
118
126
|
</CheckoutComponentsProvider>
|
|
119
127
|
);
|
|
120
128
|
}
|
|
@@ -29,15 +29,15 @@ import type { CheckoutScreens } from "./types";
|
|
|
29
29
|
import { useAddCard, type AddCardController } from "./use-add-card";
|
|
30
30
|
|
|
31
31
|
/** `visa •••• 4242`, or just the brand when the provider shared no last4. */
|
|
32
|
-
function displayLabel(display: VaultedCardDisplay): string {
|
|
33
|
-
const brand = display.brand ??
|
|
32
|
+
function displayLabel(copy: CheckoutCopyFE, display: VaultedCardDisplay): string {
|
|
33
|
+
const brand = display.brand ?? copy.cardUnknownBrand;
|
|
34
34
|
return display.last4 ? `${brand} •••• ${display.last4}` : brand;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/** `Validade 12/2031`, or nothing when the provider shared no expiry. */
|
|
38
|
-
function expiryLabel(display: VaultedCardDisplay): string | null {
|
|
38
|
+
function expiryLabel(copy: CheckoutCopyFE, display: VaultedCardDisplay): string | null {
|
|
39
39
|
if (display.expMonth === null || display.expYear === null) return null;
|
|
40
|
-
return
|
|
40
|
+
return copy.cardExpiry(String(display.expMonth).padStart(2, "0"), display.expYear);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/** The card is on file — display metadata only, never the vault token. */
|
|
@@ -49,7 +49,7 @@ function SavedConfirmation({
|
|
|
49
49
|
copy: CheckoutCopyFE;
|
|
50
50
|
}): JSX.Element {
|
|
51
51
|
const { Alert, Text } = useCheckoutComponents();
|
|
52
|
-
const expiry = expiryLabel(display);
|
|
52
|
+
const expiry = expiryLabel(copy, display);
|
|
53
53
|
return (
|
|
54
54
|
<Box data-testid="add-card-saved" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
|
55
55
|
<Alert
|
|
@@ -60,7 +60,7 @@ function SavedConfirmation({
|
|
|
60
60
|
/>
|
|
61
61
|
<Box>
|
|
62
62
|
<Text variant="body" size="sm" weight="semibold" as="p">
|
|
63
|
-
{displayLabel(display)}
|
|
63
|
+
{displayLabel(copy, display)}
|
|
64
64
|
</Text>
|
|
65
65
|
{expiry ? (
|
|
66
66
|
<Text variant="caption" size="xs" color="secondary" as="p">
|
|
@@ -195,12 +195,12 @@ function useInstrumentList(
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
/** The list itself, or the empty-state sentence. Read-only by design. */
|
|
198
|
-
function CardList({ cards,
|
|
198
|
+
function CardList({ cards, copy }: { cards: SavedCard[]; copy: CheckoutCopyFE }): JSX.Element {
|
|
199
199
|
const { Text } = useCheckoutComponents();
|
|
200
200
|
if (cards.length === 0) {
|
|
201
201
|
return (
|
|
202
202
|
<Text variant="body" size="sm" color="secondary" as="p" data-testid="manage-cards-empty">
|
|
203
|
-
{
|
|
203
|
+
{copy.manageCardsEmpty}
|
|
204
204
|
</Text>
|
|
205
205
|
);
|
|
206
206
|
}
|
|
@@ -213,7 +213,7 @@ function CardList({ cards, emptyCopy }: { cards: SavedCard[]; emptyCopy: string
|
|
|
213
213
|
</Text>
|
|
214
214
|
{card.expMonth && card.expYear ? (
|
|
215
215
|
<Text variant="caption" size="xs" color="secondary" as="p">
|
|
216
|
-
{
|
|
216
|
+
{copy.cardExpiry(String(card.expMonth).padStart(2, "0"), card.expYear)}
|
|
217
217
|
</Text>
|
|
218
218
|
) : null}
|
|
219
219
|
</Box>
|
|
@@ -235,7 +235,7 @@ function ManageCardsBody({ runtime }: { runtime: FlowsRuntime }): JSX.Element {
|
|
|
235
235
|
<Text variant="heading" size="md" weight="bold" as="h2">
|
|
236
236
|
{copy.manageCardsTitle}
|
|
237
237
|
</Text>
|
|
238
|
-
{pending ? null : <CardList cards={cards}
|
|
238
|
+
{pending ? null : <CardList cards={cards} copy={copy} />}
|
|
239
239
|
{adding ? (
|
|
240
240
|
<AddCardSection runtime={runtime} onSaved={() => setRefresh((count) => count + 1)} />
|
|
241
241
|
) : (
|
package/src/flows/types.ts
CHANGED
|
@@ -37,7 +37,10 @@ import type {
|
|
|
37
37
|
OrderStatus,
|
|
38
38
|
PaymentMethod,
|
|
39
39
|
} from "../components/checkout/types";
|
|
40
|
-
import type {
|
|
40
|
+
import type {
|
|
41
|
+
CheckoutTransportBinding,
|
|
42
|
+
VaultedCardDisplay,
|
|
43
|
+
} from "../components/checkout/transport";
|
|
41
44
|
import type { useCheckoutController } from "../components/checkout/use-checkout-controller";
|
|
42
45
|
import type { Result } from "../result";
|
|
43
46
|
|
|
@@ -88,7 +91,7 @@ export interface CheckoutPorts {
|
|
|
88
91
|
/** What `createPaymentFlows` is configured with. */
|
|
89
92
|
export interface PaymentFlowsConfig {
|
|
90
93
|
/** Where the `createPaymentFlowsBE` mount lives. Default `/api/checkout`. */
|
|
91
|
-
transport?:
|
|
94
|
+
transport?: CheckoutTransportBinding;
|
|
92
95
|
|
|
93
96
|
/**
|
|
94
97
|
* The store being paid. A HOOK because the host's router owns it: taking a
|
|
@@ -143,11 +143,12 @@ export function useAddCard(
|
|
|
143
143
|
|
|
144
144
|
const brand = detectBrand(onlyDigits(card.number));
|
|
145
145
|
|
|
146
|
+
const fieldCopy = runtime.copy.views.screens.card.fields;
|
|
146
147
|
const validate = (): CardFieldErrors => ({
|
|
147
|
-
number: validateCardNumber(card.number),
|
|
148
|
-
holder: validateHolder(card.holder),
|
|
149
|
-
expiry: validateExpiry(card.expiry),
|
|
150
|
-
cvv: validateCvv(card.cvv, brand),
|
|
148
|
+
number: validateCardNumber(card.number, fieldCopy),
|
|
149
|
+
holder: validateHolder(card.holder, fieldCopy),
|
|
150
|
+
expiry: validateExpiry(card.expiry, fieldCopy),
|
|
151
|
+
cvv: validateCvv(card.cvv, fieldCopy, brand),
|
|
151
152
|
});
|
|
152
153
|
|
|
153
154
|
const submit = async (): Promise<void> => {
|
|
@@ -158,7 +159,11 @@ export function useAddCard(
|
|
|
158
159
|
if (Object.values(errors).some(Boolean)) return;
|
|
159
160
|
|
|
160
161
|
setSaving(true);
|
|
161
|
-
const minted = await tokenizeForCheckout(
|
|
162
|
+
const minted = await tokenizeForCheckout(
|
|
163
|
+
card,
|
|
164
|
+
sessionTokenization(phase.session, config),
|
|
165
|
+
runtime.copy.views.screens.card,
|
|
166
|
+
);
|
|
162
167
|
if (!minted.ok) {
|
|
163
168
|
setError(minted.error);
|
|
164
169
|
setSaving(false);
|
package/src/index.ts
CHANGED
|
@@ -37,6 +37,17 @@ export {
|
|
|
37
37
|
type CheckoutPaymentProps,
|
|
38
38
|
type SavedCardOption,
|
|
39
39
|
} from './components/CheckoutPayment';
|
|
40
|
+
// The words that step renders — required on the prop above, so a host wiring
|
|
41
|
+
// the legacy surface has to answer them (FUT-760).
|
|
42
|
+
export type {
|
|
43
|
+
CheckoutPaymentCopy,
|
|
44
|
+
LegacyCardCopy,
|
|
45
|
+
LegacyMethodCopy,
|
|
46
|
+
LegacyMoneyCopy,
|
|
47
|
+
LegacyPixCopy,
|
|
48
|
+
LegacyRefusalCopy,
|
|
49
|
+
} from './components/checkout-payment-copy';
|
|
50
|
+
export { PT_BR_CHECKOUT_PAYMENT_COPY } from './components/checkout-payment-pt-BR';
|
|
40
51
|
|
|
41
52
|
// ---------------------------------------------------------------------------
|
|
42
53
|
// The buyer checkout surface (FUT-564) — the storefront's three-step flow,
|
|
@@ -80,6 +91,7 @@ export {
|
|
|
80
91
|
type BuyerVaultSession,
|
|
81
92
|
type CheckoutClient,
|
|
82
93
|
type CheckoutTransport,
|
|
94
|
+
type CheckoutTransportBinding,
|
|
83
95
|
type CompleteVaultInput,
|
|
84
96
|
type VaultedCardDisplay,
|
|
85
97
|
} from './components/checkout/transport';
|
|
@@ -98,9 +110,37 @@ export type {
|
|
|
98
110
|
StatusOutcomeCopy,
|
|
99
111
|
} from './components/checkout/view-copy';
|
|
100
112
|
export {
|
|
113
|
+
PT_BR_CHECKOUT_COPY,
|
|
101
114
|
PT_BR_CHECKOUT_VIEW_COPY,
|
|
102
115
|
PT_BR_PAYMENT_STATUS_COPY,
|
|
103
116
|
} from './components/checkout/pt-BR';
|
|
117
|
+
// The provider the card fields and buyer inputs read their words from — a host
|
|
118
|
+
// mounting a screen BELOW `CheckoutFlow` (or composing by hand) opens it
|
|
119
|
+
// itself; `CheckoutFlow` and `FlowsShell` already do (FUT-760).
|
|
120
|
+
export {
|
|
121
|
+
CheckoutCopyProvider,
|
|
122
|
+
useCheckoutCopy,
|
|
123
|
+
type BuyerInfoCopy,
|
|
124
|
+
type CheckoutCopy,
|
|
125
|
+
} from './components/checkout/copy-context';
|
|
126
|
+
// The screens BELOW the steps — the method tiles, the PIX and card panes, the
|
|
127
|
+
// wallet buttons, the hosted handover and the transport's own three sentences.
|
|
128
|
+
// Every slice is named on the barrel, not just the whole: a host writing its
|
|
129
|
+
// pack section by section types the section it is writing (FUT-760).
|
|
130
|
+
export type {
|
|
131
|
+
CardPaneCopy,
|
|
132
|
+
CheckoutScreensCopy,
|
|
133
|
+
CheckoutTransportCopy,
|
|
134
|
+
CheckoutValidationCopy,
|
|
135
|
+
HostedHandoverCopy,
|
|
136
|
+
MethodPickerCopy,
|
|
137
|
+
PayerSummaryCopy,
|
|
138
|
+
PaymentErrorCopy,
|
|
139
|
+
PixPaneCopy,
|
|
140
|
+
SettlingCopy,
|
|
141
|
+
WalletCopy,
|
|
142
|
+
} from './components/checkout/screens-copy';
|
|
143
|
+
export { PT_BR_CHECKOUT_SCREENS_COPY } from './components/checkout/screens-pt-BR';
|
|
104
144
|
export { fetchCheckoutConfig } from './components/checkout/client';
|
|
105
145
|
/**
|
|
106
146
|
* The `sessionStorage` key the hosted-checkout return leg parks the raised
|
|
@@ -199,7 +239,11 @@ export {
|
|
|
199
239
|
validateExpiry,
|
|
200
240
|
validateHolder,
|
|
201
241
|
NEW_CARD,
|
|
242
|
+
PT_BR_CARD_COPY,
|
|
202
243
|
type CardBrand,
|
|
244
|
+
type CardCopy,
|
|
245
|
+
type CardFieldCopy,
|
|
246
|
+
type CardTokenizeCopy,
|
|
203
247
|
type CardDetails,
|
|
204
248
|
type CardFieldErrors,
|
|
205
249
|
type CardToken,
|
|
@@ -207,6 +251,25 @@ export {
|
|
|
207
251
|
type CardTokenizer,
|
|
208
252
|
type SavedCard,
|
|
209
253
|
} from './card';
|
|
254
|
+
// The settings surface's words. `PaymentProviderSettings` takes the pack as a
|
|
255
|
+
// required prop and mounts the provider; the provider is exported for a host
|
|
256
|
+
// that composes the pieces itself (FUT-760).
|
|
257
|
+
export {
|
|
258
|
+
PaymentsSettingsCopyProvider,
|
|
259
|
+
usePaymentsSettingsCopy,
|
|
260
|
+
} from './components/settings-copy-context';
|
|
261
|
+
export { PT_BR_PAYMENTS_SETTINGS_COPY } from './components/settings-pt-BR';
|
|
262
|
+
export type {
|
|
263
|
+
ConnectionBadgeCopy,
|
|
264
|
+
ConnectionCardCopy,
|
|
265
|
+
ConnectionStatusCopy,
|
|
266
|
+
CredentialFormCopy,
|
|
267
|
+
EnvironmentCopy,
|
|
268
|
+
OAuthConnectionCopy,
|
|
269
|
+
PaymentsSettingsCopy,
|
|
270
|
+
ProviderPriorityCopy,
|
|
271
|
+
SetupGuideCopy,
|
|
272
|
+
} from './components/settings-copy';
|
|
210
273
|
export {
|
|
211
274
|
ProviderConnection,
|
|
212
275
|
type ProviderConnectionProps,
|