Files
openswarm/frontend/src/shared/subscription/checkout.ts
T
ciregenz c8360cc921 [eric] fix dead annual subscribe buttons: desktop was sending billing_interval="annual" but cloud zod enum only accepted
"monthly"|"yearly" so the POST returned 400 and the click silently no-opped — now map annual→yearly at the fetch boundary,
  UI/types/analytics stay on "annual"
2026-04-21 16:48:05 -07:00

57 lines
1.9 KiB
TypeScript

import { trackEvent } from '@/shared/analytics';
export type OpenSwarmPlan = 'pro' | 'pro_plus' | 'ultra';
export type BillingInterval = 'monthly' | 'annual';
export type CheckoutSource = 'settings' | 'onboarding' | 'upgrade_cta';
interface SubscribeOptions {
wasSubscribed?: boolean;
}
// Kicks off a Stripe Checkout session for the given plan + interval and opens
// the returned URL in the user's default browser (or a new tab fallback).
// All subscribe CTAs across Settings, Onboarding, and the 429 error card go
// through this helper so analytics shape and error handling stay consistent.
export async function subscribeToPlan(
plan: OpenSwarmPlan,
billingInterval: BillingInterval,
source: CheckoutSource,
opts: SubscribeOptions = {},
): Promise<void> {
trackEvent('subscription.subscribe_clicked', {
source,
plan,
billing_interval: billingInterval,
was_subscribed: !!opts.wasSubscribed,
});
try {
// Cloud schema uses "yearly"; the desktop UI/analytics uses "annual".
// Normalize at the boundary so the rest of the client stays consistent.
const wireInterval = billingInterval === 'annual' ? 'yearly' : billingInterval;
const r = await fetch('https://api.openswarm.com/api/stripe/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ plan, billing_interval: wireInterval }),
});
if (!r.ok) {
console.error(`Checkout request failed: ${r.status}`);
return;
}
const { url } = await r.json();
if (!url) return;
trackEvent('subscription.checkout_opened', {
source,
plan,
billing_interval: billingInterval,
});
const api = (window as any).openswarm;
if (api?.openExternal) api.openExternal(url);
else window.open(url, '_blank');
} catch (e) {
console.error('Failed to create checkout session:', e);
}
}