@12-apps/payments-frontend 3.8.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.
Files changed (72) hide show
  1. package/package.json +2 -2
  2. package/src/activation/charge-copy.ts +10 -0
  3. package/src/activation/use-activation-charge.ts +10 -8
  4. package/src/card/copy.ts +115 -0
  5. package/src/card/cpf.ts +12 -5
  6. package/src/card/fields.tsx +17 -12
  7. package/src/card/format.ts +32 -16
  8. package/src/card/index.ts +3 -1
  9. package/src/card/pt-BR.ts +51 -0
  10. package/src/card/stripe-token.ts +5 -6
  11. package/src/card/tokenize.ts +28 -32
  12. package/src/components/CheckoutPayment.tsx +37 -201
  13. package/src/components/ConfirmCredentialSave.tsx +8 -10
  14. package/src/components/ConnectionCard.tsx +23 -18
  15. package/src/components/ConnectionProbe.tsx +3 -1
  16. package/src/components/CredentialFieldStack.tsx +8 -2
  17. package/src/components/CredentialFields.tsx +13 -6
  18. package/src/components/CredentialFormAlerts.tsx +28 -19
  19. package/src/components/EnvironmentTabs.tsx +23 -15
  20. package/src/components/OAuthPanel.tsx +5 -6
  21. package/src/components/PaymentProviderSettings.tsx +45 -13
  22. package/src/components/ProviderConnection.tsx +37 -28
  23. package/src/components/ProviderList.tsx +6 -3
  24. package/src/components/ProviderPriorityList.tsx +18 -16
  25. package/src/components/ProviderStatusBar.tsx +24 -24
  26. package/src/components/SetupGuideSection.tsx +15 -6
  27. package/src/components/checkout/apple-pay-button.tsx +19 -9
  28. package/src/components/checkout/buyer-fields.ts +5 -3
  29. package/src/components/checkout/buyer-gate.ts +68 -0
  30. package/src/components/checkout/buyer-info-form.tsx +14 -15
  31. package/src/components/checkout/card-instruments.ts +31 -12
  32. package/src/components/checkout/card-view.tsx +11 -7
  33. package/src/components/checkout/checkout-flow.tsx +4 -1
  34. package/src/components/checkout/checkout-steps.tsx +3 -1
  35. package/src/components/checkout/client-context.tsx +28 -16
  36. package/src/components/checkout/client.ts +43 -16
  37. package/src/components/checkout/copy-context.tsx +83 -0
  38. package/src/components/checkout/google-pay-button.tsx +19 -7
  39. package/src/components/checkout/method-picker.tsx +34 -10
  40. package/src/components/checkout/payer-summary.tsx +8 -6
  41. package/src/components/checkout/payment-error-panel.tsx +8 -5
  42. package/src/components/checkout/pix-view.tsx +16 -9
  43. package/src/components/checkout/providers/hosted-link.tsx +26 -16
  44. package/src/components/checkout/pt-BR.ts +27 -0
  45. package/src/components/checkout/screens-copy.ts +201 -0
  46. package/src/components/checkout/screens-pt-BR.ts +101 -0
  47. package/src/components/checkout/transport.ts +34 -8
  48. package/src/components/checkout/use-card-checkout.ts +13 -5
  49. package/src/components/checkout/use-checkout-controller.ts +5 -48
  50. package/src/components/checkout/view-copy.ts +13 -0
  51. package/src/components/checkout/wallet-pane.tsx +13 -8
  52. package/src/components/checkout-payment-copy.ts +88 -0
  53. package/src/components/checkout-payment-pt-BR.ts +50 -0
  54. package/src/components/checkout-payment-types.ts +25 -0
  55. package/src/components/checkout-payment-views.tsx +225 -0
  56. package/src/components/connection-state.ts +22 -10
  57. package/src/components/credential-rules.ts +8 -3
  58. package/src/components/platform/ConnectApplicationPanel.tsx +6 -2
  59. package/src/components/platform/ConnectEnvironmentCard.tsx +19 -15
  60. package/src/components/platform/HomologacaoGuideCard.tsx +23 -19
  61. package/src/components/platform/HomologacaoOutcomeCard.tsx +24 -16
  62. package/src/components/platform/PlatformHomologacao.tsx +12 -8
  63. package/src/components/settings-copy-context.tsx +50 -0
  64. package/src/components/settings-copy.ts +304 -0
  65. package/src/components/settings-pt-BR.ts +169 -0
  66. package/src/flows/copy.ts +12 -0
  67. package/src/flows/create-payment-flows.tsx +8 -1
  68. package/src/flows/runtime.tsx +12 -4
  69. package/src/flows/screens-vault.tsx +10 -10
  70. package/src/flows/types.ts +5 -2
  71. package/src/flows/use-add-card.ts +10 -5
  72. 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
- const client = createCheckoutClient(config.transport);
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) => {
@@ -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
- <CheckoutClientProvider client={runtime.client}>
116
- <CheckoutNavigateProvider navigate={runtime.navigate}>{children}</CheckoutNavigateProvider>
117
- </CheckoutClientProvider>
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 ?? "Cartão";
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 `Validade ${String(display.expMonth).padStart(2, "0")}/${display.expYear}`;
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, emptyCopy }: { cards: SavedCard[]; emptyCopy: string }): JSX.Element {
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
- {emptyCopy}
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
- {`Validade ${String(card.expMonth).padStart(2, "0")}/${card.expYear}`}
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} emptyCopy={copy.manageCardsEmpty} />}
238
+ {pending ? null : <CardList cards={cards} copy={copy} />}
239
239
  {adding ? (
240
240
  <AddCardSection runtime={runtime} onSaved={() => setRefresh((count) => count + 1)} />
241
241
  ) : (
@@ -37,7 +37,10 @@ import type {
37
37
  OrderStatus,
38
38
  PaymentMethod,
39
39
  } from "../components/checkout/types";
40
- import type { CheckoutTransport, VaultedCardDisplay } from "../components/checkout/transport";
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?: CheckoutTransport;
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(card, sessionTokenization(phase.session, config));
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,