# React Native - Combined Checkout

## Combined Checkout overview

The Combined Checkout flow uses your store’s normal Shopify cart, unlike the Express flow, which uses Purple Dot’s own cart. This means that pre-order and in-stock items coexist within the same Shopify cart, and Purple Dot’s integration layers on top of your existing cart implementation to manage pre-order metadata and checkout routing.

There are 5 key requirements an integration needs to implement for a Combined Checkout:

* **Displaying pre-order items.** Purple Dot is the source of data for which items are on pre-order and their estimated ship dates. The integration needs to show that those items are on pre-order, with the estimated ship dates, the "Learn more” informational content and a “Pre-order” call to action.
* **Adding pre-order items to cart.** When adding an item to cart, the integration needs to determine if it is a pre-order, and attach the waitlist ID and ship dates if it is.
* **Showing pre-order items in cart.** When a pre-order item is in the cart, the estimated ship dates need to be displayed.
* **Using the Combined pre-order checkout.** The Purple Dot pre-order checkout lets shoppers checkout with pre-order items. When a pre-order item is in the cart, the integration needs to direct the shopper to the Purple Dot pre-order checkout instead of the Shopify checkout.
* **Shopper portal.** The shopper self-service portal lets shoppers manage their pre-orders. The integration needs to be embedded on a page in the storefront that the shoppers will be directed to in their confirmation email.

### Displaying pre-order items

