50 lines · 2.2 KB
import { env } from "cloudflare:workers";
import { stripeEnabled } from "./billing/config";
import { customerFor } from "./billing/checkout";
import { billingSettings } from "./metering/settings";

const SUPPORTER_CACHE_SECONDS = 10 * 60;
/** "Not now" on the Pro nudge hides it for about six months. */
export const NUDGE_SNOOZE_SECONDS = 182 * 24 * 60 * 60;

const supporterKey = (accountId: string) => `supporter:v1:${accountId}`;
const nudgeKey = (userId: string) => `pro-nudge-dismissed:v1:${userId}`;

async function computeSupporter(accountId: string): Promise<boolean> {
  const [settings, customer] = await Promise.all([billingSettings(accountId), customerFor(accountId)]);
  return settings.plan.id === "pro" || customer !== null;
}

/**
 * Whether an account is on Pro or has a card on file with Stripe. Supporters get the Pro badge and never
 * see the upgrade nudge. Always false without Stripe billing (self-hosted instances have nothing to sell).
 */
export async function isSupporter(accountId: string): Promise<boolean> {
  if (!stripeEnabled()) return false;
  const cached = await env.KV.get(supporterKey(accountId));
  if (cached !== null) return cached === "1";
  const value = await computeSupporter(accountId);
  await env.KV.put(supporterKey(accountId), value ? "1" : "0", { expirationTtl: SUPPORTER_CACHE_SECONDS });
  return value;
}

/** Drops the cached supporter flag, e.g. right after checkout or a subscription change. */
export async function forgetSupporter(accountId: string): Promise<void> {
  await env.KV.delete(supporterKey(accountId));
}

export async function nudgeDismissed(userId: string): Promise<boolean> {
  return (await env.KV.get(nudgeKey(userId))) !== null;
}

export async function dismissNudge(userId: string): Promise<void> {