Integrations

Payments

Connect your payment provider and every transaction shows up in the Revenue dashboard, attributed to the session, referrer, and user that produced it.

TL;DR: Generate a webhook URL in the dashboard, add it to Stripe or Paddle, and pass Databuddy IDs in your payment metadata.


1. Generate your webhook URL

  1. Open your Databuddy dashboard and go to Website → Revenue.
  2. Click Generate webhook URLs. You get one URL per provider, each with a unique secret path.
  3. Keep this page open, you'll paste the URL into your provider next.

2. Add the webhook to your provider

In the Stripe dashboard, add an endpoint with your generated URL and subscribe to these events:

EventPurpose
payment_intent.succeededRecords successful one-time payments and payment context (required)
invoice.paidCarries the invoice metadata that links a payment to a visitor (required)
invoice_payment.paidRecords the canonical payment for modern Stripe invoices (required on API 2025-05-28.basil or later)
payment_intent.payment_failedTracks failed payment attempts (required)
payment_intent.canceledTracks canceled payment attempts (required)
invoice.payment_failedTracks failed invoice attempts and retries (required)
charge.refundedRecords each refund (required)

Databuddy expects Stripe API version 2025-05-28.basil or later, because invoice_payment.paid was introduced in that version and is the canonical money event for anything billed through an invoice. Keep payment_intent.succeeded enabled for one-time payments.

invoice.paid and invoice_payment.paid carry different halves of the same fact, and you need both. The payment event carries the amount but references its invoice by ID only, so it cannot see your metadata. The invoice event carries the metadata but no payment amount, because Stripe does not include the invoice's payments list in webhook deliveries. Databuddy records the amount from the payment event and joins the metadata from the invoice event, which is why a subscription payment stays unattributed if only one of the two is enabled. Order does not matter, and redelivering either event is safe.

Paste the endpoint's signing secret into the Revenue settings so Databuddy can verify each delivery.

In the Paddle dashboard, create a notification destination with your generated URL and subscribe to transaction.completed.

Paste the webhook secret key into the Revenue settings so Databuddy can verify each delivery.

3. Pass Databuddy IDs with each payment

Metadata connects a payment to the visitor who made it. Include what you have: every field is optional, and more fields mean stronger attribution.

ts
import { getProfileId, getTrackingIds } from "@databuddy/sdk";

// In the browser, collect the visitor's IDs...
const { anonId, sessionId } = getTrackingIds();
const profileId = getProfileId();

// ...send them to your server and reuse the same metadata:
const databuddyMetadata = {
databuddy_client_id: websiteId,
databuddy_session_id: sessionId,
databuddy_anonymous_id: anonId,
databuddy_profile_id: profileId,
};

await stripe.paymentIntents.create({
amount,
currency,
metadata: databuddyMetadata,
});

// For recurring billing, attach it to the Subscription too.
await stripe.subscriptions.create({
customer,
items: [{ price: priceId }],
metadata: databuddyMetadata,
});

Stripe does not automatically copy PaymentIntent metadata to future subscription invoices. Put the IDs on the Subscription (or subscription_data.metadata when using Checkout) so recurring revenue stays attributable.

ts
import { getProfileId, getTrackingIds } from "@databuddy/sdk";

const { anonId, sessionId } = getTrackingIds();

Paddle.Checkout.open({
items,
customData: {
  website_id: websiteId,
  session_id: sessionId,
  anonymous_id: anonId,
  profile_id: getProfileId(),
},
});
FieldWhat it unlocks
Session IDTies revenue to the exact visit, referrer, and campaign
Anonymous IDTies revenue to the device even without a session
Profile IDTies revenue to the identified user, see Identify Users

Anonymous IDs are hashed on arrival, the same way the tracker hashes them; raw device IDs are never stored. Profile IDs are your own user IDs and are stored as sent.

Verify it works

Send a test payment (Stripe test mode works), then check Website → Revenue. The transaction appears within seconds of the webhook delivery. If you passed a profile ID, the revenue also shows on that user in Website → Users.

Troubleshooting

  • Nothing appears: confirm the webhook URL matches the generated one exactly and the signing secret is saved in Revenue settings; deliveries with bad signatures are rejected.
  • Revenue appears but isn't attributed: the payment had no Databuddy metadata. Attribution only works for payments created after you started passing IDs.
  • One payment type never attributes: check that your endpoint subscribes to every required event. Stripe subscribes an endpoint to a fixed event list, and adding a new payment flow does not update it. Revenue settings shows when Databuddy last recorded each event, which helps you spot a type that stopped arriving, but an event that simply has not happened yet (a refund, a failed payment) also shows no activity, so compare it against the event list in your Stripe dashboard rather than treating it as proof.
  • A session ID you sent isn't recognised: it has to come from the browser tracker. IDs generated on the server, or sent through server-side track(), never produce a joinable session.
  • Stripe shows failed deliveries: regenerating webhook URLs invalidates the old path, so update Stripe after regenerating.

How is this guide?