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 | Provides invoice context and records out-of-band payment facts (required) |
invoice_payment.paid | Records the canonical payment for modern Stripe invoices (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) |
Databuddy expects Stripe API version 2025-05-28.basil or later. Keep payment_intent.succeeded enabled for one-time payments; invoice_payment.paid is the canonical event for payments attached to an invoice. Databuddy uses the immutable InvoicePayment ID, so the same allocation embedded in invoice.paid and delivered later as invoice_payment.paid is counted once. invoice.paid also supplies the invoice-level remainder for payments made outside Stripe; Databuddy records that remainder only when Stripe includes a complete payments list.
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?