npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@charingcross/procuremesh-client

v1.1.0

Published

ProcureMesh browser client for punchout storefront integrations

Downloads

88

Readme

@charingcross/procuremesh-client

ProcureMesh browser client for punchout storefront integrations.

This project is proprietary and distributed under the terms in LICENSE.md.

Use it when your storefront needs to hand the current cart back to a buyer procurement system.

The library handles the punchout session lifecycle for you:

  • detects pm_token in the page URL
  • starts the ProcureMesh session automatically
  • removes pm_token from the browser URL
  • stores the token for the current browser session
  • performs the gateway handshake
  • transfers the cart back to procurement when checkout is triggered

The automatic punchout flow starts when the storefront is opened with a pm_token query parameter.

In practice, your integration only needs to:

  • load the library with npm or a script tag
  • provide your ProcureMesh gateway url
  • provide checkoutButtonSelector
  • return the current cart from onCheckout

Install

npm

npm install @charingcross/procuremesh-client
import { ProcureMesh } from "@charingcross/procuremesh-client";

CDN

You can use the UMD bundle with a script tag.

jsDelivr example:

<script
  src="https://cdn.jsdelivr.net/npm/@charingcross/[email protected]/dist/procuremesh-client.umd.js"
  defer
></script>

For production, pin the CDN URL to a package version so upgrades are explicit.

  • Exact version: @1.1.0
  • Minor range: @1.1
  • Major range: @1

Example versioned URL:

<script
  src="https://cdn.jsdelivr.net/npm/@charingcross/[email protected]/dist/procuremesh-client.umd.js"
  defer
></script>

Self-hosted example:

<script
  src="https://your-assets.example.com/procuremesh-client.umd.js"
  defer
></script>

You can either use jsDelivr or serve dist/procuremesh-client.umd.js from your own gateway, asset CDN, or storefront static files.

When loaded from a script tag, the library is available as window.ProcureMesh.

Quick Start

<script
  src="https://cdn.jsdelivr.net/npm/@charingcross/[email protected]/dist/procuremesh-client.umd.js"
  defer
></script>
<script>
  document.addEventListener("DOMContentLoaded", async () => {
    await ProcureMesh.configure({
      url: "https://gateway.example.com",
      checkoutButtonSelector: "button.cart__checkout-button",
      checkoutButtonText: "Transfer Cart to Procurement",
      onSessionStarted: () => {
        document.querySelector("#punchout-banner")?.removeAttribute("hidden");
      },
      onCheckoutButton: (element) => {
        element.classList.add("procuremesh-ready");
      },
      onCheckout: async () => {
        const response = await fetch("/cart.js");
        const data = await response.json();

        return {
          total_amount: data.items_subtotal_price / 100,
          currency: data.currency,
          items: data.items.map((line) => ({
            unit_price: line.price / 100,
            quantity: line.quantity,
            description: line.title,
            supplier_part_id: String(line.variant_id),
            unit_of_measure: "EA",
            classification: "14111500",
          })),
        };
      },
      onSessionEnd: async () => {
        await fetch("/cart/clear.js");
      },
    });
  });
</script>

npm Usage

import { ProcureMesh } from "@charingcross/procuremesh-client";

await ProcureMesh.configure({
  url: "https://gateway.example.com",
  checkoutButtonSelector: "[data-procuremesh-checkout]",
  onCheckout: async () => {
    return {
      total_amount: 125.5,
      currency: "USD",
      items: [
        {
          unit_price: 125.5,
          quantity: 1,
          description: "Sample item",
          supplier_part_id: "SKU-1000",
          unit_of_measure: "EA",
          classification: "14111500",
        },
      ],
    };
  },
});

TypeScript

The package exports these TypeScript types:

  • ProcureMeshApi
  • ProcureMeshConfig
  • TransferCartItem
  • TransferCartPayload
  • TransferResponsePayload

Import example:

import {
  ProcureMesh,
  type ProcureMeshConfig,
  type TransferCartPayload,
} from "@charingcross/procuremesh-client";

If you do not need the clicked element or event in onCheckout, you can omit those parameters in your implementation.

Example:

type StorefrontCartLine = {
  price: number;
  quantity: number;
  title: string;
  variant_id: string | number;
};

type StorefrontCart = {
  total_price: number;
  currency: string;
  items: StorefrontCartLine[];
};

