Refuel Docs

Shopify Setup

Integrate Refuel with your Shopify store to run a full affiliate program. This guide covers widget installation, automatic conversion tracking via Shopify webhooks, and troubleshooting common issues.

Prerequisites

  • A Refuel account with your company registered (see Getting Started)
  • Your publishable key (pk_...) and secret API key (sk_...)
  • A Shopify store on any plan (Basic, Shopify, Advanced, or Plus)
  • Access to your Shopify admin panel

1Install the Affiliate Widget

Add the Refuel widget script to your Shopify theme so the affiliate signup button appears on every page of your store.

Option A: Theme Editor (Recommended)

  1. In your Shopify admin, go to Online Store → Themes
  2. Click Actions → Edit code on your active theme
  3. Open theme.liquid from the Layout folder
  4. Find the closing </body> tag
  5. Paste the following snippet directly above it:
theme.liquidhtml
{% comment %} Refuel Affiliate Widget {% endcomment %}
<script
  src="https://sdk.refuel.dev/v1.js"
  data-key="pk_your_publishable_key"
  data-position="bottom-right"
  async
></script>

Option B: Shopify App Embed

If you prefer not to edit theme code, you can use the Refuel Shopify app (if available) which adds the widget as an app embed block via the theme customizer. Go to Online Store → Themes → Customize → App embeds and toggle on Refuel.

2Set Up Conversion Tracking

Conversion tracking requires a server-side integration so that Refuel is notified when an order is placed. The most reliable approach is using Shopify webhooks.

Create a Shopify Webhook

  1. Go to Settings → Notifications → Webhooks in your Shopify admin
  2. Click Create webhook
  3. Set the event to Order payment
  4. Set the format to JSON
  5. Set the URL to your webhook handler (see below)
  6. Click Save

Webhook Handler

You need a small server-side handler that receives the Shopify order webhook and forwards the conversion to Refuel. Here is an example using Express.js:

shopify-webhook-handler.tstypescript
import express from "express";
import crypto from "crypto";

const app = express();
const SHOPIFY_SECRET = process.env.SHOPIFY_WEBHOOK_SECRET!;
const REFUEL_API_KEY = process.env.REFUEL_API_KEY!; // your sk_... key

app.post(
  "/webhooks/shopify/orders",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    // Verify Shopify signature
    const hmac = req.headers["x-shopify-hmac-sha256"] as string;
    const hash = crypto
      .createHmac("sha256", SHOPIFY_SECRET)
      .update(req.body)
      .digest("base64");

    if (hmac !== hash) {
      return res.status(401).send("Invalid signature");
    }

    const order = JSON.parse(req.body.toString());

    // Extract the affiliate code from the order's landing page URL
    // Shopify stores the landing_site in the order object
    const landingSite = order.landing_site || "";
    const refMatch = landingSite.match(/[?&]ref=([^&]+)/);

    if (!refMatch) {
      // No affiliate attribution -- skip
      return res.json({ ok: true, attributed: false });
    }

    const affiliateCode = refMatch[1];

    // Send conversion to Refuel
    const response = await fetch("https://api.refuel.dev/track/conversion", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": REFUEL_API_KEY,
      },
      body: JSON.stringify({
        orderId: String(order.id),
        orderValue: Math.round(parseFloat(order.total_price) * 100), // convert to cents
        currency: order.currency,
        affiliateCode,
        metadata: {
          shopifyOrderNumber: order.order_number,
          customerEmail: order.email,
        },
      }),
    });

    const result = await response.json();
    console.log("Refuel conversion:", result);

    res.json({ ok: true, attributed: true, conversionId: result.data?.conversionId });
  }
);

app.listen(3000);

3How Order Attribution Works

Understanding the attribution flow helps you debug issues and optimize your program.

1

Affiliate shares link

The affiliate shares a link like https://yourstore.com?ref=abc123

2

Visitor clicks the link

