Your customer bought your software 18 months ago for $1,000. Their maintenance plan expired 6 months ago. They call support with a bug. You fixed it in version 2.1, but they're still on 1.8. Now what?
Without a clear maintenance plan strategy, you're stuck: give them free updates forever (losing revenue), or tell them to buy an upgrade (losing customers). This article shows you how to implement maintenance plans that keep customers happy while maximizing renewal revenue.
The Maintenance Plan Model
What is a maintenance plan?
A maintenance plan (sometimes called "Software Assurance" or "Support & Updates") is an optional yearly subscription that grants:
- Free software updates
- Technical support
- Bug fixes
- Minor version upgrades
Perpetual License + Maintenance Plan = Hybrid Model
Purchase: $1,000 (one-time) → Lifetime license
Maintenance: $200/year (optional) → Updates + support
Timeline:
Year 0: $1,000 (license) + $200 (first year maintenance) = $1,200
Year 1: $200 (renewal)
Year 2: $200 (renewal)
Year 3: $200 (renewal)
Why maintenance plans work:
- Customers own the software (perpetual license)
- Vendors get recurring revenue (maintenance renewals)
- Customers choose when to pay (not forced subscriptions)
- Updates are tied to active maintenance
More info: QLM Maintenance Plans
Maintenance Plans vs Subscriptions
Key Differences
Maintenance Plan (Optional):
✅ Customer owns license forever
✅ Software works even if maintenance expires
⚠️ No updates without active maintenance
⚠️ Technical support may be limited
Subscription (Required):
⚠️ Customer doesn't own license
❌ Software stops working if subscription expires
✅ Always on latest version
✅ Included support
Revenue Comparison
Perpetual + Maintenance:
Year 0: $1,000 + $200 = $1,200
Year 1: $200 (60% renew) = $120 average
Year 2: $200 (60% renew) = $120 average
Year 3: $200 (60% renew) = $120 average
5-year LTV: $1,560
Subscription Only:
Year 0: $400
Year 1: $400 (70% retain) = $280 average
Year 2: $400 (70% retain) = $196 average
Year 3: $400 (70% retain) = $137 average
5-year LTV: $1,013
Key insight: Perpetual + Maintenance generates 54% more revenue over 5 years with better retention (customers already paid upfront).
Learn more: Perpetual vs Subscription
Implementing Maintenance Plans
Step 1: Create License with Maintenance Plan
using QLM.LicenseLib;
public class MaintenancePlanManager
{
private LicenseValidator lv;
public MaintenancePlanManager(string settingsFile)
{
lv = new LicenseValidator(settingsFile);
}
public string CreateLicenseWithMaintenance(
string customerEmail,
int maintenanceYears = 1)
{
string activationKey;
string response;
// Calculate maintenance expiry
DateTime maintenanceExpiry = DateTime.Now.AddYears(maintenanceYears);
bool success = lv.QlmLicenseObject.CreateActivationKey(
webServiceUrl: lv.QlmLicenseObject.DefaultWebServiceUrl,
email: customerEmail,
features: 1,
productID: 1,
majorVersion: 1,
minorVersion: 0,
licenseModel: ELicenseModel.permanent, // Perpetual license
numberOfLicenses: 1,
expiryDate: DateTime.MaxValue, // License never expires
maintenanceExpiryDate: maintenanceExpiry, // Maintenance DOES expire
activationKey: out activationKey,
response: out response
);
if (success)
{
SendLicenseEmail(customerEmail, activationKey, maintenanceExpiry);
return activationKey;
}
return null;
}
}
Step 2: Check Maintenance Plan Status
public class MaintenanceChecker
{
public MaintenanceStatus CheckMaintenanceStatus()
{
var lv = new LicenseValidator("settings.xml");
// Get maintenance plan expiry date
DateTime maintenanceExpiry = lv.QlmLicenseObject.MaintenancePlanExpiryDate;
// Check if maintenance is active
bool isActive = DateTime.Now <= maintenanceExpiry;
// Calculate days until expiry
int daysRemaining = (maintenanceExpiry - DateTime.Now).Days;
return new MaintenanceStatus
{
IsActive = isActive,
ExpiryDate = maintenanceExpiry,
DaysRemaining = Math.Max(0, daysRemaining),
CanUpdate = isActive
};
}
public bool IsMaintenanceActive()
{
var status = CheckMaintenanceStatus();
return status.IsActive;
}
}
public class MaintenanceStatus
{
public bool IsActive { get; set; }
public DateTime ExpiryDate { get; set; }
public int DaysRemaining { get; set; }
public bool CanUpdate { get; set; }
}
Step 3: Enforce Maintenance for Updates
public class UpdateChecker
{
public void CheckForUpdates()
{
var maintenanceChecker = new MaintenanceChecker();
var status = maintenanceChecker.CheckMaintenanceStatus();
if (!status.IsActive)
{
ShowMaintenanceExpiredDialog(status.ExpiryDate);
return;
}
// Maintenance active - check for updates
CheckForAvailableUpdates();
}
private void ShowMaintenanceExpiredDialog(DateTime expiryDate)
{
DialogResult result = MessageBox.Show(
$"Your maintenance plan expired on {expiryDate:yyyy-MM-dd}.\n\n" +
"Renew your maintenance plan to receive:\n" +
"• Latest software updates\n" +
"• New features\n" +
"• Bug fixes\n" +
"• Technical support\n\n" +
"Would you like to renew now?",
"Maintenance Plan Expired",
MessageBoxButtons.YesNo,
MessageBoxIcon.Information
);
if (result == DialogResult.Yes)
{
OpenRenewalPage();
}
}
private void OpenRenewalPage()
{
// Open renewal page with pre-filled activation key
var lv = new LicenseValidator("settings.xml");
string renewalUrl = $"https://yoursite.com/renew?key={lv.ActivationKey}";
Process.Start(renewalUrl);
}
}
More details: Maintenance plan implementation
Grace Periods
Why Grace Periods Matter
The problem: Customer's maintenance expired 2 days ago. They want to download the latest update. Do you:
- Block them? (frustrating)
- Let them? (no revenue)
- Offer a grace period? (balanced)
The solution: Grace period = extra days after expiry where updates still work.
Industry standards:
- 30 days: Enterprise software
- 14 days: SMB software
- 7 days: Consumer software
Implementing Grace Periods
public class GracePeriodManager
{
private const int GracePeriodDays = 30;
public GracePeriodStatus CheckGracePeriod()
{
var lv = new LicenseValidator("settings.xml");
DateTime maintenanceExpiry = lv.QlmLicenseObject.MaintenancePlanExpiryDate;
DateTime gracePeriodEnd = maintenanceExpiry.AddDays(GracePeriodDays);
bool isInGracePeriod =
DateTime.Now > maintenanceExpiry &&
DateTime.Now <= gracePeriodEnd;
int graceDaysRemaining = (gracePeriodEnd - DateTime.Now).Days;
return new GracePeriodStatus
{
IsActive = isInGracePeriod,
DaysRemaining = Math.Max(0, graceDaysRemaining),
GracePeriodEnd = gracePeriodEnd,
MaintenanceExpired = DateTime.Now > maintenanceExpiry
};
}
public bool CanDownloadUpdates()
{
var maintenanceChecker = new MaintenanceChecker();
var maintenanceStatus = maintenanceChecker.CheckMaintenanceStatus();
// Allow if maintenance is active
if (maintenanceStatus.IsActive)
return true;
// Allow if in grace period
var graceStatus = CheckGracePeriod();
if (graceStatus.IsActive)
{
ShowGracePeriodWarning(graceStatus.DaysRemaining);
return true;
}
// Block - both expired
return false;
}
private void ShowGracePeriodWarning(int daysRemaining)
{
MessageBox.Show(
$"⚠️ Grace Period Active\n\n" +
$"Your maintenance plan has expired.\n" +
$"Grace period remaining: {daysRemaining} days\n\n" +
"You can still download updates during the grace period.\n" +
"Please renew your maintenance plan to continue receiving updates.",
"Maintenance Grace Period",
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
}
}
public class GracePeriodStatus
{
public bool IsActive { get; set; }
public int DaysRemaining { get; set; }
public DateTime GracePeriodEnd { get; set; }
public bool MaintenanceExpired { get; set; }
}
Version Entitlement
Controlling Which Versions Customers Can Download
Scenario: Customer bought v1.0 with 1 year maintenance. They renewed for year 2. What versions can they download?
Answer: Any version released while their maintenance was active.
public class VersionEntitlementChecker
{
public bool CanDownloadVersion(
string requestedVersion,
DateTime versionReleaseDate)
{
var lv = new LicenseValidator("settings.xml");
// Get original purchase date
DateTime purchaseDate = lv.QlmLicenseObject.ActivationDate;
// Get maintenance expiry date
DateTime maintenanceExpiry = lv.QlmLicenseObject.MaintenancePlanExpiryDate;
// Check if version was released during active maintenance
bool wasMaintenanceActive =
versionReleaseDate >= purchaseDate &&
versionReleaseDate <= maintenanceExpiry;
if (!wasMaintenanceActive)
{
ShowVersionNotEntitledDialog(
requestedVersion,
versionReleaseDate,
maintenanceExpiry
);
return false;
}
return true;
}
private void ShowVersionNotEntitledDialog(
string version,
DateTime releaseDate,
DateTime maintenanceExpiry)
{
MessageBox.Show(
$"Version {version} was released on {releaseDate:yyyy-MM-dd}.\n\n" +
$"Your maintenance plan expired on {maintenanceExpiry:yyyy-MM-dd}.\n\n" +
"To download this version, please renew your maintenance plan.",
"Version Not Available",
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
}
}
More info: QLM version entitlement
Automated Maintenance Renewals
E-Commerce Integration
QLM integrates with e-commerce platforms to automatically extend maintenance when customers pay:
public class MaintenanceRenewalIntegration
{
// Called by e-commerce webhook (FastSpring, Stripe, PayPal)
public bool ProcessMaintenanceRenewal(
string activationKey,
int years = 1)
{
var lv = new LicenseValidator("settings.xml");
string response;
// Calculate new expiry date
// If current maintenance active: extend from current expiry
// If expired: extend from today
DateTime currentExpiry = GetCurrentMaintenanceExpiry(activationKey);
DateTime newExpiry = currentExpiry > DateTime.Now
? currentExpiry.AddYears(years)
: DateTime.Now.AddYears(years);
// Renew maintenance plan
bool success = lv.QlmLicenseObject.RenewMaintenancePlan(
webServiceUrl: lv.QlmLicenseObject.DefaultWebServiceUrl,
activationKey: activationKey,
newExpiryDate: newExpiry,
response: out response
);
if (success)
{
SendRenewalConfirmationEmail(activationKey, newExpiry);
return true;
}
return false;
}
}
Supported e-commerce platforms:
- WooCommerce
- FastSpring
- 2checkout
- Stripe Checkout
- PayPal
- Shopify
- Chargify
- HubSpot
- Cleverbridge
- BlueSnap
- MyCommerce
- UltraCart
More details: FastSpring maintenance integration
Automatic Renewal Reminders
public class MaintenanceReminderService
{
public void CheckAndSendReminders()
{
var lv = new LicenseValidator("settings.xml");
// Get all licenses with expiring maintenance
var expiringLicenses = GetLicensesExpiringInDays(30);
foreach (var license in expiringLicenses)
{
SendRenewalReminder(license);
}
}
private void SendRenewalReminder(LicenseInfo license)
{
int daysRemaining = (license.MaintenanceExpiry - DateTime.Now).Days;
string emailBody = $@"
Dear {license.CustomerName},
Your maintenance plan for {license.ProductName} expires in {daysRemaining} days
on {license.MaintenanceExpiry:yyyy-MM-dd}.
Renew now to continue receiving:
• Software updates
• New features
• Bug fixes
• Technical support
Renew here: https://yoursite.com/renew?key={license.ActivationKey}
Questions? Contact support@yourcompany.com
";
SendEmail(license.CustomerEmail, "Maintenance Plan Renewal", emailBody);
}
}
Recommended reminder schedule:
- 30 days before expiry
- 7 days before expiry
- 1 day before expiry
- 7 days after expiry (grace period reminder)
- 30 days after expiry (final reminder)
Learn more: Automated email notifications
Handling Expired Maintenance
Scenario 1: Updates Check
public class UpdateManager
{
public void CheckForUpdates()
{
var maintenanceChecker = new MaintenanceChecker();
var maintenanceStatus = maintenanceChecker.CheckMaintenanceStatus();
if (!maintenanceStatus.IsActive)
{
// Check grace period
var graceManager = new GracePeriodManager();
var graceStatus = graceManager.CheckGracePeriod();
if (graceStatus.IsActive)
{
// In grace period - allow but warn
ShowGracePeriodUpdateDialog(graceStatus.DaysRemaining);
DownloadAndInstallUpdates();
}
else
{
// Fully expired - block updates
ShowMaintenanceExpiredDialog(maintenanceStatus.ExpiryDate);
}
}
else
{
// Active maintenance - proceed normally
DownloadAndInstallUpdates();
}
}
}
Scenario 2: Technical Support Request
public class SupportRequestValidator
{
public bool CanAccessSupport(string activationKey)
{
var maintenanceChecker = new MaintenanceChecker();
var status = maintenanceChecker.CheckMaintenanceStatus();
if (status.IsActive)
{
return true;
}
// Check if expired recently (30 days grace for support)
int daysSinceExpiry = (DateTime.Now - status.ExpiryDate).Days;
if (daysSinceExpiry <= 30)
{
ShowSupportGracePeriodMessage(30 - daysSinceExpiry);
return true;
}
ShowSupportExpiredMessage(status.ExpiryDate);
return false;
}
private void ShowSupportExpiredMessage(DateTime expiryDate)
{
MessageBox.Show(
$"Your maintenance plan expired on {expiryDate:yyyy-MM-dd}.\n\n" +
"Technical support requires an active maintenance plan.\n\n" +
"Options:\n" +
"1. Renew your maintenance plan\n" +
"2. Purchase a support incident\n" +
"3. Check community forums",
"Support Not Available",
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
}
}
Scenario 3: Feature Access
public class FeatureGating
{
public bool CanAccessPremiumFeature(string featureName)
{
var lv = new LicenseValidator("settings.xml");
// Check if license includes feature
bool hasFeature = lv.QlmLicenseObject.HasFeature(featureName);
if (!hasFeature)
{
return false;
}
// Check if maintenance is required for this feature
bool requiresMaintenance = IsMaintenanceRequiredFeature(featureName);
if (requiresMaintenance)
{
var maintenanceChecker = new MaintenanceChecker();
var status = maintenanceChecker.CheckMaintenanceStatus();
if (!status.IsActive)
{
ShowFeatureRequiresMaintenanceDialog(featureName);
return false;
}
}
return true;
}
}
Maintenance Plan Pricing Strategies
Strategy 1: Percentage of License Price
License: $1,000
Maintenance: 20% = $200/year
Pros:
✅ Simple to calculate
✅ Industry standard
✅ Scales with product value
Cons:
❌ May be too expensive for low-end customers
Strategy 2: Flat Rate
License: $1,000
Maintenance: $150/year (flat)
Pros:
✅ Predictable for customers
✅ Easy to communicate
✅ Lower barrier to renewal
Cons:
❌ Doesn't scale with product tiers
Strategy 3: Tiered Pricing
Basic License ($500) → $100/year maintenance
Professional ($1,500) → $250/year maintenance
Enterprise ($5,000) → $750/year maintenance
Pros:
✅ Fair across product tiers
✅ Encourages upgrades
✅ Maximizes revenue
Cons:
❌ More complex to manage
Industry data:
- Average maintenance: 18-22% of license price
- Enterprise: 15-18% (volume discounts)
- SMB: 20-25% (higher support costs)
- Renewal rates: 60-75% for well-managed plans
Using Quick License Manager
Quick License Manager provides complete maintenance plan capabilities:
✅ Maintenance plan management - creation and tracking
✅ Grace period support - configurable grace periods
✅ Version entitlement - control which versions customers can download
✅ Automated renewals - e-commerce integration
✅ Renewal reminders - scheduled email campaigns
✅ Self-service renewal - customer portal
✅ Maintenance analytics - track renewal rates
✅ QLM Management Console - manual renewal processing
Download QLM and implement maintenance plans today.
Conclusion
Maintenance plans offer the best of both worlds:
For Customers:
- Own the software (perpetual license)
- Choose when to pay (not forced)
- Get updates when needed
- No risk of losing access
For Vendors:
- Recurring revenue stream
- Higher lifetime value than pure perpetual
- Better than pure subscription (higher retention)
- Control update access
The numbers don't lie:
Pure Perpetual (no maintenance):
5-year revenue: $1,000
Pure Subscription:
5-year revenue: $1,013 (with 30% churn)
Perpetual + Maintenance:
5-year revenue: $1,560 (with 40% renewal rate)
Companies that implement maintenance plans see:
- 60-75% renewal rates (vs 70% subscription retention)
- 40-50% higher LTV than pure subscription
- 50-60% lower churn than subscriptions
- 2-3x higher Net Promoter Scores
The key is making renewal easy and valuable: automated reminders, seamless payment integration, and grace periods that give customers breathing room.
With Quick License Manager, you get comprehensive maintenance plan capabilities that maximize renewal revenue while keeping customers happy.
Resources
- QLM Maintenance Plans
- Perpetual vs Subscription Licensing
- FastSpring Maintenance Integration
- QLM Features
- Download QLM
Do you use maintenance plans? What's your renewal rate? Share in the comments! 💬
Top comments (0)