import {
  ProcureMesh,
  type ProcureMeshConfig,
  type TransferCartPayload,
} from "@charingcross/procuremesh-client";

const buildCartPayload = async (): Promise<TransferCartPayload> => {
  const response = await fetch("/cart.js");
  const data = (await response.json()) as StorefrontCart;

  return {
    total_amount: data.items_subtotal_price / 100,
    currency: data.currency,
    items: data.items.map((line) => ({
      unit_price: line.price / 100,
      quantity: line.quantity,
      description: line.title,
      supplier_part_id: String(line.variant_id),
      unit_of_measure: "EA",
      classification: "14111500",
    })),
  };
};

const config: ProcureMeshConfig = {
  url: "https://gateway.example.com",
  checkoutButtonSelector: "button.cart__checkout-button",
  onCheckout: buildCartPayload,
};

await ProcureMesh.configure(config);

Configuration

url

  • Type: string
  • Required: yes
  • Set this to your ProcureMesh gateway URL

Example:

url: "https://gateway.example.com";

checkoutButtonSelector

  • Type: string
  • Required: yes
  • Set this to the CSS selector for the checkout button users click during punchout

Example:

checkoutButtonSelector: "button.cart__checkout-button";

checkoutButtonText

  • Type: string
  • Required: no
  • Replaces the checkout button label during punchout

Example:

checkoutButtonText: "Transfer Cart to Procurement";

onSessionStarted

  • Type: () => void | Promise<void>
  • Required: no
  • Use this to update the storefront UI when punchout starts

Example:

onSessionStarted: () => {
  document.querySelector("#punchout-banner")?.removeAttribute("hidden");
};

onSessionEnd

  • Type: () => void | Promise<void>
  • Required: no
  • Use this to clean up the storefront before the cart is returned to procurement

Example:

onSessionEnd: async () => {
  await fetch("/cart/clear.js");
};

onCheckoutButton

  • Type: (element: HTMLElement) => void | Promise<void>
  • Required: no
  • Use this to style or annotate the checkout button during punchout

Example:

onCheckoutButton: (element) => {
  element.classList.add("procuremesh-ready");
  element.setAttribute("aria-label", "Transfer cart to procurement");
};

onCheckout

  • Type: (element: Element, event: MouseEvent) => TransferCartPayload | Promise<TransferCartPayload>
  • Required: yes
  • Return the current storefront cart in ProcureMesh format

Example:

onCheckout: async () => {
  const response = await fetch("/cart.js");
  const data = await response.json();

  return {
    total_amount: data.items_subtotal_price / 100,
    currency: data.currency,
    shipping_amount: 0,
    tax_amount: 0,
    items: data.items.map((line) => ({
      unit_price: line.price / 100,
      quantity: line.quantity,
      description: line.title,
      supplier_part_id: String(line.variant_id),
      unit_of_measure: "EA",
      classification: "14111500",
      image_url: line.image,
      product_url: line.url,
    })),
  };
};

Transfer Payload

Your onCheckout callback must return an object with this full shape:

  • shipping_amount and tax_amount are optional.
  • short_name, manufacturer_name, manufacturer_part_id, image_url, product_url, and lead_time are optional.
  • classification is required.
  • classification_domain is optional and defaults to UNSPSC.
  • classification_value is still accepted for backward compatibility, but classification is preferred.
{
  total_amount: 125.5,
  currency: 'USD',
  shipping_amount: 10,
  tax_amount: 8.75,
  items: [
    {
      unit_price: 50,
      quantity: 2,
      description: 'Printer paper A4',
      supplier_part_id: 'PAPER-A4-500',
      unit_of_measure: 'EA',
      classification: '14111507',
      short_name: 'A4 paper',
      manufacturer_name: 'Acme Office',
      manufacturer_part_id: 'AC-4455',
      image_url: 'https://store.example.com/images/paper-a4.jpg',
      product_url: 'https://store.example.com/products/paper-a4',
      lead_time: '3 days'
    }
  ]
}

Template:

return {
  total_amount: 0,
  currency: "USD",
  shipping_amount: 0,
  tax_amount: 0,
  items: [
    {
      unit_price: 0,
      quantity: 1,
      description: "",
      supplier_part_id: "",
      unit_of_measure: "EA",
      classification: "",
      classification_domain: "",
      short_name: "",
      manufacturer_name: "",
      manufacturer_part_id: "",
      lead_time: "",
    },
  ],
};

