Skip to content
  • There are no suggestions because the search field is empty.

How to send BigCommerce orders to ProfitMetrics using a Cloudflare Worker (free Zapier alternative)

This guide covers Part 1 of the Server-side Hybrid Universal Integration — sending orders to ProfitMetrics server-side — using a free Cloudflare Worker instead of Zapier.

 

NOTE: This is an experimental guide and has not been tested by ProfitMetrics. We recommend implementing this under the guidance of a developer and testing with caution. If you find any issues with the guide, please let us know at support@profitmetrics.io

There are 3 parts to this guide.

Part 1: Prerequisites & BigCommerce API credentials

Part 2: Deploy the Cloudflare Worker

Part 3: Create the BigCommerce webhook

After completing this guide, continue with:


Part 1: Prerequisites & BigCommerce API credentials

You will need the following before starting:

Item Where to find it
ProfitMetrics Public ID (pid) ProfitMetrics → Settings → Website → Public ID
BigCommerce Store Hash Your BigCommerce admin URL: https://store-**{STORE_HASH}**.mybigcommerce.com
BigCommerce API Access Token Create one in BigCommerce → Settings → API → API Accounts (see below)
Cloudflare account (free) Sign up at cloudflare.com
Node.js (v16+) and npm Required locally to deploy the Worker via Wrangler CLI

Create a BigCommerce API Account

  1. In your BigCommerce admin, go to Settings → API → API Accounts → Create API Account → Create V2/V3 API Token.
  2. Give it a descriptive name, e.g. ProfitMetrics Worker.
  3. Set the following OAuth scopes:
Scope Permission
Orders Read-Only
Order Transactions Read-Only
Products Read-Only
  1. Click Save. Copy the Access Token and Store Hash — you will need these in Part 2.

IMPORTANT: The Access Token is only shown once. Store it securely.


Part 2: Deploy the Cloudflare Worker

Step 1 — Install Wrangler CLI

Open a terminal and install the Cloudflare Wrangler CLI:

npm install -g wrangler

Authenticate with your Cloudflare account:

wrangler login

Step 2 — Create the project

Create a new directory and initialise the project:

mkdir bigcommerce-profitmetrics
cd bigcommerce-profitmetrics

Create a file called wrangler.toml with the following content:

name = "bigcommerce-profitmetrics"
main = "worker.js"
compatibility_date = "2024-01-01"

Step 3 — Add the Worker code

Create a file called worker.js and paste the full Worker code provided below.

Click to expand: worker.js (full source)

/**
* BigCommerce → ProfitMetrics Server-Side Hybrid Universal Integration
* Cloudflare Worker
*
* Environment variables (set via `wrangler secret put`):
* BC_STORE_HASH - Your BigCommerce store hash
* BC_ACCESS_TOKEN - BigCommerce API access token
* PM_PUBLIC_ID - ProfitMetrics website Public ID
*/

export default {
async fetch(request, env) {
if (request.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}

try {
const payload = await request.json();

const orderId = payload?.data?.id;
if (!orderId) {
return new Response("No order ID in payload", { status: 400 });
}

console.log(`Received webhook for order ${orderId}`);

// Fetch full order from BigCommerce V2 Orders API
const bcBase = `https://api.bigcommerce.com/stores/${env.BC_STORE_HASH}`;
const bcHeaders = {
"X-Auth-Token": env.BC_ACCESS_TOKEN,
"Accept": "application/json",
"Content-Type": "application/json",
};

const orderRes = await fetch(`${bcBase}/v2/orders/${orderId}`, {
headers: bcHeaders,
});

if (!orderRes.ok) {
console.error(`Order fetch failed: ${orderRes.status}`);
return new Response("Failed to fetch order", { status: 502 });
}

const order = await orderRes.json();

// Fetch order products
const productsRes = await fetch(
`${bcBase}/v2/orders/${orderId}/products`,
{ headers: bcHeaders }
);

if (!productsRes.ok) {
console.error(`Products fetch failed: ${productsRes.status}`);
return new Response("Failed to fetch order products", { status: 502 });
}

const products = await productsRes.json();

// Build ProfitMetrics orderspec
const orderspec = buildOrderspec(order, products);

// Send to ProfitMetrics
const pmUrl = buildProfitMetricsUrl(env.PM_PUBLIC_ID, orderspec);
const pmRes = await fetch(pmUrl);

if (!pmRes.ok) {
console.error(`ProfitMetrics request failed: ${pmRes.status}`);
return new Response("ProfitMetrics request failed", { status: 502 });
}

console.log(`Order ${orderId} sent to ProfitMetrics successfully`);
return new Response("OK", { status: 200 });

} catch (err) {
console.error("Worker error:", err);
return new Response("Internal error", { status: 500 });
}
},
};


