@liquidcommerce/elements-sdk 2.2.2 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +1608 -557
  2. package/dist/index.esm.js +10170 -8144
  3. package/dist/types/core/auth.service.d.ts +10 -4
  4. package/dist/types/core/base-component.service.d.ts +3 -0
  5. package/dist/types/core/circuit-breaker.service.d.ts +54 -0
  6. package/dist/types/core/client/client-action.service.d.ts +13 -11
  7. package/dist/types/core/client/client-config.service.d.ts +5 -3
  8. package/dist/types/core/command/common-command.service.d.ts +2 -1
  9. package/dist/types/core/debug-panel/debug-panel.service.d.ts +43 -0
  10. package/dist/types/core/debug-panel/debug-panel.styles.d.ts +1 -0
  11. package/dist/types/core/fingerprint.service.d.ts +4 -9
  12. package/dist/types/core/google-tag-manager.service.d.ts +127 -2
  13. package/dist/types/core/logger/logger-factory.d.ts +3 -0
  14. package/dist/types/core/logger/logger.service.d.ts +8 -5
  15. package/dist/types/core/pubsub/interfaces/core.interface.d.ts +2 -3
  16. package/dist/types/core/pubsub/interfaces/product.interface.d.ts +0 -5
  17. package/dist/types/core/store/interfaces/cart.interface.d.ts +0 -1
  18. package/dist/types/core/store/interfaces/checkout.interface.d.ts +0 -1
  19. package/dist/types/core/store/interfaces/core.interface.d.ts +2 -2
  20. package/dist/types/core/store/interfaces/product.interface.d.ts +0 -1
  21. package/dist/types/core/store/store.service.d.ts +1 -0
  22. package/dist/types/core/telemetry/telemetry.interface.d.ts +80 -0
  23. package/dist/types/core/telemetry/telemetry.service.d.ts +27 -0
  24. package/dist/types/enums/core.enum.d.ts +0 -1
  25. package/dist/types/enums/debug.enum.d.ts +6 -0
  26. package/dist/types/enums/index.d.ts +1 -0
  27. package/dist/types/interfaces/core.interface.d.ts +2 -2
  28. package/dist/types/modules/address/address.command.d.ts +1 -3
  29. package/dist/types/modules/cart/cart.commands.d.ts +1 -1
  30. package/dist/types/modules/cart/cart.component.d.ts +1 -2
  31. package/dist/types/modules/cart/components/cart-footer.component.d.ts +1 -0
  32. package/dist/types/modules/cart/components/cart-item.component.d.ts +2 -6
  33. package/dist/types/modules/checkout/checkout.commands.d.ts +3 -2
  34. package/dist/types/modules/checkout/checkout.component.d.ts +1 -2
  35. package/dist/types/modules/checkout/components/checkout-summary-section.component.d.ts +2 -0
  36. package/dist/types/modules/checkout/components/information/checkout-delivery-information-form.component.d.ts +1 -1
  37. package/dist/types/modules/checkout/components/summary/checkout-item-quantity.component.d.ts +0 -2
  38. package/dist/types/modules/checkout/components/summary/checkout-item.component.d.ts +2 -1
  39. package/dist/types/modules/checkout/components/summary/checkout-items.component.d.ts +1 -0
  40. package/dist/types/modules/checkout/components/summary/checkout-place-order-button.component.d.ts +0 -1
  41. package/dist/types/modules/checkout/constant.d.ts +0 -1
  42. package/dist/types/modules/product/components/index.d.ts +1 -0
  43. package/dist/types/modules/product/components/product-add-to-cart-section.component.d.ts +1 -0
  44. package/dist/types/modules/product/components/product-interactions.component.d.ts +4 -1
  45. package/dist/types/modules/product/components/product-price.component.d.ts +1 -0
  46. package/dist/types/modules/product/components/product-retailers.component.d.ts +1 -0
  47. package/dist/types/modules/product/product.commands.d.ts +1 -1
  48. package/dist/types/modules/ui-components/engraving/engraving-form.component.d.ts +13 -11
  49. package/dist/types/modules/ui-components/engraving/engraving-view.component.d.ts +4 -9
  50. package/dist/types/static/icon/index.d.ts +0 -1
  51. package/dist/types/utils/format.d.ts +2 -1
  52. package/docs/ACTIONS.md +1235 -0
  53. package/docs/BROWSER_SUPPORT.md +279 -0
  54. package/docs/CONFIGURATION.md +613 -0
  55. package/docs/DOCUMENTATION_INDEX.md +311 -0
  56. package/docs/EVENTS.md +532 -0
  57. package/docs/PROXY.md +228 -0
  58. package/docs/THEMING.md +592 -0
  59. package/docs/TROUBLESHOOTING.md +755 -0
  60. package/package.json +17 -17
  61. package/umd/elements.js +1 -1
  62. package/dist/types/static/icon/completed.icon.d.ts +0 -2
