@12-apps/payments-frontend 3.23.1 → 3.24.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.23.1",
3
+ "version": "3.24.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.",
@@ -7,6 +7,7 @@ import { EmptyCart, PaymentStep } from "./checkout-steps";
7
7
  import { DadosStep } from "./dados-step";
8
8
  import { ArrowBackIcon } from "./icons";
9
9
  import { PaymentStatus } from "./payment-status";
10
+ import type { BackActionEmphasis } from "./payment-status-parts";
10
11
  import type { BuyerInfo, CheckoutProviderConfig, SettlementCheckout } from "./types";
11
12
  import { CheckoutCopyProvider } from "./copy-context";
12
13
  import { OneClickProvider, useOneClick } from "./one-click";
@@ -94,6 +95,10 @@ export interface CheckoutFlowProps extends CheckoutHostPorts {
94
95
  validateApplePayMerchant?: (validationURL: string) => Promise<unknown>;
95
96
  /** Host content shown on the paid confirmation (the storefront's install invite). */
96
97
  confirmationExtra?: ReactNode;
98
+ /** Host content shown AFTER the confirmation's actions — `PaymentStatusProps.paidFooter`. */
99
+ confirmationFooter?: ReactNode;
100
+ /** Whether the way out leads the paid screen — see {@link BackActionEmphasis}. */
101
+ backActionEmphasis?: BackActionEmphasis;
97
102
  /** Design-system slots; unfilled slots render the raw-MUI defaults. */
98
103
  components?: Partial<CheckoutComponents>;
99
104
  }
