@12-apps/payments-frontend 3.33.0 → 3.34.1

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.
@@ -1,9 +1,11 @@
1
1
  import { Box } from "@mui/material";
2
- import { useEffect, useMemo, useState, type JSX } from "react";
3
- import QRCode from "react-qr-code";
2
+ import { useEffect, useMemo, useRef, useState, type JSX } from "react";
4
3
 
5
4
  import { useCheckoutCopy } from "./copy-context";
6
- import { ContentCopyIcon } from "./icons";
5
+ import { PixAfterCopy } from "./pix-after-copy";
6
+ import { PixPane, type PixTab } from "./pix-pane";
7
+ import { PIX_CONTAINER, PIX_WIDE, useClipboardCopy } from "./pix-parts";
8
+ import { usePixStage } from "./pix-stage";
7
9
  import type { PixPaneCopy, SettlingCopy } from "./screens-copy";
8
10
  import { StalledWait } from "./stalled-wait";
9
11
  import type { CheckoutOrder, OrderStatus, PixCharge } from "./types";
@@ -11,10 +13,11 @@ import { useCheckoutComponents } from "./ui";
11
13
  import { usePaymentPolling } from "./use-payment-polling";
12
14
 
13
15
  /**
14
- * PIX payment view: a scannable QR (rendered client-side from the "copia e cola"
15
- * payload), the copyable code, and a live pending indicator. Polls the order in
16
- * the background and hands a terminal status up to the parent, which then shows
17
- * the payment-status screen.
16
+ * PIX payment view: the pane to pay from (tabs on a phone, QR and code side by
17
+ * side on a desktop — `./pix-pane`), and after a copy the wait that follows
18
+ * (`./pix-after-copy`). Polls the order in the background throughout and hands
19
+ * a terminal status up to the parent, which then shows the payment-status
20
+ * screen. The 2026-10-06 redesign moved the layout out; the wait stays here.
18
21
  */
19
22
 
