--- title: "Adding webview customizations to the cart" slug: "cart-webview-integration" updated: 2026-09-28T12:20:20Z published: 2026-09-28T12:20:20Z canonical: "docs.newstore.com/cart-webview-integration" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.newstore.com/llms.txt > Use this file to discover all available pages before exploring further. # Adding webview customizations to the cart Webviews embedded in the **cart** of Associate App receive a live snapshot of the cart from the app and can post messages back to trigger native app actions. Use them to surface information next to the items the associate is selling — for example a loyalty enrollment prompt, available coupons from an external promotion engine, warranty validation of scanned serial numbers, or brand-specific purchase advisories. > [!WARNING] > Available from Associate App v1.83.0 > > Cart webview slots and the `meta.allowExpansion` option are available from Associate App version 1.83.0. The `cart.customer` object is available from version 1.85.0. > [!TIP] > Further reading > > - [Adding webview customizations to the product detail page](/v1/docs/pdp-webview-integration) — The same mechanism on the PDP, including the full postMessage reference. > - [Adding webview customizations using external authentication](/v1/docs/adding-webview-customizations-using-external-authentication) — Setting up external identity providers and reading context in any Associate App webview. ## Slots Two slots are available in the cart. Configure which slot the webview appears via the [Customization Configuration API](https://docs.newstore.net/api/configuration/apps/customization-config-api_config). | Slot ID | Position | | --- | --- | | `cart_top` | Top of the cart, below the customer and address cards, above the item list | | `cart_footer` | Bottom of the cart, above the totals summary | One slot can host multiple customizations, and they are rendered in configuration order. > [!TIP] > Note > > Cart slots only render when the cart contains at least one item. An empty cart shows the standard empty-cart screen without any customizations. When the cart has items, an enabled inline webview always renders and reserves its configured `meta.height`. Design your page to display a sensible empty or collapsed state when it has nothing to display. ## Configuring a cart webview Send a `PUT` request to the [Customization Configuration API](https://docs.newstore.net/api/configuration/apps/customization-config-api_config/#operation/putCustomizationConfig). The `customization_id` in the path is provided by you, to generate a UUID. ```json {  "enabled": true,  "type": "webview",  "slot": "cart_top",  "label": "Loyalty",  "title": "Loyalty",  "caption": "Open",  "url": "https://your-webview-url.com/loyalty",  "meta": {    "inline": true,    "scrollEnabled": false,    "height": 150  } } ``` ## Display modes Webviews can be configured as: - **Full-screen** (default) — Opens as a new screen when the associate taps a button in the cart - **Inline** — Rendered directly within the cart. Set `meta.inline: true` in the configuration, along with `meta.height` (pixels), and optionally `meta.scrollEnabled`, `meta.scrollXEnabled`, `meta.scrollYEnabled`. > [!WARNING] > Available from Associate App v1.83.0 An inline webview displays a header with the configured `title` and a button (labeled with `caption`) that opens the same page in full-screen mode. To hide that button and keep the webview inline-only, set `meta.allowExpansion: false`. The `allowExpansion` property defaults to `true`. Only an explicit `false` value hides the button. For the full display mode reference and code examples, see [Adding webview customizations to the product detail page](/v1/docs/pdp-webview-integration#display-modes). ```json {  "meta": {    "inline": true,    "height": 180,    "allowExpansion": false  } } ``` ## Delivering context When the app renders the webview, it adds a **base64-encoded JSON payload** to the URL as a fragment (`#`): ```plaintext https://your-webview-url.com/page# ``` Decode and parse it on the client side: ```javascript const raw = decodeURIComponent(window.location.hash.slice(1)) const context = JSON.parse(atob(raw)) ``` The context object has the following payload: ```typescript {  contextProps: {    formData: {      // --- Cart snapshot ---      cart: {        items: Array<{          productId: string          itemIds: string[]             // Cart line item IDs — use with the Cart API          quantity: number          unitPrice: number          itemDiscount: number          externalIdentifiers: Array<{            serialNumber: string | null            epc: string | null          }>        }>        grandTotal: number        subtotal: number        discountTotal: number        taxTotal: number        remainingAmount: number        currency: string | null        coupons: string[]               // Applied coupon codes        customerId: string | null       // Customer assigned to the cart, if any        // Present only when a customer is attached and their profile has loaded        customer?: {          id: string                    // Same value as customerId          displayId: string | null      // Human-readable customer number, e.g. "C-001"          extendedAttributes: {         // Profile extended attributes, flattened            [name: string]: string      // e.g. { "loyalty_id": "L-123" }          }        }      }      // --- Session fields (always present) ---      associateId: string               // ID of the logged-in associate      storeId: string                   // ID of the current store      cartId: string                    // Active cart ID    }  }  auth: {    accessToken: string                 // NewStore access token — use to call NewStore APIs  }  externalIdentities: Array<{    identityProvider: string    identity: string  }>  theme: object                         // App color, spacing, and typography tokens  dimensions: { top: number, bottom: number, left: number, right: number }  enums: { DocumentType: object }       // Print document type constants  securityToken: string                 // One-time token generated per webview session } ``` A page that only needs the customer's identity should use `customerId`. It is available from the first render and is never absent when a customer is on the cart. The `customer` object is for pages that need the profile's extended attributes, and is only present once the customer's profile has loaded. A customer has two identifiers. `id` is the internal identifier NewStore APIs expect, and it is the same value as `customerId` on the cart. `displayId` is the short, human-readable customer number the app shows to associates, for example `C-001`. Use `id` for API calls and `displayId` when you want to show the customer their own reference. `extendedAttributes` contains the customer profile's extended attributes as a flat map of name to value. Retailers typically use these to carry integration keys such as a loyalty membership number. Reading them here saves your page a separate call to the customer profile API. > [!NOTE] > Privacy > > The payload contains no customer contact details: no name, email address, phone number, or postal address. The `customer` object is limited to identifiers and the profile's extended attributes. If you need anything beyond that, use `customer.id` together with `auth.accessToken` to fetch the customer profile from NewStore APIs. > [!NOTE] > Timing > > `customer` is populated from the customer profile, which the app may still be loading when your page first renders. When that happens, the object is absent from the initial fragment and arrives moments later in a `hashchange` update. Drive your rendering from the newest fragment rather than reading the hash only once. `customerId` is available immediately in both cases. ## Reacting to cart changes The app re-serializes the payload whenever the cart changes, such as when the associate scans another item or applies a discount, and when the attached customer's profile finishes loading. Read the fragment at load time **and** listen for the `hashchange` event: ```javascript function render() {  const context = getWebViewContext()  if (!context) return  const { cart } = context.contextProps.formData  // update your UI from the fresh snapshot } window.addEventListener('hashchange', render) render() ``` Whether an update arrives as a soft navigation or a page reload is platform-dependent. Handle both by keeping the `render()` property idempotent. ## Making a call to NewStore APIs Use `auth.accessToken` to make authenticated calls to NewStore APIs. For example, attaching a warranty add-on to an item already in the cart via the [Cart Line Item API](https://docs.newstore.net/api-portal/#/http/checkout/operations-on-carts/create-line-item): ```javascript const { auth, contextProps: { formData } } = context const baseUrl = `https://.p.newstore.net/v0/d` await fetch(`${baseUrl}/checkout/carts/${formData.cartId}/items/${formData.cart.items[0].itemIds[0]}/add-ons`, {  method: 'POST',  headers: {    'Authorization': `Bearer ${auth.accessToken}`,    'Content-Type': 'application/json',  },  body: JSON.stringify({    product_id: '',    fulfillment: 'IN_STORE_HANDOVER',    price: { source: 'INTERNAL', source_id: '' },  }), }) ``` After the webview modifies the cart, trigger Associate App to reload the cart view so the associate sees the change immediately: ```javascript window.open(`com.newstore.associate-one://cart/load?cartId=${formData.cartId}`) ``` ## Posting messages back to the app The webview can trigger native app actions via `window.ReactNativeWebView.postMessage`. The same messages are available as on the product detail page. See the [full postMessage reference](/v1/docs/pdp-webview-integration#posting-messages-back-to-the-app): ```javascript // Close a full-screen webview window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'close' })) // Open an https:// URL in the system browser window.ReactNativeWebView.postMessage(JSON.stringify({  type: 'openExternalUrl',  message: { url: 'https://...' } })) // Print a document window.ReactNativeWebView.postMessage(JSON.stringify({  type: 'print',  message: { resources: [{ documentType: context.enums.DocumentType.salesReceipt, url: 'https://...' }] } })) ``` ## What cart webviews cannot do A cart webview is informational and additive: it can display content and call NewStore APIs, but it **cannot block the checkout flow** or prevent the associate from completing a sale. If your use case requires enforcement, such as for legally binding quantity restrictions per brand, you must implement it in backend order validation in addition to the in-cart advisory. Inline webviews require network connectivity and do not render meaningful content when the device is offline. ## Example: loyalty enrollment banner A minimal `cart_top` page that shows how many points the current purchase would earn: ```javascript function getWebViewContext() {  const hash = window.location.hash.slice(1)  if (!hash) return null  try {    return JSON.parse(atob(decodeURIComponent(hash)))  } catch {    return null  } } function render() {  const context = getWebViewContext()  if (!context) return  const { cart } = context.contextProps.formData  const points = Math.round((cart.grandTotal || 0) * 10)  const loyaltyId = cart.customer?.extendedAttributes?.loyalty_id  document.getElementById('root').textContent = loyaltyId    ? `Member ${loyaltyId} earns ${points} points on this purchase.`    : `Add the customer to the cart to earn ${points} points on this purchase.` } window.addEventListener('hashchange', render) render() ``` The optional chaining on `cart.customer` keeps the page safe during the window before the customer's profile has loaded.