fix: payment upgrade issue (#918)

This commit is contained in:
MaheshtheDev 2026-05-09 19:52:21 +00:00
parent 8f6c16de49
commit 1acc156363
6 changed files with 194 additions and 189 deletions

View file

@ -243,13 +243,19 @@ export function ConnectContent({ selectedProject }: ConnectContentProps) {
const handleUpgrade = async () => {
setIsUpgrading(true)
try {
await autumn.attach({
const result = await autumn.attach({
planId: "api_pro",
successUrl: window.location.href,
})
if (result?.paymentUrl) {
window.open(result.paymentUrl, "_self")
return
}
autumn.refetch?.()
} catch (error) {
console.error("Upgrade error:", error)
toast.error("Failed to start upgrade process")
} finally {
setIsUpgrading(false)
}
}

View file

@ -393,13 +393,19 @@ export function AddDocument({
onClick={async () => {
setIsUpgrading(true)
try {
await autumn.attach({
const result = await autumn.attach({
planId: "api_pro",
successUrl: "https://app.supermemory.ai/settings#account",
successUrl: `${window.location.origin}/settings#account`,
})
window.location.reload()
if (result?.paymentUrl) {
window.open(result.paymentUrl, "_self")
return
}
autumn.refetch?.()
} catch (error) {
console.error(error)
toast.error("Failed to start checkout. Please try again.")
} finally {
setIsUpgrading(false)
}
}}

View file

@ -495,13 +495,18 @@ export function PluginsDetail() {
const handleUpgrade = async () => {
try {
await autumn.attach({
const result = await autumn.attach({
planId: "api_pro",
successUrl: "https://app.supermemory.ai/?view=integrations",
successUrl: `${window.location.origin}/?view=integrations`,
})
window.location.reload()
if (result?.paymentUrl) {
window.open(result.paymentUrl, "_self")
return
}
autumn.refetch?.()
} catch (error) {
console.error(error)
toast.error("Failed to start checkout. Please try again.")
}
}

View file

@ -9,7 +9,6 @@ import {
} from "@/hooks/use-account-settings"
import { Avatar, AvatarFallback, AvatarImage } from "@ui/components/avatar"
import { useTokenUsage } from "@/hooks/use-token-usage"
import { formatUsageNumber } from "@/lib/billing-utils"
import {
Dialog,
DialogContent,
@ -57,36 +56,123 @@ function SettingsCard({ children }: { children: React.ReactNode }) {
)
}
function PlanFeatureRow({
icon,
text,
variant = "muted",
function PlanComparisonCard({
name,
price,
period,
description,
credits,
features,
highlight,
}: {
icon: "check" | "x"
text: string
variant?: "muted" | "highlight"
name: string
price: string
period: string
description: string
credits: string
features: string[]
highlight: boolean
}) {
return (
<div className="flex items-center gap-2">
{icon === "check" ? (
<Check
className={cn(
"size-4 shrink-0",
variant === "highlight" ? "text-[#4BA0FA]" : "text-[#737373]",
)}
/>
) : (
<X className="size-4 shrink-0 text-[#737373]" />
<div
className={cn(
"relative flex flex-col gap-3 p-4 rounded-[10px] overflow-hidden",
highlight
? "bg-[#1B1F24] border border-[#4BA0FA]/30 shadow-[0px_2.842px_14.211px_rgba(0,0,0,0.25)]"
: "border border-white/10",
)}
<span
>
<div className="flex items-center justify-between">
<p
className={cn(
dmSans125ClassName(),
"font-mono uppercase tracking-[0.12em] text-[10px]",
highlight ? "text-[#4BA0FA]" : "text-[#737373]",
)}
>
{name}
</p>
{highlight && (
<span className="bg-[#4BA0FA] text-[#00171A] text-[10px] font-bold tracking-[0.36px] px-1.5 py-0.5 rounded-[3px]">
RECOMMENDED
</span>
)}
</div>
<div className="flex items-baseline gap-1">
<span
className={cn(
dmSans125ClassName(),
"font-bold text-[28px] leading-none text-[#FAFAFA] tabular-nums",
)}
>
{price}
</span>
{period && (
<span
className={cn(dmSans125ClassName(), "text-[12px] text-[#737373]")}
>
{period}
</span>
)}
</div>
<p
className={cn(
dmSans125ClassName(),
"text-[14px] tracking-[-0.14px]",
variant === "highlight" ? "text-white" : "text-[#737373]",
"text-[12px] tracking-[-0.12px] text-[#A3A3A3] leading-snug",
)}
>
{text}
</span>
{description}
</p>
<div
className={cn(
"flex items-center gap-2 rounded-lg px-3 py-2",
highlight ? "bg-[#4BA0FA]/10" : "bg-white/5",
)}
>
<div className="min-w-0">
<p
className={cn(
dmSans125ClassName(),
"font-semibold text-[12px] tabular-nums leading-none",
highlight ? "text-[#4BA0FA]" : "text-[#A3A3A3]",
)}
>
{credits}
</p>
<p
className={cn(
dmSans125ClassName(),
"mt-1 text-[10px] leading-none",
highlight ? "text-[#4BA0FA]/70" : "text-[#737373]",
)}
>
of usage included
</p>
</div>
</div>
<ul className="flex flex-col gap-2">
{features.map((text) => (
<li
key={text}
className={cn(
dmSans125ClassName(),
"flex items-start gap-2 text-[12px] tracking-[-0.12px] leading-snug text-[#A3A3A3]",
)}
>
<Check
className={cn(
"mt-0.5 size-3 shrink-0",
highlight ? "text-[#4BA0FA]" : "text-[#737373]",
)}
/>
<span>{text}</span>
</li>
))}
</ul>
</div>
)
}
@ -152,8 +238,8 @@ export default function Account() {
}
const {
tokensUsed,
searchesUsed,
usdIncluded,
usdSpent,
planUsagePct,
currentPlan,
hasPaidPlan,
@ -161,6 +247,12 @@ export default function Account() {
daysRemaining,
} = useTokenUsage(autumn)
const formatUsd = (n: number) =>
n.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
const planDisplayNames: Record<string, string> = {
free: "Free",
pro: "Pro",
@ -172,13 +264,19 @@ export default function Account() {
const handleUpgrade = async () => {
setIsUpgrading(true)
try {
await autumn.attach({
const result = await autumn.attach({
planId: "api_pro",
successUrl: "https://app.supermemory.ai/settings#account",
successUrl: `${window.location.origin}/settings#account`,
})
window.location.reload()
if (result?.paymentUrl) {
window.open(result.paymentUrl, "_self")
return
}
autumn.refetch?.()
} catch (error) {
console.error(error)
toast.error("Failed to start checkout. Please try again.")
} finally {
setIsUpgrading(false)
}
}
@ -418,7 +516,7 @@ export default function Account() {
? "#ef4444"
: "linear-gradient(to right, #4BA0FA 80%, #002757 100%)",
}}
title={`${formatUsageNumber(tokensUsed)} tokens · ${formatUsageNumber(searchesUsed)} queries`}
title={`$${formatUsd(usdSpent)} of $${formatUsd(usdIncluded)} used`}
/>
</div>
<p
@ -427,14 +525,9 @@ export default function Account() {
"text-sm tracking-[-0.14px] text-[#737373] tabular-nums",
)}
>
{formatUsageNumber(tokensUsed)} tokens ·{" "}
{formatUsageNumber(searchesUsed)} queries
{daysRemaining !== null && (
<>
{" · resets in "}
{daysRemaining} day{daysRemaining !== 1 ? "s" : ""}
</>
)}
{daysRemaining !== null
? `Resets in ${daysRemaining} day${daysRemaining !== 1 ? "s" : ""}`
: ""}
</p>
</div>
@ -457,73 +550,6 @@ export default function Account() {
Manage billing
<div className="absolute inset-0 pointer-events-none rounded-[inherit] shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.7)]" />
</button>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{/* Free plan card */}
<div className="flex flex-col gap-4 p-4 rounded-[10px] border border-white/10 overflow-hidden">
<p
className={cn(
dmSans125ClassName(),
"font-medium text-[16px] tracking-[-0.16px] text-[#FAFAFA]",
)}
>
Free plan
</p>
<div className="flex flex-col gap-2">
<PlanFeatureRow icon="check" text="1M tokens / month" />
<PlanFeatureRow icon="check" text="10K search queries" />
<PlanFeatureRow icon="x" text="No connections" />
<PlanFeatureRow icon="check" text="Basic support" />
</div>
</div>
{/* Current plan card - highlighted */}
<div
className={cn(
"flex flex-col gap-4 p-4 rounded-[10px]",
"bg-[#1B1F24]",
"shadow-[0px_2.842px_14.211px_rgba(0,0,0,0.25)]",
"relative overflow-hidden",
)}
>
<div className="flex items-center justify-between">
<p
className={cn(
dmSans125ClassName(),
"font-medium text-[16px] tracking-[-0.16px] text-[#FAFAFA]",
)}
>
{planDisplayNames[currentPlan]} plan
</p>
<span className="bg-[#4BA0FA] text-[#00171A] text-[12px] font-bold tracking-[0.36px] px-1 py-[3px] rounded-[3px] h-[18px] flex items-center justify-center">
ACTIVE
</span>
</div>
<div className="flex flex-col gap-2">
<PlanFeatureRow
icon="check"
text="3M tokens / month"
variant="highlight"
/>
<PlanFeatureRow
icon="check"
text="100K search queries"
variant="highlight"
/>
<PlanFeatureRow
icon="check"
text="10 connections"
variant="highlight"
/>
<PlanFeatureRow
icon="check"
text="Priority support"
variant="highlight"
/>
</div>
<div className="absolute inset-0 pointer-events-none rounded-[inherit] shadow-[inset_0.711px_0.711px_0.711px_rgba(255,255,255,0.1)]" />
</div>
</div>
</>
) : (
<>
@ -576,7 +602,7 @@ export default function Account() {
width: `${planUsagePct}%`,
background: planUsagePct > 80 ? "#ef4444" : "#0054AD",
}}
title={`${formatUsageNumber(tokensUsed)} tokens · ${formatUsageNumber(searchesUsed)} queries`}
title={`$${formatUsd(usdSpent)} of $${formatUsd(usdIncluded)} used`}
/>
</div>
<p
@ -585,14 +611,9 @@ export default function Account() {
"text-sm tracking-[-0.14px] text-[#737373] tabular-nums",
)}
>
{formatUsageNumber(tokensUsed)} tokens ·{" "}
{formatUsageNumber(searchesUsed)} queries
{daysRemaining !== null && (
<>
{" · resets in "}
{daysRemaining} day{daysRemaining !== 1 ? "s" : ""}
</>
)}
{daysRemaining !== null
? `Resets in ${daysRemaining} day${daysRemaining !== 1 ? "s" : ""}`
: ""}
</p>
</div>
@ -628,72 +649,32 @@ export default function Account() {
</button>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{/* Free plan card */}
<div className="flex flex-col gap-4 p-4 rounded-[10px] border border-white/10">
<p
className={cn(
dmSans125ClassName(),
"font-medium text-[16px] tracking-[-0.16px] text-[#FAFAFA]",
)}
>
Free plan
</p>
<div className="flex flex-col gap-2">
<PlanFeatureRow icon="check" text="1M tokens / month" />
<PlanFeatureRow icon="check" text="10K search queries" />
<PlanFeatureRow icon="x" text="No connections" />
<PlanFeatureRow icon="check" text="Basic support" />
</div>
</div>
{/* Pro plan card */}
<div
className={cn(
"flex flex-col gap-4 p-4 rounded-[10px]",
"bg-[#1B1F24] border border-white/10",
"shadow-[0px_2.842px_14.211px_rgba(0,0,0,0.25)]",
"relative overflow-hidden",
)}
>
{/* Header with badge */}
<div className="flex items-center justify-between">
<p
className={cn(
dmSans125ClassName(),
"font-medium text-[16px] tracking-[-0.16px] text-[#FAFAFA]",
)}
>
Pro plan
</p>
<span className="bg-[#4BA0FA] text-[#00171A] text-[12px] font-bold tracking-[0.36px] px-1 py-0.5 rounded-[3px]">
RECOMMENDED
</span>
</div>
<div className="flex flex-col gap-2">
<PlanFeatureRow
icon="check"
text="3M tokens / month"
variant="highlight"
/>
<PlanFeatureRow
icon="check"
text="100K search queries"
variant="highlight"
/>
<PlanFeatureRow
icon="check"
text="10 connections"
variant="highlight"
/>
<PlanFeatureRow
icon="check"
text="Priority support"
variant="highlight"
/>
</div>
{/* Inset highlight */}
<div className="absolute inset-0 pointer-events-none rounded-[inherit] shadow-[inset_0.711px_0.711px_0.711px_rgba(255,255,255,0.1)]" />
</div>
<PlanComparisonCard
name="Free"
price="$0"
period=""
description="Try the API with no commitment"
credits="$5"
features={[
"Pay-as-you-go after $5 runs out",
"Full search & memory API access",
"Email support",
]}
highlight={false}
/>
<PlanComparisonCard
name="Pro"
price="$19"
period="/mo"
description="For developers building with AI memory"
credits="$20"
features={[
"Auto top-up when balance runs low",
"All plugins (Claude Code, Cursor, Hermes…)",
"Priority support",
]}
highlight={true}
/>
</div>
</>
)}

View file

@ -423,13 +423,18 @@ export default function ConnectionsMCP() {
// Upgrade handler
const handleUpgrade = async () => {
try {
await autumn.attach({
const result = await autumn.attach({
planId: "api_pro",
successUrl: "https://app.supermemory.ai/settings#connections",
successUrl: `${window.location.origin}/settings#connections`,
})
window.location.reload()
if (result?.paymentUrl) {
window.open(result.paymentUrl, "_self")
return
}
autumn.refetch?.()
} catch (error) {
console.error(error)
toast.error("Failed to start checkout. Please try again.")
}
}

View file

@ -54,6 +54,8 @@ export function useTokenUsage(autumn: ReturnType<typeof useCustomer>) {
return {
tokensUsed,
searchesUsed,
usdIncluded,
usdSpent,
planUsagePct,
currentPlan,
hasPaidPlan,