Paddle
Set up revenue attribution with Paddle.
Paddle
Paddle is a merchant of record that handles payments, taxes, and subscriptions globally. This guide shows how to pass EngageTrack visitor and session IDs through Paddle's custom_data field for revenue attribution.
How It Works
- The EngageTrack SDK generates a visitor ID and session ID (stored in browser storage, accessible via the SDK's JavaScript API)
- When opening a Paddle checkout, you pass these values in the
custom_datafield - After payment, Paddle sends a webhook to EngageTrack with the custom data
- EngageTrack reads the visitor ID and attributes the revenue to the traffic source
Overlay Checkout (Paddle.js)
The most common Paddle integration uses the overlay checkout via Paddle.js. Pass the EngageTrack IDs in the customData option:
<script src="https://cdn.paddle.com/paddle/v2/paddle.js"></script>
<script>
Paddle.Initialize({
token: "live_xxxxxxxxxxxxx", // your client-side token
});
function openCheckout(priceId) {
const visitorId = window.engagetrack?.getVisitorId() || "";
const sessionId = window.engagetrack?.getSessionId() || "";
Paddle.Checkout.open({
items: [{ priceId: priceId, quantity: 1 }],
customData: {
engagetrack_visitor_id: visitorId,
engagetrack_session_id: sessionId,
},
settings: {
successUrl: "https://yoursite.com/success",
},
});
}
</script>
<button onclick="openCheckout('pri_xxxxxxxxxxxxx')">Subscribe</button>React / Next.js
"use client";
import { initializePaddle, type Paddle } from "@paddle/paddle-js";
import { useEffect, useState } from "react";
export default function CheckoutButton({ priceId }: { priceId: string }) {
const [paddle, setPaddle] = useState<Paddle | null>(null);
useEffect(() => {
initializePaddle({
token: process.env.NEXT_PUBLIC_PADDLE_CLIENT_TOKEN!,
}).then(setPaddle);
}, []);
function handleCheckout() {
if (!paddle) return;
const visitorId = window.engagetrack?.getVisitorId() || "";
const sessionId = window.engagetrack?.getSessionId() || "";
paddle.Checkout.open({
items: [{ priceId, quantity: 1 }],
customData: {
engagetrack_visitor_id: visitorId,
engagetrack_session_id: sessionId,
},
settings: {
successUrl: "https://yoursite.com/success",
},
});
}
return <button onClick={handleCheckout}>Subscribe</button>;
}Paddle uses custom_data to pass arbitrary data through the checkout.
EngageTrack expects the keys engagetrack_visitor_id and
engagetrack_session_id inside this object.
Checkout API (Server-Side)
If you create transactions server-side using the Paddle API, attach the custom data there:
import { Paddle } from "@paddle/paddle-node-sdk";
const paddle = new Paddle(process.env.PADDLE_API_KEY);
app.post("/create-checkout", async (req, res) => {
// Receive visitor/session IDs from the client request body
const { engagetrack_visitor_id: visitorId, engagetrack_session_id: sessionId } = req.body;
const transaction = await paddle.transactions.create({
items: [
{
priceId: "pri_xxxxxxxxxxxxx",
quantity: 1,
},
],
customData: {
engagetrack_visitor_id: visitorId,
engagetrack_session_id: sessionId,
},
});
res.json({ transactionId: transaction.id });
});Set Up the Webhook
- Go to Paddle Dashboard → Developer Tools → Notifications
- Click New destination
- Select Webhook as the type
- Enter the webhook URL:
https://api.engagetrack.net/api/v1/webhooks/revenue/paddle/{YOUR_SITE_PUBLIC_ID}
- Under Events, select:
transaction.completed(handles both one-time payments and subscription renewals — renewals are detected whenoriginissubscription_recurring)adjustment.created(for refunds)
- Click Save
If you connected Paddle via the EngageTrack dashboard (Site Settings → Revenue), the webhook is registered automatically and you can skip this step.
Verify
- Open your site and confirm the SDK is loaded by running
window.engagetrack.getVisitorId()in the browser console - Complete a test purchase using Paddle's sandbox environment
- Go to your EngageTrack dashboard → Revenue tab
- The test payment should appear within a few seconds, attributed to your traffic source
If the payment appears without attribution:
- Open the Paddle transaction in your Paddle dashboard and check the
custom_datafield containsengagetrack_visitor_id - Verify webhook delivery in Paddle Dashboard → Developer Tools → Notifications → select destination → Event logs
- Ensure the EngageTrack SDK is loaded before
Paddle.Checkout.open()is called