Top-level fields

total_amount

  • Type: number
  • Required: yes
  • Cart subtotal excluding tax and shipping

currency

  • Type: string
  • Required: yes
  • Three-letter ISO 4217 currency code such as USD, GBP, or EUR

shipping_amount

  • Type: number
  • Required: no
  • Sum of all line item shipping or cart level shipping

tax_amount

  • Type: number
  • Required: no
  • Sum of all line item tax or cart level tax

items

  • Type: array
  • Required: yes
  • List of cart items

Item fields

unit_price

  • Type: number
  • Required: yes
  • Unit price for the item

quantity

  • Type: number
  • Required: yes
  • Quantity for the item

description

  • Type: string
  • Required: yes
  • Full item name and description shown to the buyer

supplier_part_id

  • Type: string
  • Required: yes
  • Your storefront SKU or variant identifier

unit_of_measure

  • Type: string
  • Required: yes
  • Unit such as EA, BX, or PK

short_name

  • Type: string
  • Required: no
  • Short display name for the item

classification

  • Type: string
  • Required: yes
  • Classification code value such as 14111507
  • ProcureMesh uses UNSPSC as the default classification domain

classification_domain

  • Type: string
  • Required: no
  • Classification system name such as UNSPSC
  • Defaults to UNSPSC when omitted

classification_value

  • Type: string
  • Required: no
  • Backward-compatible alias for classification
  • Prefer classification in new integrations

manufacturer_name

  • Type: string
  • Required: no
  • Manufacturer name

manufacturer_part_id

  • Type: string
  • Required: no
  • Manufacturer part number

image_url

  • Type: string
  • Required: no
  • Public image URL for the item

product_url

  • Type: string
  • Required: no
  • Public product page URL for the item

lead_time

  • Type: string
  • Required: no
  • Delivery or availability text such as 3 days

Shopify Example

<script
  src="https://cdn.jsdelivr.net/npm/@charingcross/[email protected]/dist/procuremesh-client.umd.js"
  defer
></script>
<script>
  document.addEventListener("DOMContentLoaded", async () => {
    await ProcureMesh.configure({
      url: "https://gateway.example.com",
      checkoutButtonSelector: "button.cart__checkout-button",
      checkoutButtonText: "Transfer Cart to Procurement",
      onSessionStarted: () => {
        document.querySelectorAll(".shopify-payment-button").forEach((el) => {
          el.hidden = true;
        });
      },
      onCheckout: async () => {
        const response = await fetch("/cart.js");
        const data = await response.json();

        return {
          total_amount: data.items_subtotal_price / 100,
          currency: data.currency,
          items: data.items.map((line) => ({
            unit_price: line.price / 100,
            quantity: line.quantity,
            description: line.title,
            supplier_part_id: String(line.variant_id),
            unit_of_measure: "EA",
            classification: "14111500",
          })),
        };
      },
      onSessionEnd: async () => {
        await fetch("/cart/clear.js");
      },
    });
  });
</script>

WooCommerce Example

<script
  src="https://cdn.jsdelivr.net/npm/@charingcross/[email protected]/dist/procuremesh-client.umd.js"
  defer
></script>
<script>
  document.addEventListener("DOMContentLoaded", async () => {
    await ProcureMesh.configure({
      url: "https://gateway.example.com",
      checkoutButtonSelector: "a.wc-block-cart__submit-button",
      checkoutButtonText: "Transfer Cart to Procurement",
      onCheckout: async () => {
        const response = await fetch("/wp-json/wc/store/v1/cart");
        const data = await response.json();

        return {
          total_amount: Number(data.totals.total_items) / 100,
          currency: data.totals.currency_code,
          items: data.items.map((line) => ({
            unit_price: line.prices.price / 100,
            quantity: line.quantity,
            description: line.name,
            supplier_part_id: String(line.sku),
            unit_of_measure: "EA",
            classification: "14111500",
            image_url: line.images?.[0]?.src || "",
          })),
        };
      },
      onSessionEnd: async () => {
        const cartResponse = await fetch("/wp-json/wc/store/v1/cart");
        const nonce = cartResponse.headers.get("nonce");

        await fetch("/wp-json/wc/store/v1/cart/items", {
          method: "DELETE",
          headers: {
            "Content-Type": "application/json",
            Nonce: nonce,
          },
        });
      },
    });
  });
</script>