pageState == 'cancel')pageState == 'error'){
"pageState": null,
"status": "Start",
"checkout": {
"netValue": 0,
"netValueText": "0.00 €",
"vatValue": 0,
"vatValueText": "0.00 €",
"finalPrice": 0,
"finalPriceText": "0.00 €",
"billingAddress": {
"id": "Address Id",
"name": "Sample name",
"firstName": "Sample first name",
"lastName": "Sample last name",
"phoneNumber": "Sample phone",
"address1": "Sample address",
"city": "Sample city",
"state": "Sample state",
"country": "Sample country",
"countryCode": "XX",
"postalCode": "Sample postal code",
"email": "sample@example.com"
},
"shippingAddress": {
"id": "Address Id",
"name": "Sample name",
"firstName": "Sample first name",
"lastName": "Sample last name",
"phoneNumber": "Sample phone",
"address1": "Sample address",
"city": "Sample city",
"state": "Sample state",
"country": "Sample country",
"countryCode": "XX",
"postalCode": "Sample postal code",
"email": "sample@example.com"
},
"cartItems": [
{
"id": "Cart line Id",
"productId": "Product Id",
"productVariantId": "Variant Id",
"quantity": 1,
"productTitle": "Sample product title",
"link": "sample-product-link",
"imageLink": "https://example.com/sample",
"finalPrice": 0,
"finalPriceText": "0.00 €",
"originalPrice": 0,
"originalPriceText": "0.00 €",
"subTotalNetValue": 0,
"subTotalPrice": 0,
"subTotalVatValue": 0
},
...
],
"pricingBeforeDiscount": {
"netValue": 0,
"netValueText": "0.00 €",
"vatValue": 0,
"vatValueText": "0.00 €",
"finalPrice": 0,
"finalPriceText": "0.00 €"
}
},
"carriers": {
"globalConfig": {
"freeAtPrice": 0,
"freeAtPriceText": "0.00 €",
"freeAtWeightText": " kg"
},
"list": [
{
"carrierId": "Carrier Id",
"carrierCode": "sample",
"title": "Sample carrier title",
"type": "Manual",
"netPrice": 0,
"totalAmount": 0,
"totalAmountText": "0.00 €",
"vatLines": []
},
...
]
},
"payments": [
{
"provider": "Sample provider",
"providerId": "Provider Id",
"name": "Sample payment name",
"message": "<p>Sample message</p>",
"installmentsEnabled": false
},
...
],
"deliveryZonesEnabled": true,
"countriesList": [
{
"code": "XX",
"name": "Sample country name"
},
...
],
"addresses": [
{
"name": "",
"isSelectable": true,
"address": {
"id": "Address Id",
"firstName": "Sample first name",
"lastName": "Sample last name",
"phoneNumber": "Sample phone",
"address1": "Sample address",
"city": "Sample city",
"state": "Sample state",
"country": "Sample country",
"countryCode": "XX",
"postalCode": "Sample postal code",
"email": "sample@example.com"
},
"requiresInvoice": false
},
...
],
"name": "Checkout",
"view": "Default",
"section": "SectionA",
"settings": {
"id": "Component Id",
"section": "SectionA",
"type": "NoirCheckout",
"name": "Checkout",
"configuredInContentApi": true,
"view": "Default",
"displayName": "Sample display name",
"cssClass": ""
},
"translations": {
"completeOrder": "Sample translation",
"backToCart": "Sample translation",
"orderSummary": "Sample translation",
"...": "..."
}
}settings.idcomp-{{ id }}checkoutsettings.cssClass(UNDEFINED).pageStatecancel → Payment cancelled/failed pageerror → Order creation failed pageaddresses, countriesListcarriers, paymentsx-data='checkoutdefault.initComponent({{ checkout | serialize | escape }})'initComponent(...) returns the Alpine component state + methods for:servicesreusabledefault.updateCheckout(...))servicesreusabledefault.proceedToCheckout(...))initComponent(checkoutData)checkout object and returns an Alpine object containing:init()createObserver(), checkValidity()handlePoints(...), handleCoupons()sendSelectEvent(...), completeCheckout(...)checkoutData is expected to be a valid object; if it’s missing fields, downstream methods can break, so avoid passing partial objects.init() (async)checkoutToken cookie:const checkoutCookie = getCookie("checkoutToken");No checkout token found in cookies.)localStorage.setItem("checkoutToken", checkoutCookie)cartData state field):beginCheckout with prepared itemscreateObserver()this.checkout = this.checkoutDataAlpine.store("checkout").set(this.checkout)this.emptyData = truecheckoutToken is missing is intentional: checkout shouldn’t proceed without a token.cartData existing and being in the expected shape.createObserver()#aside-complete-btn (the target to observe).sticky-element (used to compute a dynamic rootMargin)stickyHeight = stickyElement.offsetHeight || 0IntersectionObserver that sets:this.isAsideButtonVisible = entries[0].isIntersectingrootMargin:-${stickyHeight}px 0px -${stickyHeight}px 0pxasideButton.#aside-complete-btn and .sticky-element exist before calling this. In init() it already checks, but createObserver() itself assumes they exist.checkValidity()this.validCheckout based on whether the consent checkbox is checked:#concent-order (note the id spelling in the code)handlePoints(isEnabled, discountFromPoints, pointsErrorMessage) (async)discountFromPoints from a localized string like "12,34 €" into a number:" €"parseFloat(...)loyaltyPricing depending on:isEnabled (points on/off)this.isCouponRedeemed (coupon already applied or not)this.checkout.discountValue = discountFromPointsthis.checkout.loyaltyPricing = loyaltyPricingservicesreusabledefault.updateCheckout(this.checkout)pointsErrorMessagethis.checkout = response.dataupdateCheckout calls.handleCoupons()couponInputstate (default / valid / invalid / selected style states)stateClass:state to CSS classcouponValue:this.coupons.join(";") (prepping for multi-coupon support)addCoupon(addCouponErrorMessage) (async)handleBackspace(removeCouponErrorMessage)removeCoupon(index, removeCouponErrorMessage) (async)removeCoupons(removeCouponsErrorMessage) (async)this pointing to the nested coupon scope, but it still reads/writes parent state fields like this.checkout, this.coupons, this.isPointsRedeemed. Alpine resolves this because the nested object is created within the parent component context.addCoupon(addCouponErrorMessage) (inside handleCoupons, async)this.isCouponAdding)couponLastInputstate = "default"isCouponAdding = truethis.coupons = []loyaltyPricing:usePoints: this.isPointsRedeemeduseCoupon: truecouponCodes: [couponInput]this.checkout.loyaltyPricing, then calls:servicesreusabledefault.updateCheckout(this.checkout)addCouponErrorMessagethis.checkout = response.datacheckout.loyaltyPricing.errorCode exists:state = "invalid"couponStatus string (e.g. invalidCode, alreadyUsedCode, discountExceeded, etc.)state = "valid"this.isCouponRedeemed = truethis.couponscouponStatus = "validCode"couponLabel and couponsDiscountText from checkout responseisCouponAdding in finally.handleBackspace(removeCouponErrorMessage) (inside handleCoupons)removeCoupon(lastIndex, ...)removeCoupon(index, removeCouponErrorMessage) (inside handleCoupons, async)index from this.coupons.this.checkout.loyaltyPricing:useCoupon = hasCouponscouponCodes = this.couponsservicesreusabledefault.updateCheckout(this.checkout)removeCouponErrorMessagethis.checkout = response.datastate to "invalid" if there are still coupons and backend returns errorCode, else "default"couponsDiscountText from responsecheckout.loyaltyPricing object; make sure points-related flags don’t get lost when refactoring.removeCoupons(removeCouponsErrorMessage) (inside handleCoupons, async)isCouponAdding = true (re-using the “busy” flag).usePoints: this.isPointsRedeemeduseCoupon: falsecouponCodes: []updateCheckout.isCouponAdding, returns.state = "default"isCouponRedeemed = falseisCouponAddingsendSelectEvent(item, listName, position)prepareListProducts([item])sendGAEvent("selectItem", { listName, items, position })selectItem, beginCheckout, etc.).completeCheckout(completeCheckoutErrorMessage) (async)const checkoutData = Alpine.store("checkout").data;this.checkout:billingAddress, shippingAddress, invoiceDatathis.isCompleting = trueservicesreusabledefault.proceedToCheckout(this.checkout)completeCheckoutErrorMessageisCompleting and returnsthis.checkout = response.dataisCompletingthis.checkout.status == "Completed":isRedirecting = truecartToken, checkoutToken)/checkout/order-completed/<checkoutToken>proceedToCheckout returns a payment redirect URL (for card payments), you may need to handle that case separately; current logic focuses on status == "Completed".$store.checkout (checkout shared state)Alpine.store("checkout").set(this.checkout)Alpine.store("checkout").datathis.checkout before proceeding$store.toast (user feedback)Alpine.store("toast").removeAll();
Alpine.store("toast").add(message, "ic-warning", "error");servicesreusabledefault.updateCheckout(checkout)servicesreusabledefault.proceedToCheckout(checkout)checkout.status == "Completed"), it:/checkout/order-completed/<checkoutToken>checkout, toastcheckoutToken cookiecartData localStorage (GA + summary consistency)updateCheckout(...)proceedToCheckout(...)checkoutToken available in cookies. If missing, init throws an error.pageState == 'cancel' → renders a “payment failed/cancelled” UI and CTA back to checkout.pageState == 'error' → renders a “checkout error” UI and CTA back to checkout.x-data='checkoutdefault.initComponent({{ checkout | serialize | escape }})'Reusables\\BillingRetail\\Default)Reusables\\Shipping\\Default)Reusables\\Payment\\Default)window.checkoutConfig and also available as Liquid settings).handleCoupons().