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.

Used Newie, our AI search tool in the docs? Take a 2-minute survey to rate your experience!

Adding webview customizations to the cart

Prev Next

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.

Available from Associate App v1.83.0

Cart webview slots and the meta.allowExpansion option are available from Associate App version 1.83.0

Further reading

Slots

Two slots are available in the cart. Configure which slot the webview appears via the Customization Configuration API.

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.

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. The customization_id in the path is provided by you, to generate a UUID.

{
  "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.

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.

{
  "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 (#):

https://your-webview-url.com/page#<base64-encoded-JSON>

Decode and parse it on the client side:

const raw = decodeURIComponent(window.location.hash.slice(1))
const context = JSON.parse(atob(raw))

The context object has the following payload:

{
  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
      }

      // --- 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
}

Privacy

The cart snapshot deliberately contains no customer PII, such as a name, email address, or postal address. Use customerId together with auth.accessToken to fetch more customer data from NewStore APIs when you need it.

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. Read the fragment at load time and listen for the hashchange event:

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:

const { auth, contextProps: { formData } } = context
const baseUrl = `https://<tenant>.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: '<warranty-product-id>',
    fulfillment: 'IN_STORE_HANDOVER',
    price: { source: 'INTERNAL', source_id: '<pricebook-id>' },
  }),
})

After the webview modifies the cart, trigger Associate App to reload the cart view so the associate sees the change immediately:

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:

// 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:

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)

  document.getElementById('root').textContent = cart.customerId
    ? `This purchase earns ${points} points for the customer on the cart.`
    : `Add the customer to the cart to earn ${points} points on this purchase.`
}

window.addEventListener('hashchange', render)
render()