@final-commerce/command-frame 0.8.0-online.4 → 0.8.0-online.5

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.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Attach Checkout Contact action
3
+ * Calls the attachCheckoutContact action on the parent window
4
+ */
5
+ import type { AttachCheckoutContact } from './types';
6
+ export declare const attachCheckoutContact: AttachCheckoutContact;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Attach Checkout Contact action
3
+ * Calls the attachCheckoutContact action on the parent window
4
+ */
5
+ import { commandFrameClient } from '../../client';
6
+ export const attachCheckoutContact = async (params) => {
7
+ return await commandFrameClient.call('attachCheckoutContact', params);
8
+ };
@@ -0,0 +1,15 @@
1
+ import type { AttachCheckoutContact } from './types';
2
+ /**
3
+ * Standalone mock of attaching contact to an online order.
4
+ *
5
+ * It mirrors the one REFUSAL the real command makes — a missing email — because
6
+ * that is the mistake a builder actually hits, and a mock that accepted anything
7
+ * would let a checkout screen look finished and lose receipts on the published
8
+ * site.
9
+ *
10
+ * It does NOT mirror "no order to attach to". The real command answers
11
+ * `attached: false` there rather than throwing, precisely so a flow can call it
12
+ * optimistically and still let the shopper pay; the mock always has a checkout
13
+ * to attach to, so it reports `true`.
14
+ */
15
+ export declare const mockAttachCheckoutContact: AttachCheckoutContact;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Standalone mock of attaching contact to an online order.
3
+ *
4
+ * It mirrors the one REFUSAL the real command makes — a missing email — because
5
+ * that is the mistake a builder actually hits, and a mock that accepted anything
6
+ * would let a checkout screen look finished and lose receipts on the published
7
+ * site.
8
+ *
9
+ * It does NOT mirror "no order to attach to". The real command answers
10
+ * `attached: false` there rather than throwing, precisely so a flow can call it
11
+ * optimistically and still let the shopper pay; the mock always has a checkout
12
+ * to attach to, so it reports `true`.
13
+ */
14
+ export const mockAttachCheckoutContact = async (params) => {
15
+ console.log('[Mock] attachCheckoutContact called', params);
16
+ if (!params)
17
+ throw new Error('Params required');
18
+ if (!params.email || !params.email.trim()) {
19
+ throw new Error('attachCheckoutContact: email is required — it is where the receipt goes');
20
+ }
21
+ return { attached: true, timestamp: new Date().toISOString() };
22
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Guest contact for the order. There is no shopper account, so this is the only
3
+ * way to reach them — `email` is where the receipt goes and is required.
4
+ */
5
+ export interface AttachCheckoutContactParams {
6
+ email: string;
7
+ name?: string;
8
+ phone?: string;
9
+ /** Defaults to the outlet the storefront booted against. */
10
+ outletId?: string;
11
+ }
12
+ export interface AttachCheckoutContactResponse {
13
+ /**
14
+ * `false` means there was nothing to attach to — no checkout had been started
15
+ * for this cart. It is NOT a failure to record the contact on an order that
16
+ * exists, and it never means the shopper was charged.
17
+ */
18
+ attached: boolean;
19
+ timestamp: string;
20
+ }
21
+ /**
22
+ * Put the shopper's contact on the order `startCheckout` already created.
23
+ *
24
+ * Call it BEFORE paying, whenever the email is known — on blur, on submit, or
25
+ * as the shopper leaves the contact step. It reuses the order's idempotency
26
+ * key, so it updates that order rather than creating a second one.
27
+ *
28
+ * SAFE TO CALL TWICE. The server only ever ADDS contact to an order that has
29
+ * none, so a later call cannot redirect a receipt that was already addressed.
30
+ *
31
+ * NEVER THROWS FOR A MISSING ORDER. Failing to record an email must not stop a
32
+ * payment the shopper is trying to make, so the caller gets `attached: false`
33
+ * and can carry on to pay.
34
+ */
35
+ export type AttachCheckoutContact = (params: AttachCheckoutContactParams) => Promise<AttachCheckoutContactResponse>;
@@ -0,0 +1,16 @@
1
+ // Attach Checkout Contact Types
2
+ //
3
+ // The second half of the online checkout. `startCheckout` creates the order and
4
+ // mounts the payment fields the moment a shopper ARRIVES; this puts their email
5
+ // on that same order once they have typed it.
6
+ //
7
+ // WHY THESE ARE TWO COMMANDS. The card fields cannot render without a payment
8
+ // session, a session needs a server-priced amount, and an amount needs an order.
9
+ // So the order has to exist before the shopper has typed anything. Demanding an
10
+ // email first is what forced the old "fill the form, then see the card fields"
11
+ // checkout — and it also meant every cart edit minted a NEW order, because the
12
+ // idempotency key moved with the cart.
13
+ //
14
+ // Served ONLY by the storefront runtime (a published website), like
15
+ // `startCheckout` itself. On a register it does not exist.
16
+ export {};
@@ -2,7 +2,7 @@ import { StartCheckout } from './types';
2
2
  /**
3
3
  * Standalone mock of the storefront checkout.
4
4
  *
5
- * It mirrors the REFUSALS of the real command (empty cart, missing email,
5
+ * It mirrors the REFUSALS of the real command (empty cart,
6
6
  * container not on the page) in the same order, because those are what a
7
7
  * builder actually hits first — a mock that accepted anything would let a
8
8
  * checkout screen look finished and fail on the published site.
@@ -2,7 +2,7 @@ import { MOCK_CART, MOCK_COMPANY, mockPublishEvent } from '../../demo/database';
2
2
  /**
3
3
  * Standalone mock of the storefront checkout.
4
4
  *
5
- * It mirrors the REFUSALS of the real command (empty cart, missing email,
5
+ * It mirrors the REFUSALS of the real command (empty cart,
6
6
  * container not on the page) in the same order, because those are what a
7
7
  * builder actually hits first — a mock that accepted anything would let a
8
8
  * checkout screen look finished and fail on the published site.
@@ -17,9 +17,9 @@ export const mockStartCheckout = async (params) => {
17
17
  console.log('[Mock] startCheckout called', params);
18
18
  if (!params)
19
19
  throw new Error('Params required');
20
- if (!params.contact?.email || !params.contact.email.trim()) {
21
- throw new Error('startCheckout: contact.email is required — it is where the receipt goes');
22
- }
20
+ // No email check: contact is optional now, because checkout starts on arrival.
21
+ // Refusing here would make "start on arrival" fail in PREVIEW while working on
22
+ // the published site, which is the worst way for this to be wrong.
23
23
  if (!params.container || typeof params.container !== 'string') {
24
24
  throw new Error('startCheckout: container must be a CSS selector string (e.g. "#card-fields") — a DOM element cannot cross the command boundary');
25
25
  }
@@ -8,7 +8,16 @@ export interface StartCheckoutContact {
8
8
  phone?: string;
9
9
  }
10
10
  export interface StartCheckoutParams {
11
- contact: StartCheckoutContact;
11
+ /**
12
+ * OPTIONAL, because checkout starts when the shopper ARRIVES rather than when
13
+ * they submit a form. The card fields cannot render without a payment session,
14
+ * a session needs a server-priced amount, and an amount needs an order — so the
15
+ * order has to exist before they have typed anything.
16
+ *
17
+ * Supply it afterwards with `attachCheckoutContact`, which re-sends the same
18
+ * idempotency key and so reaches the SAME order.
19
+ */
20
+ contact?: StartCheckoutContact;
12
21
  /**
13
22
  * CSS SELECTOR for the element the payment fields mount into, e.g.
14
23
  * `'#card-fields'`. The element must already be in the DOM when this is
package/dist/index.d.ts CHANGED
@@ -46,6 +46,7 @@ export declare const command: {
46
46
  readonly createPaymentLink: import(".").CreatePaymentLink;
47
47
  readonly chargeMoto: import(".").ChargeMoto;
48
48
  readonly startCheckout: import(".").StartCheckout;
49
+ readonly attachCheckoutContact: import(".").AttachCheckoutContact;
49
50
  readonly resumeCheckout: import(".").ResumeCheckout;
50
51
  readonly addCustomerNote: import(".").AddCustomerNote;
51
52
  readonly removeCustomerNote: import(".").RemoveCustomerNote;
@@ -185,6 +186,7 @@ export type { IntegrationPayment, IntegrationPaymentParams, IntegrationPaymentRe
185
186
  export type { CreatePaymentLink, CreatePaymentLinkParams, CreatePaymentLinkResponse, } from './actions/create-payment-link/types';
186
187
  export type { ChargeMoto, ChargeMotoParams, ChargeMotoResponse, ChargeMotoCardFields, } from './actions/charge-moto/types';
187
188
  export type { StartCheckout, StartCheckoutParams, StartCheckoutResponse, StartCheckoutContact, StartCheckoutOrder, } from './actions/start-checkout/types';
189
+ export type { AttachCheckoutContact, AttachCheckoutContactParams, AttachCheckoutContactResponse, } from './actions/attach-checkout-contact/types';
188
190
  export type { ResumeCheckout, ResumeCheckoutParams, ResumeCheckoutResponse, ResumeCheckoutResult, } from './actions/resume-checkout/types';
189
191
  export type { AddCustomerNote, AddCustomerNoteParams, AddCustomerNoteResponse, } from './actions/add-customer-note/types';
190
192
  export type { RemoveCustomerNote, RemoveCustomerNoteParams, RemoveCustomerNoteResponse, } from './actions/remove-customer-note/types';
package/dist/index.js CHANGED
@@ -46,6 +46,7 @@ import { integrationPayment } from './actions/integration-payment/action';
46
46
  import { createPaymentLink } from './actions/create-payment-link/action';
47
47
  import { chargeMoto } from './actions/charge-moto/action';
48
48
  import { startCheckout } from './actions/start-checkout/action';
49
+ import { attachCheckoutContact } from './actions/attach-checkout-contact/action';
49
50
  import { resumeCheckout } from './actions/resume-checkout/action';
50
51
  // Customer Actions
51
52
  import { addCustomerNote } from './actions/add-customer-note/action';
@@ -187,6 +188,7 @@ export const command = {
187
188
  createPaymentLink,
188
189
  chargeMoto,
189
190
  startCheckout,
191
+ attachCheckoutContact,
190
192
  resumeCheckout,
191
193
  // Customer Actions
192
194
  addCustomerNote,
@@ -34,6 +34,7 @@ import { mockGetCashRoundingAmount } from '../../actions/get-cash-rounding-amoun
34
34
  import { mockCreatePaymentLink } from '../../actions/create-payment-link/mock';
35
35
  import { mockChargeMoto } from '../../actions/charge-moto/mock';
36
36
  import { mockStartCheckout } from '../../actions/start-checkout/mock';
37
+ import { mockAttachCheckoutContact } from '../../actions/attach-checkout-contact/mock';
37
38
  import { mockResumeCheckout } from '../../actions/resume-checkout/mock';
38
39
  import { mockGetTimeClockStatus } from '../../actions/get-time-clock-status/mock';
39
40
  import { mockClearCart } from '../../actions/clear-cart/mock';
@@ -135,6 +136,7 @@ export const RENDER_MOCKS = {
135
136
  createPaymentLink: mockCreatePaymentLink,
136
137
  chargeMoto: mockChargeMoto,
137
138
  startCheckout: mockStartCheckout,
139
+ attachCheckoutContact: mockAttachCheckoutContact,
138
140
  resumeCheckout: mockResumeCheckout,
139
141
  clearCart: mockClearCart,
140
142
  deleteParkedOrder: mockDeleteParkedOrder,
@@ -1,4 +1,4 @@
1
- import type { ExampleFunction, GetProducts, AddCustomSale, EditCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, GetProductModifierSelections, SetProductModifierSelections, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, VoidOrder, InitiateRefund, CashPayment, GetCashRoundingAmount, TapToPayPayment, TerminalPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, RedeemRefund, GetRefundPlan, CheckPermission, 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, ApplyTransition, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout, SendEmail, SendSms, CreatePaymentLink, ChargeMoto, StartCheckout, ResumeCheckout, GetTimeClockStatus } from '../../index';
1
+ import type { ExampleFunction, GetProducts, AddCustomSale, EditCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, GetProductModifierSelections, SetProductModifierSelections, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, VoidOrder, InitiateRefund, CashPayment, GetCashRoundingAmount, TapToPayPayment, TerminalPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, RedeemRefund, GetRefundPlan, CheckPermission, 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, ApplyTransition, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout, SendEmail, SendSms, CreatePaymentLink, ChargeMoto, StartCheckout, AttachCheckoutContact, ResumeCheckout, GetTimeClockStatus } from '../../index';
2
2
  export interface RenderProviderActions {
3
3
  exampleFunction: ExampleFunction;
4
4
  getProducts: GetProducts;
@@ -51,6 +51,7 @@ export interface RenderProviderActions {
51
51
  * a register, because the two declare different things to the card networks.
52
52
  */
53
53
  startCheckout: StartCheckout;
54
+ attachCheckoutContact: AttachCheckoutContact;
54
55
  /**
55
56
  * The RETURN LEG of an online checkout, after a redirect payment method or
56
57
  * 3-D Secure sent the shopper away and back. Storefront-only, like
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@final-commerce/command-frame",
3
- "version": "0.8.0-online.4",
3
+ "version": "0.8.0-online.5",
4
4
  "description": "Commands Frame library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",