20
23
  /**
@@ -65,63 +68,6 @@ function pixWaitMs(expiresAt: string | undefined): number {
65
68
  return Math.max(0, deadline - Date.now()) + PIX_EXPIRY_GRACE_MS;
66
69
  }
67
70
 
68
- /** The copyable "copia e cola" strip with its copy button. */
69
- function PixCodeBox({ pix, onCopied }: { pix: PixCharge; onCopied: () => void }): JSX.Element {
70
- const { Button, Text } = useCheckoutComponents();
71
- const copy = useCheckoutCopy().screens.pix;
72
- const [copied, setCopied] = useState(false);
73
-
74
- const copyCode = async (): Promise<void> => {
75
- try {
76
- await navigator.clipboard?.writeText(pix.copyPaste);
77
- setCopied(true);
78
- onCopied();
79
- setTimeout(() => setCopied(false), 2000);
80
- } catch {
81
- /* clipboard unavailable — the code is still visible to copy manually */
82
- }
83
- };
84
-
85
- return (
86
- <Box
87
- sx={{
88
- display: "flex",
89
- alignItems: "center",
90
- gap: 1,
91
- width: "100%",
92
- maxWidth: 420,
93
- p: 1,
94
- borderRadius: 1,
95
- border: "1px solid",
96
- borderColor: "divider",
97
- bgcolor: "background.default",
98
- }}
99
- >
100
- <Text
101
- variant="code"
102
- size="xs"
103
- as="span"
104
- data-testid="pix-code"
105
- style={{ flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}
106
- >
107
- {pix.copyPaste}
108
- </Text>
109
- <Button
110
- variant="outline"
111
- color="neutral"
112
- size="sm"
113
- icon={<ContentCopyIcon fontSize="small" />}
114
- onClick={() => {
115
- void copyCode();
116
- }}
117
- dataTestId="pix-copy"
118
- >
119
- {copied ? copy.copiedAction : copy.copyAction}
120
- </Button>
121
- </Box>
122
- );
123
- }
124
-
125
71
  /**
126
72
  * Which of the wait's three faces the footer is showing — STOPPED, then STILL
127
73
  * TRYING, then working.
@@ -164,18 +110,17 @@ function PixPollFooter({
164
110
  error,
165
111
  timedOut,
166
112
  onCheckAgain,
167
- manual,
113
+ awaiting,
168
114
  }: {
169
115
  error: string | null;
170
116
  timedOut: boolean;
171
117
  onCheckAgain: () => void;
172
- /** The store confirms this Pix: the wait is for THEM, not for the bank. */
173
- manual: boolean;
118
+ /** What the healthy wait says: whose confirmation it is waiting for, or "Verificando…" after a copy. */
119
+ awaiting: string;
174
120
  }): JSX.Element {
175
121
  const { Text } = useCheckoutComponents();
176
- const { pix, settling } = useCheckoutCopy().screens;
122
+ const { settling } = useCheckoutCopy().screens;
177
123
  const panel = pixWaitPanel(settling, error, timedOut);
178
- const awaiting = (manual ? pix.manual?.awaiting : undefined) ?? pix.awaiting;
179
124
  if (panel) {
180
125
  // The same panel the card and wallet panes show, held to the width of the
181
126
  // copy-and-paste strip above it so the centred PIX column stays a column.
@@ -194,7 +139,7 @@ function PixPollFooter({
194
139
  return (
195
140
  <Box sx={{ display: "flex", alignItems: "center", gap: 1, color: "text.secondary" }}>
196
141
  <LoadingDot />
197
- <Text variant="caption" size="xs" color="secondary" as="span" data-testid="pix-awaiting">
142
+ <Text variant="body" size="xs" color="secondary" as="span" data-testid="pix-awaiting">
198
143
  {awaiting}
199
144
  </Text>
200
145
  </Box>
@@ -202,128 +147,154 @@ function PixPollFooter({
202
147
  }
203
148
 
204
149
  /**
205
- * What a store-confirmed Pix says once its code is copied (FUT-3232).
206
- *
207
- * The footer's caption already names the store's wait, but in a grey line
208
- * under the QR that a buyer on their way to the bank app does not read. Copying
209
- * is that moment, so it answers with a notice that stays: the wait can run for
210
- * minutes, and "volte para esta tela" is the instruction that keeps the buyer
211
- * where the confirmation will land.
150
+ * The words that depend on who confirms. A store-confirmed Pix must not say
151
+ * "a confirmação é automática": nothing confirms it but the store.
212
152
  */
213
- function ManualCopiedNotice({ manual, copied }: { manual: boolean; copied: boolean }): JSX.Element | null {
214
- const { Alert } = useCheckoutComponents();
215
- const notice = useCheckoutCopy().screens.pix.manual?.copied;
216
- if (!manual || !copied || !notice) return null;
217
- return (
218
- <Box sx={{ width: "100%", maxWidth: 420, textAlign: "left" }}>
219
- <Alert
220
- variant="info"
221
- title={notice.title}
222
- description={notice.description}
223
- showIcon
224
- data-testid="pix-manual-copied"
225
- />
226
- </Box>
227
- );
228
- }
229
-
230
- /** The code strip and, once it has been copied, what that copy means. */
231
- function PixCopyRow({ pix, manual }: { pix: PixCharge; manual: boolean }): JSX.Element {
232
- const [copied, setCopied] = useState(false);
233
- return (
234
- <>
235
- <PixCodeBox pix={pix} onCopied={() => setCopied(true)} />
236
- <ManualCopiedNotice manual={manual} copied={copied} />
237
- </>
238
- );
153
+ function pixSentences(copy: PixPaneCopy, manual: boolean): Pick<PixPaneCopy, "validUntil" | "awaiting" | "afterCopy"> {
154
+ return (manual ? copy.manual : undefined) ?? copy;
239
155
  }
240
156
 
241
157
  /**
242
- * The pane's two sentences that depend on who confirms. A store-confirmed Pix
243
- * must not say "a confirmação é automática": nothing confirms it but the store.
158
+ * The deadline and the wait, rendered once for both layouts: narrow, two
159
+ * centred lines under the pane; wide, one footer row under a hairline with the
160
+ * wait on the left and the deadline on the right.
244
161
  */
245
- function pixSentences(
246
- copy: PixPaneCopy,
247
- manual: boolean,
248
- ): Pick<PixPaneCopy, "instructions" | "validUntil"> {
249
- return (manual ? copy.manual : undefined) ?? copy;
162
+ function PixFooter({ deadline, children }: { deadline: string; children: JSX.Element }): JSX.Element {
163
+ const { Text } = useCheckoutComponents();
164
+ return (
165
+ <Box
166
+ sx={{
167
+ display: "flex",
168
+ flexDirection: "column",
169
+ alignItems: "center",
170
+ gap: 1,
171
+ textAlign: "center",
172
+ [PIX_WIDE]: { flexDirection: "row-reverse", justifyContent: "space-between", textAlign: "left", pt: 2.25, borderTop: "1px solid", borderColor: "divider" },
173
+ }}
174
+ >
175
+ <Text variant="body" size="xs" color="secondary" as="p" data-testid="pix-expiry" style={{ margin: 0 }}>
176
+ {deadline}
177
+ </Text>
178
+ {children}
179
+ </Box>
180
+ );
250
181
  }
251
182
 
252
- export function PixView({
253
- order,
254
- onResolved,
255
- pollIntervalMs = 2500,
256
- }: {
257
- order: CheckoutOrder;
258
- onResolved: (status: OrderStatus) => void;
259
- pollIntervalMs?: number;
260
- }): JSX.Element {
261
- const { Text } = useCheckoutComponents();
262
- const copy = useCheckoutCopy().screens.pix;
183
+ /** The poll, bounded by the charge's own expiry (FUT-1170). */
184
+ function usePixPolling(order: CheckoutOrder, onResolved: (status: OrderStatus) => void, pollIntervalMs: number) {
263
185
  // Fixed once per charge (FUT-1170): the bound is a span, so recomputing it
264
186
  // every render would keep pushing the deadline out — and it is an effect
265
187
  // dependency, so it would also restart the wait on every render.
266
188
  const expiresAt = order.pix?.expiresAt;
267
189
  const maxWaitMs = useMemo(() => pixWaitMs(expiresAt), [expiresAt]);
268
- const { status, error, timedOut, checkAgain } = usePaymentPolling(order.orderId, {
190
+ const polling = usePaymentPolling(order.orderId, {
269
191
  intervalMs: pollIntervalMs,
270
192
  slowAfterPolls: PIX_FAST_POLLS,
271
193
  // Never FASTER than the opening cadence: a host that opens slowly means it.
272
194
  slowIntervalMs: Math.max(pollIntervalMs, PIX_SLOW_INTERVAL_MS),
273
195
  maxWaitMs,
274
196
  });
275
-
197
+ const { status } = polling;
276
198
  // Bubble a terminal status up once, so the parent can advance to the status step.
277
199
  useEffect(() => {
278
- if (status && status !== "AWAITING_PAYMENT") {
279
- onResolved(status);
280
- }
200
+ if (status && status !== "AWAITING_PAYMENT") onResolved(status);
281
201
  }, [status, onResolved]);
202
+ return polling;
203
+ }
282
204
 
283
- const pix = order.pix;
284
- if (!pix) {
285
- return (
286
- <Text variant="body" size="sm" color="danger" as="p" data-testid="pix-missing">
287
- {copy.chargeMissing}
288
- </Text>
289
- );
290
- }
205
+ function PixBody({ order, pix, poll }: { order: CheckoutOrder; pix: PixCharge; poll: ReturnType<typeof usePixPolling> }): JSX.Element {
206
+ const { Text } = useCheckoutComponents();
207
+ const copy = useCheckoutCopy().screens.pix;
208
+ const stage = usePixStage();
209
+ const [tab, setTab] = useState<PixTab>("copy");
210
+ const { copy: writeClipboard, justCopied } = useClipboardCopy(pix.copyPaste);
211
+ // Leaving the payment step (a method switch, a terminal status) ends the
212
+ // after-copy face with it: the next pane starts on the code.
213
+ const { setAfterCopy } = stage;
214
+ // A clipboard write that settles after the pane is gone must not reopen the
215
+ // after-copy face over whatever replaced it (it would hide the picker).
216
+ const mounted = useRef(true);
217
+ useEffect(() => {
218
+ mounted.current = true;
219
+ return () => {
220
+ mounted.current = false;
221
+ setAfterCopy(false);
222
+ };
223
+ }, [setAfterCopy]);
291
224
 
292
225
  // The locale is the HOST's (FUT-760): it decides what a buyer reads off the
293
226
  // clock, so it travels with the sentence it feeds rather than being frozen
294
227
  // to the origin host's here.
295
- const validUntil = new Date(pix.expiresAt).toLocaleTimeString(copy.expiryLocale, { hour: "2-digit", minute: "2-digit" });
296
- const manual = pix.confirmation === "MANUAL";
297
- const sentences = pixSentences(copy, manual);
228
+ const time = new Date(pix.expiresAt).toLocaleTimeString(copy.expiryLocale, { hour: "2-digit", minute: "2-digit" });
229
+ const sentences = pixSentences(copy, pix.confirmation === "MANUAL");
230
+ const footerLine = (awaiting: string): JSX.Element => (
231
+ <PixPollFooter error={poll.error} timedOut={poll.timedOut} onCheckAgain={poll.checkAgain} awaiting={awaiting} />
232
+ );
298
233
 
234
+ if (stage.afterCopy) {
235
+ return (
236
+ <PixAfterCopy
237
+ pix={pix}
238
+ words={sentences.afterCopy}
239
+ totalLabel={order.totalLabel}
240
+ deadline={time}
241
+ justCopied={justCopied}
242
+ onCopyAgain={() => void writeClipboard()}
243
+ onShowQr={() => {
244
+ setTab("qr");
245
+ setAfterCopy(false);
246
+ }}
247
+ onPreferCard={stage.preferCard}
248
+ footer={footerLine(copy.verifying)}
249
+ />
250
+ );
251
+ }
299
252
  return (
300
- <Box
301
- data-testid="pix-view"
302
- sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 2, textAlign: "center" }}
303
- >
304
- <Text variant="heading" size="md" weight="bold" as="h2">
253
+ <>
254
+ <Text variant="heading" size="md" weight="bold" as="h2" style={{ margin: 0 }}>
305
255
  {copy.heading}
306
256
  </Text>
307
- <Text variant="body" size="sm" color="secondary" as="p">
308
- {sentences.instructions(order.totalLabel)}
309
- </Text>
310
-
311
- <Box
312
- data-testid="pix-qr"
313
- role="img"
314
- aria-label={copy.qrAlt}
315
- sx={{ bgcolor: "background.paper", p: 2, borderRadius: 2, border: "1px solid", borderColor: "divider" }}
316
- >
317
- <QRCode value={pix.copyPaste} size={200} />
318
- </Box>
319
-
320
- <PixCopyRow pix={pix} manual={manual} />
257
+ <PixPane
258
+ pix={pix}
259
+ tab={tab}
260
+ onTab={setTab}
261
+ onCopy={() => {
262
+ // Only a copy the clipboard TOOK moves on: the next face opens on
263
+ // "Código Pix copiado", and a failed write leaves the code on screen
264
+ // to select by hand.
265
+ void writeClipboard().then((ok) => {
266
+ if (ok && mounted.current) setAfterCopy(true);
267
+ });
268
+ }}
269
+ />
270
+ <PixFooter deadline={sentences.validUntil(time)}>{footerLine(sentences.awaiting)}</PixFooter>
271
+ </>
272
+ );
273
+ }
321
274
 
322
- <Text variant="caption" size="xs" color="secondary" as="p" data-testid="pix-expiry">
323
- {sentences.validUntil(validUntil)}
275
+ export function PixView({
276
+ order,
277
+ onResolved,
278
+ pollIntervalMs = 2500,
279
+ }: {
280
+ order: CheckoutOrder;
281
+ onResolved: (status: OrderStatus) => void;
282
+ pollIntervalMs?: number;
283
+ }): JSX.Element {
284
+ const { Text } = useCheckoutComponents();
285
+ const copy = useCheckoutCopy().screens.pix;
286
+ const poll = usePixPolling(order, onResolved, pollIntervalMs);
287
+ const pix = order.pix;
288
+ if (!pix) {
289
+ return (
290
+ <Text variant="body" size="sm" color="danger" as="p" data-testid="pix-missing">
291
+ {copy.chargeMissing}
324
292
  </Text>
325
-
326
- <PixPollFooter error={error} timedOut={timedOut} onCheckAgain={checkAgain} manual={manual} />
293
+ );
294
+ }
295
+ return (
296
+ <Box data-testid="pix-view" sx={{ ...PIX_CONTAINER, display: "flex", flexDirection: "column", gap: 2 }}>
297
+ <PixBody order={order} pix={pix} poll={poll} />
327
298
  </Box>
328
299
  );
329
300
  }
@@ -21,25 +21,35 @@
21
21
 
22
22
  /** The method tiles, and what each one promises. */
23
23
  export interface MethodPickerCopy {
24
- /** The group's heading, and its `aria-label` — one string, said once. */
24
+ /**
25
+ * The group's accessible name. Not drawn: the tiles are self-explaining
26
+ * ("Pix", "Cartão") and the heading cost the phone a line above the code
27
+ * (the 2026-10-06 Pix redesign).
28
+ */
25
29
  groupLabel: string;
26
30
  /**
27
- * What each method is CALLED. "PIX" is a Brazilian instrument and keeps its
31
+ * What each method is CALLED. "Pix" is a Brazilian instrument and keeps its
28
32
  * name everywhere, the way "Visa" does; "Cartão" is just the word for card,
29
33
  * and was the tile's label — so both are stated rather than one assumed.
30
34
  */
31
35
  pixLabel: string;
32
36
  cardLabel: string;
33
- /** What each method is like to use — shown under its name. */
37
+ /**
38
+ * What each method is like to use. The checkout's tiles no longer draw it
39
+ * (the 2026-10-06 Pix redesign kept the picker to one line); the flow
40
+ * pipeline's method list (`flows/pipeline/methods.ts`) still says it.
41
+ */
34
42
  pixDescription: string;
35
43
  /**
36
- * PIX's line when the store confirms it by hand (the chain's first PIX
37
- * provider declares `confirmation: 'MANUAL'`). Optional; absent falls back
38
- * to `pixDescription`, so a host enabling such a provider should set it.
44
+ * PIX's line when the store confirms it by hand. Optional; absent falls
45
+ * back to `pixDescription`.
39
46
  */
40
47
  pixManualDescription?: string;
41
48
  cardDescription: string;
42
- /** This store's providers cannot take that method. */
49
+ /**
50
+ * This store's providers cannot take that method: the disabled tile's
51
+ * accessible description, and a caption under the picker.
52
+ */
43
53
  unavailableHere: string;
44
54
  }
45
55
 
@@ -88,16 +98,59 @@ export interface SettlingCopy {
88
98
  checkAgainAction: string;
89
99
  }
90
100
 
101
+ /**
102
+ * The words the after-copy state says (the 2026-10-06 Pix redesign). One shape
103
+ * for both kinds of Pix: the manual one says the STORE confirms, the automatic
104
+ * one that the BANK does — the screen around it is the same.
105
+ */
106
+ export interface PixAfterCopyCopy {
107
+ title: string;
108
+ /**
109
+ * The sentence under the title. `wide` is the desktop layout, whose reader is
110
+ * on a "page" rather than a "screen"; `**…**` marks the bold span.
111
+ */
112
+ body(totalLabel: string, wide: boolean): string;
113
+ /** The three steps: copied (done), pay (current), the confirmation (pending). */
114
+ stepCopied: string;
115
+ stepPay(wide: boolean): string;
116
+ stepConfirm(time: string): string;
117
+ }
118
+
91
119
  /** The PIX pane: the QR, the copyable code, and the wait. */
92
120
  export interface PixPaneCopy {
93
121
  heading: string;
94
- /** What to do with the QR, carrying the order total the buyer is paying. */
95
- instructions(totalLabel: string): string;
96
122
  /** The QR image's alternative text — the only thing a screen reader gets. */
97
123
  qrAlt: string;
124
+ /** The primary copy button. */
98
125
  copyAction: string;
99
- /** The same button for the two seconds after a successful copy. */
126
+ /** Replaces a copy button's label for the two seconds after a successful copy. */
100
127
  copiedAction: string;
128
+ /** The after-copy state's copy-again button. */
129
+ copyAgainAction: string;
130
+ /** The after-copy state's way back to the QR (narrow layout only). */
131
+ showQrAction: string;
132
+ /** The after-copy state's way to the card, when the store takes one. */
133
+ preferCardAction: string;
134
+ /** The after-copy state's poll line. */
135
+ verifying: string;
136
+ /** The narrow layout's tabs: their group name and the two labels. */
137
+ tabsLabel: string;
138
+ copyPasteTab: string;
139
+ qrTab: string;
140
+ /** The wide layout's two column headings and the word between them. */
141
+ qrHeading: string;
142
+ copyPasteHeading: string;
143
+ or: string;
144
+ /** Under the QR in the wide layout; `**…**` marks the bold span. */
145
+ qrInstructions: string;
146
+ /** Under the QR in the narrow layout's QR tab. */
147
+ qrTabCaption: string;
148
+ /** Under the copy button: narrow and wide. `**…**` marks the bold span. */
149
+ copyPasteHint: string;
150
+ internetBankingHint: string;
151
+ /** The wide after-copy state's QR box. */
152
+ notPaidYet: string;
153
+ scanFromPhone: string;
101
154
  /**
102
155
  * How long the code lasts. Takes the already-formatted clock time, and
103
156
  * {@link expiryLocale} is what formatted it.
@@ -113,6 +166,14 @@ export interface PixPaneCopy {
113
166
  awaiting: string;
114
167
  /** The order came back with no PIX charge on it — nothing to show. */
115
168
  chargeMissing: string;
169
+ /**
170
+ * @deprecated Unread since the 2026-10-06 redesign (FUT-3368): the pane's
171
+ * hints replaced the one instructions line. Kept, optional and in its old
172
+ * shape, so an old pack only has to ADD the new keys; remove it from yours.
173
+ */
174
+ instructions?: (totalLabel: string) => string;
175
+ /** After a copy, for a Pix the BANK confirms. */
176
+ afterCopy: PixAfterCopyCopy;
116
177
  /**
117
178
  * The same pane for a Pix the STORE confirms (`PixCharge.confirmation:
118
179
  * 'MANUAL'`). Optional so a host's own table keeps compiling; absent, the
@@ -120,15 +181,13 @@ export interface PixPaneCopy {
120
181
  * enables a store-confirmed provider should set them.
121
182
  */
122
183
  manual?: {
123
- instructions(totalLabel: string): string;
124
184
  validUntil(time: string): string;
125
185
  awaiting: string;
126
- /**
127
- * Shown once the buyer copies the code, and kept: copying is the moment
128
- * they leave for their bank app, so it is when they must learn the wait
129
- * that follows is the STORE's, and that the answer arrives on this screen.
130
- * Optional so a host's own copy without it simply shows no notice.
131
- */
186
+ /** Required: the automatic sentences promise "automática" (FUT-3232). */
187
+ afterCopy: PixAfterCopyCopy;
188
+ /** @deprecated Unread since FUT-3368; see {@link PixPaneCopy.instructions}. */
189
+ instructions?: (totalLabel: string) => string;
190
+ /** @deprecated Unread since FUT-3368: the after-copy screen replaced the notice. */
132
191
  copied?: { title: string; description: string };
133
192
  };
134
193
  }
@@ -19,12 +19,12 @@ import type { CheckoutScreensCopy } from './screens-copy';
19
19
  export const EN_US_CHECKOUT_SCREENS_COPY: CheckoutScreensCopy = {
20
20
  method: {
21
21
  groupLabel: 'Payment method',
22
- pixLabel: 'PIX',
22
+ pixLabel: 'Pix',
23
23
  cardLabel: 'Card',
24
24
  pixDescription: 'Approved instantly',
25
25
  pixManualDescription: 'Confirmed by the store',
26
26
  cardDescription: 'Credit, paid in full',
27
- unavailableHere: 'Unavailable in this store',
27
+ unavailableHere: 'Not available at this store',
28
28
  },
29
29
  settling: {
30
30
  cannotConfirm: 'Could not confirm the payment',
@@ -42,25 +42,50 @@ export const EN_US_CHECKOUT_SCREENS_COPY: CheckoutScreensCopy = {
42
42
  checkAgainAction: 'Check again',
43
43
  },
44
44
  pix: {
45
- heading: 'Pay with PIX',
46
- instructions: (totalLabel) =>
47
- `Scan the QR code in your banking app, or copy the code. Total ${totalLabel}.`,
45
+ heading: 'Pay with Pix',
48
46
  qrAlt: 'PIX QR code for payment',
49
- copyAction: 'Copy',
47
+ copyAction: 'Copy code',
50
48
  copiedAction: 'Copied!',
49
+ copyAgainAction: 'Copy again',
50
+ showQrAction: 'Show QR code',
51
+ preferCardAction: 'Rather pay by card?',
52
+ verifying: 'Checking automatically…',
53
+ tabsLabel: 'How to pay the Pix',
54
+ copyPasteTab: 'Copy and paste',
55
+ qrTab: 'QR code',
56
+ qrHeading: 'On your phone',
57
+ copyPasteHeading: 'Pix copy and paste',
58
+ or: 'or',
59
+ qrInstructions: 'Open your banking app on your phone, choose **Pix › Scan QR code** and point it at the screen.',
60
+ qrTabCaption: 'Scan it with the banking app on another phone.',
61
+ copyPasteHint: 'Paste it into your banking app under **Pix copy and paste**.',
62
+ internetBankingHint: 'Paying through internet banking? Paste the code under **Pix copy and paste**.',
63
+ notPaidYet: 'Not paid yet?',
64
+ scanFromPhone: 'Scan it with the banking app on your phone.',
51
65
  validUntil: (time) => `Valid until ${time}. Confirmation is automatic.`,
52
66
  expiryLocale: 'en-US',
53
67
  awaiting: 'Waiting for payment…',
54
68
  chargeMissing: 'Could not generate the PIX code.',
69
+ afterCopy: {
70
+ title: 'Waiting for the payment',
71
+ body: (totalLabel, wide) =>
72
+ `As soon as the bank confirms the payment of ${wide ? `**${totalLabel}**` : totalLabel}, your order shows up here. You can leave this ${wide ? 'page' : 'screen'} open.`,
73
+ stepCopied: 'Pix code copied',
74
+ stepPay: (wide) =>
75
+ wide ? 'Pay in your banking app or internet banking (Pix copy and paste)' : 'Pay in your banking app (Pix copy and paste)',
76
+ stepConfirm: () => 'The bank confirms the payment automatically',
77
+ },
55
78
  manual: {
56
- instructions: (totalLabel) =>
57
- `Pay ${totalLabel} with the QR code or the code below. The store checks it arrived and confirms your order.`,
58
79
  validUntil: (time) => `The store confirms the payment by ${time}.`,
59
- awaiting: 'Waiting for the store to confirm the payment…',
60
- copied: {
61
- title: 'Code copied. Now pay in your bank app',
62
- description:
63
- 'Once you have paid, come back to this screen and wait: the store checks it arrived and confirms your order right here.',
80
+ awaiting: 'Waiting for the store to confirm…',
81
+ afterCopy: {
82
+ title: 'Waiting for the store',
83
+ body: (totalLabel, wide) =>
84
+ `As soon as the store confirms it received ${wide ? `**${totalLabel}**` : totalLabel}, your order shows up here. You can leave this ${wide ? 'page' : 'screen'} open.`,
85
+ stepCopied: 'Pix code copied',
86
+ stepPay: (wide) =>
87
+ wide ? 'Pay in your banking app or internet banking (Pix copy and paste)' : 'Pay in your banking app (Pix copy and paste)',
88
+ stepConfirm: (time) => `The store confirms the payment — by ${time}`,
64
89
  },
65
90
  },
66
91
  },
@@ -12,7 +12,7 @@ import type { CheckoutScreensCopy } from './screens-copy';
12
12
  export const PT_BR_CHECKOUT_SCREENS_COPY: CheckoutScreensCopy = {
13
13
  method: {
14
14
  groupLabel: 'Forma de pagamento',
15
- pixLabel: 'PIX',
15
+ pixLabel: 'Pix',
16
16
  cardLabel: 'Cartão',
17
17
  pixDescription: 'Aprovação imediata',
18
18
  pixManualDescription: 'Confirmado pela loja',
@@ -31,25 +31,50 @@ export const PT_BR_CHECKOUT_SCREENS_COPY: CheckoutScreensCopy = {
31
31
  checkAgainAction: 'Verificar de novo',
32
32
  },
33
33
  pix: {
34
- heading: 'Pague com PIX',
35
- instructions: (totalLabel) =>
36
- `Escaneie o QR code no app do seu banco ou copie o código. Total ${totalLabel}.`,
34
+ heading: 'Pague com Pix',
37
35
  qrAlt: 'QR Code PIX para pagamento',
38
- copyAction: 'Copiar',
36
+ copyAction: 'Copiar código',
39
37
  copiedAction: 'Copiado!',
38
+ copyAgainAction: 'Copiar de novo',
39
+ showQrAction: 'Ver QR code',
40
+ preferCardAction: 'Prefere pagar com cartão?',
41
+ verifying: 'Verificando automaticamente…',
42
+ tabsLabel: 'Forma de pagar o Pix',
43
+ copyPasteTab: 'Copia e cola',
44
+ qrTab: 'QR code',
45
+ qrHeading: 'Pelo celular',
46
+ copyPasteHeading: 'Pix Copia e Cola',
47
+ or: 'ou',
48
+ qrInstructions: 'Abra o app do seu banco no celular, escolha **Pix › Ler QR code** e aponte para a tela.',
49
+ qrTabCaption: 'Escaneie com o app do banco em outro celular.',
50
+ copyPasteHint: 'Cole no app do seu banco em **Pix Copia e Cola**.',
51
+ internetBankingHint: 'Pagando pelo internet banking? Cole o código em **Pix Copia e Cola**.',
52
+ notPaidYet: 'Ainda não pagou?',
53
+ scanFromPhone: 'Escaneie com o app do banco no celular.',
40
54
  validUntil: (time) => `Válido até ${time}. A confirmação é automática.`,
41
55
  expiryLocale: 'pt-BR',
42
56
  awaiting: 'Aguardando pagamento…',
43
57
  chargeMissing: 'Não foi possível gerar o código PIX.',
58
+ afterCopy: {
59
+ title: 'Aguardando o pagamento',
60
+ body: (totalLabel, wide) =>
61
+ `Assim que o banco confirmar o pagamento de ${wide ? `**${totalLabel}**` : totalLabel}, seu pedido aparece aqui. Pode deixar esta ${wide ? 'página' : 'tela'} aberta.`,
62
+ stepCopied: 'Código Pix copiado',
63
+ stepPay: (wide) =>
64
+ wide ? 'Pague no app ou no internet banking (Pix Copia e Cola)' : 'Pague no app do seu banco (Pix Copia e Cola)',
65
+ stepConfirm: () => 'O banco confirma o pagamento automaticamente',
66
+ },
44
67
  manual: {
45
- instructions: (totalLabel) =>
46
- `Pague ${totalLabel} com o QR code ou o código abaixo. A loja confere o recebimento e confirma o seu pedido.`,
47
68
  validUntil: (time) => `A loja confirma o pagamento até ${time}.`,
48
- awaiting: 'Aguardando confirmação do pagamento pela loja…',
49
- copied: {
50
- title: 'Código copiado. Agora pague no app do seu banco',
51
- description:
52
- 'Depois de pagar, volte para esta tela e aguarde: a loja confere o recebimento e confirma o seu pedido aqui mesmo.',
69
+ awaiting: 'Aguardando confirmação da loja…',
70
+ afterCopy: {
71
+ title: 'Aguardando a loja',
72
+ body: (totalLabel, wide) =>
73
+ `Assim que a loja confirmar o recebimento de ${wide ? `**${totalLabel}**` : totalLabel}, seu pedido aparece aqui. Pode deixar esta ${wide ? 'página' : 'tela'} aberta.`,
74
+ stepCopied: 'Código Pix copiado',
75
+ stepPay: (wide) =>
76
+ wide ? 'Pague no app ou no internet banking (Pix Copia e Cola)' : 'Pague no app do seu banco (Pix Copia e Cola)',
77
+ stepConfirm: (time) => `A loja confirma o pagamento — até ${time}`,
53
78
  },
54
79
  },
55
80
  },