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 | Carries the invoice metadata that links a payment to a visitor (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, 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.
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?