flowglad-subscriptions
Manage subscription lifecycle including cancellation, plan changes, reactivation, and status display. Use this skill when users need to upgrade, downgrade, cancel, or reactivate subscriptions.
Install
mkdir -p .claude/skills/flowglad-subscriptions && curl -L -o skill.zip "https://mcp.directory/api/skills/download/4641" && unzip -o skill.zip -d .claude/skills/flowglad-subscriptions && rm skill.zipInstalls to .claude/skills/flowglad-subscriptions
About this skill
Subscriptions Management
Abstract
This skill covers subscription lifecycle management including cancellation, plan changes, reactivation, trial handling, and status display. Proper subscription management ensures users can upgrade, downgrade, cancel, and reactivate subscriptions with correct billing behavior.
Table of Contents
- Reload After Mutations — CRITICAL
- Cancel Timing Options — HIGH
- Upgrade vs Downgrade Behavior — HIGH
- Reactivation with uncancelSubscription — MEDIUM
- Trial Status Detection — MEDIUM
- Subscription Status Display — MEDIUM
1. Reload After Mutations
Impact: CRITICAL
After any subscription mutation (cancel, upgrade, downgrade, reactivate), the local billing state is stale. Failing to reload causes UI to show outdated subscription information.
1.1 Client-Side State Sync
Impact: CRITICAL (users see incorrect subscription status)
When using useBilling() on the client, mutations update the server but the local state remains stale until explicitly reloaded.
Incorrect: assumes state updates automatically
function CancelButton() {
const { cancelSubscription, currentSubscription } = useBilling()
const handleCancel = async () => {
await cancelSubscription({
id: currentSubscription.id,
cancellation: { timing: 'at_end_of_current_billing_period' },
})
// BUG: currentSubscription still shows old status!
// UI will not reflect cancellation until page refresh
}
return (
<div>
<button onClick={handleCancel}>Cancel Subscription</button>
{/* Shows incorrect status because we didn't reload */}
<p>Status: {currentSubscription?.status}</p>
</div>
)
}
The UI continues showing the old subscription status because the local useBilling() state wasn't refreshed.
Correct: reload after mutation
function CancelButton() {
const { cancelSubscription, currentSubscription, reload } = useBilling()
const [isLoading, setIsLoading] = useState(false)
const handleCancel = async () => {
setIsLoading(true)
try {
await cancelSubscription({
id: currentSubscription.id,
cancellation: { timing: 'at_end_of_current_billing_period' },
})
// Refresh local state to reflect the cancellation
await reload()
} finally {
setIsLoading(false)
}
}
return (
<div>
<button onClick={handleCancel} disabled={isLoading}>
{isLoading ? 'Canceling...' : 'Cancel Subscription'}
</button>
{/* Now shows correct status after reload */}
<p>Status: {currentSubscription?.status}</p>
</div>
)
}
1.2 Server-Side Reload Pattern
Impact: CRITICAL (server actions may return stale data)
When performing mutations server-side and returning billing data to the client, you must fetch fresh data after the mutation.
Incorrect: returns stale billing data
// Server action
export async function upgradeSubscription(priceSlug: string) {
const session = await auth()
const billing = await flowglad(session.user.id).getBilling()
await billing.adjustSubscription({ priceSlug })
// BUG: billing object still has old data!
return {
success: true,
subscription: billing.currentSubscription, // Stale!
}
}
Correct: fetch fresh billing after mutation
// Server action
export async function upgradeSubscription(priceSlug: string) {
const session = await auth()
const billing = await flowglad(session.user.id).getBilling()
await billing.adjustSubscription({ priceSlug })
// Fetch fresh billing state after mutation
const freshBilling = await flowglad(session.user.id).getBilling()
return {
success: true,
subscription: freshBilling.currentSubscription, // Fresh!
}
}
2. Cancel Timing Options
Impact: HIGH
Flowglad supports two cancellation timing modes. Using the wrong mode leads to billing disputes and poor user experience.
2.1 End of Period vs Immediate
Impact: HIGH (billing and access implications)
Most SaaS applications should cancel at the end of the billing period to let users keep access for time they've paid for.
Incorrect: immediately cancels without understanding impact
async function handleCancel() {
await billing.cancelSubscription({
id: billing.currentSubscription.id,
// This immediately ends access!
// User loses features they already paid for
cancellation: { timing: 'immediately' },
})
}
Immediate cancellation removes access right away, even if the user paid for the full month. This often leads to support tickets and refund requests.
Correct: cancel at end of period (default for most cases)
async function handleCancel() {
await billing.cancelSubscription({
id: billing.currentSubscription.id,
// User keeps access until their paid period ends
cancellation: { timing: 'at_end_of_current_billing_period' },
})
await billing.reload()
}
Use immediately only for specific cases like fraud prevention, user request for immediate refund, or account deletion.
2.2 User Communication
Impact: HIGH (user confusion)
When showing cancellation options, clearly communicate what each timing option means.
Incorrect: vague cancellation UI
function CancelModal() {
return (
<div>
<h2>Cancel Subscription</h2>
<button onClick={() => handleCancel('immediately')}>
Cancel Now
</button>
<button onClick={() => handleCancel('at_end_of_current_billing_period')}>
Cancel Later
</button>
</div>
)
}
"Cancel Now" and "Cancel Later" don't explain the billing implications.
Correct: clear communication of timing
function CancelModal() {
const { currentSubscription } = useBilling()
const endDate = currentSubscription?.currentPeriodEnd
return (
<div>
<h2>Cancel Subscription</h2>
<div>
<button onClick={() => handleCancel('at_end_of_current_billing_period')}>
Cancel at End of Billing Period
</button>
<p>
You'll keep access until {formatDate(endDate)}.
No further charges will occur.
</p>
</div>
<div>
<button onClick={() => handleCancel('immediately')}>
Cancel Immediately
</button>
<p>
Access ends now. You may be eligible for a prorated refund.
</p>
</div>
</div>
)
}
3. Upgrade vs Downgrade Behavior
Impact: HIGH
Upgrades and downgrades have different default behaviors. Not understanding this leads to incorrect UI and user confusion.
3.1 Immediate Upgrades
Impact: HIGH (billing timing)
By default, upgrades apply immediately with prorated billing. Users get instant access to the new plan.
Incorrect: suggests upgrade happens later
function UpgradeButton({ targetPriceSlug }: { targetPriceSlug: string }) {
const { adjustSubscription, reload } = useBilling()
return (
<button onClick={async () => {
await adjustSubscription({ priceSlug: targetPriceSlug })
await reload()
}}>
{/* Misleading: upgrade happens immediately, not next month */}
Upgrade Starting Next Month
</button>
)
}
Correct: communicate immediate effect
function UpgradeButton({ targetPriceSlug }: { targetPriceSlug: string }) {
const { adjustSubscription, reload, getPrice } = useBilling()
const price = getPrice(targetPriceSlug)
return (
<div>
<button onClick={async () => {
await adjustSubscription({ priceSlug: targetPriceSlug })
await reload()
}}>
Upgrade Now to {price?.product.name}
</button>
<p>
Your new plan starts immediately.
You'll be charged a prorated amount for the remainder of this billing period.
</p>
</div>
)
}
3.2 Deferred Downgrades
Impact: HIGH (user expectation mismatch)
Downgrades typically apply at the end of the current billing period. Users keep their current plan until then.
Incorrect: implies immediate downgrade
function DowngradeButton({ targetPriceSlug }: { targetPriceSlug: string }) {
const { adjustSubscription, reload } = useBilling()
return (
<button onClick={async () => {
await adjustSubscription({ priceSlug: targetPriceSlug })
await reload()
}}>
{/* Misleading: downgrade doesn't happen immediately */}
Switch to Basic Now
</button>
)
}
Correct: communicate deferred effect
function DowngradeButton({ targetPriceSlug }: { targetPriceSlug: string }) {
const { adjustSubscription, currentSubscription, reload, getPrice } = useBilling()
const price = getPrice(targetPriceSlug)
const endDate = currentSubscription?.currentPeriodEnd
return (
<div>
<button onC
---
*Content truncated.*
More by flowglad
View all skills by flowglad →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversOfficial Contentful integration for advanced content management software, automating full content lifecycle via Contentf
Probe Kit offers 23 essential tools for IDE's, app dev, and mobile app dev, streamlining project management and code qua
Manage virtual machines with VMware Fusion's REST API for easy lifecycle, power, and status control in local dev environ
Qwen Package Manager is a Node.js package manager with npm and Bower package support, transactional installs, rollback,
Manage cloud infrastructure and services easily with Civo. Automate instance lifecycle, Kubernetes, and networks via pow
Easily manage torrent downloads with rqbit: add, pause, start, or delete torrents using magnet links and our advanced ma
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.