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
2. Add the webhook to your provider
In the Stripe dashboard, add an endpoint with your generated URL and subscribe to these events:
| Event | Purpose |
|---|---|
payment_intent.succeeded | Records successful one-time payments and payment context (required) |
invoice.paid | Records invoice and subscription context (required) |
invoice_payment.paid | Records exact invoice allocations on modern Stripe versions (required on API 2025-05-28.basil or later) |
payment_intent.payment_failed | Tracks failed payment attempts (required) |
payment_intent.canceled | Tracks canceled payment attempts (required) |
invoice.payment_failed | Tracks failed invoice attempts and retries (required) |
charge.refunded | Records each refund (required) |
Stripe introduced invoice_payment.paid in 2025-05-28.basil. Subscribe to it on that version or later for exact allocation details. Databuddy also keeps the total from invoice.paid as a safe fallback, so adding the newer event after an API upgrade does not drop or double-count revenue. Earlier versions, including the 2025-03-31.basil transition, use embedded invoice allocations when the full list is available.
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.
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.
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(),
},
});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
How is this guide?