@final-commerce/command-frame 0.3.0-beta.6 → 0.3.0-beta.8

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.
Files changed (45) hide show
  1. package/README.md +16 -0
  2. package/dist/CommonTypes.d.ts +7 -0
  3. package/dist/actions/add-cart-discount/types.d.ts +1 -1
  4. package/dist/actions/add-cart-fee/types.d.ts +1 -1
  5. package/dist/actions/add-custom-sale/types.d.ts +1 -0
  6. package/dist/actions/add-non-revenue-item/types.d.ts +1 -1
  7. package/dist/actions/add-product-discount/types.d.ts +1 -1
  8. package/dist/actions/add-product-fee/types.d.ts +1 -0
  9. package/dist/actions/cash-payment/action.js +5 -5
  10. package/dist/actions/cash-payment/mock.js +44 -26
  11. package/dist/actions/cash-payment/types.d.ts +45 -4
  12. package/dist/actions/extension-payment/types.d.ts +8 -2
  13. package/dist/actions/get-cash-rounding-amount/action.d.ts +9 -0
  14. package/dist/actions/get-cash-rounding-amount/action.js +11 -0
  15. package/dist/actions/get-cash-rounding-amount/mock.d.ts +2 -0
  16. package/dist/actions/get-cash-rounding-amount/mock.js +15 -0
  17. package/dist/actions/get-cash-rounding-amount/types.d.ts +25 -0
  18. package/dist/actions/get-cash-rounding-amount/types.js +2 -0
  19. package/dist/actions/get-context/mock.js +1 -0
  20. package/dist/actions/integration-payment/types.d.ts +2 -2
  21. package/dist/actions/partial-payment/types.d.ts +1 -1
  22. package/dist/actions/redeem-payment/types.d.ts +1 -0
  23. package/dist/actions/send-email/action.d.ts +6 -0
  24. package/dist/actions/send-email/action.js +8 -0
  25. package/dist/actions/send-email/mock.d.ts +2 -0
  26. package/dist/actions/send-email/mock.js +11 -0
  27. package/dist/actions/send-email/types.d.ts +28 -0
  28. package/dist/actions/send-email/types.js +2 -0
  29. package/dist/actions/send-sms/action.d.ts +6 -0
  30. package/dist/actions/send-sms/action.js +8 -0
  31. package/dist/actions/send-sms/mock.d.ts +2 -0
  32. package/dist/actions/send-sms/mock.js +11 -0
  33. package/dist/actions/send-sms/types.d.ts +24 -0
  34. package/dist/actions/send-sms/types.js +2 -0
  35. package/dist/actions/set-active-product-discount/types.d.ts +1 -1
  36. package/dist/actions/set-active-product-fee/types.d.ts +1 -0
  37. package/dist/actions/tap-to-pay-payment/types.d.ts +10 -2
  38. package/dist/actions/terminal-payment/types.d.ts +10 -2
  39. package/dist/actions/vendara-payment/types.d.ts +10 -2
  40. package/dist/demo/database.js +1 -0
  41. package/dist/index.d.ts +6 -0
  42. package/dist/index.js +8 -1
  43. package/dist/projects/render/mocks.js +7 -1
  44. package/dist/projects/render/types.d.ts +4 -1
  45. package/package.json +1 -1
package/README.md CHANGED
@@ -47,6 +47,22 @@ npm install @final-commerce/command-frame
47
47
 
48
48
  Commands let the extension iframe call typed functions on the host. Each host environment (Render, Manage) exposes its own set of commands.
49
49
 
50
+ ### Money values: integer minor units
51
+
52
+ Every money value on this API — params **and** responses — is an **integer in
53
+ minor currency units**: `1575` means $15.75 in USD, ¥1575 in JPY. The host
54
+ does all money math in minor units and never converts your inputs. Use
55
+ `getContext().minorUnits` (the currency's decimal exponent, e.g. `2` for USD,
56
+ `0` for JPY) to convert user-typed values before sending:
57
+
58
+ ```typescript
59
+ const { minorUnits } = await command.getContext();
60
+ const amount = Math.round(parseFloat(userInput) * 10 ** (minorUnits ?? 2));
61
+ ```
62
+
63
+ The two exceptions, always flagged in the field docs: percentages (`isPercent:
64
+ true` → `amount` is `0–100`), and quantities.
65
+
50
66
  ### Render (POS System)
51
67
 
52
68
  For building applications that run inside the Render Point of Sale interface.
