@12-apps/payments-frontend 3.6.0 → 3.7.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 +4 -4
- package/src/components/checkout/checkout-flow.tsx +56 -23
- package/src/components/checkout/checkout-steps.tsx +21 -12
- package/src/components/checkout/payment-status.tsx +50 -62
- package/src/components/checkout/payments-unavailable.tsx +14 -5
- package/src/components/checkout/pt-BR.ts +74 -0
- package/src/components/checkout/view-copy.ts +94 -0
- package/src/flows/copy.ts +27 -3
- package/src/flows/create-payment-flows.tsx +1 -0
- package/src/flows/screens-buyer.tsx +6 -4
- package/src/flows/screens-pay.tsx +1 -0
- package/src/index.ts +14 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/payments-frontend",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"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.",
|
|
6
6
|
"exports": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"storybook:build": "storybook build"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@12-apps/payments-backend": "^4.
|
|
20
|
+
"@12-apps/payments-backend": "^4.15.0",
|
|
21
21
|
"react-qr-code": "^2.2.0"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"react-dom": ">=19.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@12-apps/eslint-config": "^1.21.
|
|
32
|
-
"@12-apps/typescript-config": "^1.20.
|
|
31
|
+
"@12-apps/eslint-config": "^1.21.1",
|
|
32
|
+
"@12-apps/typescript-config": "^1.20.1",
|
|
33
33
|
"@emotion/react": "^11.14.0",
|
|
34
34
|
"@emotion/styled": "^11.14.0",
|
|
35
35
|
"@mui/material": "^6.5.0",
|
|
@@ -7,13 +7,17 @@ import { ArrowBackIcon } from "./icons";
|
|
|
7
7
|
import { PaymentStatus } from "./payment-status";
|
|
8
8
|
import type { BuyerInfo, CheckoutProviderConfig, SettlementCheckout } from "./types";
|
|
9
9
|
import { CheckoutComponentsProvider, useCheckoutComponents, type CheckoutComponents } from "./ui";
|
|
10
|
+
import type { CheckoutViewCopy } from "./view-copy";
|
|
10
11
|
import { useCheckoutController, type CheckoutHostPorts } from "./use-checkout-controller";
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
/** Step ids are the flow's own contract; the labels beside them are host copy. */
|
|
14
|
+
function stepperSteps(copy: CheckoutViewCopy): { id: string; label: string }[] {
|
|
15
|
+
return [
|
|
16
|
+
{ id: "dados", label: copy.steps.dados },
|
|
17
|
+
{ id: "payment", label: copy.steps.payment },
|
|
18
|
+
{ id: "status", label: copy.steps.status },
|
|
19
|
+
];
|
|
20
|
+
}
|
|
17
21
|
|
|
18
22
|
/** What the flow reads off the host's cart — display facts, never money math. */
|
|
19
23
|
export interface CheckoutCartView {
|
|
@@ -35,6 +39,14 @@ export interface CheckoutCartView {
|
|
|
35
39
|
* pixels render through the slot contract (`components`, see `ui.tsx`).
|
|
36
40
|
*/
|
|
37
41
|
export interface CheckoutFlowProps extends CheckoutHostPorts {
|
|
42
|
+
/**
|
|
43
|
+
* Every sentence the flow's own chrome renders — stepper labels, the Dados
|
|
44
|
+
* step, the empty cart, the confirmation screen. REQUIRED, with no default:
|
|
45
|
+
* a pt-BR host passes `PT_BR_CHECKOUT_VIEW_COPY` from the package root by
|
|
46
|
+
* hand, so choosing Portuguese is a line in the host's diff, never a
|
|
47
|
+
* silence (FUT-760's doctrine, finally applied to the legacy flow too).
|
|
48
|
+
*/
|
|
49
|
+
copy: CheckoutViewCopy;
|
|
38
50
|
/** The host's cart, reduced to what the flow displays. */
|
|
39
51
|
cart: CheckoutCartView;
|
|
40
52
|
defaultBuyer?: BuyerInfo;
|
|
@@ -97,23 +109,23 @@ function statusTotalLabel(
|
|
|
97
109
|
* per-line remove. Offering it here put a destructive control next to the form
|
|
98
110
|
* a buyer is filling in, one tap away from the field they are typing into.
|
|
99
111
|
*/
|
|
100
|
-
function CheckoutHeader({ step, onBack }: { step: string; onBack: () => void }): JSX.Element {
|
|
112
|
+
function CheckoutHeader({ copy, step, onBack }: { copy: CheckoutViewCopy; step: string; onBack: () => void }): JSX.Element {
|
|
101
113
|
const { Button } = useCheckoutComponents();
|
|
102
114
|
return (
|
|
103
115
|
<Box sx={{ minHeight: 36, display: "flex", alignItems: "center", gap: 1 }}>
|
|
104
116
|
<Button variant="text" color="neutral" size="sm" icon={<ArrowBackIcon fontSize="small" />} iconPosition="left" onClick={onBack} dataTestId="checkout-back">
|
|
105
|
-
{step === "dados" ?
|
|
117
|
+
{step === "dados" ? copy.dados.keepShopping : copy.dados.back}
|
|
106
118
|
</Button>
|
|
107
119
|
</Box>
|
|
108
120
|
);
|
|
109
121
|
}
|
|
110
122
|
|
|
111
123
|
/** Compact 50px progress header so the form stays above the fold on mobile. */
|
|
112
|
-
function ProgressHeader({ step, completed }: { step: string; completed: Set<string> }): JSX.Element {
|
|
124
|
+
function ProgressHeader({ copy, step, completed }: { copy: CheckoutViewCopy; step: string; completed: Set<string> }): JSX.Element {
|
|
113
125
|
const { Stepper } = useCheckoutComponents();
|
|
114
126
|
return (
|
|
115
127
|
<Box sx={{ height: 50, display: "flex", flexDirection: "column", justifyContent: "center" }}>
|
|
116
|
-
<Stepper steps={
|
|
128
|
+
<Stepper steps={stepperSteps(copy)} activeId={step} completed={completed} orientation="horizontal" size="sm" data-testid="checkout-stepper" />
|
|
117
129
|
</Box>
|
|
118
130
|
);
|
|
119
131
|
}
|
|
@@ -124,8 +136,37 @@ function ProgressHeader({ step, completed }: { step: string; completed: Set<stri
|
|
|
124
136
|
* chrome (reduced to {@link CheckoutCartView} here), and the provider SDK +
|
|
125
137
|
* card public key are loaded lazily by the card path (order-scoped REST).
|
|
126
138
|
*/
|
|
139
|
+
/** Step 3, with the controller's facts mapped onto the status screen. */
|
|
140
|
+
function StatusStep({
|
|
141
|
+
copy,
|
|
142
|
+
c,
|
|
143
|
+
settlement,
|
|
144
|
+
cart,
|
|
145
|
+
confirmationExtra,
|
|
146
|
+
}: {
|
|
147
|
+
copy: CheckoutViewCopy;
|
|
148
|
+
c: ReturnType<typeof useCheckoutController>;
|
|
149
|
+
settlement: SettlementCheckout | null | undefined;
|
|
150
|
+
cart: CheckoutCartView;
|
|
151
|
+
confirmationExtra: ReactNode;
|
|
152
|
+
}): JSX.Element {
|
|
153
|
+
return (
|
|
154
|
+
<PaymentStatus
|
|
155
|
+
copy={copy.status}
|
|
156
|
+
status={c.finalStatus}
|
|
157
|
+
totalLabel={statusTotalLabel(c.order, settlement, cart)}
|
|
158
|
+
{...confirmationFacts(c.order, c.buyer)}
|
|
159
|
+
onRetry={c.retry}
|
|
160
|
+
onRegenerate={() => { c.setStep("payment"); void c.startPayment("PIX"); }}
|
|
161
|
+
onBackToMenu={c.goToMenu}
|
|
162
|
+
paidExtra={confirmationExtra}
|
|
163
|
+
awaitingTimedOut={c.resumeTimedOut}
|
|
164
|
+
/>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
127
168
|
function CheckoutFlowBody(props: Omit<CheckoutFlowProps, "components">): JSX.Element {
|
|
128
|
-
const { cart, defaultBuyer, settlement, taxIdOnFile = false, providerConfig, tenantSlug, confirmationExtra, validateApplePayMerchant, ...ports } = props;
|
|
169
|
+
const { copy, cart, defaultBuyer, settlement, taxIdOnFile = false, providerConfig, tenantSlug, confirmationExtra, validateApplePayMerchant, ...ports } = props;
|
|
129
170
|
// Resolved for NO method on purpose (FUT-595): the Dados step opens before
|
|
130
171
|
// the picker, and the form is filled once — so it asks for the union of what
|
|
131
172
|
// any chain member may need rather than re-opening after the choice. A chain
|
|
@@ -141,17 +182,18 @@ function CheckoutFlowBody(props: Omit<CheckoutFlowProps, "components">): JSX.Ele
|
|
|
141
182
|
// method picker. It holds until an order exists — once one does, its lines are
|
|
142
183
|
// snapshotted server-side and the cart no longer speaks for it.
|
|
143
184
|
if (!settlement && cart.empty && !c.order && c.step !== "status") {
|
|
144
|
-
return <EmptyCart onBack={c.goToMenu} />;
|
|
185
|
+
return <EmptyCart copy={copy.emptyCart} onBack={c.goToMenu} />;
|
|
145
186
|
}
|
|
146
187
|
|
|
147
188
|
return (
|
|
148
189
|
<Box sx={{ display: "flex", flexDirection: "column", gap: { xs: 2, sm: 3 } }}>
|
|
149
|
-
<CheckoutHeader step={c.step} onBack={c.back} />
|
|
190
|
+
<CheckoutHeader copy={copy} step={c.step} onBack={c.back} />
|
|
150
191
|
|
|
151
|
-
<ProgressHeader step={c.step} completed={c.completed} />
|
|
192
|
+
<ProgressHeader copy={copy} step={c.step} completed={c.completed} />
|
|
152
193
|
|
|
153
194
|
{c.step === "dados" ? (
|
|
154
195
|
<DadosStep
|
|
196
|
+
copy={copy.dados}
|
|
155
197
|
buyer={c.buyer}
|
|
156
198
|
onBuyerChange={c.setBuyer}
|
|
157
199
|
saveProfile={c.saveProfile}
|
|
@@ -189,16 +231,7 @@ function CheckoutFlowBody(props: Omit<CheckoutFlowProps, "components">): JSX.Ele
|
|
|
189
231
|
) : null}
|
|
190
232
|
|
|
191
233
|
{c.step === "status" ? (
|
|
192
|
-
<
|
|
193
|
-
status={c.finalStatus}
|
|
194
|
-
totalLabel={statusTotalLabel(c.order, settlement, cart)}
|
|
195
|
-
{...confirmationFacts(c.order, c.buyer)}
|
|
196
|
-
onRetry={c.retry}
|
|
197
|
-
onRegenerate={() => { c.setStep("payment"); void c.startPayment("PIX"); }}
|
|
198
|
-
onBackToMenu={c.goToMenu}
|
|
199
|
-
paidExtra={confirmationExtra}
|
|
200
|
-
awaitingTimedOut={c.resumeTimedOut}
|
|
201
|
-
/>
|
|
234
|
+
<StatusStep copy={copy} c={c} settlement={settlement} cart={cart} confirmationExtra={confirmationExtra} />
|
|
202
235
|
) : null}
|
|
203
236
|
</Box>
|
|
204
237
|
);
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
OrderStatus,
|
|
18
18
|
PaymentMethod,
|
|
19
19
|
} from "./types";
|
|
20
|
+
import type { DadosStepCopy, EmptyCartCopy } from "./view-copy";
|
|
20
21
|
import { useCheckoutComponents } from "./ui";
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -100,15 +101,15 @@ function PaymentBody({
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
/** Empty-cart state shown when there's nothing to check out. */
|
|
103
|
-
export function EmptyCart({ onBack }: { onBack: () => void }): JSX.Element {
|
|
104
|
+
export function EmptyCart({ copy, onBack }: { copy: EmptyCartCopy; onBack: () => void }): JSX.Element {
|
|
104
105
|
const { Button, Text } = useCheckoutComponents();
|
|
105
106
|
return (
|
|
106
107
|
<Box data-testid="checkout-empty" sx={{ py: 8, textAlign: "center", display: "flex", flexDirection: "column", gap: 2, alignItems: "center" }}>
|
|
107
108
|
<Text variant="heading" size="md" as="p">
|
|
108
|
-
|
|
109
|
+
{copy.title}
|
|
109
110
|
</Text>
|
|
110
111
|
<Button variant="solid" color="primary" size="md" onClick={onBack} dataTestId="checkout-empty-back">
|
|
111
|
-
|
|
112
|
+
{copy.action}
|
|
112
113
|
</Button>
|
|
113
114
|
</Box>
|
|
114
115
|
);
|
|
@@ -158,6 +159,7 @@ function PayBarTotal({
|
|
|
158
159
|
* — no charge yet.
|
|
159
160
|
*/
|
|
160
161
|
export function DadosStep({
|
|
162
|
+
copy,
|
|
161
163
|
buyer,
|
|
162
164
|
onBuyerChange,
|
|
163
165
|
saveProfile,
|
|
@@ -170,6 +172,8 @@ export function DadosStep({
|
|
|
170
172
|
discountLines,
|
|
171
173
|
totalOverride,
|
|
172
174
|
}: {
|
|
175
|
+
/** The step's own sentences — the HOST's words (see `./view-copy`). */
|
|
176
|
+
copy: DadosStepCopy;
|
|
173
177
|
buyer: BuyerInfo;
|
|
174
178
|
onBuyerChange: (buyer: BuyerInfo) => void;
|
|
175
179
|
saveProfile: boolean;
|
|
@@ -206,12 +210,13 @@ export function DadosStep({
|
|
|
206
210
|
<Checkbox
|
|
207
211
|
checked={saveProfile}
|
|
208
212
|
onChange={(_event, checked) => onSaveProfileChange(checked)}
|
|
209
|
-
label=
|
|
213
|
+
label={copy.saveProfile}
|
|
210
214
|
data-testid="buyer-save-profile"
|
|
211
215
|
/>
|
|
212
216
|
</Box>
|
|
213
217
|
|
|
214
218
|
<DadosPayBar
|
|
219
|
+
copy={copy}
|
|
215
220
|
totalLabel={totalLabel}
|
|
216
221
|
totalItems={totalItems}
|
|
217
222
|
createError={createError}
|
|
@@ -227,12 +232,14 @@ export function DadosStep({
|
|
|
227
232
|
|
|
228
233
|
/** The sticky "Continuar" bar: the refusal, the money, and the one action. */
|
|
229
234
|
function DadosPayBar({
|
|
235
|
+
copy,
|
|
230
236
|
totalLabel,
|
|
231
237
|
totalItems,
|
|
232
238
|
createError,
|
|
233
239
|
onContinue,
|
|
234
240
|
children,
|
|
235
241
|
}: {
|
|
242
|
+
copy: DadosStepCopy;
|
|
236
243
|
totalLabel: string;
|
|
237
244
|
totalItems: number;
|
|
238
245
|
createError: string | null;
|
|
@@ -244,20 +251,22 @@ function DadosPayBar({
|
|
|
244
251
|
<ActionBar dataTestId="checkout-pay-bar">
|
|
245
252
|
<Box sx={{ width: "100%", display: "flex", flexDirection: "column", gap: 1.5 }}>
|
|
246
253
|
{createError ? (
|
|
247
|
-
<Alert variant="danger" title=
|
|
254
|
+
<Alert variant="danger" title={copy.cannotContinueTitle} description={createError} showIcon data-testid="checkout-error" />
|
|
248
255
|
) : null}
|
|
249
256
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
|
250
257
|
<PayBarTotal totalLabel={totalLabel} totalItems={totalItems}>{children}</PayBarTotal>
|
|
251
258
|
<Box sx={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 0.5, minWidth: 0 }}>
|
|
252
259
|
<Button variant="solid" color="primary" size="lg" fullWidth onClick={onContinue} dataTestId="checkout-continue">
|
|
253
|
-
|
|
260
|
+
{copy.continueAction}
|
|
254
261
|
</Button>
|
|
255
|
-
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
262
|
+
{copy.secureNotice ? (
|
|
263
|
+
<Box sx={{ display: "flex", alignItems: "center", gap: 0.5, color: "text.secondary" }}>
|
|
264
|
+
<LockOutlinedIcon sx={{ fontSize: 13 }} />
|
|
265
|
+
<Text variant="caption" size="xs" color="secondary" as="span">
|
|
266
|
+
{copy.secureNotice}
|
|
267
|
+
</Text>
|
|
268
|
+
</Box>
|
|
269
|
+
) : null}
|
|
261
270
|
</Box>
|
|
262
271
|
</Box>
|
|
263
272
|
</Box>
|
|
@@ -4,6 +4,7 @@ import type { JSX, ReactNode } from "react";
|
|
|
4
4
|
import { CheckCircleOutlineIcon, ErrorOutlineIcon, ScheduleIcon } from "./icons";
|
|
5
5
|
import type { OrderStatus } from "./types";
|
|
6
6
|
import { useCheckoutComponents } from "./ui";
|
|
7
|
+
import type { PaymentStatusCopy } from "./view-copy";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* The last screen of checkout.
|
|
@@ -28,67 +29,38 @@ import { useCheckoutComponents } from "./ui";
|
|
|
28
29
|
* screen says and how it is arranged has changed.
|
|
29
30
|
*/
|
|
30
31
|
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
/**
|
|
33
|
+
* The per-outcome VISUAL grammar — icon and semantic tone. The heading and
|
|
34
|
+
* supporting line beside them come from {@link PaymentStatusCopy}: an icon is
|
|
35
|
+
* the component's own vocabulary, a sentence never is. (The FAILED support
|
|
36
|
+
* line's job — say "nothing was charged" plainly and first — and the
|
|
37
|
+
* timed-out wait's "do not pay again" now live with the host's words, where
|
|
38
|
+
* FUT-556's reasoning is documented on the copy port.)
|
|
39
|
+
*/
|
|
40
|
+
interface OutcomeVisual {
|
|
35
41
|
icon: JSX.Element;
|
|
36
42
|
/** Semantic theme token — never a raw colour. */
|
|
37
43
|
tone: "success" | "danger" | "warning" | "neutral";
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
const
|
|
41
|
-
PAID: {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
tone: "success",
|
|
46
|
-
},
|
|
47
|
-
AWAITING_PAYMENT: {
|
|
48
|
-
heading: "Confirmando seu pagamento",
|
|
49
|
-
support: "Isso costuma levar alguns segundos. Pode deixar esta tela aberta.",
|
|
50
|
-
icon: <ScheduleIcon fontSize="large" />,
|
|
51
|
-
tone: "neutral",
|
|
52
|
-
},
|
|
53
|
-
FAILED: {
|
|
54
|
-
heading: "Pagamento não concluído",
|
|
55
|
-
// Said plainly and first: the fear on this screen is having been charged
|
|
56
|
-
// for an order that failed.
|
|
57
|
-
support: "Nenhum valor foi cobrado. Você pode tentar novamente.",
|
|
58
|
-
icon: <ErrorOutlineIcon fontSize="large" />,
|
|
59
|
-
tone: "danger",
|
|
60
|
-
},
|
|
61
|
-
EXPIRED: {
|
|
62
|
-
heading: "O código expirou",
|
|
63
|
-
support: "Nenhum valor foi cobrado. Gere um novo código para continuar.",
|
|
64
|
-
icon: <ScheduleIcon fontSize="large" />,
|
|
65
|
-
tone: "warning",
|
|
66
|
-
},
|
|
46
|
+
const OUTCOME_VISUAL: Record<OrderStatus, OutcomeVisual> = {
|
|
47
|
+
PAID: { icon: <CheckCircleOutlineIcon fontSize="large" />, tone: "success" },
|
|
48
|
+
AWAITING_PAYMENT: { icon: <ScheduleIcon fontSize="large" />, tone: "neutral" },
|
|
49
|
+
FAILED: { icon: <ErrorOutlineIcon fontSize="large" />, tone: "danger" },
|
|
50
|
+
EXPIRED: { icon: <ScheduleIcon fontSize="large" />, tone: "warning" },
|
|
67
51
|
};
|
|
68
52
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
*
|
|
78
|
-
* So it leads with what is true and unglamorous — we have not heard yet — and
|
|
79
|
-
* spends its remaining words on the one instruction that matters, which is not
|
|
80
|
-
* to pay again.
|
|
81
|
-
*/
|
|
82
|
-
const AWAITING_TIMED_OUT: Outcome = {
|
|
83
|
-
heading: "Ainda não recebemos a confirmação",
|
|
84
|
-
support:
|
|
85
|
-
"Se você já pagou, o pedido é confirmado assim que a operadora avisar — " +
|
|
86
|
-
"não pague de novo. Você pode fechar esta tela.",
|
|
87
|
-
icon: <ScheduleIcon fontSize="large" />,
|
|
88
|
-
tone: "warning",
|
|
53
|
+
const OUTCOME_COPY_KEY: Record<OrderStatus, keyof Pick<
|
|
54
|
+
PaymentStatusCopy,
|
|
55
|
+
"paid" | "awaiting" | "failed" | "expired"
|
|
56
|
+
>> = {
|
|
57
|
+
PAID: "paid",
|
|
58
|
+
AWAITING_PAYMENT: "awaiting",
|
|
59
|
+
FAILED: "failed",
|
|
60
|
+
EXPIRED: "expired",
|
|
89
61
|
};
|
|
90
62
|
|
|
91
|
-
const TONE_COLOR: Record<
|
|
63
|
+
const TONE_COLOR: Record<OutcomeVisual["tone"], string> = {
|
|
92
64
|
success: "success.main",
|
|
93
65
|
danger: "error.main",
|
|
94
66
|
warning: "warning.main",
|
|
@@ -109,14 +81,22 @@ function orderReference(orderId: string): string {
|
|
|
109
81
|
|
|
110
82
|
/** The headline block: icon, outcome, and one supporting line. */
|
|
111
83
|
function OutcomeHero({
|
|
84
|
+
copy,
|
|
112
85
|
status,
|
|
113
86
|
timedOut = false,
|
|
114
87
|
}: {
|
|
88
|
+
copy: PaymentStatusCopy;
|
|
115
89
|
status: OrderStatus;
|
|
116
90
|
timedOut?: boolean;
|
|
117
91
|
}): JSX.Element {
|
|
118
92
|
const { Text } = useCheckoutComponents();
|
|
119
|
-
const
|
|
93
|
+
const timedOutWait = timedOut && status === "AWAITING_PAYMENT";
|
|
94
|
+
// The timed-out wait keeps AWAITING's neutral clock icon but WARNING's tone:
|
|
95
|
+
// the order is not resolved, and calm-but-alert is the visual for that.
|
|
96
|
+
const visual = timedOutWait
|
|
97
|
+
? { icon: OUTCOME_VISUAL.AWAITING_PAYMENT.icon, tone: "warning" as const }
|
|
98
|
+
: OUTCOME_VISUAL[status];
|
|
99
|
+
const outcome = timedOutWait ? copy.awaitingTimedOut : copy[OUTCOME_COPY_KEY[status]];
|
|
120
100
|
return (
|
|
121
101
|
<Box
|
|
122
102
|
// `payment-paid` is load-bearing for the storefront journeys — it is how
|
|
@@ -133,7 +113,7 @@ function OutcomeHero({
|
|
|
133
113
|
}
|
|
134
114
|
sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 1, textAlign: "center" }}
|
|
135
115
|
>
|
|
136
|
-
<Box sx={{ color: TONE_COLOR[
|
|
116
|
+
<Box sx={{ color: TONE_COLOR[visual.tone], display: "flex" }}>{visual.icon}</Box>
|
|
137
117
|
<Text variant="heading" size="md" weight="bold" as="h2">
|
|
138
118
|
{outcome.heading}
|
|
139
119
|
</Text>
|
|
@@ -165,10 +145,12 @@ function Fact({ label, value, testId }: { label: string; value: string; testId?:
|
|
|
165
145
|
* outcome these facts are either untrue or not yet knowable.
|
|
166
146
|
*/
|
|
167
147
|
function PaidFacts({
|
|
148
|
+
copy,
|
|
168
149
|
totalLabel,
|
|
169
150
|
orderId,
|
|
170
151
|
buyerEmail,
|
|
171
152
|
}: {
|
|
153
|
+
copy: PaymentStatusCopy;
|
|
172
154
|
totalLabel: string;
|
|
173
155
|
orderId?: string;
|
|
174
156
|
buyerEmail?: string;
|
|
@@ -185,22 +167,24 @@ function PaidFacts({
|
|
|
185
167
|
bgcolor: "action.hover",
|
|
186
168
|
}}
|
|
187
169
|
>
|
|
188
|
-
<Fact label=
|
|
170
|
+
<Fact label={copy.amountLabel} value={totalLabel} testId="payment-amount" />
|
|
189
171
|
{orderId ? (
|
|
190
|
-
<Fact label=
|
|
172
|
+
<Fact label={copy.referenceLabel} value={`#${orderReference(orderId)}`} testId="payment-reference" />
|
|
191
173
|
) : null}
|
|
192
|
-
{buyerEmail ? <Fact label=
|
|
174
|
+
{buyerEmail ? <Fact label={copy.receiptEmailLabel} value={buyerEmail} /> : null}
|
|
193
175
|
</Box>
|
|
194
176
|
);
|
|
195
177
|
}
|
|
196
178
|
|
|
197
179
|
/** The next-action row: retry / regenerate for failures, always back-to-menu. */
|
|
198
180
|
function StatusActions({
|
|
181
|
+
copy,
|
|
199
182
|
status,
|
|
200
183
|
onRetry,
|
|
201
184
|
onRegenerate,
|
|
202
185
|
onBackToMenu,
|
|
203
186
|
}: {
|
|
187
|
+
copy: PaymentStatusCopy;
|
|
204
188
|
status: OrderStatus;
|
|
205
189
|
onRetry?: () => void;
|
|
206
190
|
onRegenerate?: () => void;
|
|
@@ -211,12 +195,12 @@ function StatusActions({
|
|
|
211
195
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
|
|
212
196
|
{status === "FAILED" && onRetry ? (
|
|
213
197
|
<Button variant="solid" color="primary" size="lg" onClick={onRetry} dataTestId="payment-retry">
|
|
214
|
-
|
|
198
|
+
{copy.retryAction}
|
|
215
199
|
</Button>
|
|
216
200
|
) : null}
|
|
217
201
|
{status === "EXPIRED" && onRegenerate ? (
|
|
218
202
|
<Button variant="solid" color="primary" size="lg" onClick={onRegenerate} dataTestId="payment-regenerate">
|
|
219
|
-
|
|
203
|
+
{copy.regenerateAction}
|
|
220
204
|
</Button>
|
|
221
205
|
) : null}
|
|
222
206
|
<Button
|
|
@@ -228,13 +212,14 @@ function StatusActions({
|
|
|
228
212
|
onClick={onBackToMenu}
|
|
229
213
|
dataTestId="payment-back-to-menu"
|
|
230
214
|
>
|
|
231
|
-
|
|
215
|
+
{copy.backAction}
|
|
232
216
|
</Button>
|
|
233
217
|
</Box>
|
|
234
218
|
);
|
|
235
219
|
}
|
|
236
220
|
|
|
237
221
|
export function PaymentStatus({
|
|
222
|
+
copy,
|
|
238
223
|
status,
|
|
239
224
|
totalLabel,
|
|
240
225
|
orderId,
|
|
@@ -245,6 +230,8 @@ export function PaymentStatus({
|
|
|
245
230
|
paidExtra,
|
|
246
231
|
awaitingTimedOut = false,
|
|
247
232
|
}: {
|
|
233
|
+
/** Every sentence and label this screen renders — the HOST's words. */
|
|
234
|
+
copy: PaymentStatusCopy;
|
|
248
235
|
status: OrderStatus | null;
|
|
249
236
|
totalLabel: string;
|
|
250
237
|
/** The created order, when there is one — absent before the charge is raised. */
|
|
@@ -278,10 +265,10 @@ export function PaymentStatus({
|
|
|
278
265
|
data-timed-out={awaitingTimedOut ? "true" : undefined}
|
|
279
266
|
sx={{ display: "flex", flexDirection: "column", gap: 3, alignItems: "stretch", py: 2 }}
|
|
280
267
|
>
|
|
281
|
-
<OutcomeHero status={effective} timedOut={awaitingTimedOut} />
|
|
268
|
+
<OutcomeHero copy={copy} status={effective} timedOut={awaitingTimedOut} />
|
|
282
269
|
|
|
283
270
|
{effective === "PAID" ? (
|
|
284
|
-
<PaidFacts totalLabel={totalLabel} orderId={orderId} buyerEmail={buyerEmail} />
|
|
271
|
+
<PaidFacts copy={copy} totalLabel={totalLabel} orderId={orderId} buyerEmail={buyerEmail} />
|
|
285
272
|
) : null}
|
|
286
273
|
|
|
287
274
|
{effective === "PAID" ? paidExtra : null}
|
|
@@ -291,6 +278,7 @@ export function PaymentStatus({
|
|
|
291
278
|
) : null}
|
|
292
279
|
|
|
293
280
|
<StatusActions
|
|
281
|
+
copy={copy}
|
|
294
282
|
status={effective}
|
|
295
283
|
onRetry={onRetry}
|
|
296
284
|
onRegenerate={onRegenerate}
|
|
@@ -23,8 +23,16 @@ import { Box } from "@mui/material";
|
|
|
23
23
|
import type { JSX } from "react";
|
|
24
24
|
|
|
25
25
|
import { useCheckoutComponents } from "./ui";
|
|
26
|
+
import type { PaymentsUnavailableCopy } from "./view-copy";
|
|
26
27
|
|
|
27
28
|
interface PaymentsUnavailableProps {
|
|
29
|
+
/**
|
|
30
|
+
* Every sentence this screen can say — the HOST's words (see
|
|
31
|
+
* `./view-copy`). Until 6.0.0 the two remedies and the button were compiled
|
|
32
|
+
* in, in one application's Portuguese, and a host that dutifully passed the
|
|
33
|
+
* flows copy port still got them.
|
|
34
|
+
*/
|
|
35
|
+
copy: PaymentsUnavailableCopy;
|
|
28
36
|
/**
|
|
29
37
|
* Whether THIS shopper can settle with a waiter — mesas on AND a mesa of
|
|
30
38
|
* their own. Decides which remedy is offered.
|
|
@@ -37,6 +45,7 @@ interface PaymentsUnavailableProps {
|
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
export function PaymentsUnavailable({
|
|
48
|
+
copy,
|
|
40
49
|
waiterAvailable,
|
|
41
50
|
onCallWaiter,
|
|
42
51
|
calling = false,
|
|
@@ -47,8 +56,8 @@ export function PaymentsUnavailable({
|
|
|
47
56
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }} data-testid="checkout-payments-disabled">
|
|
48
57
|
<Alert
|
|
49
58
|
variant="info"
|
|
50
|
-
title=
|
|
51
|
-
description=
|
|
59
|
+
title={copy.title}
|
|
60
|
+
description={copy.body}
|
|
52
61
|
showIcon
|
|
53
62
|
/>
|
|
54
63
|
</Box>
|
|
@@ -59,8 +68,8 @@ export function PaymentsUnavailable({
|
|
|
59
68
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }} data-testid="checkout-call-waiter">
|
|
60
69
|
<Alert
|
|
61
70
|
variant="info"
|
|
62
|
-
title=
|
|
63
|
-
description=
|
|
71
|
+
title={copy.remedyTitle}
|
|
72
|
+
description={copy.remedyBody}
|
|
64
73
|
showIcon
|
|
65
74
|
/>
|
|
66
75
|
{onCallWaiter ? (
|
|
@@ -71,7 +80,7 @@ export function PaymentsUnavailable({
|
|
|
71
80
|
onClick={onCallWaiter}
|
|
72
81
|
dataTestId="checkout-call-waiter-button"
|
|
73
82
|
>
|
|
74
|
-
{calling ?
|
|
83
|
+
{calling ? copy.callingAction : copy.callAction}
|
|
75
84
|
</Button>
|
|
76
85
|
) : null}
|
|
77
86
|
</Box>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CheckoutViewCopy,
|
|
3
|
+
PaymentsUnavailableCopy,
|
|
4
|
+
PaymentStatusCopy,
|
|
5
|
+
} from "./view-copy";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The pt-BR pack — the exact sentences these views compiled in until 6.0.0,
|
|
9
|
+
* now NAMED exports a host passes by hand, never defaults. The filename is
|
|
10
|
+
* what exempts this file from the copy-portability gate: Portuguese may ship,
|
|
11
|
+
* it may not be silent.
|
|
12
|
+
*/
|
|
13
|
+
export const PT_BR_PAYMENTS_UNAVAILABLE_COPY: PaymentsUnavailableCopy = {
|
|
14
|
+
title: "Pagamento online indisponível",
|
|
15
|
+
body: "Esta loja não recebe pagamentos pelo site. Combine o pagamento diretamente com a loja para concluir seu pedido.",
|
|
16
|
+
remedyTitle: "Pagamento com o garçom",
|
|
17
|
+
remedyBody: "Esta loja não recebe pagamentos pelo site. Chame o garçom para fechar a conta na mesa.",
|
|
18
|
+
callAction: "Chamar garçom",
|
|
19
|
+
callingAction: "Chamando...",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const PT_BR_PAYMENT_STATUS_COPY: PaymentStatusCopy = {
|
|
23
|
+
paid: {
|
|
24
|
+
heading: "Pedido confirmado",
|
|
25
|
+
support: "Recebemos seu pagamento e já registramos o pedido.",
|
|
26
|
+
},
|
|
27
|
+
awaiting: {
|
|
28
|
+
heading: "Confirmando seu pagamento",
|
|
29
|
+
support: "Isso costuma levar alguns segundos. Pode deixar esta tela aberta.",
|
|
30
|
+
},
|
|
31
|
+
failed: {
|
|
32
|
+
// Said plainly and first: the fear on this screen is having been charged
|
|
33
|
+
// for an order that failed.
|
|
34
|
+
heading: "Pagamento não concluído",
|
|
35
|
+
support: "Nenhum valor foi cobrado. Você pode tentar novamente.",
|
|
36
|
+
},
|
|
37
|
+
expired: {
|
|
38
|
+
heading: "O código expirou",
|
|
39
|
+
support: "Nenhum valor foi cobrado. Gere um novo código para continuar.",
|
|
40
|
+
},
|
|
41
|
+
awaitingTimedOut: {
|
|
42
|
+
heading: "Ainda não recebemos a confirmação",
|
|
43
|
+
support:
|
|
44
|
+
"Se você já pagou, o pedido é confirmado assim que a operadora avisar — " +
|
|
45
|
+
"não pague de novo. Você pode fechar esta tela.",
|
|
46
|
+
},
|
|
47
|
+
retryAction: "Tentar novamente",
|
|
48
|
+
regenerateAction: "Gerar novo código",
|
|
49
|
+
backAction: "Voltar ao cardápio",
|
|
50
|
+
amountLabel: "Valor pago",
|
|
51
|
+
referenceLabel: "Pedido",
|
|
52
|
+
receiptEmailLabel: "Comprovante enviado para",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const PT_BR_CHECKOUT_VIEW_COPY: CheckoutViewCopy = {
|
|
56
|
+
steps: {
|
|
57
|
+
dados: "Dados",
|
|
58
|
+
payment: "Pagamento",
|
|
59
|
+
status: "Confirmação",
|
|
60
|
+
},
|
|
61
|
+
dados: {
|
|
62
|
+
saveProfile: "Salvar meus dados para a próxima compra",
|
|
63
|
+
cannotContinueTitle: "Não foi possível continuar",
|
|
64
|
+
continueAction: "Continuar",
|
|
65
|
+
secureNotice: "Pagamento seguro",
|
|
66
|
+
keepShopping: "Continuar comprando",
|
|
67
|
+
back: "Voltar",
|
|
68
|
+
},
|
|
69
|
+
emptyCart: {
|
|
70
|
+
title: "Seu carrinho está vazio.",
|
|
71
|
+
action: "Ver cardápio",
|
|
72
|
+
},
|
|
73
|
+
status: PT_BR_PAYMENT_STATUS_COPY,
|
|
74
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every string the legacy checkout views render — required props, with NO
|
|
3
|
+
* defaults, deliberately (the payments extraction's own doctrine, FUT-760):
|
|
4
|
+
* a default in the origin host's language reads as finished to the next host
|
|
5
|
+
* right up until a buyer sees it. These three views proved the point — the
|
|
6
|
+
* flows factory's copy port existed, was REQUIRED, declared
|
|
7
|
+
* `unavailableWithRemedyTitle` and `emptyCartAction`, and the views simply
|
|
8
|
+
* never read it, so a host that dutifully passed copy still rendered another
|
|
9
|
+
* product's voice.
|
|
10
|
+
*
|
|
11
|
+
* A pt-BR host imports {@link PT_BR_CHECKOUT_VIEW_COPY} from `./pt-BR` (the
|
|
12
|
+
* package root re-exports it) and passes it by hand — one reviewable line,
|
|
13
|
+
* never a silence.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** The screen shown when the store cannot take money online. */
|
|
17
|
+
export interface PaymentsUnavailableCopy {
|
|
18
|
+
/** No remedy: the store simply does not charge online. */
|
|
19
|
+
title: string;
|
|
20
|
+
body: string;
|
|
21
|
+
/** A remedy exists (the origin host: the waiter; a clinic: the front desk). */
|
|
22
|
+
remedyTitle: string;
|
|
23
|
+
remedyBody: string;
|
|
24
|
+
/** The remedy button, idle and in flight. */
|
|
25
|
+
callAction: string;
|
|
26
|
+
callingAction: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The stepper's three labels, in flow order. */
|
|
30
|
+
export interface CheckoutStepperCopy {
|
|
31
|
+
dados: string;
|
|
32
|
+
payment: string;
|
|
33
|
+
status: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The buyer-details step: its bar, its refusal, and the slim header. */
|
|
37
|
+
export interface DadosStepCopy {
|
|
38
|
+
/** The save-my-details checkbox. */
|
|
39
|
+
saveProfile: string;
|
|
40
|
+
/** The refusal Alert's title (its body is the server's own sentence). */
|
|
41
|
+
cannotContinueTitle: string;
|
|
42
|
+
/** The sticky bar's one action. */
|
|
43
|
+
continueAction: string;
|
|
44
|
+
/**
|
|
45
|
+
* The reassurance caption under it. OPTIONAL, on the flows port's
|
|
46
|
+
* `secureNotice` precedent: a host that has not written the sentence gets
|
|
47
|
+
* NO caption rather than someone else's.
|
|
48
|
+
*/
|
|
49
|
+
secureNotice?: string;
|
|
50
|
+
/** The slim header's back control, on Dados and after it. */
|
|
51
|
+
keepShopping: string;
|
|
52
|
+
back: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Nothing to check out (cart mode only). */
|
|
56
|
+
export interface EmptyCartCopy {
|
|
57
|
+
title: string;
|
|
58
|
+
action: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** One outcome's headline and its single supporting line. */
|
|
62
|
+
export interface StatusOutcomeCopy {
|
|
63
|
+
heading: string;
|
|
64
|
+
support: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The confirmation screen. Icons and tones stay the component's — they are
|
|
69
|
+
* visual grammar, not language; every SENTENCE is the host's, including the
|
|
70
|
+
* timed-out wait's "do not pay again", which is the one instruction on this
|
|
71
|
+
* screen that matters (FUT-556).
|
|
72
|
+
*/
|
|
73
|
+
export interface PaymentStatusCopy {
|
|
74
|
+
paid: StatusOutcomeCopy;
|
|
75
|
+
awaiting: StatusOutcomeCopy;
|
|
76
|
+
failed: StatusOutcomeCopy;
|
|
77
|
+
expired: StatusOutcomeCopy;
|
|
78
|
+
awaitingTimedOut: StatusOutcomeCopy;
|
|
79
|
+
retryAction: string;
|
|
80
|
+
regenerateAction: string;
|
|
81
|
+
backAction: string;
|
|
82
|
+
/** The paid receipt's three row labels. */
|
|
83
|
+
amountLabel: string;
|
|
84
|
+
referenceLabel: string;
|
|
85
|
+
receiptEmailLabel: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** What the legacy `CheckoutFlow` itself renders and must be handed. */
|
|
89
|
+
export interface CheckoutViewCopy {
|
|
90
|
+
steps: CheckoutStepperCopy;
|
|
91
|
+
dados: DadosStepCopy;
|
|
92
|
+
emptyCart: EmptyCartCopy;
|
|
93
|
+
status: PaymentStatusCopy;
|
|
94
|
+
}
|
package/src/flows/copy.ts
CHANGED
|
@@ -18,8 +18,20 @@
|
|
|
18
18
|
* the port and nothing else.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import type { CheckoutViewCopy } from "../components/checkout/view-copy";
|
|
22
|
+
|
|
23
|
+
export type { CheckoutViewCopy };
|
|
24
|
+
|
|
21
25
|
/** Every buyer-facing string the factory's own screens render. */
|
|
22
26
|
export interface CheckoutCopyFE {
|
|
27
|
+
/**
|
|
28
|
+
* Everything the wrapped `CheckoutFlow` itself renders — stepper labels,
|
|
29
|
+
* the Dados step, the empty cart, the whole confirmation screen. FUT-741
|
|
30
|
+
* scoped those screens out as "already carried their own product copy";
|
|
31
|
+
* that copy was one application's Portuguese, so the scope-out WAS the
|
|
32
|
+
* leak. A pt-BR host passes `PT_BR_CHECKOUT_VIEW_COPY` here by hand.
|
|
33
|
+
*/
|
|
34
|
+
views: CheckoutViewCopy;
|
|
23
35
|
/** No provider connected AND no host remedy: the store simply does not charge. */
|
|
24
36
|
unavailableTitle: string;
|
|
25
37
|
unavailableBody: string;
|
|
@@ -47,11 +59,23 @@ export interface CheckoutCopyFE {
|
|
|
47
59
|
* part only a host can own.
|
|
48
60
|
*/
|
|
49
61
|
returnTimedOut?: string;
|
|
50
|
-
/** Nothing to check out (cart mode only). */
|
|
51
|
-
emptyCartTitle: string;
|
|
52
|
-
emptyCartAction: string;
|
|
53
62
|
/** The Dados step's primary action. */
|
|
54
63
|
continueAction: string;
|
|
64
|
+
/**
|
|
65
|
+
* The reassurance caption under that action.
|
|
66
|
+
*
|
|
67
|
+
* The screen rendered "Pagamento seguro" inline — one line below a sibling
|
|
68
|
+
* reading `runtime.copy.continueAction`, so the port was already there and
|
|
69
|
+
* already required; this string simply never asked it. That is the shape
|
|
70
|
+
* FUT-760 left behind: defaults removed from the CONFIG surface without the
|
|
71
|
+
* rendering being rewired, so a host that dutifully passes copy still gets
|
|
72
|
+
* the origin product's voice.
|
|
73
|
+
*
|
|
74
|
+
* OPTIONAL, on the {@link CheckoutCopyFE.returnTimedOut} precedent: a host
|
|
75
|
+
* that has not written the sentence gets NO caption rather than someone
|
|
76
|
+
* else's. Reassurance nobody chose is worth less than silence.
|
|
77
|
+
*/
|
|
78
|
+
secureNotice?: string;
|
|
55
79
|
/** The add-card screen (FUT-183): putting a card on file outside a purchase. */
|
|
56
80
|
addCardTitle: string;
|
|
57
81
|
addCardAction: string;
|
|
@@ -87,9 +87,11 @@ function buildBuyerDetails(runtime: FlowsRuntime): CheckoutScreens["BuyerDetails
|
|
|
87
87
|
{runtime.copy.continueAction}
|
|
88
88
|
</Button>
|
|
89
89
|
</Box>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
{runtime.copy.secureNotice ? (
|
|
91
|
+
<Text variant="caption" size="xs" color="secondary" as="p">
|
|
92
|
+
{runtime.copy.secureNotice}
|
|
93
|
+
</Text>
|
|
94
|
+
) : null}
|
|
93
95
|
</Box>
|
|
94
96
|
);
|
|
95
97
|
}
|
|
@@ -155,7 +157,7 @@ function buildEmptyCart(runtime: FlowsRuntime): CheckoutScreens["EmptyCart"] {
|
|
|
155
157
|
return function EmptyCart() {
|
|
156
158
|
return (
|
|
157
159
|
<FlowsShell runtime={runtime}>
|
|
158
|
-
<EmptyCartView onBack={runtime.config.ports.exitToCatalog} />
|
|
160
|
+
<EmptyCartView copy={runtime.copy.views.emptyCart} onBack={runtime.config.ports.exitToCatalog} />
|
|
159
161
|
</FlowsShell>
|
|
160
162
|
);
|
|
161
163
|
};
|
|
@@ -82,6 +82,7 @@ function buildPaymentStatus(runtime: FlowsRuntime): CheckoutScreens["PaymentStat
|
|
|
82
82
|
return (
|
|
83
83
|
<FlowsShell runtime={runtime}>
|
|
84
84
|
<PaymentStatusView
|
|
85
|
+
copy={runtime.copy.views.status}
|
|
85
86
|
status={status}
|
|
86
87
|
totalLabel={payable?.totalLabel ?? ""}
|
|
87
88
|
orderId={payable?.orderId}
|
package/src/index.ts
CHANGED
|
@@ -85,6 +85,20 @@ export {
|
|
|
85
85
|
} from './components/checkout/transport';
|
|
86
86
|
export { type CheckoutHostPorts } from './components/checkout/use-checkout-controller';
|
|
87
87
|
export { PaymentsUnavailable } from './components/checkout/payments-unavailable';
|
|
88
|
+
export type {
|
|
89
|
+
CheckoutStepperCopy,
|
|
90
|
+
CheckoutViewCopy,
|
|
91
|
+
DadosStepCopy,
|
|
92
|
+
EmptyCartCopy,
|
|
93
|
+
PaymentStatusCopy,
|
|
94
|
+
PaymentsUnavailableCopy,
|
|
95
|
+
StatusOutcomeCopy,
|
|
96
|
+
} from './components/checkout/view-copy';
|
|
97
|
+
export {
|
|
98
|
+
PT_BR_CHECKOUT_VIEW_COPY,
|
|
99
|
+
PT_BR_PAYMENTS_UNAVAILABLE_COPY,
|
|
100
|
+
PT_BR_PAYMENT_STATUS_COPY,
|
|
101
|
+
} from './components/checkout/pt-BR';
|
|
88
102
|
export { fetchCheckoutConfig } from './components/checkout/client';
|
|
89
103
|
/**
|
|
90
104
|
* The `sessionStorage` key the hosted-checkout return leg parks the raised
|