@@ -180,12 +185,16 @@ function StatusStep({
180
185
  settlement,
181
186
  cart,
182
187
  confirmationExtra,
188
+ confirmationFooter,
189
+ backActionEmphasis,
183
190
  }: {
184
191
  copy: CheckoutViewCopy;
185
192
  c: ReturnType<typeof useCheckoutController>;
186
193
  settlement: SettlementCheckout | null | undefined;
187
194
  cart: CheckoutCartView;
188
195
  confirmationExtra: ReactNode;
196
+ confirmationFooter: ReactNode;
197
+ backActionEmphasis: BackActionEmphasis | undefined;
189
198
  }): JSX.Element {
190
199
  return (
191
200
  <PaymentStatus
@@ -197,6 +206,8 @@ function StatusStep({
197
206
  onRegenerate={() => { c.setStep("payment"); void c.startPayment("PIX"); }}
198
207
  onBackToMenu={c.goToMenu}
199
208
  paidExtra={confirmationExtra}
209
+ paidFooter={confirmationFooter}
210
+ backActionEmphasis={backActionEmphasis}
200
211
  awaitingTimedOut={c.awaitingTimedOut}
201
212
  // How the wait behind this screen is going, and the way out of it
202
213
  // (FUT-1144). It is the resumed leg's wait for a checkout that came back
@@ -297,7 +308,7 @@ function PagamentoStep({
297
308
  }
298
309
 
299
310
  function CheckoutFlowBody(props: Omit<CheckoutFlowProps, "components">): JSX.Element {
300
- const { copy, cart, defaultBuyer, settlement, taxIdOnFile = false, providerConfig, tenantSlug, confirmationExtra, validateApplePayMerchant, oneClick = false, ...ports } = props;
311
+ const { copy, cart, defaultBuyer, settlement, taxIdOnFile = false, providerConfig, tenantSlug, confirmationExtra, confirmationFooter, backActionEmphasis, validateApplePayMerchant, oneClick = false, ...ports } = props;
301
312
  // Resolved for NO method on purpose (FUT-595): the Dados step opens before
302
313
  // the picker, and the form is filled once — so it asks for the union of what
303
314
  // any chain member may need rather than re-opening after the choice. A chain
@@ -354,7 +365,7 @@ function CheckoutFlowBody(props: Omit<CheckoutFlowProps, "components">): JSX.Ele
354
365
  ) : null}
355
366
 
356
367
  {c.step === "status" ? (
357
- <StatusStep copy={copy} c={c} settlement={settlement} cart={cart} confirmationExtra={confirmationExtra} />
368
+ <StatusStep copy={copy} c={c} settlement={settlement} cart={cart} confirmationExtra={confirmationExtra} confirmationFooter={confirmationFooter} backActionEmphasis={backActionEmphasis} />
358
369
  ) : null}
359
370
  </Box>
360
371
  );
@@ -234,6 +234,38 @@ function retryable(decline: CheckoutDecline | null): boolean {
234
234
  return decline?.retriable !== false;
235
235
  }
236
236
 
237
+ /**
238
+ * How much of the eye the way out asks for on a PAID confirmation.
239
+ *
240
+ * `"primary"` is the default and the historical look: back-to-menu is the only
241
+ * control a settled screen carries, so it wears the solid fill. A host that
242
+ * renders an action of its OWN in {@link PaymentStatusProps.paidExtra} — a
243
+ * route to the order it just raised, say — has two controls on one screen, and
244
+ * only one of them may lead. `"secondary"` is how such a host says which.
245
+ *
246
+ * A prop rather than a guess, because the package cannot make it: whether the
247
+ * buyer's next move is "look at the pedido" or "buy something else" is a fact
248
+ * about the way this shop serves people, and nothing in a payment knows it.
249
+ * Every other status is unaffected — an unsettled screen already paints this
250
+ * button as the quiet way out, and the retry beside it is the lead.
251
+ */
252
+ export type BackActionEmphasis = "primary" | "secondary";
253
+
254
+ /**
255
+ * The back-to-menu button's look, from the outcome and the host's emphasis.
256
+ *
257
+ * Its own function so the two attributes cannot disagree: they moved together
258
+ * as a pair of inline ternaries, and a third condition would have made that
259
+ * four places to keep in step.
260
+ */
261
+ function backLook(
262
+ status: OrderStatus,
263
+ emphasis: BackActionEmphasis,
264
+ ): { variant: "solid" | "outline"; color: "primary" | "neutral" } {
265
+ const leads = status === "PAID" && emphasis === "primary";
266
+ return leads ? { variant: "solid", color: "primary" } : { variant: "outline", color: "neutral" };
267
+ }
268
+
237
269
  /** The next-action row: retry / regenerate / check-again, always back-to-menu. */
238
270
  export function StatusActions({
239
271
  copy,
@@ -244,6 +276,7 @@ export function StatusActions({
244
276
  onCheckAgain,
245
277
  onNotPaid,
246
278
  onBackToMenu,
279
+ backActionEmphasis = "primary",
247
280
  }: {
248
281
  copy: PaymentStatusCopy;
249
282
  status: OrderStatus;
@@ -259,8 +292,11 @@ export function StatusActions({
259
292
  /** The buyer's "I did not pay" (FUT-1146) — present only while it applies. */
260
293
  onNotPaid?: () => void;
261
294
  onBackToMenu: () => void;
295
+ /** Whether back-to-menu leads a PAID screen. See {@link BackActionEmphasis}. */
296
+ backActionEmphasis?: BackActionEmphasis;
262
297
  }): JSX.Element {
263
298
  const { Button } = useCheckoutComponents();
299
+ const back = backLook(status, backActionEmphasis);
264
300
  return (
265
301
  <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
266
302
  {onCheckAgain ? (
@@ -298,8 +334,8 @@ export function StatusActions({
298
334
  <Button
299
335
  // Full width and last, so the thumb lands on the same place in every
300
336
  // outcome instead of hunting a button that moves with the state.
301
- variant={status === "PAID" ? "solid" : "outline"}
302
- color={status === "PAID" ? "primary" : "neutral"}
337
+ variant={back.variant}
338
+ color={back.color}
303
339
  size="lg"
304
340
  onClick={onBackToMenu}
305
341
  dataTestId="payment-back-to-menu"
@@ -2,7 +2,13 @@ import { Box } from "@mui/material";
2
2
  import type { JSX, ReactNode } from "react";
3
3
 
4
4
  import type { CheckoutDecline } from "./decline";
5
- import { OutcomeHero, PaidFacts, StatusActions, type WaitState } from "./payment-status-parts";
5
+ import {
6
+ OutcomeHero,
7
+ PaidFacts,
8
+ StatusActions,
9
+ type BackActionEmphasis,
10
+ type WaitState,
11
+ } from "./payment-status-parts";
6
12
  import type { OrderStatus } from "./types";
7
13
  import { useCheckoutComponents } from "./ui";
8
14
  import type { PaymentStatusCopy } from "./view-copy";
@@ -50,6 +56,23 @@ interface PaymentStatusProps {
50
56
  * is the difference between an offer and an interruption.
51
57
  */
52
58
  paidExtra?: ReactNode;
59
+ /**
60
+ * Host content rendered AFTER the action row, and ONLY on PAID.
61
+ *
62
+ * The other half of {@link paidExtra}, and the difference between them is the
63
+ * actions. What a buyer has to DO next belongs beside the way out, in one
64
+ * block: a panel dropped between two controls splits the only decision on the
65
+ * screen in half. What is merely OFFERED — an install invite, an ask to turn
66
+ * on alerts — belongs after that block, because it is about everything that
67
+ * happens once this screen is closed and must not come between a shopper and
68
+ * the order they just paid for.
69
+ */
70
+ paidFooter?: ReactNode;
71
+ /**
72
+ * Whether back-to-menu leads a PAID screen (default) or stands down for an
73
+ * action the host renders in {@link paidExtra}. See {@link BackActionEmphasis}.
74
+ */
75
+ backActionEmphasis?: BackActionEmphasis;
53
76
  /**
54
77
  * The wait has been given up on. Only meaningful while AWAITING_PAYMENT;
55
78
  * every other status has already resolved, so a stale flag cannot change what
@@ -180,7 +203,10 @@ export function PaymentStatus(props: PaymentStatusProps): JSX.Element {
180
203
  onCheckAgain={view.checkAgain}
181
204
  onNotPaid={view.notPaid}
182
205
  onBackToMenu={onBackToMenu}
206
+ backActionEmphasis={props.backActionEmphasis}
183
207
  />
208
+
209
+ {view.paid ? props.paidFooter : null}
184
210
  </Box>
185
211
  );
186
212
  }
package/src/index.ts CHANGED
@@ -59,6 +59,7 @@ export {
59
59
  type CheckoutCartView,
60
60
  type CheckoutFlowProps,
61
61
  } from './components/checkout/checkout-flow';
62
+ export type { BackActionEmphasis } from './components/checkout/payment-status-parts';
62
63
 
63
64
  // ---------------------------------------------------------------------------
64
65
  // The MOUNTED buyer checkout (FUT-741) and the checkout PIPELINE (FUT-1240).