@@ -113,6 +113,13 @@ export interface CFContextRender {
113
113
  buildIsPremium: boolean;
114
114
  isOffline: boolean;
115
115
  currency: string | null;
116
+ /**
117
+ * Number of minor-unit decimals for the company currency (e.g. 2 for USD
118
+ * — $15.75 = 1575 minor units; 0 for JPY). Every money param/response on
119
+ * this API is expressed in integer minor units; use this to convert user
120
+ * input.
121
+ */
122
+ minorUnits: number | null;
116
123
  currencySymbol: string | null;
117
124
  currencyPrefix: string | null;
118
125
  currencySuffix: string | null;
@@ -1,5 +1,5 @@
1
1
  export interface AddCartDiscountParams {
2
- /** The discount amount. If isPercent is true, this is a percentage (0-100). */
2
+ /** The discount amount in integer MINOR currency units (e.g. 500 = $5.00). If isPercent is true, this is a percentage (0-100) instead. */
3
3
  amount: number;
4
4
  /** Defaults to `false`. */
5
5
  isPercent?: boolean;
@@ -1,5 +1,5 @@
1
1
  export interface AddCartFeeParams {
2
- /** The fee amount. If isPercent is true, this is a percentage. */
2
+ /** The fee amount in integer MINOR currency units (e.g. 500 = $5.00). If isPercent is true, this is a percentage (0-100) instead. */
3
3
  amount: number;
4
4
  /** Defaults to `false`. */
5
5
  isPercent?: boolean;
@@ -1,5 +1,6 @@
1
1
  export interface AddCustomSaleParams {
2
2
  label: string;
3
+ /** Price in integer MINOR currency units (e.g. 1575 = $15.75). */
3
4
  price: number | string;
4
5
  applyTaxes?: boolean;
5
6
  taxTableId?: string;
@@ -8,7 +8,7 @@ export interface AddNonRevenueItemParams {
8
8
  * The cart uses a separate unique line id per add so multiple lines can share the same refId.
9
9
  */
10
10
  id: string;
11
- /** Amount in major currency units (e.g. dollars), same as cart totals */
11
+ /** Amount in integer MINOR currency units (e.g. 1575 = $15.75), same as cart totals. */
12
12
  amount: number;
13
13
  /** Short label for receipts/UI */
14
14
  label?: string;
@@ -1,5 +1,5 @@
1
1
  export interface AddProductDiscountParams {
2
- /** The discount amount. If isPercent is true, this is a percentage. */
2
+ /** The discount amount in integer MINOR currency units (e.g. 500 = $5.00). If isPercent is true, this is a percentage (0-100) instead. */
3
3
  amount: number;
4
4
  /** Defaults to `false`. */
5
5
  isPercent?: boolean;
@@ -1,4 +1,5 @@
1
1
  export interface AddProductFeeParams {
2
+ /** The fee amount in integer MINOR currency units (e.g. 500 = $5.00). If isPercent is true, this is a percentage (0-100) instead. */
2
3
  amount: number;
3
4
  /** Defaults to `false`. */
4
5
  isPercent?: boolean;
@@ -4,9 +4,9 @@
4
4
  */
5
5
  import { commandFrameClient } from "../../client";
6
6
  export const cashPayment = async (params) => {
7
- const finalParams = {
8
- ...params,
9
- openChangeCalculator: params?.openChangeCalculator ?? true
10
- };
11
- return await commandFrameClient.call("cashPayment", finalParams);
7
+ // NOTE: beta.6 defaulted `openChangeCalculator` to true here. The change
8
+ // calculator is deprecated (the flow owns the tender UI via
9
+ // `tenderedAmount` + `getCashRoundingAmount`), so the flag now passes
10
+ // through as-sent and defaults to false host-side.
11
+ return await commandFrameClient.call("cashPayment", params);
12
12
  };
@@ -1,40 +1,58 @@
1
1
  import { applyMockPayment, MOCK_CART } from "../../demo/database";
2
2
  export const mockCashPayment = async (params) => {
3
3
  console.log("[Mock] cashPayment called", params);
4
- // Default to true to match action behavior.
5
- const openChangeCalculator = params?.openChangeCalculator ?? true;
6
- // Amount due for THIS tender (the queued amount-to-be-charged), in minor units.
7
- const due = params?.amount ?? MOCK_CART.amountToBeCharged ?? MOCK_CART.total;
8
- if (openChangeCalculator) {
4
+ const fail = (reason) => {
5
+ console.warn(`[Mock] cashPayment rejected: ${reason}`);
6
+ return {
7
+ success: false,
8
+ amount: 0,
9
+ openChangeCalculator: params?.openChangeCalculator ?? false,
10
+ change: 0,
11
+ cashRounding: 0,
12
+ paymentType: "cash",
13
+ order: null,
14
+ timestamp: new Date().toISOString()
15
+ };
16
+ };
17
+ // Contract: `amount` is required, integer MINOR currency units — same
18
+ // scale as the mock cart's totals, so comparisons are direct.
19
+ if (params?.amount === undefined || params.amount === null) {
20
+ return fail("amount is required (integer minor currency units)");
21
+ }
22
+ const balanceDue = MOCK_CART.amountToBeCharged ?? MOCK_CART.total;
23
+ if (params.amount > balanceDue) {
24
+ return fail(`amount ${params.amount} exceeds balance due ${balanceDue}`);
25
+ }
26
+ // Flow-owned tender: compute change here (no rounding in the mock).
27
+ let change = 0;
28
+ if (params.tenderedAmount !== undefined) {
29
+ if (params.tenderedAmount < params.amount) {
30
+ return fail(`tenderedAmount ${params.tenderedAmount} is less than amount ${params.amount}`);
31
+ }
32
+ change = params.tenderedAmount - params.amount;
33
+ }
34
+ else if (params.openChangeCalculator) {
35
+ // Deprecated legacy path: simulate the POS-owned change calculator.
9
36
  try {
10
- const input = window.prompt(`Amount due: $${(due / 100).toFixed(2)}\nEnter amount tendered:`, (due / 100).toFixed(2));
11
- if (input === null) {
12
- return {
13
- success: false,
14
- amount: 0,
15
- openChangeCalculator,
16
- paymentType: "cash",
17
- order: null,
18
- timestamp: new Date().toISOString()
19
- };
20
- }
21
- const tendered = parseFloat(input); // dollars
22
- if (!isNaN(tendered)) {
23
- const change = tendered - due / 100;
24
- window.alert(change >= 0
25
- ? `Change Due: $${change.toFixed(2)}`
26
- : `Warning: Tendered is short by: $${Math.abs(change).toFixed(2)}`);
27
- }
37
+ const input = window.prompt(`Amount due: $${(params.amount / 100).toFixed(2)}\nEnter amount tendered (dollars):`, (params.amount / 100).toFixed(2));
38
+ if (input === null)
39
+ return fail("cancelled");
40
+ const tendered = parseFloat(input);
41
+ if (!isNaN(tendered))
42
+ change = Math.round(tendered * 100) - params.amount;
28
43
  }
29
44
  catch (e) {
30
45
  console.warn("Could not open prompt/alert (possibly in non-interactive environment)", e);
31
46
  }
32
47
  }
33
- const order = applyMockPayment(due, "cash", "cash");
48
+ const order = applyMockPayment(params.amount, "cash", "cash");
34
49
  return {
35
50
  success: true,
36
- amount: due,
37
- openChangeCalculator,
51
+ amount: params.amount,
52
+ openChangeCalculator: params.openChangeCalculator ?? false,
53
+ change,
54
+ tenderedAmount: params.tenderedAmount,
55
+ cashRounding: 0,
38
56
  paymentType: "cash",
39
57
  order,
40
58
  timestamp: new Date().toISOString()
@@ -1,21 +1,62 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
2
  import type { CFTransitionResult } from "../../common-types/order-state";
3
3
  export interface CashPaymentParams {
4
- /** If not provided, uses the cart total. */
5
- amount?: number;
6
- /** Defaults to false. */
4
+ /**
5
+ * The amount to pay with this tender, in integer MINOR currency units
6
+ * (e.g. 1575 = $15.75 — see `getContext().minorUnits` for the currency's
7
+ * exponent). Required. Semantics against the cart's balance due:
8
+ * - missing → error
9
+ * - less than balance → partial payment (the POS enters a fixed
10
+ * split-payment leg for this amount)
11
+ * - equal to balance → full payment
12
+ * - more than balance → error (overpayment is `tenderedAmount`'s job)
13
+ */
14
+ amount: number;
15
+ /**
16
+ * Cash physically handed over by the customer, in integer MINOR currency
17
+ * units. When provided, the POS computes the change itself (after applying
18
+ * the company's cash-rounding setting to the charge) and does NOT open its
19
+ * own change-calculator modal — the flow owns the tender UI. Must be >= the
20
+ * (rounded) charge or the payment fails.
21
+ * Pair with `getCashRoundingAmount` to display the rounded total before
22
+ * collecting the tender.
23
+ */
24
+ tenderedAmount?: number;
25
+ /**
26
+ * @deprecated The change calculator now lives in the flow: call
27
+ * `getCashRoundingAmount`, collect the tender in your own UI, and pass
28
+ * `tenderedAmount` instead. When true (and `tenderedAmount` is absent) the
29
+ * POS still opens its legacy change-calculator modal.
30
+ */
7
31
  openChangeCalculator?: boolean;
8
32
  /** Override the fulfillment state after full payment. Render resolves the cascade. */
9
33
  checkoutFulfillmentTarget?: string;
10
34
  }
11
35
  export interface CashPaymentResponse {
12
36
  success: boolean;
37
+ /** The amount paid with this tender, in integer MINOR currency units. */
13
38
  amount: number;
39
+ /** @deprecated Mirror of the deprecated request flag. */
14
40
  openChangeCalculator: boolean;
41
+ /**
42
+ * Change due back to the customer in integer MINOR currency units.
43
+ * Non-zero only when `tenderedAmount` exceeded the (cash-rounded) charge.
44
+ * Display this — don't recompute it client-side: it accounts for cash
45
+ * rounding.
46
+ */
47
+ change: number;
48
+ /** Echo of the tendered cash (MINOR units) when it was provided. */
49
+ tenderedAmount?: number;
50
+ /**
51
+ * Signed cash-rounding delta applied to the charge, in integer MINOR
52
+ * currency units (positive = rounded up). 0 when the company has no
53
+ * cash-rounding setting.
54
+ */
55
+ cashRounding: number;
15
56
  paymentType: string;
16
57
  order: CFOrder | null;
17
58
  timestamp: string;
18
59
  /** Present when the state machine blocked or forced the transition. */
19
60
  transitionResult?: CFTransitionResult;
20
61
  }
21
- export type CashPayment = (params?: CashPaymentParams) => Promise<CashPaymentResponse>;
62
+ export type CashPayment = (params: CashPaymentParams) => Promise<CashPaymentResponse>;
@@ -4,7 +4,13 @@ import type { CFTransitionResult } from "../../common-types/order-state";
4
4
  export interface ExtensionPaymentParams {
5
5
  paymentType: string;
6
6
  processor?: string;
7
- amount?: number;
7
+ /**
8
+ * The amount to pay with this tender, in integer MINOR currency units
9
+ * (e.g. 1575 = $15.75). Required. Semantics against the cart's balance
10
+ * due: missing → error; less than balance → partial payment (fixed
11
+ * split-payment leg); equal → full payment; more → error.
12
+ */
13
+ amount: number;
8
14
  label?: string;
9
15
  referenceId?: string;
10
16
  extensionId?: string;
@@ -13,7 +19,7 @@ export interface ExtensionPaymentParams {
13
19
  checkoutFulfillmentTarget?: string;
14
20
  /** EMV data when the underlying payment carries one (typed as `IntegrationEmvData` by the integration wrapper). */
15
21
  emvData?: unknown;
16
- /** Processor fee in minor units; recorded on the order's paymentMethod.processorFee. */
22
+ /** Processor fee in integer MINOR currency units; recorded on the order's paymentMethod.processorFee. */
17
23
  processorFee?: number;
18
24
  }
19
25
  export interface ExtensionPaymentResponse {
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Get cash rounding amount action — preview the company's cash rounding for an
3
+ * amount (or the cart's balance due) WITHOUT touching any state. Read-only.
4
+ *
5
+ * Built for flow-owned cash tender UIs: fetch the rounded total, display it,
6
+ * collect the tendered cash, then call `cashPayment({ amount, tenderedAmount })`.
7
+ */
8
+ import type { GetCashRoundingAmount } from "./types";
9
+ export declare const getCashRoundingAmount: GetCashRoundingAmount;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Get cash rounding amount action — preview the company's cash rounding for an
3
+ * amount (or the cart's balance due) WITHOUT touching any state. Read-only.
4
+ *
5
+ * Built for flow-owned cash tender UIs: fetch the rounded total, display it,
6
+ * collect the tendered cash, then call `cashPayment({ amount, tenderedAmount })`.
7
+ */
8
+ import { commandFrameClient } from "../../client";
9
+ export const getCashRoundingAmount = async (params) => {
10
+ return await commandFrameClient.call("getCashRoundingAmount", params);
11
+ };
@@ -0,0 +1,2 @@
1
+ import { GetCashRoundingAmount } from "./types";
2
+ export declare const mockGetCashRoundingAmount: GetCashRoundingAmount;
@@ -0,0 +1,15 @@
1
+ import { MOCK_CART } from "../../demo/database";
2
+ /** Mock rounds to the nearest 5 minor units (a typical $0.05 cash-rounding setting). */
3
+ const MOCK_INCREMENT_MINOR = 5;
4
+ export const mockGetCashRoundingAmount = async (params) => {
5
+ console.log("[Mock] getCashRoundingAmount called", params);
6
+ const amount = Math.round(params?.amount ?? MOCK_CART.amountToBeCharged ?? MOCK_CART.total);
7
+ const roundedAmount = Math.round(amount / MOCK_INCREMENT_MINOR) * MOCK_INCREMENT_MINOR;
8
+ return {
9
+ success: true,
10
+ amount,
11
+ roundedAmount,
12
+ cashRounding: roundedAmount - amount,
13
+ timestamp: new Date().toISOString()
14
+ };
15
+ };
@@ -0,0 +1,25 @@
1
+ export interface GetCashRoundingAmountParams {
2
+ /**
3
+ * The amount to round, in integer MINOR currency units (e.g. 1577 =
4
+ * $15.77). Defaults to the cart's current balance due when omitted.
5
+ */
6
+ amount?: number;
7
+ }
8
+ export interface GetCashRoundingAmountResponse {
9
+ success: boolean;
10
+ /** The input amount that was rounded, in integer MINOR currency units. */
11
+ amount: number;
12
+ /**
13
+ * The amount after applying the company's cash-rounding setting, in
14
+ * integer MINOR currency units. Equals `amount` when no cash-rounding
15
+ * setting is configured.
16
+ */
17
+ roundedAmount: number;
18
+ /**
19
+ * Signed rounding delta (`roundedAmount - amount`) in integer MINOR
20
+ * currency units. Positive = rounded up. 0 when no setting is configured.
21
+ */
22
+ cashRounding: number;
23
+ timestamp: string;
24
+ }
25
+ export type GetCashRoundingAmount = (params?: GetCashRoundingAmountParams) => Promise<GetCashRoundingAmountResponse>;
@@ -0,0 +1,2 @@
1
+ // Get Cash Rounding Amount Types
2
+ export {};
@@ -18,6 +18,7 @@ export const mockGetContext = () => {
18
18
  buildIsPremium: true,
19
19
  isOffline: false,
20
20
  currency: "USD",
21
+ minorUnits: 2,
21
22
  currencySymbol: "$",
22
23
  currencyPrefix: "$",
23
24
  currencySuffix: "",
@@ -33,7 +33,7 @@ export interface IntegrationEmvData {
33
33
  * Render can record the transaction + order.
34
34
  *
35
35
  * Required fields (compile-time enforced by TS, runtime-enforced by the host handler):
36
- * - `amount` — minor units of the captured amount
36
+ * - `amount` — integer MINOR currency units of the captured amount (e.g. 1575 = $15.75)
37
37
  * - `emvData` — typed card display fields; the host maps + JSON-serializes to `paymentMethod.emv`
38
38
  * (same persisted shape as the native card flow). Required: if the integration
39
39
  * doesn't produce card data, use redeemPayment instead.
@@ -49,7 +49,7 @@ export interface IntegrationPaymentParams {
49
49
  processor?: string;
50
50
  referenceId?: string;
51
51
  metadata?: Record<string, unknown>;
52
- /** Provider fee in minor units — stored on paymentMethod.processorFee. */
52
+ /** Provider fee in integer MINOR currency units — stored on paymentMethod.processorFee. */
53
53
  processorFee?: number;
54
54
  }
55
55
  export type IntegrationPaymentResponse = ExtensionPaymentResponse;
@@ -1,7 +1,7 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
2
  import type { CFTransitionResult } from "../../common-types/order-state";
3
3
  export interface PartialPaymentParams {
4
- /** The payment amount (required if openUI is false). */
4
+ /** The payment amount in integer MINOR currency units (required if openUI is false). If isPercent is true, this is a percentage (0-100) instead. */
5
5
  amount?: number;
6
6
  /** Defaults to false. */
7
7
  isPercent?: boolean;
@@ -5,6 +5,7 @@ import type { ExtensionPaymentResponse } from "../extension-payment/types";
5
5
  * total. The host handler also re-validates this at runtime to catch raw-postMessage callers.
6
6
  */
7
7
  export interface RedeemPaymentParams {
8
+ /** Amount in integer MINOR currency units (e.g. 1575 = $15.75). */
8
9
  amount: number;
9
10
  label?: string;
10
11
  extensionId?: string;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Send email action — email the customer the active order's (or a refund's) receipt.
3
+ * Calls the sendEmail action on the parent window.
4
+ */
5
+ import type { SendEmail } from "./types";
6
+ export declare const sendEmail: SendEmail;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Send email action — email the customer the active order's (or a refund's) receipt.
3
+ * Calls the sendEmail action on the parent window.
4
+ */
5
+ import { commandFrameClient } from "../../client";
6
+ export const sendEmail = async (params) => {
7
+ return await commandFrameClient.call("sendEmail", params);
8
+ };
@@ -0,0 +1,2 @@
1
+ import { SendEmail } from "./types";
2
+ export declare const mockSendEmail: SendEmail;
@@ -0,0 +1,11 @@
1
+ export const mockSendEmail = async (params) => {
2
+ console.log("[Mock] sendEmail called", params);
3
+ return {
4
+ success: true,
5
+ channel: "email",
6
+ email: params?.email || "mock@example.com",
7
+ entityId: params?.refundId || params?.orderId || "mock_order_1",
8
+ type: params?.type || "order",
9
+ timestamp: new Date().toISOString()
10
+ };
11
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Which receipt to send: a finalized order (default) or a refund.
3
+ * Values MUST stay in lockstep with `ReceiptType` in `@final-commerce/common` (used by hub-api);
4
+ * this SDK intentionally has no dependency on that package, so the literal is duplicated here.
5
+ */
6
+ export type SendReceiptType = 'order' | 'refund';
7
+ export interface SendEmailParams {
8
+ /** Recipient email. Defaults to the active customer's email. */
9
+ email?: string;
10
+ /** Order id to send the receipt for. Defaults to the active order. */
11
+ orderId?: string;
12
+ /** Refund id — required when `type` is 'refund'. */
13
+ refundId?: string;
14
+ /** 'order' (default) or 'refund'. */
15
+ type?: SendReceiptType;
16
+ }
17
+ export interface SendEmailResponse {
18
+ success: boolean;
19
+ /** Always 'email' for this action. */
20
+ channel: 'email';
21
+ /** The email the receipt was sent to. */
22
+ email: string;
23
+ /** The order or refund id the receipt was sent for. */
24
+ entityId: string;
25
+ type: SendReceiptType;
26
+ timestamp: string;
27
+ }
28
+ export type SendEmail = (params?: SendEmailParams) => Promise<SendEmailResponse>;
@@ -0,0 +1,2 @@
1
+ // Send Email Types — email the customer their order/refund receipt.
2
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Send SMS action — text the customer the active order's (or a refund's) receipt.
3
+ * Calls the sendSms action on the parent window.
4
+ */
5
+ import type { SendSms } from "./types";
6
+ export declare const sendSms: SendSms;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Send SMS action — text the customer the active order's (or a refund's) receipt.
3
+ * Calls the sendSms action on the parent window.
4
+ */
5
+ import { commandFrameClient } from "../../client";
6
+ export const sendSms = async (params) => {
7
+ return await commandFrameClient.call("sendSms", params);
8
+ };
@@ -0,0 +1,2 @@
1
+ import { SendSms } from "./types";
2
+ export declare const mockSendSms: SendSms;
@@ -0,0 +1,11 @@
1
+ export const mockSendSms = async (params) => {
2
+ console.log("[Mock] sendSms called", params);
3
+ return {
4
+ success: true,
5
+ channel: "text",
6
+ phone: params?.phone || "+15555550123",
7
+ entityId: params?.refundId || params?.orderId || "mock_order_1",
8
+ type: params?.type || "order",
9
+ timestamp: new Date().toISOString()
10
+ };
11
+ };
@@ -0,0 +1,24 @@
1
+ import type { SendReceiptType } from "../send-email/types";
2
+ export type { SendReceiptType };
3
+ export interface SendSmsParams {
4
+ /** Recipient phone in E.164 format (e.g. +15555550123). Defaults to the active customer's phone. */
5
+ phone?: string;
6
+ /** Order id to send the receipt for. Defaults to the active order. */
7
+ orderId?: string;
8
+ /** Refund id — required when `type` is 'refund'. */
9
+ refundId?: string;
10
+ /** 'order' (default) or 'refund'. */
11
+ type?: SendReceiptType;
12
+ }
13
+ export interface SendSmsResponse {
14
+ success: boolean;
15
+ /** Always 'text' for this action. */
16
+ channel: 'text';
17
+ /** The phone the receipt was sent to (E.164). */
18
+ phone: string;
19
+ /** The order or refund id the receipt was sent for. */
20
+ entityId: string;
21
+ type: SendReceiptType;
22
+ timestamp: string;
23
+ }
24
+ export type SendSms = (params?: SendSmsParams) => Promise<SendSmsResponse>;
@@ -0,0 +1,2 @@
1
+ // Send SMS Types — text the customer their order/refund receipt.
2
+ export {};
@@ -1,5 +1,5 @@
1
1
  export interface SetActiveProductDiscountParams {
2
- /** The discount amount. If isPercent is true, this is a percentage. */
2
+ /** The discount amount in integer MINOR currency units (e.g. 500 = $5.00). If isPercent is true, this is a percentage (0-100) instead. */
3
3
  amount: number;
4
4
  /** Defaults to `false`. */
5
5
  isPercent?: boolean;
@@ -1,4 +1,5 @@
1
1
  export interface SetActiveProductFeeParams {
2
+ /** The fee amount in integer MINOR currency units (e.g. 500 = $5.00). If isPercent is true, this is a percentage (0-100) instead. */
2
3
  amount: number;
3
4
  /** Defaults to `false`. */
4
5
  isPercent?: boolean;
@@ -1,8 +1,16 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
2
  import type { CFTransitionResult } from "../../common-types/order-state";
3
3
  export interface TapToPayPaymentParams {
4
- /** If not provided, uses the cart total. */
5
- amount?: number;
4
+ /**
5
+ * The amount to pay with this tender, in integer MINOR currency units
6
+ * (e.g. 1575 = $15.75). Required. Semantics against the cart's balance due:
7
+ * - missing → error
8
+ * - less than balance → partial payment (the POS enters a fixed
9
+ * split-payment leg for this amount)
10
+ * - equal to balance → full payment
11
+ * - more than balance → error
12
+ */
13
+ amount: number;
6
14
  /** Override the fulfillment state after full payment. Render resolves the cascade. */
7
15
  checkoutFulfillmentTarget?: string;
8
16
  }
@@ -1,8 +1,16 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
2
  import type { CFTransitionResult } from "../../common-types/order-state";
3
3
  export interface TerminalPaymentParams {
4
- /** If not provided, uses the cart total. */
5
- amount?: number;
4
+ /**
5
+ * The amount to pay with this tender, in integer MINOR currency units
6
+ * (e.g. 1575 = $15.75). Required. Semantics against the cart's balance due:
7
+ * - missing → error
8
+ * - less than balance → partial payment (the POS enters a fixed
9
+ * split-payment leg for this amount)
10
+ * - equal to balance → full payment
11
+ * - more than balance → error
12
+ */
13
+ amount: number;
6
14
  /** "Bluetooth" or "Cloud". Defaults to "Cloud". */
7
15
  paymentType?: 'Bluetooth' | 'Cloud';
8
16
  /** Override the fulfillment state after full payment. Render resolves the cascade. */
@@ -1,8 +1,16 @@
1
1
  import { CFOrder } from "../../CommonTypes";
2
2
  import type { CFTransitionResult } from "../../common-types/order-state";
3
3
  export interface VendaraPaymentParams {
4
- /** If not provided, uses the cart total. */
5
- amount?: number;
4
+ /**
5
+ * The amount to pay with this tender, in integer MINOR currency units
6
+ * (e.g. 1575 = $15.75). Required. Semantics against the cart's balance due:
7
+ * - missing → error
8
+ * - less than balance → partial payment (the POS enters a fixed
9
+ * split-payment leg for this amount)
10
+ * - equal to balance → full payment
11
+ * - more than balance → error
12
+ */
13
+ amount: number;
6
14
  /** Override the fulfillment state after full payment. Render resolves the cascade. */
7
15
  checkoutFulfillmentTarget?: string;
8
16
  }
@@ -25,6 +25,7 @@ export const MOCK_COMPANY = {
25
25
  settings: {
26
26
  currencyPrefix: "$",
27
27
  currencySuffix: "",
28
+ minorUnits: 2,
28
29
  currencySymbol: "$",
29
30
  decimalSeparator: ".",
30
31
  thousandSeparator: ",",
package/dist/index.d.ts CHANGED
@@ -33,6 +33,7 @@ export declare const command: {
33
33
  readonly resumeParkedOrder: import(".").ResumeParkedOrder;
34
34
  readonly deleteParkedOrder: import(".").DeleteParkedOrder;
35
35
  readonly cashPayment: import(".").CashPayment;
36
+ readonly getCashRoundingAmount: import(".").GetCashRoundingAmount;
36
37
  readonly tapToPayPayment: import(".").TapToPayPayment;
37
38
  readonly terminalPayment: import(".").TerminalPayment;
38
39
  readonly vendaraPayment: import(".").VendaraPayment;
@@ -68,6 +69,8 @@ export declare const command: {
68
69
  readonly removeOrderNote: import(".").RemoveOrderNote;
69
70
  readonly removeCustomSale: import(".").RemoveCustomSale;
70
71
  readonly removeNonRevenueItem: import(".").RemoveNonRevenueItem;
72
+ readonly sendEmail: import(".").SendEmail;
73
+ readonly sendSms: import(".").SendSms;
71
74
  readonly initiateRefund: import(".").InitiateRefund;
72
75
  readonly setRefundStockAction: import(".").SetRefundStockAction;
73
76
  readonly selectAllRefundItems: import(".").SelectAllRefundItems;
@@ -157,6 +160,7 @@ export type { ParkOrder, ParkOrderResponse } from "./actions/park-order/types";
157
160
  export type { ResumeParkedOrder, ResumeParkedOrderParams, ResumeParkedOrderResponse } from "./actions/resume-parked-order/types";
158
161
  export type { DeleteParkedOrder, DeleteParkedOrderParams, DeleteParkedOrderResponse } from "./actions/delete-parked-order/types";
159
162
  export type { CashPayment, CashPaymentParams, CashPaymentResponse } from "./actions/cash-payment/types";
163
+ export type { GetCashRoundingAmount, GetCashRoundingAmountParams, GetCashRoundingAmountResponse } from "./actions/get-cash-rounding-amount/types";
160
164
  export type { TapToPayPayment, TapToPayPaymentParams, TapToPayPaymentResponse } from "./actions/tap-to-pay-payment/types";
161
165
  export type { TerminalPayment, TerminalPaymentParams, TerminalPaymentResponse } from "./actions/terminal-payment/types";
162
166
  export type { VendaraPayment, VendaraPaymentParams, VendaraPaymentResponse } from "./actions/vendara-payment/types";
@@ -198,6 +202,8 @@ export type { RemoveCartFee, RemoveCartFeeParams, RemoveCartFeeResponse } from "
198
202
  export type { RemoveOrderNote, RemoveOrderNoteResponse } from "./actions/remove-order-note/types";
199
203
  export type { RemoveCustomSale, RemoveCustomSaleParams, RemoveCustomSaleResponse } from "./actions/remove-custom-sale/types";
200
204
  export type { RemoveNonRevenueItem, RemoveNonRevenueItemParams, RemoveNonRevenueItemResponse } from "./actions/remove-non-revenue-item/types";
205
+ export type { SendEmail, SendEmailParams, SendEmailResponse, SendReceiptType } from "./actions/send-email/types";
206
+ export type { SendSms, SendSmsParams, SendSmsResponse } from "./actions/send-sms/types";
201
207
  export * from "./CommonTypes";
202
208
  export { setMockDatabase, setMockActiveProduct } from "./demo/database";
203
209
  export type { MockDatabaseConfig } from "./demo/database";
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ import { parkOrder } from "./actions/park-order/action";
33
33
  import { resumeParkedOrder } from "./actions/resume-parked-order/action";
34
34
  import { deleteParkedOrder } from "./actions/delete-parked-order/action";
35
35
  import { cashPayment } from "./actions/cash-payment/action";
36
+ import { getCashRoundingAmount } from "./actions/get-cash-rounding-amount/action";
36
37
  import { tapToPayPayment } from "./actions/tap-to-pay-payment/action";
37
38
  import { terminalPayment } from "./actions/terminal-payment/action";
38
39
  import { vendaraPayment } from "./actions/vendara-payment/action";
@@ -72,6 +73,9 @@ import { removeOrderNote } from "./actions/remove-order-note/action";
72
73
  import { removeCustomSale } from "./actions/remove-custom-sale/action";
73
74
  import { removeNonRevenueItem } from "./actions/remove-non-revenue-item/action";
74
75
  // Integration Actions
76
+ // Receipt send Actions
77
+ import { sendEmail } from "./actions/send-email/action";
78
+ import { sendSms } from "./actions/send-sms/action";
75
79
  // Refund Actions
76
80
  import { getRefunds } from "./actions/get-refunds/action";
77
81
  import { initiateRefund } from "./actions/initiate-refund/action";
@@ -158,6 +162,7 @@ export const command = {
158
162
  resumeParkedOrder,
159
163
  deleteParkedOrder,
160
164
  cashPayment,
165
+ getCashRoundingAmount,
161
166
  tapToPayPayment,
162
167
  terminalPayment,
163
168
  vendaraPayment,
@@ -197,6 +202,9 @@ export const command = {
197
202
  removeCustomSale,
198
203
  removeNonRevenueItem,
199
204
  // Integration Actions
205
+ // Receipt send Actions
206
+ sendEmail,
207
+ sendSms,
200
208
  // Refund Actions
201
209
  initiateRefund,
202
210
  setRefundStockAction,
@@ -245,7 +253,6 @@ export const command = {
245
253
  getAvailableTransitions,
246
254
  applyTransition
247
255
  };
248
- // Integration Actions
249
256
  // Export Common Types
250
257
  export * from "./CommonTypes";
251
258
  // Mock database override (standalone / extension dev)
@@ -27,6 +27,7 @@ import { mockAssignCustomer } from "../../actions/assign-customer/mock";
27
27
  import { mockAuthenticateUser } from "../../actions/authenticate-user/mock";
28
28
  import { mockCalculateRefundTotal } from "../../actions/calculate-refund-total/mock";
29
29
  import { mockCashPayment } from "../../actions/cash-payment/mock";
30
+ import { mockGetCashRoundingAmount } from "../../actions/get-cash-rounding-amount/mock";
30
31
  import { mockClearCart } from "../../actions/clear-cart/mock";
31
32
  import { mockDeleteParkedOrder } from "../../actions/delete-parked-order/mock";
32
33
  import { mockExampleFunction } from "../../actions/example-function/mock";
@@ -91,6 +92,8 @@ import { canTransitionMock } from "../../actions/can-transition/mock";
91
92
  import { getAvailableTransitionsMock } from "../../actions/get-available-transitions/mock";
92
93
  import { mockGetSmartGridLayout } from "../../actions/get-smart-grid-layout/mock";
93
94
  import { mockSaveSmartGridLayout } from "../../actions/save-smart-grid-layout/mock";
95
+ import { mockSendEmail } from "../../actions/send-email/mock";
96
+ import { mockSendSms } from "../../actions/send-sms/mock";
94
97
  export const RENDER_MOCKS = {
95
98
  addCartDiscount: mockAddCartDiscount,
96
99
  addCartFee: mockAddCartFee,
@@ -115,6 +118,7 @@ export const RENDER_MOCKS = {
115
118
  authenticateUser: mockAuthenticateUser,
116
119
  calculateRefundTotal: mockCalculateRefundTotal,
117
120
  cashPayment: mockCashPayment,
121
+ getCashRoundingAmount: mockGetCashRoundingAmount,
118
122
  clearCart: mockClearCart,
119
123
  deleteParkedOrder: mockDeleteParkedOrder,
120
124
  exampleFunction: mockExampleFunction,
@@ -186,5 +190,7 @@ export const RENDER_MOCKS = {
186
190
  canTransition: canTransitionMock,
187
191
  getAvailableTransitions: getAvailableTransitionsMock,
188
192
  getSmartGridLayout: mockGetSmartGridLayout,
189
- saveSmartGridLayout: mockSaveSmartGridLayout
193
+ saveSmartGridLayout: mockSaveSmartGridLayout,
194
+ sendEmail: mockSendEmail,
195
+ sendSms: mockSendSms
190
196
  };
@@ -1,4 +1,4 @@
1
- import type { ExampleFunction, GetProducts, AddCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, InitiateRefund, CashPayment, TapToPayPayment, TerminalPayment, VendaraPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, GetActiveStation, GetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem, CanTransition, GetAvailableTransitions, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout } from "../../index";
1
+ import type { ExampleFunction, GetProducts, AddCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, InitiateRefund, CashPayment, GetCashRoundingAmount, TapToPayPayment, TerminalPayment, VendaraPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, GetActiveStation, GetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem, CanTransition, GetAvailableTransitions, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout, SendEmail, SendSms } from "../../index";
2
2
  import type { OpenExtensionOverlay } from "../../actions/open-extension-overlay/types";
3
3
  import type { ResolveExtensionOverlay } from "../../actions/resolve-extension-overlay/types";
4
4
  export interface RenderProviderActions {
@@ -37,6 +37,7 @@ export interface RenderProviderActions {
37
37
  openExtensionOverlay: OpenExtensionOverlay;
38
38
  resolveExtensionOverlay: ResolveExtensionOverlay;
39
39
  cashPayment: CashPayment;
40
+ getCashRoundingAmount: GetCashRoundingAmount;
40
41
  tapToPayPayment: TapToPayPayment;
41
42
  terminalPayment: TerminalPayment;
42
43
  vendaraPayment: VendaraPayment;
@@ -97,4 +98,6 @@ export interface RenderProviderActions {
97
98
  getAvailableTransitions: GetAvailableTransitions;
98
99
  getSmartGridLayout: GetSmartGridLayout;
99
100
  saveSmartGridLayout: SaveSmartGridLayout;
101
+ sendEmail: SendEmail;
102
+ sendSms: SendSms;
100
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@final-commerce/command-frame",
3
- "version": "0.3.0-beta.6",
3
+ "version": "0.3.0-beta.8",
4
4
  "description": "Commands Frame library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",