function buildOrderspec(order, products) {
const email = order.billing_address?.email || "";
const rawPhone = order.billing_address?.phone || "";
const phone = rawPhone.replace(/[^0-9]/g, "") || null;

const totalInclVat = parseFloat(order.total_inc_tax) || 0;
const totalExVat = parseFloat(order.total_ex_tax) || 0;
const shippingExVat = parseFloat(order.shipping_cost_ex_tax) || 0;

const shippingCountry = order.shipping_addresses?.length
? order.shipping_addresses[0].country_iso2
: order.billing_address?.country_iso2 || null;

const shippingZipcode = order.shipping_addresses?.length
? order.shipping_addresses[0].zip
: order.billing_address?.zip || null;

const shippingState = order.shipping_addresses?.length
? order.shipping_addresses[0].state
: order.billing_address?.state || null;

// IMPORTANT: The "sku" field must match the product ID used in your
// ProfitMetrics product feed. Adjust if your feed uses product_id
// or variant_id instead of the actual SKU.
const pmProducts = products.map((product) => ({
sku: product.sku || String(product.product_id),
qty: product.quantity,
priceExVat: parseFloat(product.price_ex_tax) || 0,
}));

const orderspec = {
id: String(order.id),
ts: Math.floor(new Date(order.date_created).getTime() / 1000),
orderEmail: email,
shippingMethod: order.shipping_method || "Standard",
paymentMethod: order.payment_method || "Unknown",
currency: order.currency_code || "GBP",
priceShippingExVat: shippingExVat,
priceTotalExVat: totalExVat,
priceTotalInclVat: totalInclVat,
products: pmProducts,
};

if (phone) orderspec.customerPhone = phone;
if (shippingCountry) orderspec.shippingCountry = shippingCountry;
if (shippingZipcode) orderspec.shippingZipcode = shippingZipcode;
if (shippingState) orderspec.shippingState = shippingState;

const firstName = order.billing_address?.first_name || null;
const lastName = order.billing_address?.last_name || null;
if (firstName) orderspec.customerFirstname = firstName;
if (lastName) orderspec.customerLastname = lastName;

return orderspec;
}


function buildProfitMetricsUrl(publicId, orderspec) {
const params = new URLSearchParams({
v: "3uh",
pid: publicId,
o: JSON.stringify(orderspec),
});

return `https://my.profitmetrics.io/l.php?${params.toString()}`;
}

Step 4 — Add your secrets

Store your credentials as encrypted environment variables. Run each command and paste the value when prompted:

wrangler secret put BC_STORE_HASH
wrangler secret put BC_ACCESS_TOKEN
wrangler secret put PM_PUBLIC_ID

For PM_PUBLIC_ID, use your ProfitMetrics Public ID (e.g. PUBLIC_ID).

Step 5 — Deploy

wrangler deploy

Wrangler will output your Worker URL. It will look something like:

https://bigcommerce-profitmetrics.<your-subdomain>.workers.dev

Copy this URL — you will need it in Part 3.


Part 3: Create the BigCommerce webhook

Register a webhook so BigCommerce notifies your Worker whenever a new order is created.

Option A — Using the BigCommerce API directly (cURL)

Replace {STORE_HASH}{ACCESS_TOKEN}, and {WORKER_URL} with your values:

curl -X POST \
https://api.bigcommerce.com/stores/{STORE_HASH}/v3/hooks \
-H "X-Auth-Token: {ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"scope": "store/order/created",
"destination": "{WORKER_URL}",
"is_active": true,
"headers": {}
}'

Option B — Using the BigCommerce Control Panel

  1. Go to Settings → API → Webhooks (if available in your BigCommerce plan).
  2. Click Create Webhook.
  3. Set Event to store/order/created.
  4. Set Destination URL to your Cloudflare Worker URL.
  5. Ensure Active is toggled on.
  6. Click Save.