The Refuel SDK detects the ?ref= parameter and records a click event with the visitor's fingerprint, IP hash, and landing page.

3

Visitor browses and purchases

Shopify stores the landing_site URL (including the ?ref= parameter) on the order object.

4

Order webhook fires

Shopify sends the order/payment webhook to your handler. Your handler extracts the ref code and sends it to Refuel.

5

Refuel attributes the conversion

Refuel looks up the affiliate link, finds the most recent click within the attribution window (default 30 days), calculates the commission split, and runs fraud detection.

6

Commission is credited

If the conversion passes fraud checks (or is manually approved), the affiliate's pending balance is credited. Payouts happen on the configured schedule via Stripe Connect.

Attribution window: By default, a conversion is attributed to an affiliate if the click happened within the last 30 days. You can configure this (1-90 days) in your company settings.

4Configure Your Program

Fine-tune your affiliate program settings from the Refuel dashboard or via the API.

SettingDefaultDescription
commissionRate0.10 (10%)Percentage of order value paid as commission
attributionWindow30 daysHow long after a click a conversion can be attributed
payoutSchedulemonthlyHow often payouts are sent (weekly, biweekly, monthly)
payoutMinimum$50.00Minimum balance required before a payout is triggered

Troubleshooting

Widget does not appear on my store

  • Verify the script tag is inside theme.liquid, before </body>
  • Check that the data-key matches your publishable key exactly
  • Open your browser console and look for any Refuel-related errors
  • Ensure your company status is "active" (not paused)
  • Try a hard refresh (Ctrl+Shift+R) to clear cached theme files

Conversions are not being tracked

  • Check that your webhook handler is receiving Shopify order events (check server logs)
  • Verify the Shopify webhook URL is correct and publicly accessible
  • Ensure the x-api-key in your handler matches your secret key
  • Check that the order's landing_site contains the ?ref= parameter
  • Test with a manual curl request to POST /track/conversion

Affiliate code is missing from the order

  • Shopify only stores landing_site for the first page the customer visits. If they navigate away and come back without the ?ref= parameter, it will be lost.
  • Consider using the Refuel SDK's cookie-based tracking as a fallback. The SDK stores the ref code in a first-party cookie that persists across page views.
  • For Shopify Plus stores, you can add the ref code to the checkout as a cart attribute for more reliable attribution.

Duplicate conversion errors

  • The Refuel API rejects duplicate conversions by order ID. If you see DUPLICATE errors, the conversion was already recorded.
  • Shopify may send the order/payment webhook multiple times. Your handler should gracefully handle 409 responses.

Conversion stuck in "pending" status

  • Conversions with fraud flags require manual review in your Refuel dashboard
  • Go to Dashboard → Conversions and filter by "pending" to review flagged conversions
  • You can approve or reject each one, or adjust your fraud sensitivity in settings

Alternative: Thank-You Page Script

If you cannot set up a webhook handler, you can track conversions client-side by adding a script to the Shopify order confirmation (thank-you) page. This approach is less reliable than server-side webhooks but requires no backend.

  1. Go to Settings → Checkout → Order status page → Additional scripts
  2. Paste the following script:
Shopify thank-you page scripthtml
{% if first_time_accessed %}
<script>
  // Read the affiliate code from the Refuel cookie
  const refCode = document.cookie
    .split("; ")
    .find(c => c.startsWith("_refuel_ref="))
    ?.split("=")[1];

  if (refCode) {
    fetch("https://api.refuel.dev/track/conversion", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": "sk_your_secret_key"  // Note: exposed client-side
      },
      body: JSON.stringify({
        orderId: "{{ order.id }}",
        orderValue: {{ order.total_price | times: 100 | round }},
        currency: "{{ order.currency }}",
        affiliateCode: refCode
      })
    });
  }
</script>
{% endif %}

Warning: The thank-you page approach exposes your secret API key in client-side JavaScript. For production use, we strongly recommend the server-side webhook approach described above, or proxy the conversion call through a serverless function.