@final-commerce/command-frame 0.12.0 → 0.13.1-preprod.1
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/dist/CommonTypes.d.ts +18 -1
- package/dist/actions/assign-order-user/mock.js +9 -9
- package/dist/actions/assign-order-user/types.d.ts +14 -3
- package/dist/actions/attach-checkout-contact/action.d.ts +6 -0
- package/dist/actions/attach-checkout-contact/action.js +8 -0
- package/dist/actions/attach-checkout-contact/mock.d.ts +15 -0
- package/dist/actions/attach-checkout-contact/mock.js +22 -0
- package/dist/actions/attach-checkout-contact/types.d.ts +35 -0
- package/dist/actions/attach-checkout-contact/types.js +16 -0
- package/dist/actions/get-address-options/action.d.ts +6 -0
- package/dist/actions/get-address-options/action.js +8 -0
- package/dist/actions/get-address-options/mock.d.ts +3 -0
- package/dist/actions/get-address-options/mock.js +15 -0
- package/dist/actions/get-address-options/types.d.ts +25 -0
- package/dist/actions/get-address-options/types.js +8 -0
- package/dist/actions/get-checkout-status/action.d.ts +6 -0
- package/dist/actions/get-checkout-status/action.js +8 -0
- package/dist/actions/get-checkout-status/mock.d.ts +3 -0
- package/dist/actions/get-checkout-status/mock.js +11 -0
- package/dist/actions/get-checkout-status/types.d.ts +27 -0
- package/dist/actions/get-checkout-status/types.js +12 -0
- package/dist/actions/get-orders/mock.js +9 -6
- package/dist/actions/get-orders/types.d.ts +8 -2
- package/dist/actions/get-outlets/action.js +2 -2
- package/dist/actions/get-outlets/mock.d.ts +12 -0
- package/dist/actions/get-outlets/mock.js +28 -4
- package/dist/actions/get-outlets/types.d.ts +19 -1
- package/dist/actions/get-product-modifier-selections/mock.js +7 -0
- package/dist/actions/get-product-modifier-selections/types.d.ts +4 -0
- package/dist/actions/get-products/types.d.ts +8 -8
- package/dist/actions/get-refund-plan/mockRows.js +1 -1
- package/dist/actions/get-refund-plan/types.d.ts +9 -6
- package/dist/actions/get-remaining-refundable-quantities/mock.js +1 -0
- package/dist/actions/get-remaining-refundable-quantities/types.d.ts +5 -0
- package/dist/actions/get-shipping-options/action.d.ts +6 -0
- package/dist/actions/get-shipping-options/action.js +8 -0
- package/dist/actions/get-shipping-options/mock.d.ts +3 -0
- package/dist/actions/get-shipping-options/mock.js +15 -0
- package/dist/actions/get-shipping-options/types.d.ts +43 -0
- package/dist/actions/get-shipping-options/types.js +1 -0
- package/dist/actions/process-partial-refund/types.d.ts +1 -1
- package/dist/actions/resume-checkout/action.d.ts +6 -0
- package/dist/actions/resume-checkout/action.js +8 -0
- package/dist/actions/resume-checkout/mock.d.ts +16 -0
- package/dist/actions/resume-checkout/mock.js +51 -0
- package/dist/actions/resume-checkout/types.d.ts +59 -0
- package/dist/actions/resume-checkout/types.js +13 -0
- package/dist/actions/select-shipping-option/action.d.ts +6 -0
- package/dist/actions/select-shipping-option/action.js +8 -0
- package/dist/actions/select-shipping-option/mock.d.ts +3 -0
- package/dist/actions/select-shipping-option/mock.js +31 -0
- package/dist/actions/select-shipping-option/types.d.ts +15 -0
- package/dist/actions/select-shipping-option/types.js +1 -0
- package/dist/actions/set-order-address/action.d.ts +6 -0
- package/dist/actions/set-order-address/action.js +8 -0
- package/dist/actions/set-order-address/mock.d.ts +7 -0
- package/dist/actions/set-order-address/mock.js +29 -0
- package/dist/actions/set-order-address/types.d.ts +19 -0
- package/dist/actions/set-order-address/types.js +1 -0
- package/dist/actions/set-outlet/action.d.ts +6 -0
- package/dist/actions/set-outlet/action.js +8 -0
- package/dist/actions/set-outlet/mock.d.ts +13 -0
- package/dist/actions/set-outlet/mock.js +24 -0
- package/dist/actions/set-outlet/types.d.ts +31 -0
- package/dist/actions/set-outlet/types.js +21 -0
- package/dist/actions/start-checkout/action.d.ts +6 -0
- package/dist/actions/start-checkout/action.js +8 -0
- package/dist/actions/start-checkout/mock.d.ts +16 -0
- package/dist/actions/start-checkout/mock.js +80 -0
- package/dist/actions/start-checkout/types.d.ts +90 -0
- package/dist/actions/start-checkout/types.js +10 -0
- package/dist/demo/shipping.d.ts +8 -0
- package/dist/demo/shipping.js +69 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.js +19 -0
- package/dist/projects/render/mocks.js +20 -0
- package/dist/projects/render/types.d.ts +23 -1
- package/dist/pubsub/topics/checkout/checkout-started/types.d.ts +26 -0
- package/dist/pubsub/topics/checkout/checkout-started/types.js +1 -0
- package/dist/pubsub/topics/checkout/index.d.ts +7 -0
- package/dist/pubsub/topics/checkout/index.js +28 -0
- package/dist/pubsub/topics/checkout/payment-completed/types.d.ts +31 -0
- package/dist/pubsub/topics/checkout/payment-completed/types.js +1 -0
- package/dist/pubsub/topics/checkout/payment-failed/types.d.ts +23 -0
- package/dist/pubsub/topics/checkout/payment-failed/types.js +1 -0
- package/dist/pubsub/topics/checkout/types.d.ts +12 -0
- package/dist/pubsub/topics/checkout/types.js +8 -0
- package/dist/pubsub/topics/index.d.ts +18 -17
- package/dist/pubsub/topics/index.js +18 -17
- package/dist/pubsub/topics/types.d.ts +8 -0
- package/package.json +2 -2
|
@@ -20,12 +20,13 @@ export interface GetRefundPlanParams {
|
|
|
20
20
|
items?: {
|
|
21
21
|
/**
|
|
22
22
|
* `internalId` / `variantId` for a product, `customSaleId`, cart-fee id, tip
|
|
23
|
-
* `transactionId`,
|
|
23
|
+
* `transactionId`, a booking's own `internalId` on `order.reservations[]`, or
|
|
24
|
+
* `'shipping'` for the order's shipping (all or nothing, like a fee).
|
|
24
25
|
*/
|
|
25
26
|
itemKey: string;
|
|
26
27
|
quantity: number;
|
|
27
28
|
/** Optional hint; inferred from the order when omitted. */
|
|
28
|
-
type?: 'product' | 'customSale' | 'fee' | 'tip' | 'reservation';
|
|
29
|
+
type?: 'product' | 'customSale' | 'fee' | 'tip' | 'reservation' | 'shipping';
|
|
29
30
|
}[];
|
|
30
31
|
}
|
|
31
32
|
export interface RefundPlanSource {
|
|
@@ -79,7 +80,7 @@ export interface RefundPlanLeg {
|
|
|
79
80
|
rounding: number;
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
|
-
export type RefundPlanRowType = 'product' | 'customSale' | 'fee' | 'tip';
|
|
83
|
+
export type RefundPlanRowType = 'product' | 'customSale' | 'fee' | 'tip' | 'shipping';
|
|
83
84
|
/** One tax rate's share of a row's refund (minor units). */
|
|
84
85
|
export interface RefundPlanTaxLine {
|
|
85
86
|
name: string;
|
|
@@ -107,7 +108,7 @@ export interface RefundPlanAmounts {
|
|
|
107
108
|
total: number;
|
|
108
109
|
}
|
|
109
110
|
/**
|
|
110
|
-
* One refundable row of the order — a line item, custom sale, cart fee or tip —
|
|
111
|
+
* One refundable row of the order — a line item, custom sale, cart fee, shipping or tip —
|
|
111
112
|
* served ready to render. Rows with nothing left to refund are not listed.
|
|
112
113
|
*/
|
|
113
114
|
export interface RefundPlanRow {
|
|
@@ -121,7 +122,7 @@ export interface RefundPlanRow {
|
|
|
121
122
|
attributes?: string;
|
|
122
123
|
/** Tip rows only: the tender that took the tip (`card`, `cash`, …). */
|
|
123
124
|
paymentType?: string;
|
|
124
|
-
/** Quantity originally sold. Fees and tips are `1` — all or nothing. */
|
|
125
|
+
/** Quantity originally sold. Fees, shipping and tips are `1` — all or nothing. */
|
|
125
126
|
quantity: number;
|
|
126
127
|
/** Quantity still refundable — the stepper's max. */
|
|
127
128
|
refundableQuantity: number;
|
|
@@ -137,7 +138,7 @@ export interface RefundPlanSelectedRow {
|
|
|
137
138
|
}
|
|
138
139
|
/**
|
|
139
140
|
* The selection's goods value broken down for display. Minor units;
|
|
140
|
-
* `items − discounts + fees + tax + tip === total === allocation.itemTotal`.
|
|
141
|
+
* `items − discounts + fees + shipping + tax + tip === total === allocation.itemTotal`.
|
|
141
142
|
*/
|
|
142
143
|
export interface RefundPlanTotals {
|
|
143
144
|
/** Σ selected line subtotals (before discounts, tax excluded). */
|
|
@@ -146,6 +147,8 @@ export interface RefundPlanTotals {
|
|
|
146
147
|
discounts: number;
|
|
147
148
|
/** Σ selected cart fees, tax excluded. */
|
|
148
149
|
fees: number;
|
|
150
|
+
/** The order's shipping when selected, tax excluded; 0 otherwise. */
|
|
151
|
+
shipping: number;
|
|
149
152
|
tax: number;
|
|
150
153
|
tip: number;
|
|
151
154
|
total: number;
|
|
@@ -11,6 +11,11 @@ export interface GetRemainingRefundableQuantitiesResponse {
|
|
|
11
11
|
* 0/1 semantics: `1` = still refundable, `0` = already refunded.
|
|
12
12
|
*/
|
|
13
13
|
cartFees: Record<string, number>;
|
|
14
|
+
/**
|
|
15
|
+
* The order's shipping: `1` = still refundable, `0` = already refunded or the order has none.
|
|
16
|
+
* Refunded with `itemKey: 'shipping'`, all or nothing.
|
|
17
|
+
*/
|
|
18
|
+
shipping: number;
|
|
14
19
|
/**
|
|
15
20
|
* Remaining refundable tips, keyed by the paying method's
|
|
16
21
|
* `transactionId` — the same key `processPartialRefund` takes for
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GetShippingOptions action
|
|
3
|
+
* Calls the getShippingOptions action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const getShippingOptions = async () => {
|
|
7
|
+
return await commandFrameClient.call('getShippingOptions', undefined);
|
|
8
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { MOCK_SHIPPING_ZONES, mockShippingOptions } from '../../demo/shipping';
|
|
2
|
+
/** Standalone mock: the demo store's zones run through the real resolver, as the website would. */
|
|
3
|
+
export const mockGetShippingOptions = async () => {
|
|
4
|
+
console.log('[Mock] getShippingOptions called');
|
|
5
|
+
const { resolution, options } = mockShippingOptions();
|
|
6
|
+
const message = resolution.message ??
|
|
7
|
+
(options.length === 0 && resolution.reason === 'NO_COVERAGE' ? 'Sorry, we only ship within Canada.' : null);
|
|
8
|
+
return {
|
|
9
|
+
required: MOCK_SHIPPING_ZONES.length > 0,
|
|
10
|
+
options,
|
|
11
|
+
reason: resolution.reason,
|
|
12
|
+
message,
|
|
13
|
+
timestamp: new Date().toISOString(),
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ShippingNoOptionReason, ShippingOptionType } from '@final-commerce/common';
|
|
2
|
+
export interface ShippingPickupView {
|
|
3
|
+
outletId: string;
|
|
4
|
+
/** The store's name, e.g. "Duckworth Street". */
|
|
5
|
+
name: string;
|
|
6
|
+
/** One line per open day, e.g. "Mon–Fri 9:00 AM – 6:00 PM". Empty when the store set no hours. */
|
|
7
|
+
hours: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface ShippingOptionView {
|
|
10
|
+
optionId: string;
|
|
11
|
+
type: ShippingOptionType | `${ShippingOptionType}`;
|
|
12
|
+
/** "Standard shipping", or "Pickup at Duckworth Street". */
|
|
13
|
+
name: string;
|
|
14
|
+
/** What this option costs on this cart, minor units, before its tax. */
|
|
15
|
+
amount: number;
|
|
16
|
+
/** "$10.00" or "Free". */
|
|
17
|
+
priceText: string;
|
|
18
|
+
/** "Add $20.00 more for free shipping", when a free-shipping line exists and isn't reached. */
|
|
19
|
+
freeShippingHint: string | null;
|
|
20
|
+
/** "Arrives Tue, Oct 20 – Fri, Oct 23", counted in business days from today. Null for pickup. */
|
|
21
|
+
arrivalText: string | null;
|
|
22
|
+
pickup: ShippingPickupView | null;
|
|
23
|
+
/** The option on the cart now. The cheapest is chosen by default. */
|
|
24
|
+
selected: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface GetShippingOptionsResponse {
|
|
27
|
+
/**
|
|
28
|
+
* False when this store doesn't ship at all (no shipping set up) or the cart has
|
|
29
|
+
* nothing to ship: show no shipping step.
|
|
30
|
+
*/
|
|
31
|
+
required: boolean;
|
|
32
|
+
/** Ship options cheapest first, then pickups. */
|
|
33
|
+
options: ShippingOptionView[];
|
|
34
|
+
/** Why there is nothing to choose; null when `options` isn't empty. */
|
|
35
|
+
reason: ShippingNoOptionReason | `${ShippingNoOptionReason}` | null;
|
|
36
|
+
/**
|
|
37
|
+
* What to tell the customer: the store's "we don't ship here" message, or the
|
|
38
|
+
* default for the reason ("Sorry, we only ship within Canada."). Null when there are options.
|
|
39
|
+
*/
|
|
40
|
+
message: string | null;
|
|
41
|
+
timestamp: string;
|
|
42
|
+
}
|
|
43
|
+
export type GetShippingOptions = () => Promise<GetShippingOptionsResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -134,7 +134,7 @@ export interface ProcessPartialRefundParams {
|
|
|
134
134
|
/** internalId or variantId, customSaleId, or a booking's own internalId. */
|
|
135
135
|
itemKey: string;
|
|
136
136
|
quantity: number;
|
|
137
|
-
type?: 'product' | 'customSale' | 'fee' | 'tip' | 'reservation';
|
|
137
|
+
type?: 'product' | 'customSale' | 'fee' | 'tip' | 'reservation' | 'shipping';
|
|
138
138
|
/**
|
|
139
139
|
* Per-item stock disposition for a refunded **product** line — the
|
|
140
140
|
* headless equivalent of the old refund popup's per-row restock/damaged
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resume Checkout action
|
|
3
|
+
* Calls the resumeCheckout action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const resumeCheckout = async (params) => {
|
|
7
|
+
return await commandFrameClient.call('resumeCheckout', params ?? {});
|
|
8
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ResumeCheckout } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone mock of the checkout return leg.
|
|
4
|
+
*
|
|
5
|
+
* It reads the SAME query parameters the real provider sends a shopper back
|
|
6
|
+
* with, so a builder can exercise the redirect path in preview by appending
|
|
7
|
+
* them by hand:
|
|
8
|
+
*
|
|
9
|
+
* ?sessionId=CS_MOCK&redirectResult=MOCK
|
|
10
|
+
*
|
|
11
|
+
* Without them it answers `{ resumed: false }` and does nothing, which is what
|
|
12
|
+
* the real command does on an ordinary page load. That is the branch a
|
|
13
|
+
* checkout screen hits on every normal visit, so it is the one worth getting
|
|
14
|
+
* right in a mock.
|
|
15
|
+
*/
|
|
16
|
+
export declare const mockResumeCheckout: ResumeCheckout;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mockPublishEvent } from '../../demo/database';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone mock of the checkout return leg.
|
|
4
|
+
*
|
|
5
|
+
* It reads the SAME query parameters the real provider sends a shopper back
|
|
6
|
+
* with, so a builder can exercise the redirect path in preview by appending
|
|
7
|
+
* them by hand:
|
|
8
|
+
*
|
|
9
|
+
* ?sessionId=CS_MOCK&redirectResult=MOCK
|
|
10
|
+
*
|
|
11
|
+
* Without them it answers `{ resumed: false }` and does nothing, which is what
|
|
12
|
+
* the real command does on an ordinary page load. That is the branch a
|
|
13
|
+
* checkout screen hits on every normal visit, so it is the one worth getting
|
|
14
|
+
* right in a mock.
|
|
15
|
+
*/
|
|
16
|
+
export const mockResumeCheckout = async (params) => {
|
|
17
|
+
console.log('[Mock] resumeCheckout called', params);
|
|
18
|
+
const href = params?.url ?? (typeof window === 'undefined' ? '' : window.location.href);
|
|
19
|
+
let redirectResult = null;
|
|
20
|
+
try {
|
|
21
|
+
redirectResult = new URL(href).searchParams.get('redirectResult');
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
redirectResult = null;
|
|
25
|
+
}
|
|
26
|
+
if (!redirectResult) {
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
checkout: { resumed: false },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const suffix = String(Date.now()).slice(-6);
|
|
34
|
+
const checkout = {
|
|
35
|
+
resumed: true,
|
|
36
|
+
orderId: `mock-order-${suffix}`,
|
|
37
|
+
receiptId: `ON-001-${suffix}`,
|
|
38
|
+
// `Authorised` is what the real provider reports for a completed redirect.
|
|
39
|
+
// It is NOT settlement — see the checkout topic docs.
|
|
40
|
+
resultCode: 'Authorised',
|
|
41
|
+
sessionResult: `mock-session-result-${suffix}`,
|
|
42
|
+
orderPassword: `mock-pw-${suffix}`,
|
|
43
|
+
};
|
|
44
|
+
mockPublishEvent('checkout', 'payment-completed', {
|
|
45
|
+
orderId: checkout.orderId,
|
|
46
|
+
receiptId: checkout.receiptId,
|
|
47
|
+
resultCode: checkout.resultCode,
|
|
48
|
+
sessionResult: checkout.sessionResult,
|
|
49
|
+
});
|
|
50
|
+
return { success: true, timestamp: new Date().toISOString(), checkout };
|
|
51
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface ResumeCheckoutParams {
|
|
2
|
+
/**
|
|
3
|
+
* Where to read the provider's return data from. Defaults to the current
|
|
4
|
+
* page URL, which is where the shopper has just landed.
|
|
5
|
+
*
|
|
6
|
+
* Only override this if you moved the query string somewhere else before
|
|
7
|
+
* calling — for example if your router strips it on mount.
|
|
8
|
+
*/
|
|
9
|
+
url?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* What the return leg produced. Every field past `resumed` is absent when
|
|
13
|
+
* `resumed` is `false`.
|
|
14
|
+
*/
|
|
15
|
+
export interface ResumeCheckoutResult {
|
|
16
|
+
/**
|
|
17
|
+
* `false` means THIS WAS AN ORDINARY PAGE LOAD — no provider return data was
|
|
18
|
+
* in the URL, so there was nothing to finish and nothing happened. It is not
|
|
19
|
+
* an error, and it is the answer on every normal visit to the page. Call this
|
|
20
|
+
* unconditionally when your checkout or confirmation screen mounts and branch
|
|
21
|
+
* on this field.
|
|
22
|
+
*/
|
|
23
|
+
resumed: boolean;
|
|
24
|
+
orderId?: string;
|
|
25
|
+
receiptId?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The provider's result code for the completed attempt, e.g. `Authorised` or
|
|
28
|
+
* `Refused`. As everywhere else in this topic, `Authorised` is an
|
|
29
|
+
* authorisation and NOT a settlement.
|
|
30
|
+
*/
|
|
31
|
+
resultCode?: string;
|
|
32
|
+
/** Opaque proof of the payment — see `PaymentCompletedPayload.sessionResult`. */
|
|
33
|
+
sessionResult?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The one-time order password from the original checkout, recovered from
|
|
36
|
+
* browser storage. The page that held it in memory is gone, so this is the
|
|
37
|
+
* only way back to the order's status after a redirect.
|
|
38
|
+
*/
|
|
39
|
+
orderPassword?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ResumeCheckoutResponse {
|
|
42
|
+
success: boolean;
|
|
43
|
+
timestamp: string;
|
|
44
|
+
checkout: ResumeCheckoutResult;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Finish a checkout the shopper was redirected away from.
|
|
48
|
+
*
|
|
49
|
+
* SAFE TO CALL ON EVERY PAGE LOAD, and that is how it is meant to be used: on
|
|
50
|
+
* an ordinary visit it finds no return data and answers `{ resumed: false }`
|
|
51
|
+
* without touching the network. Calling it twice for one return is also safe —
|
|
52
|
+
* the return data is consumed the first time.
|
|
53
|
+
*
|
|
54
|
+
* The outcome is ALSO published on the `checkout` topic (`payment-completed` or
|
|
55
|
+
* `payment-failed`), exactly as it would have been had the shopper never left,
|
|
56
|
+
* so a page that already subscribes needs no second code path. Subscribe first,
|
|
57
|
+
* then call this, or the event fires before you are listening.
|
|
58
|
+
*/
|
|
59
|
+
export type ResumeCheckout = (params?: ResumeCheckoutParams) => Promise<ResumeCheckoutResponse>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Resume Checkout Types
|
|
2
|
+
//
|
|
3
|
+
// The RETURN LEG of an online checkout. Some payment methods — and 3-D Secure
|
|
4
|
+
// on a card — take the shopper away to their bank or wallet and send them back
|
|
5
|
+
// to `returnUrl` on a FRESH PAGE LOAD. The payment is not finished at that
|
|
6
|
+
// point: the provider hands the browser a one-time result in the URL, and it
|
|
7
|
+
// has to be handed back to the provider to complete the charge.
|
|
8
|
+
//
|
|
9
|
+
// `startCheckout` cannot do this. It ran in the page that is now gone.
|
|
10
|
+
//
|
|
11
|
+
// Served ONLY by the storefront runtime (a published website), like
|
|
12
|
+
// `startCheckout`. On a register it does not exist.
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelectShippingOption action
|
|
3
|
+
* Calls the selectShippingOption action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const selectShippingOption = async (params) => {
|
|
7
|
+
return await commandFrameClient.call('selectShippingOption', params);
|
|
8
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { MOCK_CART, mockPublishEvent } from '../../demo/database';
|
|
2
|
+
import { mockShippingOptions } from '../../demo/shipping';
|
|
3
|
+
/** Standalone mock: refuses an option the cart isn't offered, like the runtime does. */
|
|
4
|
+
export const mockSelectShippingOption = async (params) => {
|
|
5
|
+
console.log('[Mock] selectShippingOption called', params);
|
|
6
|
+
if (!params)
|
|
7
|
+
throw new Error('Params required');
|
|
8
|
+
const previous = MOCK_CART.shippingTotal ?? 0;
|
|
9
|
+
if (params.optionId === null) {
|
|
10
|
+
MOCK_CART.shipping = null;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const option = mockShippingOptions().resolution.options.find((o) => o.optionId === params.optionId);
|
|
14
|
+
if (!option)
|
|
15
|
+
throw new Error('selectShippingOption: this option is not offered for this cart');
|
|
16
|
+
MOCK_CART.shipping = {
|
|
17
|
+
zoneId: option.zoneId,
|
|
18
|
+
optionId: option.optionId,
|
|
19
|
+
type: option.type,
|
|
20
|
+
name: option.name,
|
|
21
|
+
amount: option.amount,
|
|
22
|
+
minDays: option.minDays,
|
|
23
|
+
maxDays: option.maxDays,
|
|
24
|
+
outletId: option.outletId,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
MOCK_CART.shippingTotal = MOCK_CART.shipping?.amount ?? 0;
|
|
28
|
+
MOCK_CART.total += MOCK_CART.shippingTotal - previous;
|
|
29
|
+
mockPublishEvent('cart', 'cart-updated', { shipping: MOCK_CART.shipping });
|
|
30
|
+
return { shipping: MOCK_CART.shipping, cart: MOCK_CART, timestamp: new Date().toISOString() };
|
|
31
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CFActiveCart, CFCartShipping } from '../../CommonTypes';
|
|
2
|
+
export interface SelectShippingOptionParams {
|
|
3
|
+
/** An `optionId` from `getShippingOptions`. `null` takes shipping off the cart. */
|
|
4
|
+
optionId: string | null;
|
|
5
|
+
}
|
|
6
|
+
export interface SelectShippingOptionResponse {
|
|
7
|
+
shipping: CFCartShipping | null;
|
|
8
|
+
cart: CFActiveCart;
|
|
9
|
+
timestamp: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* THROWS for an option this cart isn't offered: one from another address, a
|
|
13
|
+
* website-only option at the till, or one the store has since removed.
|
|
14
|
+
*/
|
|
15
|
+
export type SelectShippingOption = (params: SelectShippingOptionParams) => Promise<SelectShippingOptionResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SetOrderAddress action
|
|
3
|
+
* Calls the setOrderAddress action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const setOrderAddress = async (params) => {
|
|
7
|
+
return await commandFrameClient.call('setOrderAddress', params);
|
|
8
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SetOrderAddress } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone mock of putting an address on the cart. It refuses what the runtime refuses (no
|
|
4
|
+
* country, an unreadable one, a shipping address without street and city) so a checkout form
|
|
5
|
+
* that looks finished in preview doesn't lose orders on the published site.
|
|
6
|
+
*/
|
|
7
|
+
export declare const mockSetOrderAddress: SetOrderAddress;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { normalizeAddress } from '@final-commerce/common';
|
|
2
|
+
import { MOCK_CART, mockPublishEvent } from '../../demo/database';
|
|
3
|
+
/**
|
|
4
|
+
* Standalone mock of putting an address on the cart. It refuses what the runtime refuses (no
|
|
5
|
+
* country, an unreadable one, a shipping address without street and city) so a checkout form
|
|
6
|
+
* that looks finished in preview doesn't lose orders on the published site.
|
|
7
|
+
*/
|
|
8
|
+
export const mockSetOrderAddress = async (params) => {
|
|
9
|
+
console.log('[Mock] setOrderAddress called', params);
|
|
10
|
+
if (!params || (params.type !== 'shipping' && params.type !== 'billing')) {
|
|
11
|
+
throw new Error("setOrderAddress: type must be 'shipping' or 'billing'");
|
|
12
|
+
}
|
|
13
|
+
const key = params.type === 'shipping' ? 'shippingAddress' : 'billingAddress';
|
|
14
|
+
let address = params.address;
|
|
15
|
+
if (address) {
|
|
16
|
+
const read = normalizeAddress(address);
|
|
17
|
+
if (!read.country)
|
|
18
|
+
throw new Error('setOrderAddress: choose a country');
|
|
19
|
+
if (params.type === 'shipping' && (!address.address1?.trim() || !address.city?.trim())) {
|
|
20
|
+
throw new Error('setOrderAddress: a shipping address needs a street and a city');
|
|
21
|
+
}
|
|
22
|
+
address = { ...address, country: read.country, state: read.province ?? address.state, fromCustomer: false };
|
|
23
|
+
}
|
|
24
|
+
MOCK_CART[key] = address;
|
|
25
|
+
if (params.type === 'shipping')
|
|
26
|
+
MOCK_CART.shipping = null;
|
|
27
|
+
mockPublishEvent('cart', 'cart-updated', { [key]: address });
|
|
28
|
+
return { address, cart: MOCK_CART, timestamp: new Date().toISOString() };
|
|
29
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CFActiveCart, CFCartAddress } from '../../CommonTypes';
|
|
2
|
+
export type OrderAddressType = 'shipping' | 'billing';
|
|
3
|
+
export interface SetOrderAddressParams {
|
|
4
|
+
type: OrderAddressType;
|
|
5
|
+
/** `null` clears it. Country and province may be codes or names; the runtime stores codes. */
|
|
6
|
+
address: CFCartAddress | null;
|
|
7
|
+
}
|
|
8
|
+
export interface SetOrderAddressResponse {
|
|
9
|
+
/** As stored: country as ISO-2, province as its code, postal code tidied. */
|
|
10
|
+
address: CFCartAddress | null;
|
|
11
|
+
/** The cart after the change. A new shipping address re-checks the chosen shipping option. */
|
|
12
|
+
cart: CFActiveCart;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* THROWS when the address can't be used: no country, a country we can't read, or a
|
|
17
|
+
* shipping address without a street and city. The message says which, for the form.
|
|
18
|
+
*/
|
|
19
|
+
export type SetOrderAddress = (params: SetOrderAddressParams) => Promise<SetOrderAddressResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SetOutlet } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone mock of switching location.
|
|
4
|
+
*
|
|
5
|
+
* It mirrors the two REFUSALS the real command makes — a missing id, and an
|
|
6
|
+
* outlet that cannot take a payment — because those are the mistakes a builder
|
|
7
|
+
* actually hits. A mock that accepted anything would let a location picker look
|
|
8
|
+
* finished and then strand a shopper at a dead payment step on the real site.
|
|
9
|
+
*
|
|
10
|
+
* The unconnected id comes from the `getOutlets` mock rather than being repeated
|
|
11
|
+
* here, so the two cannot drift into telling different stories.
|
|
12
|
+
*/
|
|
13
|
+
export declare const mockSetOutlet: SetOutlet;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { MOCK_UNCONNECTED_OUTLET_ID } from '../get-outlets/mock';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone mock of switching location.
|
|
4
|
+
*
|
|
5
|
+
* It mirrors the two REFUSALS the real command makes — a missing id, and an
|
|
6
|
+
* outlet that cannot take a payment — because those are the mistakes a builder
|
|
7
|
+
* actually hits. A mock that accepted anything would let a location picker look
|
|
8
|
+
* finished and then strand a shopper at a dead payment step on the real site.
|
|
9
|
+
*
|
|
10
|
+
* The unconnected id comes from the `getOutlets` mock rather than being repeated
|
|
11
|
+
* here, so the two cannot drift into telling different stories.
|
|
12
|
+
*/
|
|
13
|
+
export const mockSetOutlet = async (params) => {
|
|
14
|
+
console.log('[Mock] setOutlet called', params);
|
|
15
|
+
if (!params)
|
|
16
|
+
throw new Error('Params required');
|
|
17
|
+
if (!params.outletId || !params.outletId.trim()) {
|
|
18
|
+
throw new Error('setOutlet: outletId is required');
|
|
19
|
+
}
|
|
20
|
+
if (params.outletId === MOCK_UNCONNECTED_OUTLET_ID) {
|
|
21
|
+
throw new Error(`setOutlet: outlet ${params.outletId} cannot take an online payment — offer only outlets with connected: true`);
|
|
22
|
+
}
|
|
23
|
+
return { outletId: params.outletId, rehydrated: true, timestamp: new Date().toISOString() };
|
|
24
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface SetOutletParams {
|
|
2
|
+
/** An `id` from `getOutlets`. */
|
|
3
|
+
outletId: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SetOutletResponse {
|
|
6
|
+
/** The location now in effect. */
|
|
7
|
+
outletId: string;
|
|
8
|
+
/**
|
|
9
|
+
* `true` when the catalog and tax tables were re-pulled for this location.
|
|
10
|
+
*
|
|
11
|
+
* `false` means the switch took effect but the refresh could not reach the
|
|
12
|
+
* server — the shop is running on what it already had cached. Totals may be
|
|
13
|
+
* the previous location's until the next refresh pass lands, so a flow can
|
|
14
|
+
* tell the shopper to try again rather than quietly showing stale tax.
|
|
15
|
+
*/
|
|
16
|
+
rehydrated: boolean;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Switch the shop to another of the company's locations.
|
|
21
|
+
*
|
|
22
|
+
* REFUSES AN OUTLET THAT CANNOT TAKE A PAYMENT, and one that does not belong to
|
|
23
|
+
* this company, rather than letting either fail later at the payment step. Check
|
|
24
|
+
* `connected` from `getOutlets` and do not offer those, so this refusal is a
|
|
25
|
+
* backstop rather than something a shopper ever sees.
|
|
26
|
+
*
|
|
27
|
+
* The cart survives the switch. Its lines are kept and re-priced; a line that is
|
|
28
|
+
* not purchasable at the new location is not silently dropped here, it is
|
|
29
|
+
* reported when the checkout is next priced.
|
|
30
|
+
*/
|
|
31
|
+
export type SetOutlet = (params: SetOutletParams) => Promise<SetOutletResponse>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Set Outlet Types
|
|
2
|
+
//
|
|
3
|
+
// Choose which of the company's locations this shop is ordering from.
|
|
4
|
+
//
|
|
5
|
+
// WHAT IT ACTUALLY CHANGES. An outlet decides three things, and a shop that lets
|
|
6
|
+
// a shopper pick one has to move all three together:
|
|
7
|
+
//
|
|
8
|
+
// tax rates follow the outlet's own address, so the total moves
|
|
9
|
+
// availability stock is counted per location, and a product can be hidden at one
|
|
10
|
+
// fulfilment the order records where the goods come from, so the right
|
|
11
|
+
// location's stock is the one that is drawn down
|
|
12
|
+
//
|
|
13
|
+
// So this is not a display preference. It re-pulls the catalog for the new
|
|
14
|
+
// location, re-hydrates the tax tables, and re-prices the cart. The totals a
|
|
15
|
+
// flow reads afterwards are the new location's.
|
|
16
|
+
//
|
|
17
|
+
// SAFE MID-CHECKOUT. Checkout starts when a shopper ARRIVES, so by the time
|
|
18
|
+
// anyone changes their mind an order usually already exists. That order is
|
|
19
|
+
// re-priced and moved to the new outlet in place, keeping its id and its receipt
|
|
20
|
+
// id, rather than being abandoned and replaced.
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { StartCheckout } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone mock of the storefront checkout.
|
|
4
|
+
*
|
|
5
|
+
* It mirrors the REFUSALS of the real command (empty cart,
|
|
6
|
+
* container not on the page) in the same order, because those are what a
|
|
7
|
+
* builder actually hits first — a mock that accepted anything would let a
|
|
8
|
+
* checkout screen look finished and fail on the published site.
|
|
9
|
+
*
|
|
10
|
+
* The mounted box is deliberately labelled as a mock. The real command mounts
|
|
11
|
+
* the provider's hosted card fields (iframes owned by the payment provider);
|
|
12
|
+
* nothing here takes a card number, and the placeholder must never look like
|
|
13
|
+
* it does. Its button publishes `payment-completed` so a flow's subscriber —
|
|
14
|
+
* the only way the outcome ever arrives — can be exercised in preview.
|
|
15
|
+
*/
|
|
16
|
+
export declare const mockStartCheckout: StartCheckout;
|