Testing the integration

  1. Place a test order in your BigCommerce store.
  2. Check your Cloudflare Worker logs for confirmation: wrangler tail You should see output like: Received webhook for order 12345 Order 12345 sent to ProfitMetrics successfully
  3. In ProfitMetrics, go to Orders and verify the test order appears with the correct data.

Troubleshooting

Issue Possible cause Solution
Order not appearing in ProfitMetrics Worker not receiving the webhook Run wrangler tail and place a test order. If no log output, re-check the webhook destination URL and that the webhook is active.
502 error in Worker logs BigCommerce API credentials incorrect Verify BC_STORE_HASH and BC_ACCESS_TOKEN are correct. Re-run wrangler secret put if needed.
Order appears but products show wrong SKU SKU field mismatch with product feed The Worker uses product.sku by default. If your ProfitMetrics product feed uses product_id or variant_id, update the buildOrderspec function accordingly.
Order appears but with £0 values BigCommerce returning string "0" for prices Check that the order in BigCommerce has correct tax-exclusive prices. The Worker parses total_ex_tax and total_inc_tax from the V2 API.
"shippingMethod" shows as "Standard" BigCommerce did not return a shipping method This is a fallback default. Verify that the order in BigCommerce has a shipping method assigned.

How it works (technical summary)

The Cloudflare Worker acts as middleware between BigCommerce and ProfitMetrics:

  1. BigCommerce fires a store/order/created webhook containing the order ID.
  2. The Worker calls the BigCommerce V2 Orders API to fetch the full order object and its line items.
  3. The Worker transforms the BigCommerce data into a ProfitMetrics orderspec JSON object, mapping fields like total_ex_tax → priceTotalExVatbilling_address.email → orderEmail, etc.
  4. The Worker sends the orderspec as a URL-encoded GET request to https://my.profitmetrics.io/l.php?v=3uh&pid=PUBLIC_ID&o={orderspec}.

The Cloudflare Workers free tier allows 100,000 requests per day, which is more than sufficient for order volume.


Field mapping reference

The following table shows how BigCommerce order fields are mapped to ProfitMetrics orderspec fields:

ProfitMetrics field BigCommerce source Notes
id order.id Cast to string
ts order.date_created Converted to Unix timestamp (UTC)
orderEmail order.billing_address.email Sent as plaintext; ProfitMetrics handles hashing
customerPhone order.billing_address.phone Digits only, non-numeric characters stripped
customerFirstname order.billing_address.first_name For Google Ads Enhanced Conversions
customerLastname order.billing_address.last_name For Google Ads Enhanced Conversions
shippingMethod order.shipping_method Falls back to "Standard"
shippingCountry shipping_addresses[0].country_iso2 ISO 3166-1 alpha-2; falls back to billing address
shippingZipcode shipping_addresses[0].zip Falls back to billing address
shippingState shipping_addresses[0].state Falls back to billing address
paymentMethod order.payment_method Falls back to "Unknown"
currency order.currency_code ISO 4217 (e.g. "GBP", "EUR")
priceShippingExVat order.shipping_cost_ex_tax Parsed to number
priceTotalExVat order.total_ex_tax Parsed to number
priceTotalInclVat order.total_inc_tax Parsed to number
products[].sku product.sku or product.product_id Must match your ProfitMetrics product feed ID
products[].qty product.quantity
products[].priceExVat product.price_ex_tax Parsed to number

Important notes

SKU matching: The sku field in the ProfitMetrics orderspec does NOT necessarily refer to the actual product SKU. It must match the product identifier used in your ProfitMetrics product feed. This could be the BigCommerce SKU, product ID, or variant ID. Check your feed and update the Worker code if needed.

Checkout timing: If your checkout process means the customer is only identified on the order confirmation page (e.g. using Stripe Checkout or a similar redirect flow), the order should not be transmitted until after the confirmation page has loaded and sent the identify event (setEmail) to ProfitMetrics. In most standard BigCommerce checkout flows this is not an issue, but if you are using a third-party checkout, you may need to use the store/order/statusUpdated webhook instead and add a short delay.

Null fields: Any ProfitMetrics fields that cannot be populated should be omitted from the orderspec entirely. They must not be sent as empty strings or undefined.


Next steps

After completing this guide, continue with the remaining steps of the Server-side Hybrid Universal Integration:

Step 2: Install the Server-side Hybrid Universal Integration script

Step 3: Create a Product Feed for ProfitMetrics