Subscriptions
A subscription license follows the normal lifecycle with one difference: the license stays valid while the subscription is paid, plus a grace period, so payment state expresses itself entirely through validation results. There is no billing API to integrate in your application.
How the renewal loop works
The customer activates, and the app validates offline on every start. As the
paid-through date approaches, the stored license nears the end of its validity. The
next validation renews it silently, extending the license by whatever period the
customer has paid for. If the customer stops paying, validation answers
PAYMENT_REQUIRED. The moment they pay again, the next validation recovers
automatically. Renewal needs no code; your only job is handling the lapsed state well.
Handling PAYMENT_REQUIRED
The status message carries the subscription state. Treat the states differently, rather than as one generic error:
C++
#include "softactivate/sws_licensing.h"
#include <iostream>
#include <string>
auto client = sws::licensing::client::create(endpointUrl);
client->set_metadata("trusted_domain", trustedDomain);
client->set_vendor_id(vendorId);
client->set_license_key(customerLicenseKey);
auto params = client->create_license_validation_params();
auto result = client->validate_license(params);
if (!result->is_license_valid() &&
result->get_status() == SWS_LICENSE_STATUS_PAYMENT_REQUIRED)
{
// the message carries the subscription state - each deserves its own UX
std::string state = result->get_status_message();
if (state == "past_due")
{
// a payment failed but the subscription is still recoverable:
// "please update your payment method" + a link to your portal
}
else if (state == "canceled")
{
// the customer ended the subscription: a win-back conversation,
// not an error dialog
}
}
C#
using SWS.Licensing;
var client = new LicensingClient(endpointUrl)
{
VendorId = vendorId,
LicenseKey = customerLicenseKey,
TrustedDomain = trustedDomain
};
LicenseValidationResult result = await client.ValidateLicenseAsync(null);
if (!result.IsValid && result.Status == LicenseStatus.PaymentRequired)
{
// the message carries the subscription state - each deserves its own UX
switch (result.StatusMessage)
{
case "past_due":
// a payment failed but the subscription is still recoverable:
// "please update your payment method" + a link to your portal
break;
case "canceled":
// the customer ended the subscription: a win-back conversation,
// not an error dialog
break;
}
}
VB.NET
Imports SWS.Licensing
Dim client As New LicensingClient(endpointUrl) With {
.VendorId = vendorId,
.LicenseKey = customerLicenseKey,
.TrustedDomain = trustedDomain
}
Dim result As LicenseValidationResult = Await client.ValidateLicenseAsync(Nothing)
If Not result.IsValid AndAlso result.Status = LicenseStatus.PaymentRequired Then
' the message carries the subscription state - each deserves its own UX
Select Case result.StatusMessage
Case "past_due"
' a payment failed but the subscription is still recoverable:
' "please update your payment method" + a link to your portal
Case "canceled"
' the customer ended the subscription: a win-back conversation,
' not an error dialog
End Select
End If
past_due- a payment failed, but the subscription is still recoverable. This is your moment to prompt the customer: ask them to update their payment method, link to the payment portal, and optionally soften the app's behavior while payment is pending. Cards expire and payments fail for ordinary reasons, so keep the tone matter-of-fact.canceled- the customer ended the subscription. Consider a win-back message, or a graceful downgrade to a free tier if you offer one.- Unknown states may appear as billing evolves. Fall back to a generic message such as "subscription needs attention" with a link to the payment portal.
What to keep in mind
- Grace is server-side. The license's validity already accounts for the grace period, so avoid adding a second, hidden grace period in your application unless that is a deliberate product decision.
- Machines that sleep through a renewal wake up with a lapsed license and renew
automatically at the next online validation.
EXPIREDwhile offline is not necessarily a payment problem (statuses reference). - Moving machines works exactly like any other license: deactivate and re-activate.
- Subscriptions depend on a live connection and cannot use the offline activation exchange.
The vendor-side view, orders, subscription records, and payment events, lives in the console guide.