How to migrate from the Shopify app to the Server-side hybrid Universal Integration
Move your order flow off the standard Shopify app when you need control over what is sent to ProfitMetrics — and when. This guide walks through moving an existing store from the ProfitMetrics Shopify app to the Universal Server-side Integration. You'll build your own order flow, replace the app's browser tracking with a custom pixel, and retire the app.
When the Universal Integration is the better fit
The ProfitMetrics Shopify app is the right choice for most stores: it installs in minutes and sends orders automatically. The Universal Server-side Integration is for stores that need control over the order data. Because you build the flow yourself, you can correct, enrich, or delay each order before it reaches ProfitMetrics.
| Scenario | How the Universal Integration helps |
|---|---|
| Volume-based shipping charges — a light product in a very large box is charged by size, not weight, so weight-based shipping cost rules understate the real cost | Send the actual shipping cost with each order using the shippingCostExVat override |
| Costs known only after processing — a third party handles the shipment before the final cost is confirmed, and the Shopify app can't wait | Hold the order in your own system and send it once the final numbers are in |
| Custom data requirements — the order needs values the app doesn't provide | Set exact cost prices, packaging and handling costs, or the final gross profit directly on each order |
The Universal Integration is a hybrid: your server sends the orders, while a small script in the browser handles visitor tracking, consent, and email capture. Both parts are needed — without the browser side, attribution and features like Conversion Booster stop working.
Migration at a glance
The order of these steps matters. The Shopify app keeps running until the new setup is verified, so your profit data stays complete throughout.
- Build your server-side order flow.
- Install the ProfitMetrics consolidated custom pixel in Shopify.
- Send a test order and have it verified.
- Build and install your product feed
- Remove the Shopify app.
- Switch the website type to Custom.
- Add your billing details.
Step 1: Build the server-side order flow
Your flow sends each order to ProfitMetrics as a URL-encoded GET request to https://my.profitmetrics.io/l.php. There are two ways to build it:
Option 1: Use Make.com. Follow Send orders to ProfitMetrics using Make.com — any e-commerce platform. Make watches for new Shopify orders, transforms them into the ProfitMetrics format, and sends them — nothing to host or deploy. Because the trigger filters on order status, you can hold each order until it reaches the status where its costs are final. Using 3rd party tools may incur additional fees
Option 2: Build it yourself. Follow Part 1 of How to set up a full server-side hybrid Universal Integration. It documents the full order format, including the cost overrides, and is the route for an ERP, a fulfilment platform, or code you host.
Whichever option you choose, two parts of the order format matter most for the scenarios above:
- Cost overrides — set
overrides.shippingCostExVatto send the actual shipping cost of the order. Matching override fields exist for payment cost, extra costs such as packaging, and gross profit. - Timing is yours — nothing requires the order to be sent the moment it is placed. If the final cost arrives from a third party later, wait for it. Do send each order after the customer has passed the order confirmation page, where the pixel identifies them — five minutes after purchase is a safe rule of thumb.
Skip Part 2 of the full server-side guide. The Universal Integration script it describes is replaced on Shopify by the consolidated custom pixel in the next step.
Step 2: Install the custom pixel
The Shopify app's theme embed currently loads ProfitMetrics in the browser. The consolidated custom pixel takes over that job: it loads the tracking script across the storefront and checkout, keeps ProfitMetrics in sync with the customer's consent choices through Shopify's Customer Privacy API, and captures the customer's email during checkout for identification and Conversion Booster.
- In your Shopify admin, go to Settings > Customer events.
- Click Add custom pixel (code provided below) and name it
ProfitMetrics Universal. - Under Customer privacy, set Permission to Not required. The pixel manages consent itself — it reads the customer's choices and passes them to ProfitMetrics.
- Paste the full pixel code below into the code editor.
- Replace
PUBLIC_ID_HEREwith the Public ID of your website in ProfitMetrics. - Leave
PM_COOKIE_DOMAINempty unless ProfitMetrics support has told you otherwise. - Click Save, then Connect to activate the pixel.
// ProfitMetrics – Consolidated Universal Custom Pixel (v1)
// Configuration
const PM_PUBLIC_ID = 'PUBLIC_ID_HERE';
const PM_COOKIE_DOMAIN = '';
const PM_EMAIL_SELECTORS = [
'#newsletter_text-footer',
'input[name="contact[email]"]',
'#customer_email',
'#CustomerEmail',
'#email',
'input[name="customer[email]"]',
'#RegisterForm-email',
'#RecoverEmail',
'input[type="email"][name="email"]',
'[data-cf-field-type="email"] input[type="email"]',
].join(', ');
// Consent state
let cc_statistics = init.customerPrivacy?.analyticsProcessingAllowed ?? false;
let cc_marketing = init.customerPrivacy?.marketingAllowed ?? false;
// Email state
let pmEmail = '';
// Script load guard
let pmScriptLoaded = false;
// Core: load bundle.js (once)
function pmLoadScript() {
window.profitMetrics = {
pid: PM_PUBLIC_ID,
cookieStatisticsConsent: cc_statistics,
cookieMarketingConsent: cc_marketing,
emailInputSelector: PM_EMAIL_SELECTORS,
cookieDomain: PM_COOKIE_DOMAIN,
onLoad: () => {
pmTrySetEmail();
},
};
if (pmScriptLoaded) {
if (typeof profitMetrics !== 'undefined') {
profitMetrics.cookieStatisticsConsent = cc_statistics;
profitMetrics.cookieMarketingConsent = cc_marketing;
pmTrySetEmail();
}
return;
}
pmScriptLoaded = true;
const script = document.createElement('script');
script.src = `https://cdn1.profitmetrics.io/${PM_PUBLIC_ID}/bundle.js`;
script.defer = true;
script.onerror = () => {
console.error('ProfitMetrics: Failed to load bundle.js – will retry on next event');
pmScriptLoaded = false; // allow one retry
};
document.head.appendChild(script);
}
// Email helper
function pmTrySetEmail() {
if (!pmEmail) return;
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(pmEmail)) return;
if (typeof profitMetrics !== 'undefined' && typeof profitMetrics.setEmail === 'function') {
profitMetrics.setEmail(pmEmail);
}
}
// Consent: Customer Privacy API
api.customerPrivacy.subscribe('visitorConsentCollected', (event) => {
cc_statistics = event.customerPrivacy?.analyticsProcessingAllowed ?? cc_statistics;
cc_marketing = event.customerPrivacy?.marketingAllowed ?? cc_marketing;
pmLoadScript();
});
// Storefront events
analytics.subscribe('page_viewed', () => {
pmLoadScript();
});
analytics.subscribe('product_viewed', () => {
pmLoadScript();
});
analytics.subscribe('collection_viewed', () => {
pmLoadScript();
});
analytics.subscribe('search_submitted', () => {
pmLoadScript();
});
analytics.subscribe('cart_viewed', () => {
pmLoadScript();
});
analytics.subscribe('product_added_to_cart', () => {
pmLoadScript();
});
// Checkout funnel
analytics.subscribe('checkout_started', (event) => {
const email = event.data?.checkout?.email;
if (email) pmEmail = email;
pmLoadScript();
});
analytics.subscribe('checkout_contact_info_submitted', (event) => {
const email = event.data?.checkout?.email;
if (email) {
pmEmail = email;
pmTrySetEmail();
}
pmLoadScript();
});
analytics.subscribe('checkout_address_info_submitted', (event) => {
const email = event.data?.checkout?.email;
if (email) {
pmEmail = email;
pmTrySetEmail();
}
pmLoadScript();
});
analytics.subscribe('checkout_shipping_info_submitted', (event) => {
const email = event.data?.checkout?.email;
if (email) {
pmEmail = email;
pmTrySetEmail();
}
pmLoadScript();
});
analytics.subscribe('payment_info_submitted', () => {
pmLoadScript();
});
analytics.subscribe('checkout_completed', (event) => {
const email = event.data?.checkout?.email;
if (email) {
pmEmail = email;
pmTrySetEmail();
}
cc_statistics = init.customerPrivacy?.analyticsProcessingAllowed ?? cc_statistics;
cc_marketing = init.customerPrivacy?.marketingAllowed ?? cc_marketing;
pmLoadScript();
});
// End ProfitMetrics Consolidated Universal Pixel
Note: PM_EMAIL_SELECTORS covers the email fields used by common Shopify themes, including newsletter and account forms. If your theme uses a custom email field, add its CSS selector to the list.
Step 3: Add a product feed
ProfitMetrics reads your product cost prices from a product feed. Without it, orders arrive but there are no cost prices to calculate gross profit against.
The product feed is automatically enabled when using the original Shopify app integration, so this needs to be replaced when uninstalling the ProfitMetrics app. Follow How to create a product feed for ProfitMetrics to build one — an XML feed, largely compatible with the Google Shopping feed, listing each sellable product variant with its ID, retail price, and cost price.
The product ID in the feed is how ProfitMetrics ties a tracked event or an order back to a product's cost, so it has to match the item ID used everywhere else:
- The IDs collected on your storefront by the ProfitMetrics browser conversion actions, which now load from the custom pixel.
- The IDs sent with each order by your server-side flow.
On Shopify, use the long internal Shopify product ID as the item ID in all three places — the feed, the browser conversion actions, and the order. If these don't match, ProfitMetrics can't connect an order or a tracked event to the product's cost price, and profit figures will be wrong.
Step 4: Send a test order and have it verified
Keep the Shopify app installed, place a test order, and let your new flow send it to ProfitMetrics. Then contact support@profitmetrics.io — we'll confirm the order has been received correctly and that tracking data has been applied to it.
Don't remove the Shopify app before this confirmation. The app is still your live order source, and removing it early leaves a hole in your profit data.
Step 5: Remove the Shopify app
Once your test order is confirmed, uninstall the app. In your Shopify admin, go to Settings > Apps and sales channels, open ProfitMetrics and click Uninstall — the normal Shopify app deletion process. This removes the app's order sync and its theme app embed together, and it ends the app subscription billed through Shopify.
Step 6: Switch the website type to Custom
Your site is currently registered in ProfitMetrics as a Shopify website, and it now needs to be a Custom one so ProfitMetrics stops expecting data from the app. Let us know once the app is removed — reply on the thread from your test-order check, or email support@profitmetrics.io — and we'll change the website type for you.
Step 7: Add your billing details
Uninstalling the app ended your subscription through Shopify, so billing moves to ProfitMetrics directly. Follow How to add a credit card to your account to add your payment details and keep your account running without interruption.
Still stuck? Contact us at support@profitmetrics.io and we'll help you get set up.