@liquidcommerce/elements-sdk 2.6.0-beta.84 → 2.6.0-beta.85
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/README.md +1 -1
- package/dist/index.checkout.esm.js +7071 -7010
- package/dist/index.esm.js +11572 -11515
- package/dist/types/core/pubsub/interfaces/address.interface.d.ts +3 -0
- package/dist/types/core/pubsub/interfaces/core.interface.d.ts +2 -2
- package/docs/v1/api/actions/address-actions.md +20 -15
- package/docs/v1/api/actions/cart-actions.md +22 -23
- package/docs/v1/api/actions/checkout-actions.md +72 -25
- package/docs/v1/api/actions/product-actions.md +61 -15
- package/docs/v1/api/client.md +38 -14
- package/docs/v1/api/configuration.md +5 -1
- package/docs/v1/api/injection-methods.md +8 -4
- package/docs/v1/api/typescript-types.md +6 -0
- package/docs/v1/examples/advanced-patterns.md +7 -6
- package/docs/v1/examples/checkout-flow.md +1 -2
- package/docs/v1/getting-started/concepts.md +20 -25
- package/docs/v1/getting-started/installation.md +4 -4
- package/docs/v1/guides/address-component.md +12 -8
- package/docs/v1/guides/best-practices.md +5 -5
- package/docs/v1/guides/cart-component.md +27 -39
- package/docs/v1/guides/checkout-component.md +27 -29
- package/docs/v1/guides/events.md +4 -4
- package/docs/v1/guides/product-component.md +42 -19
- package/docs/v1/guides/product-list-component.md +8 -9
- package/docs/v1/guides/theming.md +6 -9
- package/docs/v1/integration/proxy-setup.md +22 -5
- package/docs/v1/reference/browser-support.md +2 -1
- package/docs/v1/reference/error-handling.md +12 -7
- package/docs/v1/reference/performance.md +1 -3
- package/docs/v1/reference/troubleshooting.md +3 -3
- package/package.json +8 -17
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IAddressActionEventData } from '@/core/pubsub/interfaces/address.interface';
|
|
1
|
+
import type { IAddressActionEventData, IAddressFailedEventData } from '@/core/pubsub/interfaces/address.interface';
|
|
2
2
|
import type { ICartFailedEventData, ICartItemAddedEventData, ICartItemEngravingUpdatedEventData, ICartItemQuantityChangedEventData, ICartItemRemovedEventData, ICartLoadedEventData, ICartProductAddEventData, ICartProductAddFailedEventData, ICartPromoCodeEventData, ICartPromoCodeFailedEventData, ICartUpdatedEventData } from '@/core/pubsub/interfaces/cart.interface';
|
|
3
3
|
import type { ICheckoutBillingInformationUpdatedEventData, ICheckoutCustomerInformationUpdatedEventData, ICheckoutFailedEventData, ICheckoutGiftCardEventData, ICheckoutGiftCardFailedEventData, ICheckoutGiftInformationUpdatedEventData, ICheckoutItemEngravingUpdatedEventData, ICheckoutItemQuantityChangedEventData, ICheckoutItemRemovedEventData, ICheckoutLoadedEventData, ICheckoutMarketingPreferencesToggleEventData, ICheckoutProductAddEventData, ICheckoutProductAddFailedEventData, ICheckoutPromoCodeEventData, ICheckoutPromoCodeFailedEventData, ICheckoutSubmitCompletedEventData, ICheckoutSubmitFailedEventData, ICheckoutSubmitStartedEventData, ICheckoutTipUpdatedEventData, ICheckoutToggleEventData } from '@/core/pubsub/interfaces/checkout.interface';
|
|
4
4
|
import type { IProductAddToCartEventData, IProductFulfillmentChangedEventData, IProductFulfillmentTypeChangedEventData, IProductLoadedEventData, IProductQuantityChangedEventData, IProductSizeChangedEventData } from '@/core/pubsub/interfaces/product.interface';
|
|
@@ -29,7 +29,7 @@ export interface IElementsActionsEventsMap {
|
|
|
29
29
|
[ELEMENTS_ACTIONS_EVENT.CLIENT_READY]: IElementsClientIsReadyEventData;
|
|
30
30
|
[ELEMENTS_ACTIONS_EVENT.ADDRESS_UPDATED]: IAddressActionEventData;
|
|
31
31
|
[ELEMENTS_ACTIONS_EVENT.ADDRESS_CLEARED]: boolean;
|
|
32
|
-
[ELEMENTS_ACTIONS_EVENT.ADDRESS_FAILED]:
|
|
32
|
+
[ELEMENTS_ACTIONS_EVENT.ADDRESS_FAILED]: IAddressFailedEventData;
|
|
33
33
|
[ELEMENTS_ACTIONS_EVENT.PRODUCT_LOADED]: IProductLoadedEventData;
|
|
34
34
|
[ELEMENTS_ACTIONS_EVENT.PRODUCT_QUANTITY_DECREASE]: IProductQuantityChangedEventData;
|
|
35
35
|
[ELEMENTS_ACTIONS_EVENT.PRODUCT_QUANTITY_INCREASE]: IProductQuantityChangedEventData;
|
|
@@ -49,11 +49,12 @@ setAddressManually(
|
|
|
49
49
|
|
|
50
50
|
```typescript
|
|
51
51
|
interface IAddressAddress {
|
|
52
|
-
one: string;
|
|
53
|
-
two
|
|
54
|
-
city: string;
|
|
55
|
-
state: string;
|
|
56
|
-
zip: string;
|
|
52
|
+
one: string; // Street address
|
|
53
|
+
two: string; // Apt, suite, etc.
|
|
54
|
+
city: string; // City name
|
|
55
|
+
state: string; // Two-letter state code
|
|
56
|
+
zip: string; // ZIP/postal code
|
|
57
|
+
country: string; // Country
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
interface IAddressCoordinates {
|
|
@@ -71,7 +72,8 @@ await window.LiquidCommerce.elements.actions.address.setAddressManually(
|
|
|
71
72
|
two: 'Apt 4',
|
|
72
73
|
city: 'New York',
|
|
73
74
|
state: 'NY',
|
|
74
|
-
zip: '10001'
|
|
75
|
+
zip: '10001',
|
|
76
|
+
country: 'US'
|
|
75
77
|
},
|
|
76
78
|
{
|
|
77
79
|
latitude: 40.7128,
|
|
@@ -83,17 +85,19 @@ await window.LiquidCommerce.elements.actions.address.setAddressManually(
|
|
|
83
85
|
### Validation
|
|
84
86
|
|
|
85
87
|
The SDK validates:
|
|
86
|
-
- All required fields are present
|
|
87
|
-
- State is 2-letter code
|
|
88
|
+
- All required fields are present (street, city, state, zip)
|
|
88
89
|
- Latitude is between -90 and 90
|
|
89
90
|
- Longitude is between -180 and 180
|
|
90
91
|
|
|
92
|
+
> Note: the `state` field is expected to be a 2-letter code by convention, but the SDK does **not** enforce its format.
|
|
93
|
+
|
|
91
94
|
### Errors
|
|
92
95
|
|
|
93
96
|
**Throws `SDKError` if:**
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
+
- `address` or `coordinates` is missing
|
|
98
|
+
- A required field is missing (street, city, state, zip)
|
|
99
|
+
- Latitude or longitude is non-numeric
|
|
100
|
+
- Coordinates are out of range (latitude outside -90..90, longitude outside -180..180)
|
|
97
101
|
|
|
98
102
|
---
|
|
99
103
|
|
|
@@ -135,13 +139,14 @@ getDetails(): IAddressData | null
|
|
|
135
139
|
|
|
136
140
|
```typescript
|
|
137
141
|
interface IAddressData {
|
|
138
|
-
id: string; // Google Places ID
|
|
142
|
+
id: string; // Google Places ID (always populated when an object is returned)
|
|
139
143
|
address: {
|
|
140
144
|
one: string;
|
|
141
|
-
two
|
|
145
|
+
two: string;
|
|
142
146
|
city: string;
|
|
143
147
|
state: string;
|
|
144
148
|
zip: string;
|
|
149
|
+
country: string;
|
|
145
150
|
};
|
|
146
151
|
coordinates: {
|
|
147
152
|
latitude: number;
|
|
@@ -151,7 +156,7 @@ interface IAddressData {
|
|
|
151
156
|
}
|
|
152
157
|
```
|
|
153
158
|
|
|
154
|
-
Returns `null` if no address is set.
|
|
159
|
+
Returns `null` if no address is set. Manually-entered addresses (set via `setAddressManually`) are stored without a Google Places ID, so `getDetails()` also returns `null` for them.
|
|
155
160
|
|
|
156
161
|
### Example
|
|
157
162
|
|
|
@@ -194,7 +199,7 @@ Address actions trigger events:
|
|
|
194
199
|
```javascript
|
|
195
200
|
// Address updated
|
|
196
201
|
window.addEventListener('lce:actions.address_updated', (event) => {
|
|
197
|
-
const { address, coordinates, formattedAddress } = event.detail.data;
|
|
202
|
+
const { googlePlacesId, address, coordinates, formattedAddress } = event.detail.data;
|
|
198
203
|
console.log('Address set:', formattedAddress);
|
|
199
204
|
});
|
|
200
205
|
|
|
@@ -78,7 +78,7 @@ addProduct(params: IAddProductParams[], openCart?: boolean): Promise<void>
|
|
|
78
78
|
|
|
79
79
|
```typescript
|
|
80
80
|
interface IAddProductParams {
|
|
81
|
-
identifier: string; // Product UPC,
|
|
81
|
+
identifier: string; // Product UPC, size ID, or salsify grouping ID
|
|
82
82
|
fulfillmentType: FulfillmentType; // 'shipping' or 'onDemand'
|
|
83
83
|
quantity: number; // Number of items
|
|
84
84
|
engravingLines?: string[]; // Optional engraving (see below)
|
|
@@ -145,8 +145,8 @@ If no address is set, the SDK automatically:
|
|
|
145
145
|
- Identifier is invalid
|
|
146
146
|
- Fulfillment type is not `'shipping'` or `'onDemand'`
|
|
147
147
|
- Quantity is less than 1
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
|
|
149
|
+
Unavailable products and presale items are **not** thrown — they are silently skipped (a `cart.error` is set on the store). If no products could be added, a `cart_product_add_failed` event is emitted with `{ cartId, identifiers, error }` and the returned promise still resolves (it does not reject).
|
|
150
150
|
|
|
151
151
|
---
|
|
152
152
|
|
|
@@ -242,17 +242,17 @@ getDetails(): IBaseCartEventData
|
|
|
242
242
|
```typescript
|
|
243
243
|
interface IBaseCartEventData {
|
|
244
244
|
cartId: string;
|
|
245
|
-
|
|
245
|
+
promoCodeDiscount: number | null; // in cents
|
|
246
246
|
subtotal: number; // in cents
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
247
|
+
itemCount: number;
|
|
248
|
+
items: Record<string, ICartItem>; // keyed by item ID
|
|
249
|
+
retailers: Record<string, ICartRetailer>; // keyed by retailer ID
|
|
250
|
+
location: {
|
|
251
|
+
placesId: string;
|
|
252
|
+
formattedAddress: string;
|
|
253
|
+
address: IAddressAddress;
|
|
254
|
+
coordinates: IAddressCoordinates;
|
|
255
|
+
} | null;
|
|
256
256
|
}
|
|
257
257
|
```
|
|
258
258
|
|
|
@@ -262,12 +262,11 @@ interface IBaseCartEventData {
|
|
|
262
262
|
const cart = window.LiquidCommerce.elements.actions.cart.getDetails();
|
|
263
263
|
|
|
264
264
|
console.log(`Cart ID: ${cart.cartId}`);
|
|
265
|
-
console.log(`Items: ${cart.
|
|
265
|
+
console.log(`Items: ${cart.itemCount}`);
|
|
266
266
|
console.log(`Subtotal: $${cart.subtotal / 100}`);
|
|
267
|
-
console.log(`Total: $${cart.total / 100}`);
|
|
268
267
|
|
|
269
|
-
if (cart.
|
|
270
|
-
console.log(`Discount: $${cart.
|
|
268
|
+
if (cart.promoCodeDiscount) {
|
|
269
|
+
console.log(`Discount: $${cart.promoCodeDiscount / 100}`);
|
|
271
270
|
}
|
|
272
271
|
```
|
|
273
272
|
|
|
@@ -279,8 +278,8 @@ if (cart.promoCode) {
|
|
|
279
278
|
function updateCartSummary() {
|
|
280
279
|
const cart = window.LiquidCommerce.elements.actions.cart.getDetails();
|
|
281
280
|
|
|
282
|
-
document.getElementById('cart-count').textContent = cart.
|
|
283
|
-
document.getElementById('cart-total').textContent = `$${cart.
|
|
281
|
+
document.getElementById('cart-count').textContent = cart.itemCount;
|
|
282
|
+
document.getElementById('cart-total').textContent = `$${cart.subtotal / 100}`;
|
|
284
283
|
}
|
|
285
284
|
|
|
286
285
|
// Update on cart changes
|
|
@@ -293,7 +292,7 @@ window.addEventListener('lce:actions.cart_updated', updateCartSummary);
|
|
|
293
292
|
document.getElementById('checkout-btn').addEventListener('click', () => {
|
|
294
293
|
const cart = window.LiquidCommerce.elements.actions.cart.getDetails();
|
|
295
294
|
|
|
296
|
-
if (cart.
|
|
295
|
+
if (cart.itemCount === 0) {
|
|
297
296
|
alert('Your cart is empty');
|
|
298
297
|
return;
|
|
299
298
|
}
|
|
@@ -308,10 +307,10 @@ document.getElementById('checkout-btn').addEventListener('click', () => {
|
|
|
308
307
|
const cart = window.LiquidCommerce.elements.actions.cart.getDetails();
|
|
309
308
|
|
|
310
309
|
gtag('event', 'view_cart', {
|
|
311
|
-
value: cart.
|
|
310
|
+
value: cart.subtotal / 100,
|
|
312
311
|
currency: 'USD',
|
|
313
|
-
items: cart.items.map(item => ({
|
|
314
|
-
item_id: item.
|
|
312
|
+
items: Object.values(cart.items).map(item => ({
|
|
313
|
+
item_id: item.partNumber,
|
|
315
314
|
quantity: item.quantity
|
|
316
315
|
}))
|
|
317
316
|
});
|
|
@@ -60,7 +60,7 @@ window.LiquidCommerce.elements.actions.checkout.exitCheckout();
|
|
|
60
60
|
addProduct(params: IAddProductParams[], openCheckout?: boolean): Promise<void>
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Add products
|
|
63
|
+
Add products and open checkout in one call. Products are added to the cart (via the cart action, or directly when none is wired up), then checkout is prepared and opened. For a checkout without a cart session, use `addAnonymousProduct`.
|
|
64
64
|
|
|
65
65
|
`IAddProductParams.engravingLines?: string[]` — optional engraving. Same rules as [`actions.cart.addProduct()`](./cart-actions.md#actionscartaddproduct): invalid input is ignored, blanks stripped, lines clamped to the product's `maxLines` / `maxCharsPerLine`, and engraving is dropped (without failing) when the product, variant, or fulfillment doesn't support it.
|
|
66
66
|
|
|
@@ -94,15 +94,66 @@ addAnonymousProduct(
|
|
|
94
94
|
|
|
95
95
|
Add a product to checkout without requiring a cart session. Used for direct-to-checkout flows.
|
|
96
96
|
|
|
97
|
+
**Parameters** (`IAnonymousCheckoutAddProductParams`):
|
|
98
|
+
|
|
99
|
+
| Field | Type | Required | Description |
|
|
100
|
+
|-------|------|----------|-------------|
|
|
101
|
+
| `location` | `ILocation` | Yes | `{ address: IAddressAddress; coordinates: IAddressCoordinates }` |
|
|
102
|
+
| `items` | `IAnonymousCheckoutAddProductItem[]` | No | Products to add: `{ identifier, fulfillmentType, quantity }`. Provide `items` **or** `cartItems`. |
|
|
103
|
+
| `cartItems` | `ICartUpdateItem[]` | No | Pre-resolved cart line items: `{ id?, partNumber, quantity, fulfillmentId, engravingLines? }` (alternative to `items`). |
|
|
104
|
+
| `promoCode` | `string` | No | Promo code applied while creating the anonymous checkout. |
|
|
105
|
+
|
|
97
106
|
```javascript
|
|
98
107
|
const result = await window.LiquidCommerce.elements.actions.checkout
|
|
99
108
|
.addAnonymousProduct({
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
items: [
|
|
110
|
+
{ identifier: '00619947000020', fulfillmentType: 'shipping', quantity: 1 }
|
|
111
|
+
],
|
|
112
|
+
location: {
|
|
113
|
+
address: { one: '123 Main St', two: '', city: 'New York', state: 'NY', zip: '10001', country: 'US' },
|
|
114
|
+
coordinates: { latitude: 40.7128, longitude: -74.006 }
|
|
115
|
+
},
|
|
116
|
+
promoCode: 'WELCOME10' // optional
|
|
103
117
|
});
|
|
104
118
|
```
|
|
105
119
|
|
|
120
|
+
**Returns** (`IAnonymousCheckoutAddProductResponse`):
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
interface IAnonymousCheckoutAddProductResponse {
|
|
124
|
+
checkout: ICheckoutPrepare | null; // the prepared checkout (see below), or null on failure
|
|
125
|
+
unavailableItems: IAnonymousCheckoutUnavailableItem[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface IAnonymousCheckoutUnavailableItem {
|
|
129
|
+
identifier: string;
|
|
130
|
+
productName: string | null;
|
|
131
|
+
fulfillmentType: FulfillmentType; // 'shipping' | 'onDemand'
|
|
132
|
+
reason: 'product_not_available' | 'fulfillment_not_available';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface ICheckoutPrepare {
|
|
136
|
+
token: string;
|
|
137
|
+
cartId: string;
|
|
138
|
+
customer: ICheckoutCustomer;
|
|
139
|
+
isGift: boolean;
|
|
140
|
+
billingSameAsShipping: boolean;
|
|
141
|
+
payment?: string;
|
|
142
|
+
giftOptions: ICheckoutGiftRecipient;
|
|
143
|
+
marketingPreferences: ICheckoutMarketingPreferences;
|
|
144
|
+
shippingAddress: ICheckoutShippingAddress;
|
|
145
|
+
billingAddress: ICheckoutBilling;
|
|
146
|
+
promoCode: ICheckoutPromoCode | null;
|
|
147
|
+
amounts: ICheckoutAmounts;
|
|
148
|
+
giftCards: ICheckoutGiftCard[];
|
|
149
|
+
presale: ICheckoutPresale | null;
|
|
150
|
+
itemsQuantity: number;
|
|
151
|
+
items: Record<string /* cart item ID */, ICheckoutItem>;
|
|
152
|
+
retailers: Record<string /* retailer ID */, ICheckoutRetailer>;
|
|
153
|
+
events: ICheckoutEvent[];
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
106
157
|
## Promo & Gift Card Actions
|
|
107
158
|
|
|
108
159
|
### actions.checkout.applyPromoCode()
|
|
@@ -219,7 +270,7 @@ Pre-fill customer information.
|
|
|
219
270
|
**Available fields:**
|
|
220
271
|
- `firstName`, `lastName`
|
|
221
272
|
- `email`, `phone`
|
|
222
|
-
- `
|
|
273
|
+
- `birthDate`, `addressTwo`
|
|
223
274
|
- `company`
|
|
224
275
|
|
|
225
276
|
```javascript
|
|
@@ -228,7 +279,7 @@ window.LiquidCommerce.elements.actions.checkout.updateCustomerInfo({
|
|
|
228
279
|
lastName: 'Doe',
|
|
229
280
|
email: 'john@example.com',
|
|
230
281
|
phone: '+15551234567',
|
|
231
|
-
|
|
282
|
+
birthDate: '1990-01-01',
|
|
232
283
|
addressTwo: 'Apt 4B',
|
|
233
284
|
company: 'Acme Corp'
|
|
234
285
|
});
|
|
@@ -341,7 +392,7 @@ const checkout = window.LiquidCommerce.elements.actions.checkout.getDetails();
|
|
|
341
392
|
|
|
342
393
|
console.log(`Cart ID: ${checkout.cartId}`);
|
|
343
394
|
console.log(`Total: $${checkout.amounts.total / 100}`);
|
|
344
|
-
console.log(`Items: ${checkout.
|
|
395
|
+
console.log(`Items: ${checkout.itemCount}`);
|
|
345
396
|
console.log(`Is Gift: ${checkout.isGift}`);
|
|
346
397
|
```
|
|
347
398
|
|
|
@@ -349,23 +400,19 @@ console.log(`Is Gift: ${checkout.isGift}`);
|
|
|
349
400
|
|
|
350
401
|
```typescript
|
|
351
402
|
interface ICheckoutDetailsEventData {
|
|
403
|
+
token: string;
|
|
352
404
|
cartId: string;
|
|
353
|
-
items: ICheckoutItem[];
|
|
354
|
-
amounts: {
|
|
355
|
-
subtotal: number;
|
|
356
|
-
tax: number;
|
|
357
|
-
shipping: number;
|
|
358
|
-
tip: number;
|
|
359
|
-
total: number;
|
|
360
|
-
};
|
|
361
|
-
customer: ICustomerInfo;
|
|
362
|
-
shippingAddress: IAddress;
|
|
363
|
-
billingAddress?: IAddress;
|
|
364
405
|
isGift: boolean;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
406
|
+
billingSameAsShipping: boolean;
|
|
407
|
+
marketingPreferences: {
|
|
408
|
+
canEmail: boolean;
|
|
409
|
+
canSms: boolean;
|
|
410
|
+
};
|
|
411
|
+
hasPromoCode: boolean;
|
|
412
|
+
hasGiftCards: boolean;
|
|
413
|
+
amounts: ICheckoutAmounts;
|
|
414
|
+
itemCount: number;
|
|
415
|
+
items: Record<string, ICheckoutItem>;
|
|
369
416
|
}
|
|
370
417
|
```
|
|
371
418
|
|
|
@@ -419,11 +466,11 @@ window.addEventListener('lce:actions.checkout_submit_started', () => {
|
|
|
419
466
|
});
|
|
420
467
|
|
|
421
468
|
window.addEventListener('lce:actions.checkout_submit_completed', (event) => {
|
|
422
|
-
const {
|
|
469
|
+
const { orderNumber, orderTotal } = event.detail.data;
|
|
423
470
|
|
|
424
471
|
gtag('event', 'purchase', {
|
|
425
|
-
transaction_id:
|
|
426
|
-
value:
|
|
472
|
+
transaction_id: orderNumber,
|
|
473
|
+
value: orderTotal / 100,
|
|
427
474
|
currency: 'USD'
|
|
428
475
|
});
|
|
429
476
|
});
|
|
@@ -21,19 +21,22 @@ getDetails(identifier: string): IBaseProductEventData
|
|
|
21
21
|
### Returns
|
|
22
22
|
|
|
23
23
|
```typescript
|
|
24
|
+
// Inherits all IProduct fields except `sizes` (see below for the overridden `sizes` shape)
|
|
24
25
|
interface IBaseProductEventData {
|
|
25
26
|
identifier: string;
|
|
26
27
|
name: string;
|
|
27
28
|
description: string;
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
priceInfo: IProductPriceInfo | null; // { currency, minimum, average, maximum }
|
|
30
|
+
selectedSizeId: string | null;
|
|
30
31
|
selectedFulfillmentType: FulfillmentType;
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
selectedFulfillmentId: string | null;
|
|
33
|
+
productHasAvailability: boolean;
|
|
34
|
+
fulfillmentHasAvailability: boolean;
|
|
35
|
+
sizes: Record<string, IProductSizeEventData>;
|
|
33
36
|
images: string[];
|
|
34
37
|
brand: string;
|
|
35
38
|
category: string;
|
|
36
|
-
// ... additional fields
|
|
39
|
+
// ... additional IProduct fields
|
|
37
40
|
}
|
|
38
41
|
```
|
|
39
42
|
|
|
@@ -43,17 +46,16 @@ interface IBaseProductEventData {
|
|
|
43
46
|
const productData = window.LiquidCommerce.elements.actions.product.getDetails('00619947000020');
|
|
44
47
|
|
|
45
48
|
console.log(productData.name); // "Premium Whiskey"
|
|
46
|
-
console.log(productData.
|
|
47
|
-
console.log(productData.
|
|
48
|
-
console.log(productData.
|
|
49
|
+
console.log(productData.priceInfo?.minimum / 100); // 49.99
|
|
50
|
+
console.log(productData.sizes[productData.selectedSizeId]?.size); // "750ml"
|
|
51
|
+
console.log(productData.selectedSizeId); // "size_123"
|
|
49
52
|
```
|
|
50
53
|
|
|
51
54
|
### Errors
|
|
52
55
|
|
|
53
56
|
**Throws `SDKError` if:**
|
|
54
57
|
- Identifier is empty or invalid
|
|
55
|
-
- Product has not been loaded yet
|
|
56
|
-
- Product does not exist
|
|
58
|
+
- Product has not been loaded yet (a non-existent product surfaces as this same error)
|
|
57
59
|
|
|
58
60
|
```javascript
|
|
59
61
|
try {
|
|
@@ -71,7 +73,7 @@ try {
|
|
|
71
73
|
const product = window.LiquidCommerce.elements.actions.product.getDetails('00619947000020');
|
|
72
74
|
|
|
73
75
|
document.getElementById('product-name').textContent = product.name;
|
|
74
|
-
document.getElementById('product-price').textContent = `$${product.
|
|
76
|
+
document.getElementById('product-price').textContent = `$${product.priceInfo?.minimum / 100}`;
|
|
75
77
|
```
|
|
76
78
|
|
|
77
79
|
#### Sync with Analytics
|
|
@@ -84,7 +86,7 @@ window.addEventListener('lce:actions.product_loaded', () => {
|
|
|
84
86
|
items: [{
|
|
85
87
|
item_id: product.identifier,
|
|
86
88
|
item_name: product.name,
|
|
87
|
-
price: product.
|
|
89
|
+
price: product.priceInfo?.minimum / 100,
|
|
88
90
|
item_brand: product.brand
|
|
89
91
|
}]
|
|
90
92
|
});
|
|
@@ -97,7 +99,7 @@ window.addEventListener('lce:actions.product_loaded', () => {
|
|
|
97
99
|
const product1 = window.LiquidCommerce.elements.actions.product.getDetails('00619947000020');
|
|
98
100
|
const product2 = window.LiquidCommerce.elements.actions.product.getDetails('08504405135');
|
|
99
101
|
|
|
100
|
-
if (product1.
|
|
102
|
+
if (product1.priceInfo?.minimum < product2.priceInfo?.minimum) {
|
|
101
103
|
console.log(`${product1.name} is cheaper`);
|
|
102
104
|
}
|
|
103
105
|
```
|
|
@@ -124,14 +126,58 @@ getProductAvailabilityByState(
|
|
|
124
126
|
| `identifiers` | string[] | Yes | Array of product UPCs, SKUs, or IDs |
|
|
125
127
|
| `state` | string | No | Two-letter state code (e.g., `'NY'`) |
|
|
126
128
|
|
|
129
|
+
### Returns
|
|
130
|
+
|
|
131
|
+
Resolves to an `IProductAvailabilityResponse`:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
interface IProductAvailabilityResponse {
|
|
135
|
+
products: IProduct[];
|
|
136
|
+
retailers: Record<string /* retailer ID */, IRetailer>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface IRetailer {
|
|
140
|
+
id: string;
|
|
141
|
+
name: string;
|
|
142
|
+
address: IRetailerAddress; // IAddressAddress & IAddressCoordinates
|
|
143
|
+
addressFormatted: string;
|
|
144
|
+
shippingFulfillment: IFulfillment | null;
|
|
145
|
+
onDemandFulfillment: IFulfillment | null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface IFulfillment {
|
|
149
|
+
id: string;
|
|
150
|
+
type: FulfillmentType; // 'shipping' | 'onDemand'
|
|
151
|
+
doesAllowGiftCards: boolean;
|
|
152
|
+
doesAllowPromos: boolean;
|
|
153
|
+
expectation: string;
|
|
154
|
+
engravingExpectation: string;
|
|
155
|
+
fee: number;
|
|
156
|
+
timezone: string;
|
|
157
|
+
hourStatus: { isOpen: boolean; openTime: string; isClosed: boolean; closeTime: string };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface IProduct {
|
|
161
|
+
id: string; name: string; description: string; htmlDescription: string;
|
|
162
|
+
images: string[]; brand: string; region: string; country: string;
|
|
163
|
+
material: string; abv: string; proof: string; age: string; color: string;
|
|
164
|
+
flavor: string; variety: string; appellation: string; vintage: string;
|
|
165
|
+
tastingNotes: string; catPath: string; category: string; classification: string;
|
|
166
|
+
type: string; subType: string; salsifyGrouping: string;
|
|
167
|
+
priceInfo: IProductPriceInfo | null; // { currency, minimum, average, maximum } — cents
|
|
168
|
+
sizes: Record<string /* size ID */, IProductSize>;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
127
172
|
### Example
|
|
128
173
|
|
|
129
174
|
```javascript
|
|
130
175
|
// Check availability in a specific state
|
|
131
|
-
const
|
|
176
|
+
const availabilityCA = await window.LiquidCommerce.elements.actions.product
|
|
132
177
|
.getProductAvailabilityByState(['00619947000020', '08504405135'], 'CA');
|
|
133
178
|
|
|
134
|
-
//
|
|
179
|
+
// Omit the state to query without a state filter
|
|
180
|
+
// (state: undefined is sent — the SDK does NOT derive it from the stored address)
|
|
135
181
|
const availability = await window.LiquidCommerce.elements.actions.product
|
|
136
182
|
.getProductAvailabilityByState(['00619947000020']);
|
|
137
183
|
```
|
package/docs/v1/api/client.md
CHANGED
|
@@ -64,6 +64,36 @@ const client = await ElementsCheckout('YOUR_API_KEY', {
|
|
|
64
64
|
});
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
### ElementsBuilder()
|
|
68
|
+
|
|
69
|
+
Initialize the builder client, which exposes manual `inject*` / `update*` methods for fully programmatic composition.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
function ElementsBuilder(
|
|
73
|
+
apiKey: string,
|
|
74
|
+
config: ILiquidCommerceElementsBuilderConfig
|
|
75
|
+
): Promise<ILiquidCommerceElementsBuilderClient | null>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Parameters:**
|
|
79
|
+
|
|
80
|
+
| Parameter | Type | Required | Description |
|
|
81
|
+
|-----------|--------------------------------------|----------|-----------------------------|
|
|
82
|
+
| `apiKey` | string | Yes | Your LiquidCommerce API key |
|
|
83
|
+
| `config` | ILiquidCommerceElementsBuilderConfig | Yes | Builder configuration object (same shape as `ILiquidCommerceElementsConfig`) |
|
|
84
|
+
|
|
85
|
+
**Returns:** Promise that resolves to the builder client instance (`ILiquidCommerceElementsBuilderClient`), or `null` if initialization fails or it is called outside the browser.
|
|
86
|
+
|
|
87
|
+
**Example:**
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
import { ElementsBuilder } from '@liquidcommerce/elements-sdk';
|
|
91
|
+
|
|
92
|
+
const builder = await ElementsBuilder('YOUR_API_KEY', {
|
|
93
|
+
env: 'production'
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
67
97
|
## Configuration
|
|
68
98
|
|
|
69
99
|
### ILiquidCommerceElementsConfig
|
|
@@ -72,10 +102,8 @@ Complete configuration interface for the full SDK.
|
|
|
72
102
|
|
|
73
103
|
```typescript
|
|
74
104
|
interface ILiquidCommerceElementsConfig {
|
|
75
|
-
// Required
|
|
76
|
-
env: ElementsEnv; // 'development' | 'staging' | 'production'
|
|
77
|
-
|
|
78
105
|
// Optional
|
|
106
|
+
env?: ElementsEnv; // defaults to 'production' ('development' | 'staging' | 'production')
|
|
79
107
|
debugMode?: DebugMode; // 'none' | 'console' | 'panel'
|
|
80
108
|
customTheme?: IClientCustomThemeConfig;
|
|
81
109
|
promoTicker?: IPromoTicker[];
|
|
@@ -168,7 +196,7 @@ See [Proxy Setup Guide](../integration/proxy-setup.md) for implementation.
|
|
|
168
196
|
|
|
169
197
|
```typescript
|
|
170
198
|
interface ILiquidCommerceElementsCheckoutConfig {
|
|
171
|
-
pageUrl
|
|
199
|
+
pageUrl?: string; // Optional. URL pattern with {token} placeholder
|
|
172
200
|
}
|
|
173
201
|
```
|
|
174
202
|
|
|
@@ -186,6 +214,7 @@ checkout: {
|
|
|
186
214
|
interface ILiquidCommerceElementsDevelopmentConfig {
|
|
187
215
|
customApiUrl?: string;
|
|
188
216
|
openShadowDom?: boolean;
|
|
217
|
+
mockMode?: boolean; // enable mock data responses (sends 'X-Liquid-Api-Mock-Mode' header)
|
|
189
218
|
}
|
|
190
219
|
```
|
|
191
220
|
|
|
@@ -194,16 +223,18 @@ interface ILiquidCommerceElementsDevelopmentConfig {
|
|
|
194
223
|
```javascript
|
|
195
224
|
development: {
|
|
196
225
|
customApiUrl: 'http://localhost:3000/api',
|
|
197
|
-
openShadowDom: true //
|
|
226
|
+
openShadowDom: true // Use an OPEN (inspectable) Shadow DOM instead of the default closed mode; forced off in production
|
|
198
227
|
}
|
|
199
228
|
```
|
|
200
229
|
|
|
201
230
|
## Global Access
|
|
202
231
|
|
|
203
|
-
After initialization,
|
|
232
|
+
After initialization, each client is available globally under the `window.LiquidCommerce` namespace:
|
|
204
233
|
|
|
205
234
|
```javascript
|
|
206
|
-
window.LiquidCommerce.elements
|
|
235
|
+
window.LiquidCommerce.elements // full client (from Elements())
|
|
236
|
+
window.LiquidCommerce.elementsBuilder // builder client (from ElementsBuilder())
|
|
237
|
+
window.LiquidCommerce.elementsCheckout // checkout-only client (from ElementsCheckout())
|
|
207
238
|
```
|
|
208
239
|
|
|
209
240
|
This allows access from anywhere in your application:
|
|
@@ -397,13 +428,6 @@ import type {
|
|
|
397
428
|
IInjectCheckoutParams,
|
|
398
429
|
IInjectedComponent,
|
|
399
430
|
|
|
400
|
-
// Action types
|
|
401
|
-
IProductActions,
|
|
402
|
-
ICartActions,
|
|
403
|
-
ICheckoutActions,
|
|
404
|
-
IAddressActions,
|
|
405
|
-
IAddProductParams,
|
|
406
|
-
|
|
407
431
|
// Configuration types
|
|
408
432
|
IClientCustomThemeConfig,
|
|
409
433
|
IComponentGlobalConfigs,
|
|
@@ -171,6 +171,7 @@ interface IProductLayout {
|
|
|
171
171
|
showOnlyMainImage: boolean;
|
|
172
172
|
showTitle: boolean;
|
|
173
173
|
showDescription: boolean;
|
|
174
|
+
descriptionPosition: 'above' | 'below';
|
|
174
175
|
showQuantityCounter: boolean;
|
|
175
176
|
showOffHours: boolean;
|
|
176
177
|
quantityCounterStyle: 'outlined' | 'ghost';
|
|
@@ -192,6 +193,7 @@ interface IProductLayout {
|
|
|
192
193
|
| `showOnlyMainImage` | `boolean` | Show only the main product image (no carousel) |
|
|
193
194
|
| `showTitle` | `boolean` | Show product title |
|
|
194
195
|
| `showDescription` | `boolean` | Show product description |
|
|
196
|
+
| `descriptionPosition` | `'above' \| 'below'` | Placement of the product description relative to other content |
|
|
195
197
|
| `showQuantityCounter` | `boolean` | Show quantity selector |
|
|
196
198
|
| `showOffHours` | `boolean` | Show off-hours messaging |
|
|
197
199
|
| `quantityCounterStyle` | `'outlined' \| 'ghost'` | Visual style for quantity counter |
|
|
@@ -509,13 +511,15 @@ See [Proxy Setup Guide](../integration/proxy-setup.md) for implementation detail
|
|
|
509
511
|
interface ILiquidCommerceElementsDevelopmentConfig {
|
|
510
512
|
customApiUrl?: string;
|
|
511
513
|
openShadowDom?: boolean;
|
|
514
|
+
mockMode?: boolean;
|
|
512
515
|
}
|
|
513
516
|
```
|
|
514
517
|
|
|
515
518
|
| Property | Type | Description |
|
|
516
519
|
|----------|------|-------------|
|
|
517
520
|
| `customApiUrl` | `string` | Custom API URL for local or custom backend testing |
|
|
518
|
-
| `openShadowDom` | `boolean` |
|
|
521
|
+
| `openShadowDom` | `boolean` | Use an OPEN (inspectable) Shadow DOM instead of the default closed mode, for debugging. Shadow DOM is always attached; forced off in production (default: `false`) |
|
|
522
|
+
| `mockMode` | `boolean` | Enable mock mode to receive mock data from the API; sends an `X-Liquid-Api-Mock-Mode` header with all requests (default: `false`) |
|
|
519
523
|
|
|
520
524
|
> To pre-select a payment method and skip the card form (in any environment), use [`actions.checkout.setSavedPaymentMethod`](./actions/checkout-actions.md#actionscheckoutsetsavedpaymentmethod) instead.
|
|
521
525
|
|
|
@@ -53,9 +53,12 @@ injectAddressElement(
|
|
|
53
53
|
|
|
54
54
|
```javascript
|
|
55
55
|
await client.injectAddressElement('address-container', {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
showLabel: true
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Listen for address changes via the published event (dispatched on window)
|
|
60
|
+
window.addEventListener('lce:actions.address_updated', (event) => {
|
|
61
|
+
console.log('Address set:', event.detail.data);
|
|
59
62
|
});
|
|
60
63
|
```
|
|
61
64
|
|
|
@@ -127,7 +130,7 @@ injectProductList(params: IInjectProductListParams): Promise<void>
|
|
|
127
130
|
interface IInjectProductListParams {
|
|
128
131
|
containerId: string;
|
|
129
132
|
slug: string; // Product list slug identifier
|
|
130
|
-
rows?: number; // Default:
|
|
133
|
+
rows?: number; // Default: 4
|
|
131
134
|
columns?: number; // Default: 4
|
|
132
135
|
filters?: ProductListFilterType[];
|
|
133
136
|
productUrl?: PLCProductUrl; // string template OR Record<identifier, url> map
|
|
@@ -192,6 +195,7 @@ injectProductListSearch(params: IInjectProductListSearchParams): Promise<void>
|
|
|
192
195
|
interface IInjectProductListSearchParams {
|
|
193
196
|
containerId: string;
|
|
194
197
|
slug: string; // Product list slug identifier
|
|
198
|
+
filters?: ProductListFilterType[];
|
|
195
199
|
}
|
|
196
200
|
```
|
|
197
201
|
|