@12-apps/payments-frontend 3.30.0 → 3.31.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/payments-frontend",
3
- "version": "3.30.0",
3
+ "version": "3.31.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Browser half of the vendor-agnostic payments platform: plug-and-play MUI components for the per-provider settings page (credential form from each provider's schema, masked hints, verify/enable) and the checkout page (PIX QR + polling, card tokenization, hosted-checkout redirect), plus the headless hooks and fetch clients they build on. Talks only to the host's payments HTTP surface — never to a provider directly. Microfrontend-ready: no app coupling, host injects theme and auth.",
@@ -4,7 +4,7 @@ import QRCode from "react-qr-code";
4
4
 
5
5
  import { useCheckoutCopy } from "./copy-context";
6
6
  import { ContentCopyIcon } from "./icons";
7
- import type { SettlingCopy } from "./screens-copy";
7
+ import type { PixPaneCopy, SettlingCopy } from "./screens-copy";
8
8
  import { StalledWait } from "./stalled-wait";
9
9
  import type { CheckoutOrder, OrderStatus, PixCharge } from "./types";
10
10
  import { useCheckoutComponents } from "./ui";
@@ -163,14 +163,18 @@ function PixPollFooter({
163
163
  error,
164
164
  timedOut,
165
165
  onCheckAgain,
166
+ manual,
166
167
  }: {
167
168
  error: string | null;
168
169
  timedOut: boolean;
169
170
  onCheckAgain: () => void;
171
+ /** The store confirms this Pix: the wait is for THEM, not for the bank. */
172
+ manual: boolean;
170
173
  }): JSX.Element {
171
174
  const { Text } = useCheckoutComponents();
172
175
  const { pix, settling } = useCheckoutCopy().screens;
173
176
  const panel = pixWaitPanel(settling, error, timedOut);
177
+ const awaiting = (manual ? pix.manual?.awaiting : undefined) ?? pix.awaiting;
174
178
  if (panel) {
175
179
  // The same panel the card and wallet panes show, held to the width of the
176
180
  // copy-and-paste strip above it so the centred PIX column stays a column.
@@ -190,12 +194,23 @@ function PixPollFooter({
190
194
  <Box sx={{ display: "flex", alignItems: "center", gap: 1, color: "text.secondary" }}>
191
195
  <LoadingDot />
192
196
  <Text variant="caption" size="xs" color="secondary" as="span" data-testid="pix-awaiting">
193
- {pix.awaiting}
197
+ {awaiting}
194
198
  </Text>
195
199
  </Box>
196
200
  );
197
201
  }
198
202
 
203
+ /**
204
+ * The pane's two sentences that depend on who confirms. A store-confirmed Pix
205
+ * must not say "a confirmação é automática": nothing confirms it but the store.
206
+ */
207
+ function pixSentences(
208
+ copy: PixPaneCopy,
209
+ manual: boolean,
210
+ ): Pick<PixPaneCopy, "instructions" | "validUntil"> {
211
+ return (manual ? copy.manual : undefined) ?? copy;
212
+ }
213
+
199
214
  export function PixView({
200
215
  order,
201
216
  onResolved,
@@ -239,10 +254,9 @@ export function PixView({
239
254
  // The locale is the HOST's (FUT-760): it decides what a buyer reads off the
240
255
  // clock, so it travels with the sentence it feeds rather than being frozen
241
256
  // to the origin host's here.
242
- const validUntil = new Date(pix.expiresAt).toLocaleTimeString(copy.expiryLocale, {
243
- hour: "2-digit",
244
- minute: "2-digit",
245
- });
257
+ const validUntil = new Date(pix.expiresAt).toLocaleTimeString(copy.expiryLocale, { hour: "2-digit", minute: "2-digit" });
258
+ const manual = pix.confirmation === "MANUAL";
259
+ const sentences = pixSentences(copy, manual);
246
260
 
247
261
  return (
248
262
  <Box
@@ -253,7 +267,7 @@ export function PixView({
253
267
  {copy.heading}
254
268
  </Text>
255
269
  <Text variant="body" size="sm" color="secondary" as="p">
256
- {copy.instructions(order.totalLabel)}
270
+ {sentences.instructions(order.totalLabel)}
257
271
  </Text>
258
272
 
259
273
  <Box
@@ -268,10 +282,10 @@ export function PixView({
268
282
  <PixCodeBox pix={pix} />
269
283
 
270
284
  <Text variant="caption" size="xs" color="secondary" as="p" data-testid="pix-expiry">
271
- {copy.validUntil(validUntil)}
285
+ {sentences.validUntil(validUntil)}
272
286
  </Text>
273
287
 
274
- <PixPollFooter error={error} timedOut={timedOut} onCheckAgain={checkAgain} />
288
+ <PixPollFooter error={error} timedOut={timedOut} onCheckAgain={checkAgain} manual={manual} />
275
289
  </Box>
276
290
  );
277
291
  }
@@ -107,6 +107,17 @@ export interface PixPaneCopy {
107
107
  awaiting: string;
108
108
  /** The order came back with no PIX charge on it — nothing to show. */
109
109
  chargeMissing: string;
110
+ /**
111
+ * The same pane for a Pix the STORE confirms (`PixCharge.confirmation:
112
+ * 'MANUAL'`). Optional so a host's own table keeps compiling; absent, the
113
+ * automatic sentences are used — which promise "automática", so a host that
114
+ * enables a store-confirmed provider should set them.
115
+ */
116
+ manual?: {
117
+ instructions(totalLabel: string): string;
118
+ validUntil(time: string): string;
119
+ awaiting: string;
120
+ };
110
121
  }
111
122
 
112
123
  /** The card pane's own heading. Everything else in it is {@link SettlingCopy}. */
@@ -51,6 +51,12 @@ export const EN_US_CHECKOUT_SCREENS_COPY: CheckoutScreensCopy = {
51
51
  expiryLocale: 'en-US',
52
52
  awaiting: 'Waiting for payment…',
53
53
  chargeMissing: 'Could not generate the PIX code.',
54
+ manual: {
55
+ instructions: (totalLabel) =>
56
+ `Pay ${totalLabel} with the QR code or the code below. The store checks it arrived and confirms your order.`,
57
+ validUntil: (time) => `The store confirms the payment by ${time}.`,
58
+ awaiting: 'Waiting for the store to confirm the payment…',
59
+ },
54
60
  },
55
61
  card: {
56
62
  heading: 'Pay by card',
@@ -40,6 +40,12 @@ export const PT_BR_CHECKOUT_SCREENS_COPY: CheckoutScreensCopy = {
40
40
  expiryLocale: 'pt-BR',
41
41
  awaiting: 'Aguardando pagamento…',
42
42
  chargeMissing: 'Não foi possível gerar o código PIX.',
43
+ manual: {
44
+ instructions: (totalLabel) =>
45
+ `Pague ${totalLabel} com o QR code ou o código abaixo. A loja confere o recebimento e confirma o seu pedido.`,
46
+ validUntil: (time) => `A loja confirma o pagamento até ${time}.`,
47
+ awaiting: 'Aguardando confirmação do pagamento pela loja…',
48
+ },
43
49
  },
44
50
  card: {
45
51
  heading: 'Pague com cartão',
@@ -66,15 +66,15 @@ export interface BuyerInfo {
66
66
  }
67
67
 
68
68
  /**
69
- * A raised PIX charge. `copyPaste` is the EMV "copia e cola" payload — it is both
70
- * what the QR code encodes and what the copy button copies. The real backend also
71
- * returns a provider QR image; the client renders its own QR from `copyPaste`, so
72
- * no image URL is needed here.
69
+ * A raised PIX charge. `copyPaste` is the EMV "copia e cola" payload: what the
70
+ * QR encodes (rendered client-side, so no image URL) and what the button copies.
73
71
  */
74
72
  export interface PixCharge {
75
73
  copyPaste: string;
76
74
  /** ISO-8601 instant when the charge expires. */
77
75
  expiresAt: string;
76
+ /** `'MANUAL'`: only the store confirms it (`pixmanual`); absent is automatic. */
77
+ confirmation?: 'AUTOMATIC' | 'MANUAL';
78
78
  }
79
79
 
80
80
  /**