@12-apps/payments-frontend 3.30.0 → 3.31.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/payments-frontend",
3
- "version": "3.30.0",
3
+ "version": "3.31.0",
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,
@@ -243,6 +258,7 @@ export function PixView({
243
258
  hour: "2-digit",
244
259
  minute: "2-digit",
245
260
  });
261
+ const sentences = pixSentences(copy, pix.confirmation === "MANUAL");
246
262
 
247
263
  return (
248
264
  <Box
@@ -253,7 +269,7 @@ export function PixView({
253
269
  {copy.heading}
254
270
  </Text>
255
271
  <Text variant="body" size="sm" color="secondary" as="p">
256
- {copy.instructions(order.totalLabel)}
272
+ {sentences.instructions(order.totalLabel)}
257
273
  </Text>
258
274
 
259
275
  <Box
@@ -268,10 +284,10 @@ export function PixView({
268
284
  <PixCodeBox pix={pix} />
269
285
 
270
286
  <Text variant="caption" size="xs" color="secondary" as="p" data-testid="pix-expiry">
271
- {copy.validUntil(validUntil)}
287
+ {sentences.validUntil(validUntil)}
272
288
  </Text>
273
289
 
274
- <PixPollFooter error={error} timedOut={timedOut} onCheckAgain={checkAgain} />
290
+ <PixPollFooter error={error} timedOut={timedOut} onCheckAgain={checkAgain} manual={sentences !== copy} />
275
291
  </Box>
276
292
  );
277
293
  }
@@ -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 a loja confirmar o pagamento…',
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
  /**