@12-apps/payments-frontend 3.9.0 → 3.10.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 +115 -0
- package/src/card/cpf.ts +12 -5
- package/src/card/fields.tsx +17 -12
- package/src/card/format.ts +32 -16
- package/src/card/index.ts +3 -1
- package/src/card/pt-BR.ts +51 -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 +23 -18
- 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/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 +304 -0
- package/src/components/settings-pt-BR.ts +169 -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
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,
|