Purple Dot is the source of data for which items are on pre-order and their estimated ship dates. The [Pre-order availability API](#pre-order-availability-api) can be queried for the pre-order status of any product or variant on the store.

When a variant has an open waitlist, it will be set to “continue selling” in Shopify, and how it should be displayed is determined by the [Pre-order availability API](#pre-order-availability-api).

#### Product pages

Anywhere a product can be added to cart—on product pages, or collection pages and elsewhere with add to cart controls—the integration should use the variant's pre-order state provided by the [Pre-order availability API](#pre-order-availability-api) to determine how a variant should be treated. There are 4 possible states:

* `NO_OPEN_WAITLISTS` state means the variant has no open waitlists and no changes need to be made.
* `AVAILABLE_IN_STOCK` state means the variant has an open waitlist but also has stock available in Shopify, and should be treated like a regular in-stock product.
* `ON_PREORDER` state means the variant has an open waitlist and no stock in Shopify and should be treated as a pre-order. The following should be shown:
  * Change the add to cart button label to “Pre-order”, if the button has a label.
  * Show the estimated ship dates. This is the `display_dispatch_date` as returned by the 2 [Pre-order availability API](#pre-order-availability-api) endpoints.
  * Show the [Learn more](#learn-more-component) component near the button.
* `SOLD_OUT` state means the variant has an open waitlist but no stock in Purple Dot or Shopify. The add to cart button must be disabled and the label should be changed to “Sold out”.

Whenever a new variant is selected, for example using a size or colour picker, the [Pre-order availability API](#pre-order-availability-api) state should be used to determine how to display the newly selected variant. For products with multiple variants, it is possible for some variants to be on pre-order while others are in-stock or sold out.

#### Collection pages

On collection pages and in any panels that list products, the integration may show a “Pre-order” label for products that are fully on pre-order, using the state provided by the product pre-order state endpoint. The possible states are the same as for a single variant, but take into account all of the product's variants, and are therefore suitable for cases where no variant is selected.

### Adding pre-order items to cart

Whenever a pre-order item is added to cart, two line item properties must be added to it:

* `__releaseId`, a hidden property whose value indicates which waitlist it's on. This is the `id` as returned by the two [Pre-order availability API](#pre-order-availability-api) endpoints.
* `Purple Dot Pre-order`, a property whose value has the estimated ship dates and that indicates that something is a pre-order. This is the `display_dispatch_date` as returned by the two [Pre-order availability API](#pre-order-availability-api) endpoints.

Whenever an item is added to cart from anywhere on the site, it's pre-order status needs to be checked, and properties added if it is in `ON_PREORDER` or `SOLD_OUT` state (nothing needs to be done if it is `NO_OPEN_WAITLISTS` or `AVAILABLE_IN_STOCK` state).

If using the Storefront API, the integration must take care to always attach these properties when a pre-order item is added to cart.

### Showing pre-order items in cart

When displaying items in the side cart or on a cart page, the `Purple Dot Pre-order` line item property should always be displayed. It indicates that the item is a pre-order and its estimated ship dates.

Many Shopify mobile apps already display the cart item properties, so this should happen automatically. If not, you need to make sure the property is shown in the side cart and on the cart page.

### Using the Combined pre-order checkout

In order to hold pre-orders outside your downstream systems and for FTC compliance, the mirrored Purple Dot pre-order checkout is used when the shopper is checking out with pre-order items in their card. When a pre-order item is in the cart, the integration needs to open up a new WebView that presents the shopper with                                                                                                                                                                       the Purple Dot checkout instead of the Shopify checkout. Purple Dot provides a pre-built component for checking the cart state (see below), which can be used to conditionally change any links to the checkout.

The pre-order checkout can be opened on any page as an overlay iframe, or it can be hosted on the cart page and opened conditionally on load.

### Shopper portal

The shopper portal is an iframe that embeds a portal that lets shoppers manage their pre-orders. The integration must embed the shopper portal component on a dedicated page, so that shoppers can be linked to it from their confirmation emails.

## Components

Purple Dot provides a package that includes the components needed for the integration. It can be installed from `npm`, and must be included and initialised whenever the storefront is loaded.

```bash
npm install -S @purple-dot/browser
```

```jsx
import * as PurpleDot from '@purple-dot/browser';

PurpleDot.init({
  apiKey: 'xxx',
});
```

> 💡 Your API key can be found in the Merchant Portal under Settings > Integration.

When initialised, it will inject a `<script>` tag that loads additional code required to make Purple Dot work.

The package contains several modules structured like this:

<figure><img src="/files/8jxsIqMGaH1fbKO1mi3S" alt=""><figcaption><p><code>@purple-dot/browser</code> structure</p></figcaption></figure>

### Pre-order availability API

The pre-order availability API should be used for determining if a product or a variant should be displayed as a pre-order and the estimated ship dates if so.

Purple Dot provides a client library that can be used like:

```jsx
import * as api from '@purple-dot/browser/api';

const variantResponse = await api.fetchVariantsPreorderState(variant.id);

const productResponse = await api.fetchProductsPreorderState(product.handle);
```

* `variantPreorderState` provides data on the pre-order state of any variant. It should be used when interacting with a specific variant, such as on a product page. It returns:
  * `state`, one of:
    * `NO_OPEN_WAITLISTS` - the variant has no open waitlists and no pre-order related functionality should trigger
    * `AVAILABLE_IN_STOCK` - the variant is available in stock and should not be sold as a pre-order (even though there is an open waitlist)
    * `ON_PREORDER` - the variant is available as a pre-order only
    * `SOLD_OUT` - the variant is completely sold out, in stock and on pre-order
  * `waitlist`
    * `id` - the waitlist ID
    * `display_dispatch_date` - the estimated ship date to display for the variant if on pre-order
* `productPreorderState` provides data on the pre-order state of a whole product. It can be used in contexts where no variant is selected, such as on a collection page. It returns:
  * `state`, one of:
    * `NO_OPEN_WAITLISTS` - the product has no open waitlists and no pre-order related functionality should trigger
    * `AVAILABLE_IN_STOCK` - the product has some variants available in stock, and should be displayed as a regular in-stock product
    * `ON_PREORDER` - the product has all variants available on pre-order, and should be displayed as a pre-order product
    * `SOLD_OUT` - the product is completely sold out
  * `waitlist`
    * `id` - the waitlist ID
    * `display_dispatch_date` - the estimated ship date to display for the product if on pre-order

### Learn more component

When a variant is on pre-order, the estimated ship dates and the “Pre-order protected by” branding must be displayed near the “Pre-order” call to action. Purple Dot provides a Web Component that can be used with any JS framework like this:

1. Place a `<purple-dot-learn-more />` HTML element anywhere in the DOM. This component is framework-agnostic and is compatible with all popular frameworks like React and Vue.

### Shopper self-service portal component

Purple Dot provides a portal where shoppers can manage their pre-orders. The portal can be embedded onto any page in your storefront:

1. Create a new page that can host the portal, e.g. `/pages/manage-pre-orders`
2. Initialize the Purple Dot SDK on this page by running

   ```typescript
   import * as PurpleDot from '@purple-dot/browser';

   PurpleDot.init({
     apiKey: '<Purple Dot API key>'
   });
   ```
3. Place a `<purple-dot-self-service />` HTML element where you would like the self service iframe to appear. This component is framework-agnostic and is compatible with all popular frameworks like React and Vue.

### Working with the cart

For React Native apps, Purple Dot requires the use of the Storefront API, typically used by Hydrogen and headless storefronts as well as many mobile apps. In order for Purple Dot to be able to interact with your cart, use the Shopify Storefront Cart adapter provided by the SDK. This should be passed in as configuration when initialising the library.

```jsx
import * as PurpleDot from '@purple-dot/browser';
import { ShopifyStorefrontCart } from '@purple-dot/browser/shopify-storefront-cart';

PurpleDot.init({
  apiKey: '<Purple Dot API key>',
  cartAdapter: new ShopifyStorefrontCart(
    'your-store.myshopify.com',
    '<Storefront API public access token>'
  ),
});
```

### Adding pre-order items to the cart

Whenever a pre-order item is added to cart, from anywhere on the storefront, pre-order metadata must be added to it as line item properties. This can be achieved by making sure that anywhere that items can be added to the cart first checks for the pre-order state of the variant using our Preorder Availability APIs, and adds the following line item attributes via GraphQL:

* `__releaseId`: the ID of the Purple Dot waitlist
* `Purple Dot Pre-order`: the estimated ship date to display for the product if on pre-order

Here's how an `addPreorderItemToCart` function may be implemented in Javascript:

<pre class="language-jsx"><code class="lang-jsx">/**
 * Adds a line item to the Shopify cart with preorder properties.
 * 
 * @param {string} cartId - The Shopify Cart GID.
 * @param {string} variantId - The Product Variant GID.
 * @param {string} waitlistId - The Purple Dot waitlist ID.
 * @param {string} dispatchDate - The estimated ship date for the Purple Dot Pre-order property.
 * @returns {Promise&#x3C;Object>} The response data from the Storefront API.
 */
async function addPreorderItemToCart(cartId, variantId, releaseId, dispatchDate) {
  const SHOP_DOMAIN = 'your-store.myshopify.com';
  const ACCESS_TOKEN = 'your_storefront_access_token';
  const API_VERSION = '2026-04';

  const mutation = `
    mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
      cartLinesAdd(cartId: $cartId, lines: $lines) {
        cart {
          id
          lines(first: 10) {
            nodes {
              id
              attributes {
                key
                value
              }
            }
          }
        }
        userErrors {
          field
          message
        }
      }
    }
  `;

  const variables = {
    cartId: cartId,
    lines: [
      {
        merchandiseId: variantId,
        quantity: 1,
<strong>        attributes: [
</strong><strong>          { key: "__releaseId", value: waitlistId },
</strong><strong>          { key: "Purple Dot Pre-order", value: dispatchDate }
</strong>        ]
      }
    ]
  };

  const response = await fetch(`https://${SHOP_DOMAIN}/api/${API_VERSION}/graphql.json`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Shopify-Storefront-Access-Token': ACCESS_TOKEN,
    },
    body: JSON.stringify({ query: mutation, variables }),
  });

  return await response.json();
}

</code></pre>

Example call:

```javascript
import * as api from '@purple-dot/browser/api';

async function handleAddToCart(cart, variant) {
    const variantResponse = await api.fetchVariantsPreorderState(variant.id);
    
    if (variantResponse.state === 'ON_PREORDER') {
        const waitlistId = variantResponse.waitlist.id;
        const dispatchDate = variantResponse.waitlist.display_dispatch_date;
        
        addPreorderItemToCart(cart.id, variant.id, waitlistId, dispatchDate);
    } else {
        // Add item to cart as you normally would
        // ...
    }
}
```

### Cart state (`@purple-dot/browser/cart`)

In order to redirect the shopper to the Purple Dot pre-order checkout, we need to know if the cart contains a pre-order. That can be done like:

```jsx
import { cartHasPreorderItem } from '@purple-dot/browser/cart';

if (await cartHasPreorderItem(cart)) {
  // ...
}
```

The library fetches the latest cart state from the cart using the configured adapter and check if it contains a pre-order item by inspect the line item properties and checking for the presence of `__releaseId`.

### Pre-order checkout

If the cart contains a pre-order item, the shopper has to checkout using the Purple Dot pre-order checkout instead of the standard Shopify checkout. The Purple Dot checkout can be launched in a WebView by going to the following URL:

`https://www.purpledotprice.com/embedded/placements/checkout/combined?apiKey=API_KEY&cartId=CART_ID&salesChannel=APP_NAME`&#x20;

Example:

```jsx
import React, { useMemo } from 'react';
import { WebView } from 'react-native-webview';

function buildCombinedCheckoutUrl(params: {
  apiKey: string;
  cartId: string;
  currency?: string;
  locale?: string;
  salesChannel?: string;
}) {
  const q = new URLSearchParams({ apiKey: params.apiKey, cartId: params.cartId });
  if (params.currency) q.set('currency', params.currency);
  if (params.locale) q.set('locale', params.locale);
  if (params.salesChannel) q.set('salesChannel', params.salesChannel);
  return `https://www.purpledotprice.com/embedded/placements/checkout/combined?${q.toString()}`;
}

export function PurpleDotCombinedCheckoutWebView(props: {
  apiKey: string;
  cartId: string;
  currency?: string;
  locale?: string;
  onOpened?: () => void;
  onClosed?: () => void;
}) {
  const apiKey = 'your-purple-dot-api-key';
  const salesChannel = 'your-app-name';
  
  const uri = useMemo(
    () => buildCombinedCheckoutUrl({ 
      apiKey, 
      cartId, 
      currency,
      locale,
    }),
    [apiKey, props.cartId, props.currency, props.locale]
  );

  return (
    <WebView
      source={{ uri }}
      onMessage={(event) => {
        const msg = event.nativeEvent.data;
        if (msg === 'pd:checkout_opened') {
          props.onOpened?.();
        }
        if (msg === 'pd:checkout_closed') {
          props.onClosed?.();
        }
      }}
    />
  );
}
```

The WebView will fire events than can be handled by providing callback functions to add custom logic for routing, analytics, etc. on checkout being opened or closed.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.getpurpledot.com/docs/shopify-stores/integrate-purple-dot-with-your-storefront/integrate-into-react-native-apps/react-native-combined-checkout.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