@@ -0,0 +1,1235 @@
1
+ # Elements SDK Actions - Partner Control Guide
2
+
3
+ > **Browser Support**: This SDK supports 2018+ browsers (Chrome 66+, Firefox 60+, Safari 12+) with comprehensive polyfills. See `docs/BROWSER_SUPPORT.md` for details.
4
+
5
+ ## What Are Actions and Why Use Them?
6
+
7
+ Actions let you control the LiquidCommerce components programmatically - like having a remote control for your e-commerce experience. Instead of waiting for customers to click buttons, you can make things happen automatically based on your business logic.
8
+
9
+ **Business Benefits:**
10
+ - **Automate User Flow**: Guide customers through the purchase process
11
+ - **Custom Experiences**: Build unique checkout flows that match your brand
12
+ - **Reduce Cart Abandonment**: Pre-fill information and remove friction
13
+ - **Increase Conversions**: Trigger actions at the perfect moment
14
+ - **Integrate with Existing Systems**: Connect to your CRM, inventory, etc.
15
+
16
+ ## How It Works & Getting Feedback
17
+
18
+ Actions are **fire-and-forget** methods that return `void` or `Promise<void>`. Instead of return values, **all feedback comes through events** - this is by design for security and flexibility.
19
+
20
+ ```javascript
21
+ // Actions are available globally
22
+ const actions = window.elements.actions;
23
+
24
+ // 1. Fire the action (simple, clean)
25
+ await actions.cart.applyPromoCode('SAVE20');
26
+
27
+ // 2. Listen for success/failure feedback via events
28
+ window.addEventListener('lce:actions.cart_promo_code_applied', function(event) {
29
+ const { applied, discountAmount, newTotal } = event.detail.data;
30
+ showSuccessMessage(`🎉 Promo applied! You saved $${discountAmount}!`);
31
+ updateCartDisplay(newTotal);
32
+ });
33
+
34
+ window.addEventListener('lce:actions.cart_promo_code_failed', function(event) {
35
+ const { attempted, error } = event.detail.data;
36
+ showErrorMessage('Promo code could not be applied. Please try again.');
37
+ });
38
+ ```
39
+
40
+ ### 🔒 **Security-First Design**
41
+
42
+ Events **never expose sensitive information** like promo codes, gift card codes, or balance details that could be intercepted by malicious scripts. You get all the feedback you need without security risks.
43
+
44
+ **✅ Safe Data in Events:**
45
+ - Success/failure status
46
+ - Discount amounts (already visible in UI)
47
+ - Total amounts (already visible in UI)
48
+ - Item counts and identifiers
49
+ - Generic error messages
50
+
51
+ **🚫 Never Exposed:**
52
+ - Promo codes or gift card codes
53
+ - Gift card balances or details
54
+ - Specific error messages that reveal codes
55
+ - Any sensitive financial information
56
+
57
+ ## Your Action Toolkit
58
+
59
+ ### 🛍️ Product Control (`actions.product`)
60
+
61
+ **What it does**: Get information about products to make smart business decisions
62
+
63
+ #### Get Product Details
64
+ ```javascript
65
+ // Check product info before making decisions
66
+ const product = actions.product.getDetails('product-123');
67
+
68
+ if (product.price > 100) {
69
+ // Show premium support option
70
+ showPremiumSupport();
71
+ }
72
+
73
+ if (product.isAvailable) {
74
+ // Enable add to cart
75
+ enableAddToCart();
76
+ } else {
77
+ // Show waitlist signup
78
+ showWaitlistSignup();
79
+ }
80
+ ```
81
+
82
+ **Real Business Uses:**
83
+ - **Dynamic Pricing**: Show different offers based on product price
84
+ - **Inventory Management**: Hide out-of-stock items or show alternatives
85
+ - **Personalization**: Recommend accessories based on main product
86
+ - **A/B Testing**: Show different CTAs for different product categories
87
+
88
+ ### 📍 Address Control (`actions.address`)
89
+
90
+ **What it does**: Manage shipping addresses to reduce checkout friction
91
+
92
+ #### Set Address by Google Places ID
93
+ ```javascript
94
+ // Set address using Google Places ID - fastest way to capture accurate addresses
95
+ try {
96
+ await actions.address.setAddressByPlacesId('ChIJ0SRjyK5ZwokRp1TwT8dJSv8');
97
+ showMessage("Address set successfully!");
98
+ } catch (error) {
99
+ showMessage("Address not found. Please try again.");
100
+ }
101
+
102
+ // Real-world example: Let customers pick from their recent locations
103
+ const recentPlacesIds = getCustomerRecentPlaces();
104
+ if (recentPlacesIds.length > 0) {
105
+ // Use their most recent address automatically
106
+ await actions.address.setAddressByPlacesId(recentPlacesIds[0]);
107
+ }
108
+ ```
109
+
110
+ **Find Google Places IDs**: Use the [Google Places ID Finder](https://developers.google.com/maps/documentation/places/web-service/place-id#find-id) to get Place IDs for any location.
111
+
112
+ #### Set Address Manually
113
+ ```javascript
114
+ // Set address manually without Google Places - perfect for custom address forms
115
+ try {
116
+ await actions.address.setAddressManually(
117
+ {
118
+ one: '123 Main St',
119
+ two: 'Apt 4B', // Optional apartment/suite
120
+ city: 'New York',
121
+ state: 'NY',
122
+ zip: '10001',
123
+ country: 'United States' // Optional, will be included in formatted address
124
+ },
125
+ {
126
+ lat: 40.7505045, // Latitude coordinate
127
+ long: -73.9934387 // Longitude coordinate
128
+ }
129
+ );
130
+ showMessage("Manual address set successfully!");
131
+ } catch (error) {
132
+ showMessage("Invalid address or coordinates. Please check and try again.");
133
+ }
134
+
135
+ // Real-world example: Custom address form integration
136
+ function handleCustomAddressSubmit(formData) {
137
+ const addressData = {
138
+ one: formData.streetAddress,
139
+ two: formData.apartment || '', // Optional
140
+ city: formData.city,
141
+ state: formData.state,
142
+ zip: formData.zipCode,
143
+ country: formData.country || 'United States'
144
+ };
145
+
146
+ const coordinates = {
147
+ lat: parseFloat(formData.latitude),
148
+ long: parseFloat(formData.longitude)
149
+ };
150
+
151
+ // Validate coordinates before setting
152
+ if (coordinates.lat >= -90 && coordinates.lat <= 90 &&
153
+ coordinates.long >= -180 && coordinates.long <= 180) {
154
+ await actions.address.setAddressManually(addressData, coordinates);
155
+ } else {
156
+ showMessage("Invalid coordinates provided");
157
+ }
158
+ }
159
+
160
+ // Integration with geocoding services
161
+ async function setAddressWithGeocoding(streetAddress, city, state, zip) {
162
+ try {
163
+ // Use your preferred geocoding service to get coordinates
164
+ const coordinates = await geocodeAddress(`${streetAddress}, ${city}, ${state} ${zip}`);
165
+
166
+ await actions.address.setAddressManually(
167
+ {
168
+ one: streetAddress,
169
+ two: '',
170
+ city: city,
171
+ state: state,
172
+ zip: zip,
173
+ country: 'United States'
174
+ },
175
+ coordinates
176
+ );
177
+
178
+ showMessage("Address geocoded and set successfully!");
179
+ } catch (error) {
180
+ showMessage("Could not geocode address. Please verify the address.");
181
+ }
182
+ }
183
+ ```
184
+
185
+ **Key Features:**
186
+ - **Automatic Formatting**: Generates Google Places API-formatted address strings automatically
187
+ - **Empty Places ID**: Places ID is set to empty string (perfect for non-Google Places addresses)
188
+ - **Flexible Input**: Works with any address structure and coordinate source
189
+ - **Validation**: Built-in validation for required fields and coordinate ranges
190
+ - **Same Events**: Triggers the same success/failure events as Google Places addresses
191
+
192
+ #### Check Address
193
+ ```javascript
194
+ // See if customer has an address saved
195
+ const address = actions.address.getDetails();
196
+
197
+ if (address) {
198
+ // Address is saved - express checkout available!
199
+ showExpressCheckout();
200
+ } else {
201
+ // No address - guide them through address entry
202
+ highlightAddressForm();
203
+ }
204
+ ```
205
+
206
+ #### Clear Address
207
+ ```javascript
208
+ // Clear address (useful for guest checkout option)
209
+ actions.address.clear();
210
+ ```
211
+
212
+ **Real Business Uses:**
213
+ - **One-Click Address**: Let customers select from saved Google Places
214
+ - **Custom Address Forms**: Build your own address input forms with full control
215
+ - **Third-Party Integration**: Connect with non-Google geocoding services
216
+ - **Legacy System Migration**: Import addresses from existing customer databases
217
+ - **International Addresses**: Handle addresses that may not be in Google Places
218
+ - **Location-Based Services**: Auto-detect customer location and set address
219
+ - **Express Checkout**: Skip address forms for returning customers
220
+ - **Shipping Calculator**: Calculate shipping costs before checkout
221
+ - **Local Pickup**: Show pickup options based on their address
222
+ - **Tax Calculation**: Display accurate taxes before purchase
223
+
224
+ ## 📡 **Complete Action Feedback Events Reference**
225
+
226
+ All actions provide feedback through events. Here are all available success/failure events:
227
+
228
+ ### **Cart Action Events**
229
+ ```javascript
230
+ // Product management
231
+ 'lce:actions.cart_product_add_success' // { itemsAdded: number, identifiers: string[] }
232
+ 'lce:actions.cart_product_add_failed' // { identifiers: string[], error: string }
233
+
234
+ // Promo code management
235
+ 'lce:actions.cart_promo_code_applied' // { applied: true, discountAmount?: number, newTotal?: number }
236
+ 'lce:actions.cart_promo_code_removed' // { applied: false }
237
+ 'lce:actions.cart_promo_code_failed' // { attempted: true, error: string }
238
+ ```
239
+
240
+ ### **Checkout Action Events**
241
+ ```javascript
242
+ // Product management
243
+ 'lce:actions.checkout_product_add_success' // { itemsAdded: number, identifiers: string[] }
244
+ 'lce:actions.checkout_product_add_failed' // { identifiers: string[], error: string }
245
+
246
+ // Promo code management
247
+ 'lce:actions.checkout_promo_code_applied' // { applied: true, discountAmount?: number, newTotal?: number }
248
+ 'lce:actions.checkout_promo_code_removed' // { applied: false }
249
+ 'lce:actions.checkout_promo_code_failed' // { attempted: true, error: string }
250
+
251
+ // Gift card management
252
+ 'lce:actions.checkout_gift_card_applied' // { applied: true, newTotal?: number }
253
+ 'lce:actions.checkout_gift_card_removed' // { applied: false }
254
+ 'lce:actions.checkout_gift_card_failed' // { attempted: true, error: string }
255
+ ```
256
+
257
+ ### **Address Action Events**
258
+ ```javascript
259
+ // Address management (already available)
260
+ 'lce:actions.address_updated' // { googlePlacesId: string, formattedAddress: string, address: {...}, coordinates: {...} }
261
+ 'lce:actions.address_failed' // { error: string, googlePlacesId?: string }
262
+ 'lce:actions.address_cleared' // boolean
263
+ ```
264
+
265
+ ### **Event Handling Best Practices**
266
+
267
+ #### **1. Set Up Listeners Before Actions**
268
+ ```javascript
269
+ // ✅ Good: Set up listener first
270
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
271
+ await actions.cart.applyPromoCode('SAVE20');
272
+
273
+ // ❌ Bad: Listener after action (might miss event)
274
+ await actions.cart.applyPromoCode('SAVE20');
275
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
276
+ ```
277
+
278
+ #### **2. Handle Both Success and Failure**
279
+ ```javascript
280
+ // ✅ Always handle both outcomes
281
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
282
+ window.addEventListener('lce:actions.cart_promo_code_failed', handleFailure);
283
+ await actions.cart.applyPromoCode('SAVE20');
284
+ ```
285
+
286
+ #### **3. Use Event Data Effectively**
287
+ ```javascript
288
+ // ✅ Use all available event data
289
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
290
+ const { itemsAdded, identifiers } = event.detail.data;
291
+
292
+ // Show success message
293
+ showMessage(`Added ${itemsAdded} items to cart`);
294
+
295
+ // Track analytics
296
+ analytics.track('Products Added', { items: identifiers, count: itemsAdded });
297
+
298
+ // Update UI
299
+ updateCartCount();
300
+ showUpsellRecommendations(identifiers);
301
+ });
302
+ ```
303
+
304
+ #### **4. Create Reusable Event Handlers**
305
+ ```javascript
306
+ // ✅ Create reusable handlers
307
+ const handlePromoSuccess = (event) => {
308
+ const { applied, discountAmount, newTotal } = event.detail.data;
309
+ showSuccessMessage(`Discount applied! Saved: $${discountAmount}`);
310
+ updatePriceDisplay(newTotal);
311
+ trackPromoSuccess(discountAmount);
312
+ };
313
+
314
+ const handlePromoFailure = (event) => {
315
+ const { attempted, error } = event.detail.data;
316
+ showErrorMessage('Promo code could not be applied');
317
+ trackPromoFailure();
318
+ };
319
+
320
+ // Use for both cart and checkout
321
+ window.addEventListener('lce:actions.cart_promo_code_applied', handlePromoSuccess);
322
+ window.addEventListener('lce:actions.checkout_promo_code_applied', handlePromoSuccess);
323
+ window.addEventListener('lce:actions.cart_promo_code_failed', handlePromoFailure);
324
+ window.addEventListener('lce:actions.checkout_promo_code_failed', handlePromoFailure);
325
+ ```
326
+
327
+ ### 🛒 Cart Control (`actions.cart`)
328
+
329
+ **What it does**: Control the shopping cart to optimize conversions
330
+
331
+ #### Show/Hide Cart
332
+ ```javascript
333
+ // Show cart at strategic moments
334
+ actions.cart.openCart(); // Open cart
335
+ actions.cart.closeCart(); // Close cart
336
+ actions.cart.toggleCart(); // Toggle visibility
337
+
338
+ // Example: Open cart automatically after second product add
339
+ let addToCartCount = 0;
340
+ window.addEventListener('lce:actions.product_add_to_cart', function() {
341
+ addToCartCount++;
342
+ if (addToCartCount === 2) {
343
+ actions.cart.openCart(); // "Look what you've got!"
344
+ }
345
+ });
346
+ ```
347
+
348
+ #### Programmatically Add Products to Cart
349
+ ```javascript
350
+ // Add products directly to cart without product component
351
+ await actions.cart.addProduct([
352
+ {
353
+ identifier: 'product-123', // Product UPC, ID, or Salsify grouping
354
+ fulfillmentType: 'shipping', // or 'onDemand'
355
+ quantity: 2
356
+ },
357
+ {
358
+ identifier: 'product-456',
359
+ fulfillmentType: 'onDemand',
360
+ quantity: 1
361
+ }
362
+ ], true); // Optional: open cart after adding (default: false)
363
+
364
+ // Listen for success/failure feedback
365
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
366
+ const { itemsAdded, identifiers } = event.detail.data;
367
+ console.log(`✅ Added ${itemsAdded} products:`, identifiers);
368
+ showSuccessMessage('Products added to cart!');
369
+ });
370
+
371
+ window.addEventListener('lce:actions.cart_product_add_failed', function(event) {
372
+ const { identifiers, error } = event.detail.data;
373
+ console.log(`❌ Failed to add products:`, identifiers, error);
374
+ showErrorMessage('Could not add products. Please try again.');
375
+ });
376
+ ```
377
+
378
+ **Real Business Use**: Perfect for "Buy Now" buttons, quick add flows, bundle purchases, or any scenario where you want to add products without displaying the full product component first.
379
+
380
+ #### Check Cart Contents
381
+ ```javascript
382
+ // See what's in the cart to make smart decisions
383
+ const cart = actions.cart.getDetails();
384
+
385
+ if (cart.total > 100) {
386
+ // Offer free shipping
387
+ showFreeShippingBanner();
388
+ }
389
+
390
+ if (cart.items.length === 0) {
391
+ // Cart is empty - show recommendations
392
+ showPopularProducts();
393
+ }
394
+ ```
395
+
396
+ #### Add Products to Cart with Event Feedback
397
+ ```javascript
398
+ // Add products programmatically
399
+ await actions.cart.addProduct([{
400
+ identifier: 'product-123',
401
+ fulfillmentType: 'shipping',
402
+ quantity: 1
403
+ }]);
404
+
405
+ // Listen for success
406
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
407
+ const { itemsAdded, identifiers } = event.detail.data;
408
+ showMessage(`✅ Added ${itemsAdded} items to cart!`);
409
+
410
+ // Track conversion
411
+ analytics.track('Products Added to Cart', {
412
+ items: identifiers,
413
+ count: itemsAdded
414
+ });
415
+
416
+ // Maybe show related products
417
+ showRelatedProducts(identifiers);
418
+ });
419
+
420
+ // Listen for failures
421
+ window.addEventListener('lce:actions.cart_product_add_failed', function(event) {
422
+ const { identifiers, error } = event.detail.data;
423
+ showErrorMessage('Could not add some products. Please try again.');
424
+
425
+ // Log for debugging
426
+ console.error('Failed to add products:', identifiers, error);
427
+
428
+ // Show alternatives
429
+ showAlternativeProducts(identifiers);
430
+ });
431
+
432
+ // Add bundles or cross-sells
433
+ await actions.cart.addProduct([
434
+ { identifier: 'phone-123', fulfillmentType: 'shipping', quantity: 1 },
435
+ { identifier: 'case-456', fulfillmentType: 'shipping', quantity: 1 },
436
+ { identifier: 'charger-789', fulfillmentType: 'shipping', quantity: 1 }
437
+ ]);
438
+ ```
439
+
440
+ #### Apply Discounts with Event Feedback
441
+ ```javascript
442
+ // Apply promo codes automatically
443
+ await actions.cart.applyPromoCode('WELCOME10');
444
+
445
+ // Listen for success
446
+ window.addEventListener('lce:actions.cart_promo_code_applied', function(event) {
447
+ const { applied, discountAmount, newTotal } = event.detail.data;
448
+ showMessage(`Welcome discount applied! You saved $${discountAmount}!`);
449
+ updateCartDisplay(newTotal);
450
+
451
+ // Track successful promo usage
452
+ analytics.track('Promo Applied', { discount: discountAmount });
453
+ });
454
+
455
+ // Listen for failure and try fallback
456
+ window.addEventListener('lce:actions.cart_promo_code_failed', function(event) {
457
+ const { attempted, error } = event.detail.data;
458
+ console.log('First promo failed, trying backup promo');
459
+
460
+ // Try another strategy
461
+ actions.cart.applyPromoCode('FIRSTTIME');
462
+ });
463
+
464
+ // Remove promo code with feedback
465
+ await actions.cart.removePromoCode();
466
+
467
+ // Listen for removal confirmation
468
+ window.addEventListener('lce:actions.cart_promo_code_removed', function(event) {
469
+ const { applied } = event.detail.data;
470
+ showMessage("Previous promo removed. Applying better discount...");
471
+
472
+ // Apply better deal
473
+ actions.cart.applyPromoCode('BETTER20');
474
+ });
475
+ ```
476
+
477
+ #### Reset Cart
478
+ ```javascript
479
+ // Clear cart for fresh start
480
+ await actions.cart.resetCart();
481
+ ```
482
+
483
+ **Real Business Uses:**
484
+ - **Smart Cart Timing**: Open cart when customer is most likely to buy
485
+ - **Bundle Sales**: Add complementary products automatically
486
+ - **Discount Strategy**: Apply best available discount automatically
487
+ - **Abandonment Prevention**: Show cart contents at right moment
488
+
489
+ ### 💳 Checkout Control (`actions.checkout`)
490
+
491
+ **What it does**: Streamline the checkout process to maximize conversions
492
+
493
+ #### Control Checkout Flow
494
+ ```javascript
495
+ // Open checkout at the perfect moment
496
+ actions.checkout.openCheckout();
497
+ actions.checkout.closeCheckout();
498
+ actions.checkout.toggleCheckout();
499
+
500
+ // Example: Open checkout automatically for high-value carts
501
+ const cart = actions.cart.getDetails();
502
+ if (cart.total > 500) {
503
+ actions.checkout.openCheckout(); // VIP checkout experience
504
+ }
505
+ ```
506
+
507
+ #### Programmatically Add Products to Checkout
508
+ ```javascript
509
+ // Add products directly to checkout (bypasses cart, goes straight to checkout)
510
+ await actions.checkout.addProduct([
511
+ {
512
+ identifier: 'product-123',
513
+ fulfillmentType: 'shipping',
514
+ quantity: 1
515
+ }
516
+ ], true); // Optional: open checkout after adding (default: false)
517
+
518
+ // Listen for success/failure feedback
519
+ window.addEventListener('lce:actions.checkout_product_add_success', function(event) {
520
+ const { itemsAdded, identifiers } = event.detail.data;
521
+ console.log(`✅ Added ${itemsAdded} products to checkout:`, identifiers);
522
+ showSuccessMessage('Ready for checkout!');
523
+ });
524
+
525
+ window.addEventListener('lce:actions.checkout_product_add_failed', function(event) {
526
+ const { identifiers, error } = event.detail.data;
527
+ console.log(`❌ Failed to add products to checkout:`, identifiers, error);
528
+ showErrorMessage('Could not proceed to checkout. Please try again.');
529
+ });
530
+ ```
531
+
532
+ **Real Business Use**: Perfect for "Buy Now" buttons, one-click purchasing, express checkout flows, or any scenario where you want to skip the cart and go straight to checkout.
533
+
534
+ #### Pre-fill Customer Information
535
+ ```javascript
536
+ // Speed up checkout by pre-filling known information
537
+ actions.checkout.updateCustomerInfo({
538
+ firstName: 'John',
539
+ lastName: 'Doe',
540
+ email: 'john@example.com',
541
+ phone: '+1234567890'
542
+ });
543
+
544
+ // Pre-fill billing from your CRM
545
+ const customerData = getCRMData(customerId);
546
+ actions.checkout.updateBillingInfo({
547
+ firstName: customerData.firstName,
548
+ lastName: customerData.lastName,
549
+ street1: customerData.address,
550
+ city: customerData.city,
551
+ state: customerData.state,
552
+ zipCode: customerData.zip
553
+ });
554
+ ```
555
+
556
+ #### Smart Discount Application with Event Feedback
557
+ ```javascript
558
+ // Apply the best available discount automatically
559
+ const customer = getCurrentCustomer();
560
+
561
+ // Set up event listeners first
562
+ window.addEventListener('lce:actions.checkout_promo_code_applied', function(event) {
563
+ const { applied, discountAmount, newTotal } = event.detail.data;
564
+ showCheckoutSuccess(`Discount applied! You saved $${discountAmount}`);
565
+ updateCheckoutTotal(newTotal);
566
+
567
+ // Track successful conversion optimization
568
+ analytics.track('Checkout Discount Applied', { discount: discountAmount });
569
+ });
570
+
571
+ window.addEventListener('lce:actions.checkout_promo_code_failed', function(event) {
572
+ const { attempted, error } = event.detail.data;
573
+ console.log('Promo code failed:', error);
574
+
575
+ // Try fallback strategies or notify user
576
+ showCheckoutError('Discount could not be applied');
577
+ });
578
+
579
+ // Apply gift card event listeners
580
+ window.addEventListener('lce:actions.checkout_gift_card_applied', function(event) {
581
+ const { applied, newTotal } = event.detail.data;
582
+ showCheckoutSuccess('Gift card applied to your order!');
583
+ updateCheckoutTotal(newTotal);
584
+ });
585
+
586
+ window.addEventListener('lce:actions.checkout_gift_card_failed', function(event) {
587
+ const { attempted, error } = event.detail.data;
588
+ showCheckoutError('Gift card could not be applied. Please check and try again.');
589
+ });
590
+
591
+ // Now apply the discounts
592
+ if (customer.isFirstTime) {
593
+ await actions.checkout.applyPromoCode('WELCOME20');
594
+ } else if (customer.isVIP) {
595
+ await actions.checkout.applyPromoCode('VIP15');
596
+ }
597
+
598
+ // Apply gift cards automatically
599
+ if (customer.hasGiftCard) {
600
+ await actions.checkout.applyGiftCard(customer.giftCardCode);
601
+ }
602
+ ```
603
+
604
+ #### Optimize Checkout Options
605
+ ```javascript
606
+ // Smart defaults based on customer behavior
607
+ if (customer.prefersGifts) {
608
+ await actions.checkout.toggleIsGift(true);
609
+ }
610
+
611
+ // Auto-enable marketing for engaged customers
612
+ if (customer.engagementScore > 8) {
613
+ actions.checkout.toggleMarketingPreferences('canEmail', true);
614
+ actions.checkout.toggleMarketingPreferences('canSms', true);
615
+ }
616
+
617
+ // Smart billing address handling
618
+ if (customer.billingAddress === customer.shippingAddress) {
619
+ await actions.checkout.toggleBillingSameAsShipping(true);
620
+ }
621
+ ```
622
+
623
+ #### Get Checkout Details
624
+ ```javascript
625
+ // Get safe, non-sensitive checkout information
626
+ const checkout = actions.checkout.getDetails();
627
+
628
+ // Check checkout status and totals
629
+ console.log('Checkout total:', checkout.amounts.total);
630
+ console.log('Items in checkout:', checkout.itemCount);
631
+ console.log('Is gift order:', checkout.isGift);
632
+ console.log('Has age verification:', checkout.hasAgeVerify);
633
+ console.log('Has promo code applied:', checkout.hasPromoCode);
634
+ console.log('Has gift cards applied:', checkout.hasGiftCards);
635
+
636
+ // Make business decisions based on checkout state
637
+ if (checkout.amounts.total > 1000) {
638
+ // High-value order - offer white glove service
639
+ showWhiteGloveService();
640
+ }
641
+
642
+ if (checkout.itemCount > 5) {
643
+ // Large order - offer bulk discount
644
+ await actions.checkout.applyPromoCode('BULK15');
645
+ }
646
+
647
+ if (checkout.isGift && checkout.hasAgeVerify) {
648
+ // Gift order with age verification - show age verification requirements
649
+ showAgeVerificationNotice();
650
+ }
651
+
652
+ if (!checkout.hasPromoCode) {
653
+ // No promo code applied - suggest best available discount
654
+ suggestBestPromoCode(checkout.amounts.total);
655
+ }
656
+
657
+ if (checkout.hasGiftCards && checkout.amounts.total < 50) {
658
+ // Gift cards applied but low total - suggest adding more items
659
+ showUpsellSuggestions();
660
+ }
661
+
662
+ // Available checkout details (all non-sensitive):
663
+ // - amounts: { total, subtotal, shipping, discounts, tax, etc. }
664
+ // - items: { [itemId]: { name, price, quantity, brand, etc. } }
665
+ // - isGift: boolean, hasAgeVerify: boolean
666
+ // - hasPromoCode: boolean, hasGiftCards: boolean
667
+ // - itemCount: number
668
+ ```
669
+
670
+ **Real Business Uses:**
671
+ - **Reduce Checkout Time**: Pre-fill known customer information
672
+ - **Maximize Discounts**: Apply best available offer automatically
673
+ - **VIP Experience**: Different checkout flow for high-value customers
674
+ - **Gift Optimization**: Auto-enable gift options for gift buyers
675
+
676
+ ## Real-World Business Scenarios
677
+
678
+ ### 🎯 Scenario 1: VIP Customer Experience
679
+ ```javascript
680
+ // Detect VIP customers and give them special treatment
681
+ async function handleVIPCustomer(customer) {
682
+ if (customer.totalSpent > 5000) {
683
+ // Pre-fill everything for express checkout
684
+ actions.checkout.updateCustomerInfo(customer.profile);
685
+ actions.checkout.updateBillingInfo(customer.billingAddress);
686
+
687
+ // Apply VIP discount automatically
688
+ await actions.checkout.applyPromoCode('VIP20');
689
+
690
+ // Enable premium features
691
+ actions.checkout.toggleMarketingPreferences('canEmail', true);
692
+
693
+ // Open checkout immediately - no cart friction
694
+ actions.checkout.openCheckout();
695
+
696
+ showVIPMessage("Welcome back! Your VIP checkout is ready.");
697
+ }
698
+ }
699
+ ```
700
+
701
+ ### 🛍️ Scenario 2: Smart Bundle Sales
702
+ ```javascript
703
+ // Automatically add complementary products
704
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
705
+ const product = event.detail.data;
706
+
707
+ // If they bought a laptop, add accessories
708
+ if (product.category === 'laptops') {
709
+ const cart = actions.cart.getDetails();
710
+ const hasAccessories = cart.items.some(item => item.category === 'accessories');
711
+
712
+ if (!hasAccessories) {
713
+ // Add recommended accessories bundle
714
+ await actions.cart.addProduct([
715
+ { identifier: 'laptop-case-123', fulfillmentType: 'shipping', quantity: 1 },
716
+ { identifier: 'wireless-mouse-456', fulfillmentType: 'shipping', quantity: 1 }
717
+ ]);
718
+
719
+ showMessage("We've added recommended accessories to your cart!");
720
+ }
721
+ }
722
+ });
723
+ ```
724
+
725
+ ### 📱 Scenario 3: Mobile-First Quick Checkout
726
+ ```javascript
727
+ // Streamlined mobile experience
728
+ function mobileQuickCheckout() {
729
+ if (isMobileDevice()) {
730
+ // Get customer data from their last purchase
731
+ const lastOrder = getLastOrder();
732
+
733
+ if (lastOrder) {
734
+ // Pre-fill everything
735
+ actions.checkout.updateCustomerInfo(lastOrder.customer);
736
+ actions.checkout.updateBillingInfo(lastOrder.billing);
737
+
738
+ // Skip cart, go straight to checkout
739
+ actions.checkout.openCheckout();
740
+
741
+ showMessage("Express checkout ready! Same info as last time.");
742
+ }
743
+ }
744
+ }
745
+
746
+ // Trigger on Add to Cart for mobile users
747
+ window.addEventListener('lce:actions.product_add_to_cart', function() {
748
+ if (isMobileDevice() && isReturningCustomer()) {
749
+ mobileQuickCheckout();
750
+ }
751
+ });
752
+ ```
753
+
754
+ ### 💰 Scenario 4: Dynamic Pricing & Promotions
755
+ ```javascript
756
+ // Apply the best available discount
757
+ async function optimizeCheckout() {
758
+ const cart = actions.cart.getDetails();
759
+ const customer = getCurrentCustomer();
760
+
761
+ // Try different discount strategies
762
+ const discountStrategies = [
763
+ { code: 'BULK25', minTotal: 200 },
764
+ { code: 'RETURNING15', condition: customer.orderCount > 1 },
765
+ { code: 'FIRSTTIME10', condition: customer.orderCount === 0 },
766
+ { code: 'SEASONAL20', condition: isHolidaySeason() }
767
+ ];
768
+
769
+ for (const strategy of discountStrategies) {
770
+ if (strategy.minTotal && cart.total >= strategy.minTotal) {
771
+ try {
772
+ await actions.checkout.applyPromoCode(strategy.code);
773
+ break; // Success, stop trying
774
+ } catch (error) {
775
+ continue; // Try next strategy
776
+ }
777
+ } else if (strategy.condition) {
778
+ try {
779
+ await actions.checkout.applyPromoCode(strategy.code);
780
+ break;
781
+ } catch (error) {
782
+ continue;
783
+ }
784
+ }
785
+ }
786
+ }
787
+ ```
788
+
789
+ ### 🎁 Scenario 5: Holiday Gift Flow
790
+ ```javascript
791
+ // Optimize for gift purchases during holidays
792
+ async function holidayGiftOptimization() {
793
+ if (isHolidaySeason()) {
794
+ // Auto-enable gift features
795
+ actions.checkout.toggleIsGift(true);
796
+
797
+ // Pre-populate gift message templates
798
+ const giftMessages = [
799
+ "Happy Holidays!",
800
+ "Merry Christmas!",
801
+ "With love and best wishes"
802
+ ];
803
+
804
+ showGiftMessageSuggestions(giftMessages);
805
+
806
+ // Suggest gift wrapping
807
+ showGiftWrappingOption();
808
+
809
+ // Apply holiday shipping discount
810
+ await actions.checkout.applyPromoCode('FREEHOLIDAYSHIP');
811
+ }
812
+ }
813
+
814
+ // Trigger during checkout
815
+ window.addEventListener('lce:actions.checkout_opened', holidayGiftOptimization);
816
+ ```
817
+
818
+ ### 📍 Scenario 6: Smart Address Auto-Complete
819
+ ```javascript
820
+ // Use Google Places to streamline address entry
821
+ async function smartAddressFlow() {
822
+ const customer = getCurrentCustomer();
823
+
824
+ // For returning customers, use their recent addresses
825
+ if (customer.recentPlacesIds && customer.recentPlacesIds.length > 0) {
826
+ try {
827
+ // Auto-set their most recent address
828
+ await actions.address.setAddressByPlacesId(customer.recentPlacesIds[0]);
829
+
830
+ showMessage("We've set your address to your recent location. Change it if needed!");
831
+
832
+ // Show other recent addresses as quick options
833
+ showRecentAddressOptions(customer.recentPlacesIds.slice(1, 4));
834
+
835
+ } catch (error) {
836
+ // Fallback to manual entry
837
+ showAddressForm();
838
+ }
839
+ }
840
+
841
+ // For location-aware features
842
+ if (customer.hasLocationPermission) {
843
+ const nearbyBusinesses = await getNearbyBusinessPlaces();
844
+
845
+ // If they're near a business location, suggest it
846
+ if (nearbyBusinesses.length > 0) {
847
+ showMessage("We found you're near our store location. Use it for pickup?");
848
+
849
+ document.getElementById('use-store-address').onclick = async () => {
850
+ await actions.address.setAddressByPlacesId(nearbyBusinesses[0].placeId);
851
+ showMessage("Store pickup address set!");
852
+ };
853
+ }
854
+ }
855
+ }
856
+
857
+ // Use when address is updated
858
+ smartAddressFlow();
859
+
860
+ // Quick address selection from saved places
861
+ async function useQuickAddress(placeId, displayName) {
862
+ try {
863
+ await actions.address.setAddressByPlacesId(placeId);
864
+ showMessage(`Address set to ${displayName}`);
865
+
866
+ // Automatically proceed to shipping options
867
+ showShippingOptionsForAddress();
868
+
869
+ } catch (error) {
870
+ showMessage("That address is no longer available. Please select another.");
871
+ }
872
+ }
873
+ ```
874
+
875
+ ### 🏢 Scenario 7: Custom Address Form Integration
876
+ ```javascript
877
+ // Integrate with your own address forms and geocoding services
878
+ async function customAddressFormFlow() {
879
+ const customer = getCurrentCustomer();
880
+
881
+ // If customer has saved addresses in your database (not Google Places)
882
+ if (customer.savedAddresses && customer.savedAddresses.length > 0) {
883
+ showSavedAddressOptions(customer.savedAddresses);
884
+ }
885
+
886
+ // Handle custom form submission
887
+ document.getElementById('custom-address-form').addEventListener('submit', async (event) => {
888
+ event.preventDefault();
889
+
890
+ const formData = new FormData(event.target);
891
+ const addressData = {
892
+ one: formData.get('street'),
893
+ two: formData.get('apartment') || '',
894
+ city: formData.get('city'),
895
+ state: formData.get('state'),
896
+ zip: formData.get('zip'),
897
+ country: formData.get('country') || 'United States'
898
+ };
899
+
900
+ try {
901
+ // Use your geocoding service to get coordinates
902
+ const coordinates = await yourGeocodingService.geocode(
903
+ `${addressData.one}, ${addressData.city}, ${addressData.state} ${addressData.zip}`
904
+ );
905
+
906
+ // Set the address manually
907
+ await actions.address.setAddressManually(addressData, {
908
+ lat: coordinates.latitude,
909
+ long: coordinates.longitude
910
+ });
911
+
912
+ showMessage("Your address has been set successfully!");
913
+
914
+ // Save to your database for future use
915
+ saveCustomerAddress(customer.id, addressData, coordinates);
916
+
917
+ } catch (geocodeError) {
918
+ showError("Could not find coordinates for this address. Please verify and try again.");
919
+ }
920
+ });
921
+
922
+ // Handle saved address selection
923
+ async function useSavedAddress(savedAddress) {
924
+ try {
925
+ await actions.address.setAddressManually(
926
+ {
927
+ one: savedAddress.street,
928
+ two: savedAddress.apartment || '',
929
+ city: savedAddress.city,
930
+ state: savedAddress.state,
931
+ zip: savedAddress.zip,
932
+ country: savedAddress.country || 'United States'
933
+ },
934
+ {
935
+ lat: savedAddress.coordinates.lat,
936
+ long: savedAddress.coordinates.long
937
+ }
938
+ );
939
+
940
+ showMessage(`Address set to your saved location: ${savedAddress.nickname}`);
941
+
942
+ } catch (error) {
943
+ showError("There was an issue with your saved address. Please try entering it again.");
944
+ }
945
+ }
946
+ }
947
+
948
+ // Import addresses from legacy system
949
+ async function importLegacyAddresses() {
950
+ const legacyAddresses = await getLegacyCustomerAddresses();
951
+
952
+ for (const address of legacyAddresses) {
953
+ try {
954
+ // Convert legacy format to Elements format
955
+ const elementsAddress = {
956
+ one: address.address_line_1,
957
+ two: address.address_line_2 || '',
958
+ city: address.city_name,
959
+ state: address.state_code,
960
+ zip: address.postal_code,
961
+ country: address.country_name || 'United States'
962
+ };
963
+
964
+ const coordinates = {
965
+ lat: address.latitude,
966
+ long: address.longitude
967
+ };
968
+
969
+ // Set address in Elements system
970
+ await actions.address.setAddressManually(elementsAddress, coordinates);
971
+
972
+ console.log(`Successfully imported address for customer ${address.customer_id}`);
973
+
974
+ } catch (error) {
975
+ console.error(`Failed to import address for customer ${address.customer_id}:`, error);
976
+ }
977
+ }
978
+ }
979
+
980
+ // Handle international addresses that might not be in Google Places
981
+ async function handleInternationalAddress(internationalAddressData) {
982
+ try {
983
+ // Use an international geocoding service
984
+ const coordinates = await internationalGeocodingService.geocode(
985
+ internationalAddressData.fullAddress,
986
+ internationalAddressData.country
987
+ );
988
+
989
+ await actions.address.setAddressManually(
990
+ {
991
+ one: internationalAddressData.addressLine1,
992
+ two: internationalAddressData.addressLine2 || '',
993
+ city: internationalAddressData.city,
994
+ state: internationalAddressData.region, // Province, state, etc.
995
+ zip: internationalAddressData.postalCode,
996
+ country: internationalAddressData.country
997
+ },
998
+ coordinates
999
+ );
1000
+
1001
+ showMessage("International address set successfully!");
1002
+
1003
+ } catch (error) {
1004
+ showError("Could not process this international address. Please contact support.");
1005
+ }
1006
+ }
1007
+ ```
1008
+
1009
+ ## Implementation Strategy
1010
+
1011
+ ### 🚀 Quick Start (5-Minute Setup)
1012
+
1013
+ **Step 1: Choose Your Goal**
1014
+ - Increase conversions? → Focus on cart and checkout actions
1015
+ - Reduce abandonment? → Use pre-filling and automation
1016
+ - Boost sales? → Implement bundle and upsell logic
1017
+ - Improve UX? → Create smart defaults and express flows
1018
+
1019
+ **Step 2: Start Simple**
1020
+ ```javascript
1021
+ // Copy this basic template and customize
1022
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
1023
+ const product = event.detail.data;
1024
+
1025
+ // Your business logic here
1026
+ if (product.price > 100) {
1027
+ // High-value product - offer premium support
1028
+ showPremiumSupportOffer();
1029
+ }
1030
+
1031
+ // Maybe add complementary products
1032
+ if (product.category === 'phones') {
1033
+ await actions.cart.addProduct([{
1034
+ identifier: 'phone-case-recommended',
1035
+ fulfillmentType: 'shipping',
1036
+ quantity: 1
1037
+ }]);
1038
+ }
1039
+ });
1040
+ ```
1041
+
1042
+ **Step 3: Test & Iterate**
1043
+ 1. Add one action at a time
1044
+ 2. Test with real user flows
1045
+ 3. Monitor conversion impact
1046
+ 4. Adjust based on results
1047
+
1048
+ ### 💡 Pro Implementation Tips
1049
+
1050
+ #### Combine Actions with Events
1051
+ ```javascript
1052
+ // Listen for events, respond with actions
1053
+ window.addEventListener('lce:actions.cart_opened', function() {
1054
+ const cart = actions.cart.getDetails();
1055
+
1056
+ // If cart value is close to free shipping threshold
1057
+ if (cart.total > 45 && cart.total < 50) {
1058
+ showFreeShippingUpsell();
1059
+ }
1060
+ });
1061
+
1062
+ // Pre-fill checkout for VIP customers
1063
+ window.addEventListener('lce:actions.checkout_opened', async function() {
1064
+ const customer = getCurrentCustomer();
1065
+
1066
+ if (customer.isVIP) {
1067
+ actions.checkout.updateCustomerInfo(customer.profile);
1068
+ await actions.checkout.applyPromoCode('VIP20');
1069
+ }
1070
+ });
1071
+ ```
1072
+
1073
+ #### Chain Actions for Workflows
1074
+ ```javascript
1075
+ // Create complete automated flows
1076
+ async function expressCheckoutFlow(productId, customerId) {
1077
+ // 1. Add product to cart
1078
+ await actions.cart.addProduct([{
1079
+ identifier: productId,
1080
+ fulfillmentType: 'shipping',
1081
+ quantity: 1
1082
+ }]);
1083
+
1084
+ // 2. Pre-fill customer data
1085
+ const customer = getCustomerData(customerId);
1086
+ actions.checkout.updateCustomerInfo(customer);
1087
+
1088
+ // 3. Apply best discount
1089
+ await actions.checkout.applyPromoCode(getBestDiscountFor(customer));
1090
+
1091
+ // 4. Open checkout
1092
+ actions.checkout.openCheckout();
1093
+
1094
+ showMessage("Express checkout ready! Review and complete your order.");
1095
+ }
1096
+ ```
1097
+
1098
+ #### Smart Error Handling
1099
+ ```javascript
1100
+ // Graceful degradation
1101
+ async function smartAddToCart(productId) {
1102
+ try {
1103
+ await actions.cart.addProduct([{
1104
+ identifier: productId,
1105
+ fulfillmentType: 'shipping',
1106
+ quantity: 1
1107
+ }]);
1108
+
1109
+ showSuccessMessage("Added to cart!");
1110
+ } catch (error) {
1111
+ // Fallback - maybe the product is out of stock
1112
+ showMessage("This item isn't available, but here are similar options:");
1113
+ showAlternativeProducts(productId);
1114
+ }
1115
+ }
1116
+ ```
1117
+
1118
+ ## Common Integration Patterns
1119
+
1120
+ ### 🎯 **Pattern 1: Behavioral Triggers**
1121
+ ```javascript
1122
+ // Trigger actions based on customer behavior
1123
+ let pageViewCount = 0;
1124
+ let timeOnPage = 0;
1125
+
1126
+ // Track engagement
1127
+ setInterval(() => timeOnPage++, 1000);
1128
+
1129
+ // Act based on engagement
1130
+ if (timeOnPage > 60 && pageViewCount > 3) {
1131
+ // Engaged user - show special offer
1132
+ await actions.cart.applyPromoCode('ENGAGED15');
1133
+ showSpecialOffer();
1134
+ }
1135
+ ```
1136
+
1137
+ ### 🛍️ **Pattern 2: Cross-Sell Automation**
1138
+ ```javascript
1139
+ // Automatic product recommendations
1140
+ const crossSellRules = {
1141
+ 'laptop': ['laptop-case', 'wireless-mouse', 'usb-hub'],
1142
+ 'phone': ['phone-case', 'screen-protector', 'wireless-charger'],
1143
+ 'camera': ['memory-card', 'camera-bag', 'tripod']
1144
+ };
1145
+
1146
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
1147
+ const product = event.detail.data;
1148
+ const recommendations = crossSellRules[product.category];
1149
+
1150
+ if (recommendations) {
1151
+ // Add first recommendation automatically
1152
+ await actions.cart.addProduct([{
1153
+ identifier: recommendations[0],
1154
+ fulfillmentType: 'shipping',
1155
+ quantity: 1
1156
+ }]);
1157
+
1158
+ showMessage(`We've added ${recommendations[0]} - customers who bought ${product.name} love this combo!`);
1159
+ }
1160
+ });
1161
+ ```
1162
+
1163
+ ### 💰 **Pattern 3: Dynamic Pricing**
1164
+ ```javascript
1165
+ // Apply best available discount automatically
1166
+ async function optimizePricing() {
1167
+ const cart = actions.cart.getDetails();
1168
+ const customer = getCurrentCustomer();
1169
+ const now = new Date();
1170
+
1171
+ // Time-based discounts
1172
+ if (now.getHours() < 10) {
1173
+ await actions.cart.applyPromoCode('EARLYBIRD10');
1174
+ }
1175
+
1176
+ // Volume discounts
1177
+ else if (cart.total > 200) {
1178
+ await actions.cart.applyPromoCode('BULK20');
1179
+ }
1180
+
1181
+ // Customer-specific discounts
1182
+ else if (customer.orderCount === 0) {
1183
+ await actions.cart.applyPromoCode('WELCOME15');
1184
+ }
1185
+
1186
+ // Seasonal discounts
1187
+ else if (isBlackFriday()) {
1188
+ await actions.cart.applyPromoCode('BLACKFRIDAY25');
1189
+ }
1190
+ }
1191
+ ```
1192
+
1193
+ ## Business ROI Tracking
1194
+
1195
+ Track the impact of your action implementations:
1196
+
1197
+ ```javascript
1198
+ // Track business metrics
1199
+ const metrics = {
1200
+ automatedDiscountsApplied: 0,
1201
+ bundleSalesCreated: 0,
1202
+ expressCheckoutsCompleted: 0,
1203
+ cartAbandonmentsPrevented: 0
1204
+ };
1205
+
1206
+ // Track when actions create business value
1207
+ window.addEventListener('lce:actions.cart_updated', function() {
1208
+ if (wasDiscountAppliedAutomatically()) {
1209
+ metrics.automatedDiscountsApplied++;
1210
+ }
1211
+ });
1212
+
1213
+ window.addEventListener('lce:actions.checkout_submit_completed', function() {
1214
+ if (wasExpressCheckout()) {
1215
+ metrics.expressCheckoutsCompleted++;
1216
+ }
1217
+ });
1218
+
1219
+ // Send metrics to your analytics
1220
+ setInterval(() => {
1221
+ sendBusinessMetrics(metrics);
1222
+ }, 60000); // Every minute
1223
+ ```
1224
+
1225
+ ## Getting Started Today
1226
+
1227
+ 1. **Pick ONE scenario** from the examples above that matches your biggest business need
1228
+ 2. **Copy the code** and customize it for your products/customers
1229
+ 3. **Test it** with a small group first
1230
+ 4. **Measure the impact** on your conversion rates
1231
+ 5. **Scale up** what works, iterate on what doesn't
1232
+
1233
+ **Remember**: The power is in combining the right actions with the right business logic for YOUR specific use case. Start simple, then build more sophisticated flows as you see results!
1234
+
1235
+ Need help implementing? The actions work automatically once LiquidCommerce Elements are installed - just add your business logic and watch conversions improve!