Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Router } from "express";
import { stripe } from "../stripe";
import { getUser } from "../users";
import { posthog } from "../posthog";

export const checkoutRouter = Router();
Expand All @@ -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 });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
33 changes: 33 additions & 0 deletions apps/revenue/stripe/stripe-saas-demo/posthog-revenue-report.md
Original file line number Diff line number Diff line change
@@ -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.
Loading