diff --git a/apps/revenue/stripe/stripe-saas-demo/backend/routes/checkout.ts b/apps/revenue/stripe/stripe-saas-demo/backend/routes/checkout.ts index 6b1ecb095..18f61b6f1 100644 --- a/apps/revenue/stripe/stripe-saas-demo/backend/routes/checkout.ts +++ b/apps/revenue/stripe/stripe-saas-demo/backend/routes/checkout.ts @@ -1,5 +1,6 @@ import { Router } from "express"; import { stripe } from "../stripe"; +import { getUser } from "../users"; import { posthog } from "../posthog"; export const checkoutRouter = Router(); @@ -15,14 +16,20 @@ checkoutRouter.post("/", async (req, res) => { } const frontendUrl = process.env.FRONTEND_URL || "http://localhost:5173"; + const posthogDistinctId = userId ? getUser(userId)?.posthogDistinctId : undefined; + const posthogMetadata = posthogDistinctId + ? { posthog_person_distinct_id: posthogDistinctId } + : undefined; const session = await stripe.checkout.sessions.create({ mode: "subscription", line_items: [{ price: priceId, quantity: 1 }], success_url: `${frontendUrl}/dashboard?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${frontendUrl}/?canceled=true`, - client_reference_id: userId, + client_reference_id: posthogDistinctId, customer_email: customerEmail, + metadata: posthogMetadata, + subscription_data: { metadata: posthogMetadata }, }); res.json({ url: session.url }); diff --git a/apps/revenue/stripe/stripe-saas-demo/backend/routes/customers.ts b/apps/revenue/stripe/stripe-saas-demo/backend/routes/customers.ts index b80152dbf..4355d26c8 100644 --- a/apps/revenue/stripe/stripe-saas-demo/backend/routes/customers.ts +++ b/apps/revenue/stripe/stripe-saas-demo/backend/routes/customers.ts @@ -24,6 +24,9 @@ customersRouter.post("/", async (req, res) => { const customer = await stripe.customers.create({ email, name, + metadata: posthogDistinctId + ? { posthog_person_distinct_id: posthogDistinctId } + : undefined, }); const user = existing diff --git a/apps/revenue/stripe/stripe-saas-demo/backend/routes/subscriptions.ts b/apps/revenue/stripe/stripe-saas-demo/backend/routes/subscriptions.ts index 37b533d37..3879fdae1 100644 --- a/apps/revenue/stripe/stripe-saas-demo/backend/routes/subscriptions.ts +++ b/apps/revenue/stripe/stripe-saas-demo/backend/routes/subscriptions.ts @@ -15,16 +15,17 @@ subscriptionsRouter.post("/", async (req, res) => { return; } - if (userId) { - const user = getUser(userId); - if (user) { - console.log(`Creating subscription for user ${user.email} (${user.id})`); - } + const user = userId ? getUser(userId) : undefined; + if (user) { + console.log(`Creating subscription for user ${user.email} (${user.id})`); } const subscription = await stripe.subscriptions.create({ customer: customerId, items: [{ price: priceId }], + metadata: user?.posthogDistinctId + ? { posthog_person_distinct_id: user.posthogDistinctId } + : undefined, payment_behavior: "default_incomplete", payment_settings: { save_default_payment_method: "on_subscription" }, expand: ["latest_invoice.payment_intent"], diff --git a/apps/revenue/stripe/stripe-saas-demo/posthog-revenue-report.md b/apps/revenue/stripe/stripe-saas-demo/posthog-revenue-report.md new file mode 100644 index 000000000..caa8fffa5 --- /dev/null +++ b/apps/revenue/stripe/stripe-saas-demo/posthog-revenue-report.md @@ -0,0 +1,33 @@ +# PostHog Revenue Analytics Setup + +## Changes made + +- Traced the project’s PostHog person identifier from `posthog.identify(email, …)` and the frontend’s `posthog.get_distinct_id()` value. +- Added `posthog_person_distinct_id` metadata to new Stripe Customer objects when the identifier is available. +- Added the same metadata to custom-flow Stripe Subscription objects using the identifier stored on the local user. +- Added the identifier to Stripe Checkout Session metadata and the Checkout-created Subscription metadata. +- Changed Checkout `client_reference_id` to use the PostHog distinct ID, allowing webhook processing to recover the same identity. +- Configured the provided PostHog project token and host through frontend environment variables. +- Added no Stripe API calls, packages, or unrelated payment-flow changes. + +## Files modified or created + +- Modified `backend/routes/customers.ts` +- Modified `backend/routes/subscriptions.ts` +- Modified `backend/routes/checkout.ts` +- Created `frontend/.env` with `VITE_POSTHOG_API_KEY` and `VITE_POSTHOG_HOST` +- Created `posthog-revenue-report.md` + +## Verification + +- Re-read all modified routes and confirmed metadata uses the existing PostHog distinct ID rather than a fabricated Stripe or internal identifier. +- Confirmed the environment keys are present in `frontend/.env`. +- Confirmed no new Stripe API operations or dependencies were introduced. +- The backend package does not define a build, typecheck, or lint script, so verification was performed by targeted source review. + +## Manual next steps + +1. Run both the hosted Checkout and custom card subscription flows with Stripe test data. +2. Confirm the resulting Stripe Customer, Checkout Session, and Subscription objects contain `posthog_person_distinct_id` metadata. +3. Ensure the Stripe source is connected and syncing in the intended PostHog project, then allow the revenue tables and customer analytics views time to refresh. +4. Keep `frontend/.env` local and out of version control; configure the same environment variables in the deployment platform.