@12-apps/payments-frontend 1.21.1 → 2.1.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.
@@ -92,7 +92,7 @@ export interface CheckoutOrder {
92
92
  totalCents: number;
93
93
  /**
94
94
  * GROSS total before discounts, or null when the discount engine never ran
95
- * for this order (every order predating FUT-235, and the comanda path): the
95
+ * for this order (every order predating FUT-235, and the settlement path): the
96
96
  * subtotal then simply IS {@link totalCents}.
97
97
  */
98
98
  subtotalCents: number | null;
@@ -117,13 +117,25 @@ export interface CheckoutOrder {
117
117
  }
118
118
 
119
119
  /**
120
- * Comanda settlement context for the checkout (FUT-comandas): the scope plus
121
- * the host-resolved totals shown in place of the cart total when settling a
122
- * comanda. HOW a comanda is resolved is the host's business; the flow only
123
- * renders the answer.
120
+ * SETTLEMENT context: this checkout pays an already-open balance rather than
121
+ * the cart.
122
+ *
123
+ * Named for what it does, not for the one product it came from. It was
124
+ * `SettlementCheckout` with `scope: "MINE" | "TABLE"` — a restaurant tab and its
125
+ * two ways of splitting one — which is a real concept but that host's, and it
126
+ * arrived in every adopter's type surface. The shape underneath is general: a
127
+ * host has resolved SOME balance, by whatever rule it likes, and the flow shows
128
+ * that total in place of the cart's.
129
+ *
130
+ * `scope` is an opaque string the library only echoes back. It exists so a host
131
+ * can tell its own two settlement modes apart in the events it receives; this
132
+ * package neither reads it nor enumerates it, which is exactly why it must not
133
+ * publish a closed set of somebody else's words.
134
+ *
135
+ * HOW a balance is resolved is the host's business; the flow renders the answer.
124
136
  */
125
- export interface ComandaCheckout {
126
- scope: "MINE" | "TABLE";
137
+ export interface SettlementCheckout {
138
+ scope: string;
127
139
  totalLabel: string;
128
140
  totalItems: number;
129
141
  }
@@ -153,7 +165,7 @@ export type CreateOrderResult =
153
165
 
154
166
  /**
155
167
  * What the flow hands the host's `createOrder` port. Everything else an order
156
- * needs — WHICH cart, WHICH comanda scope, WHICH tenant — is the host's own
168
+ * needs — WHICH cart, WHICH settlement scope, WHICH tenant — is the host's own
157
169
  * context, closed over by its port implementation.
158
170
  */
159
171
  export interface CreateOrderRequest {
@@ -35,15 +35,15 @@ const CPF_ONLY: readonly CheckoutCustomerField[] = [
35
35
  export interface CheckoutHostPorts {
36
36
  /**
37
37
  * Raise the order (and its first charge). The host closes over everything
38
- * the flow must not know: WHICH cart, WHICH tenant, WHICH comanda scope.
39
- */
38
+ * the flow must not know: WHICH cart, WHICH tenant, WHICH settlement scope.
39
+ */
40
40
  createOrder: (input: CreateOrderRequest) => Promise<CreateOrderResult>;
41
41
  /**
42
42
  * Persist the buyer's contact when they press "Continuar" on Dados — the
43
43
  * host's account surface owns the write (and the blank-CPF-never-clears
44
44
  * rule). Fire-and-forget by contract: the flow advances regardless of the
45
45
  * outcome, and only calls this under the "salvar meus dados" consent.
46
- */
46
+ */
47
47
  saveBuyerContact?: (contact: BuyerContact) => void;
48
48
  /** Leave checkout for the host's menu/catalog. */
49
49
  onExitToMenu: () => void;
@@ -52,7 +52,7 @@ export interface CheckoutHostPorts {
52
52
  * server emptied it inside the confirmation transaction (FUT-601) and
53
53
  * nothing else tells the SPA. Never fired for FAILED/EXPIRED: that shopper
54
54
  * still has a basket to retry with.
55
- */
55
+ */
56
56
  onPaid?: () => void;
57
57
  }
58
58
 
@@ -18,21 +18,36 @@ import type { PendingSave } from './ConfirmCredentialSave';
18
18
  */
19
19
 
20
20
  /**
21
- * Is there actually something stored to probe?
21
+ * Is there enough on record to make the probe WORTH RUNNING?
22
22
  *
23
- * Read from the SERVER's answer to the save rather than from the form: blank
24
- * fields are preserved rather than cleared, so what the browser just typed does
25
- * not describe what is now on record.
23
+ * A required-field test cannot answer this, and Stripe is why: every field in its
24
+ * schema is `required: false`, because under authorization the access token
25
+ * fulfils the secret key and nothing needs typing at all. On the credentials
26
+ * path that makes the required-field test vacuously true, so a save with one
27
+ * key pasted and three boxes empty went straight to the probe and came back
28
+ * "Credenciais recusadas pela Stripe." — a rejection reported for a request
29
+ * that was never worth making, blaming the owner mid-way through typing.
30
+ *
31
+ * So completeness is measured over the schema MINUS its `advanced` fields.
32
+ * Not the whole schema: that asked for Stripe's `connectedAccountId`, which
33
+ * ordinary stores must leave empty, and owners filled it with their own account
34
+ * id to satisfy the button — producing exactly the refusal this was meant to
35
+ * prevent. A partial save is still a perfectly good save (blank fields preserve
36
+ * what is stored); it simply is not yet a connection worth testing.
26
37
  */
27
- export function allRequiredStored(
38
+ export function credentialsComplete(
28
39
  descriptor: ProviderDescriptor,
29
- config: MaskedProviderConfig,
40
+ config: MaskedProviderConfig | null,
30
41
  environment: PaymentEnvironment,
42
+ values: Record<string, string>,
31
43
  ): boolean {
32
- const stored = config.environments[environment] ?? {};
33
- return descriptor.credentialSchema
34
- .filter((spec) => spec.required)
35
- .every((spec) => stored[spec.key]?.configured === true);
44
+ const stored = config?.environments[environment] ?? {};
45
+ return descriptor.credentialSchema.every((spec) => {
46
+ if (spec.advanced) return true;
47
+ const typed = values[spec.key];
48
+ if (typed !== undefined && typed.trim() !== '') return true;
49
+ return stored[spec.key]?.configured === true;
50
+ });
36
51
  }
37
52
 
38
53
  /**
@@ -55,10 +70,31 @@ export function fieldsWellFormed(
55
70
  });
56
71
  }
57
72
 
58
- export function saveLabel(descriptor: ProviderDescriptor): string {
73
+ /**
74
+ * What the one button says it will do — and it does BOTH halves.
75
+ *
76
+ * Saving IS testing (see `FormActions`): the write is followed straight away by
77
+ * the probe, which is what reaches the provider with the pasted keys and, on a
78
+ * pass, is the only thing that makes the activation charge appear. A button
79
+ * reading "Salvar" claims half of that. On the credentials path the owner has
80
+ * no other control, so they were left looking for the one that sends the keys
81
+ * to the provider — there isn't one, because this is it.
82
+ *
83
+ * …but only once there is a connection to test. Half a credential set cannot
84
+ * be, so a partly-filled form says plain "Salvar" and the save skips the probe:
85
+ * promising a test that is then reported as "Credenciais recusadas" is worse
86
+ * than promising nothing, because it reads as a verdict on what was typed.
87
+ *
88
+ * A provider whose whole connection is ONE field keeps naming that field
89
+ * instead. The label then says what is about to be committed rather than merely
90
+ * that something is, and on a step whose entire content is that single box that
91
+ * is the more useful of the two truths. Naming one of Stripe's four would be a
92
+ * lie about what the button writes, which is why only the fallback changes.
93
+ */
94
+ export function saveLabel(descriptor: ProviderDescriptor, complete: boolean): string {
59
95
  const required = descriptor.credentialSchema.filter((field) => field.required);
60
96
  const only = required.length === 1 ? required[0] : undefined;
61
- if (!only) return 'Salvar';
97
+ if (!only) return complete ? 'Salvar e testar conexão' : 'Salvar';
62
98
  return `Salvar ${only.label.replace(/\s*\([^)]*\)\s*$/, '')}`;
63
99
  }
64
100
 
@@ -0,0 +1,166 @@
1
+ /**
2
+ * The payments screen's own palette and geometry, taken verbatim from the
3
+ * design prototype.
4
+ *
5
+ * Local rather than theme-level on purpose. `@repo/spa-shared/theme` says it in
6
+ * as many words — "re-deriving them here would quietly restyle every admin
7
+ * screen from a function nobody thinks of as owning that" — and the platform
8
+ * primary (`#6366F1`) is a decision already made for the rest of the app. This
9
+ * screen is a designed surface with its own greens, ambers and hairlines, and
10
+ * matching it must not move a single pixel anywhere else.
11
+ *
12
+ * Every value here is a hex the prototype states, not an approximation: a
13
+ * "pixel perfect" screen that rounds its own line colour to `divider` is a
14
+ * screen that looks nearly right and reviews as wrong.
15
+ */
16
+ export const T = {
17
+ ink: '#111318',
18
+ ink2: '#3d4350',
19
+ ink3: '#6b7280',
20
+ ink4: '#9aa1ad',
21
+ line: '#e4e7ec',
22
+ line2: '#eef0f4',
23
+ bg: '#ffffff',
24
+ bg2: '#f7f8fa',
25
+
26
+ brand: '#5b5bd6',
27
+ brandInk: '#4a4ac4',
28
+ brandSoft: '#eeeefc',
29
+ brandLine: '#c9c9f2',
30
+
31
+ ok: '#0f7a4d',
32
+ okSoft: '#e8f6ef',
33
+ okLine: '#bfe5d3',
34
+ okInk: '#0b5c3a',
35
+
36
+ warn: '#b45309',
37
+ warnSoft: '#fdf1e3',
38
+ warnLine: '#f3d9b5',
39
+ warnInk: '#7c3d06',
40
+
41
+ bad: '#c02626',
42
+ badSoft: '#fdeceb',
43
+ badLine: '#f5c6c2',
44
+ badInk: '#8f1d1d',
45
+
46
+ info: '#1d6fa5',
47
+ infoSoft: '#e8f3fb',
48
+ infoLine: '#bfdcf0',
49
+ infoInk: '#14496b',
50
+
51
+ mono: '"SFMono-Regular",ui-monospace,Menlo,Consolas,monospace',
52
+ } as const;
53
+
54
+ /** The card that bounds one provider: hairline, 12px, nothing clipped. */
55
+ export const CARD_SX = {
56
+ border: `1px solid ${T.line}`,
57
+ borderRadius: '12px',
58
+ background: T.bg,
59
+ overflow: 'hidden',
60
+ } as const;
61
+
62
+ /**
63
+ * A step's panel — the bordered block a single step lives in.
64
+ *
65
+ * `overflow: hidden` matters: the action bar below sticks to its bottom edge
66
+ * and would otherwise paint over the rounded corner.
67
+ */
68
+ export const PANEL_SX = {
69
+ border: `1px solid ${T.line}`,
70
+ borderRadius: '11px',
71
+ overflow: 'hidden',
72
+ mx: '20px',
73
+ mb: '20px',
74
+ } as const;
75
+
76
+ /**
77
+ * The action bar: the primary control, always the last thing in the block the
78
+ * owner just filled.
79
+ *
80
+ * Sticky rather than merely last, so on a long step (the four credential boxes,
81
+ * the card form) the button the owner is working toward stays on screen instead
82
+ * of being something they have to go and find.
83
+ */
84
+ export const BAR_SX = {
85
+ position: 'sticky',
86
+ bottom: 0,
87
+ background: 'rgba(255,255,255,.94)',
88
+ backdropFilter: 'blur(6px)',
89
+ borderTop: `1px solid ${T.line}`,
90
+ px: '18px',
91
+ py: '12px',
92
+ display: 'flex',
93
+ alignItems: 'center',
94
+ gap: '12px',
95
+ flexWrap: 'wrap',
96
+ } as const;
97
+
98
+ /** The sentence beside the button — why it is enabled, or what just happened. */
99
+ export const BAR_MSG_SX = {
100
+ fontSize: '12.5px',
101
+ color: T.ink3,
102
+ flex: 1,
103
+ minWidth: '140px',
104
+ lineHeight: 1.4,
105
+ } as const;
106
+
107
+ /** Primary: the one thing this step is for. */
108
+ export const BTN_PRIMARY_SX = {
109
+ borderRadius: '8px',
110
+ px: '18px',
111
+ py: '10px',
112
+ fontSize: '13.5px',
113
+ fontWeight: 650,
114
+ textTransform: 'none',
115
+ boxShadow: 'none',
116
+ background: T.brand,
117
+ color: '#fff',
118
+ '&:hover': { background: T.brandInk, boxShadow: 'none' },
119
+ '&.Mui-disabled': { background: '#c9cad6', color: '#fff' },
120
+ } as const;
121
+
122
+ /** Secondary: a real alternative, not a lesser primary. */
123
+ export const BTN_SECONDARY_SX = {
124
+ borderRadius: '8px',
125
+ px: '18px',
126
+ py: '10px',
127
+ fontSize: '13.5px',
128
+ fontWeight: 650,
129
+ textTransform: 'none',
130
+ background: T.bg,
131
+ border: `1px solid ${T.line}`,
132
+ color: T.ink2,
133
+ '&:hover': { borderColor: T.ink4, background: T.bg },
134
+ } as const;
135
+
136
+ /**
137
+ * Destructive, stated quietly.
138
+ *
139
+ * "Remover conexão" sits beside the step's own button and must not compete with
140
+ * it — an owner reaches this deliberately or not at all, and a red filled button
141
+ * on every step is an invitation to misclick.
142
+ */
143
+ export const BTN_QUIET_DANGER_SX = {
144
+ background: 'none',
145
+ border: 0,
146
+ color: T.bad,
147
+ px: '6px',
148
+ py: '10px',
149
+ fontSize: '13.5px',
150
+ fontWeight: 600,
151
+ textTransform: 'none',
152
+ '&:hover': { background: 'none', textDecoration: 'underline' },
153
+ } as const;
154
+
155
+ /** An inline text control — the way between the two connection paths. */
156
+ export const LINKISH_SX = {
157
+ background: 'none',
158
+ border: 0,
159
+ color: T.brand,
160
+ fontSize: '12.5px',
161
+ p: 0,
162
+ minWidth: 0,
163
+ textTransform: 'none',
164
+ verticalAlign: 'baseline',
165
+ '&:hover': { background: 'none', textDecoration: 'underline' },
166
+ } as const;
package/src/flows/copy.ts CHANGED
@@ -9,8 +9,13 @@
9
9
  * keep it; moving all of it here in the same change that introduces the factory
10
10
  * would be a copy rewrite disguised as an API.
11
11
  *
12
- * Every default below is today's pt-BR, verbatim a host that passes no `copy`
13
- * reads exactly what a buyer reads now.
12
+ * THE DEFAULTS ARE GONE (FUT-760). `DEFAULT_CHECKOUT_COPY_FE` held one
13
+ * product's RESTAURANT vocabulary "Pagamento com o garçom", "Chame o garçom
14
+ * para fechar a conta na mesa", "Ver cardápio" — and was spread into every
15
+ * adopter that passed no `copy`. A host selling insurance got a waiter, and
16
+ * nothing failed, because saying nothing is exactly how a host silently adopts
17
+ * another product's voice. `copy` is required on the config now; this file is
18
+ * the port and nothing else.
14
19
  */
15
20
 
16
21
  /** Every buyer-facing string the factory's own screens render. */
@@ -49,33 +54,3 @@ export interface CheckoutCopyFE {
49
54
  manageCardsEmpty: string;
50
55
  manageCardsAdd: string;
51
56
  }
52
-
53
- export const DEFAULT_CHECKOUT_COPY_FE: CheckoutCopyFE = {
54
- unavailableTitle: "Pagamento online indisponível",
55
- unavailableBody:
56
- "Esta loja não recebe pagamentos pelo site. Combine o pagamento diretamente com a loja para concluir seu pedido.",
57
- unavailableWithRemedyTitle: "Pagamento com o garçom",
58
- unavailableWithRemedyBody:
59
- "Esta loja não recebe pagamentos pelo site. Chame o garçom para fechar a conta na mesa.",
60
- handoffTitle: "Você será levado ao pagamento",
61
- handoffBody:
62
- "Estamos abrindo a página segura do meio de pagamento. Se ela não abrir sozinha, use o link abaixo.",
63
- handoffLink: "Abrir a página de pagamento",
64
- handoffCancel: "Voltar",
65
- returnPending: "Confirmando seu pagamento…",
66
- returnUnknown:
67
- "Não encontramos um pagamento em andamento nesta sessão. Verifique seus pedidos em instantes.",
68
- emptyCartTitle: "Seu carrinho está vazio.",
69
- emptyCartAction: "Ver cardápio",
70
- continueAction: "Continuar",
71
- addCardTitle: "Adicionar cartão",
72
- addCardAction: "Salvar cartão",
73
- addCardPreparing: "Preparando o formulário…",
74
- addCardSavedTitle: "Cartão salvo",
75
- addCardSavedBody: "Você poderá usá-lo nas próximas compras.",
76
- addCardFailedTitle: "Não foi possível salvar o cartão",
77
- addCardUnavailable: "Esta loja não aceita salvar cartões no momento.",
78
- manageCardsTitle: "Meus cartões",
79
- manageCardsEmpty: "Você ainda não tem cartões salvos.",
80
- manageCardsAdd: "Adicionar cartão",
81
- };
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * ## Why the scope arrives as HOOKS
9
9
  *
10
- * `useScope`, `useCart`, `useBuyerDefaults`, `useComanda` and
10
+ * `useScope`, `useCart`, `useBuyerDefaults`, `useSettlement` and
11
11
  * `ports.useAvailability` are hooks, not values, and they are invoked in a
12
12
  * component BODY — never read at factory time. The factory runs once, at module
13
13
  * evaluation, so a value-shaped config would freeze the first store's slug and
@@ -19,10 +19,9 @@ import { useCallback, type JSX, type ReactNode } from "react";
19
19
  import { buyerFieldsFor } from "../components/checkout/buyer-fields";
20
20
  import { CheckoutFlow } from "../components/checkout/checkout-flow";
21
21
  import { createCheckoutClient } from "../components/checkout/transport";
22
- import type { CheckoutProviderConfig, ComandaCheckout } from "../components/checkout/types";
22
+ import type { CheckoutProviderConfig, SettlementCheckout } from "../components/checkout/types";
23
23
  import { useCheckoutController } from "../components/checkout/use-checkout-controller";
24
24
 
25
- import { DEFAULT_CHECKOUT_COPY_FE } from "./copy";
26
25
  import { FlowsProvider, useResolvedConfig, type FlowsRuntime } from "./runtime";
27
26
  import { buyerScreens } from "./screens-buyer";
28
27
  import { hostedScreens } from "./screens-hosted";
@@ -50,7 +49,7 @@ function buildRuntime(config: PaymentFlowsConfig): FlowsRuntime {
50
49
  return {
51
50
  config,
52
51
  client,
53
- copy: { ...DEFAULT_CHECKOUT_COPY_FE, ...config.copy },
52
+ copy: config.copy,
54
53
  navigate,
55
54
  // Both of these are HOOKS. They are called from a component body on every
56
55
  // render, so the slug follows the host's router and the availability vote
@@ -68,10 +67,10 @@ function buildCheckout(
68
67
  const { ports } = runtime.config;
69
68
  const Unavailable = screens.PaymentsUnavailable;
70
69
 
71
- function CheckoutBody({ comanda }: { comanda?: ComandaCheckout | null }): JSX.Element {
70
+ function CheckoutBody({ settlement }: { settlement?: SettlementCheckout | null }): JSX.Element {
72
71
  const cart = runtime.config.useCart();
73
72
  const defaults = runtime.config.useBuyerDefaults?.() ?? {};
74
- const hostComanda = runtime.config.useComanda?.() ?? null;
73
+ const hostSettlement = runtime.config.useSettlement?.() ?? null;
75
74
  const { config, pending } = useResolvedConfig(runtime);
76
75
  const availability = runtime.useAvailability();
77
76
  const tenantSlug = runtime.useTenantSlug();
@@ -93,7 +92,7 @@ function buildCheckout(
93
92
  onPaid={ports.onPaid}
94
93
  defaultBuyer={defaults.buyer}
95
94
  taxIdOnFile={defaults.taxIdOnFile ?? false}
96
- comanda={comanda ?? hostComanda}
95
+ settlement={settlement ?? hostSettlement}
97
96
  providerConfig={config}
98
97
  tenantSlug={tenantSlug}
99
98
  confirmationExtra={runtime.config.confirmation?.extra}
@@ -31,7 +31,7 @@ import type {
31
31
  ChargeOutcome,
32
32
  CheckoutOrder,
33
33
  CheckoutProviderConfig,
34
- ComandaCheckout,
34
+ SettlementCheckout,
35
35
  CreateOrderRequest,
36
36
  CreateOrderResult,
37
37
  OrderStatus,
@@ -100,8 +100,8 @@ export interface PaymentFlowsConfig {
100
100
  useCart(): CheckoutCartView;
101
101
  /** The buyer's saved details, and whether a CPF is already on file. */
102
102
  useBuyerDefaults?(): { buyer?: BuyerInfo; taxIdOnFile?: boolean; pending?: boolean };
103
- /** Present ⇒ this checkout settles a comanda rather than the cart. */
104
- useComanda?(): ComandaCheckout | null;
103
+ /** Present ⇒ this checkout settles a settlement rather than the cart. */
104
+ useSettlement?(): SettlementCheckout | null;
105
105
 
106
106
  /** Design-system slots, filled ONCE instead of per screen. */
107
107
  components?: Partial<CheckoutComponents>;
@@ -117,7 +117,20 @@ export interface PaymentFlowsConfig {
117
117
  // "what happens WHEN it fires": a timed-out mint must decide whether the walk
118
118
  // advances to the next entry or the whole charge refuses, and that is a money
119
119
  // rule (FUT-563), not a wire-up.
120
- copy?: Partial<CheckoutCopyFE>;
120
+ /**
121
+ * Every buyer-facing sentence the factory's own screens render.
122
+ *
123
+ * REQUIRED, and not partial. It used to be `Partial<…>` over a pt-BR default
124
+ * spread in at `create-payment-flows.tsx`, and that default was one product's
125
+ * RESTAURANT vocabulary — "Pagamento com o garçom", "Chame o garçom para
126
+ * fechar a conta na mesa", "Ver cardápio" — reaching every adopter that said
127
+ * nothing. A host selling insurance got a waiter.
128
+ *
129
+ * That is the failure a default cannot warn about: saying nothing is exactly
130
+ * how a host adopts another product's voice, and nothing fails. Required
131
+ * makes a new host answer once, at the one call site that knows the answer.
132
+ */
133
+ copy: CheckoutCopyFE;
121
134
  /** Host content under the paid receipt (the storefront's PWA install invite). */
122
135
  confirmation?: { extra?: ReactNode };
123
136
  /**
@@ -187,7 +200,7 @@ export interface CheckoutConfigState {
187
200
  /** What `createPaymentFlows` returns. */
188
201
  export interface PaymentFlows {
189
202
  /** THE mount: a complete buyer checkout in one line. */
190
- Checkout: ComponentType<{ comanda?: ComandaCheckout | null }>;
203
+ Checkout: ComponentType<{ settlement?: SettlementCheckout | null }>;
191
204
  /** Slots + transport + scope + the fetched config, for a nesting host. */
192
205
  Provider: ComponentType<{ children: ReactNode; config?: CheckoutProviderConfig | null }>;
193
206
  screens: CheckoutScreens;
package/src/index.ts CHANGED
@@ -56,10 +56,9 @@ export {
56
56
  // hand-composing path is unchanged.
57
57
  // ---------------------------------------------------------------------------
58
58
  export { createPaymentFlows } from './flows/create-payment-flows';
59
- export {
60
- DEFAULT_CHECKOUT_COPY_FE,
61
- type CheckoutCopyFE,
62
- } from './flows/copy';
59
+ // A type and nothing else now: `DEFAULT_CHECKOUT_COPY_FE` used to sit beside it
60
+ // and was the only value this module ever published (FUT-760).
61
+ export type { CheckoutCopyFE } from './flows/copy';
63
62
  export {
64
63
  type BoundCheckoutClient,
65
64
  type BuyerDetailsProps,
@@ -143,7 +142,7 @@ export {
143
142
  type CheckoutOrder,
144
143
  type CheckoutProviderConfig,
145
144
  type CheckoutWalletType,
146
- type ComandaCheckout,
145
+ type SettlementCheckout,
147
146
  type CreateOrderRequest,
148
147
  type CreateOrderResult,
149
148
  type OrderStatus,