Industry
Use Case
Favorite Feature
A compliance and document automation SaaS vendor serves corporate legal teams and HR departments with software for contract analysis, document assembly, and compliance tracking. The platform processes sensitive documents for thousands of users across enterprise and mid-market customers, with per-user pricing and tiered feature sets (basic workflows, advanced AI analysis, unlimited templates).
The company built its billing entirely on Stripe subscription plans, which handles payment collection reliably. However, Stripe has no native understanding of feature-level entitlements, trial periods, or license lifecycle management. As the product matured and feature complexity increased, the company faced persistent entitlement drift where the billing state (what Stripe knew) diverged from the actual application state (what users could access).
NetLicensing was introduced as the authoritative entitlement layer, with Stripe retained for its payment strength. This separation of concerns enabled automated lifecycle management, eliminated feature access inconsistencies, and reduced support escalations caused by billing/entitlement mismatches.
The application’s feature access was enforced through scattered conditional logic: some checks occurred at the API gateway, others in business logic, and still others in UI rendering. Different parts of the codebase had different assumptions about what “trial” meant, what happened on plan downgrade, and how to enforce feature availability.
The result was chronic entitlement drift. A customer on a free trial could sometimes access paid features because the trial check in one module wasn’t synchronized with the check in another. When customers downgraded their plan, residual access remained intact for days or weeks because the feature gate code didn’t consistently check current subscription status. Support tickets flooded in from customers confused about why they lost access abruptly, or conversely, why they could still use features after cancelling.
Trial-to-paid conversion was another critical gap. The company had no way to predict or influence when trials would expire, no automated upgrade prompts, and no mechanism to grant a grace period to customers who were actively using the product but hadn’t yet converted. Many high-potential customers churned because the product simply stopped working when their trial expired, with no warning.
NetLicensing’s Subscription and Try & Buy (trial) models provided the framework. Each plan tier (Starter, Professional, Enterprise) became a License Template with:
Trial customers received a separate time-bounded Try & Buy license with a 14-day expiry. This license existed independently from the subscription license, enabling clean lifecycle management: trial → upgrade to paid subscription (license swap), or trial expiry → access denied (not feature removal).
Product and Module Structure:
Product: Compliance & Document Automation
└── Module: Plans & Features (Subscription model)
├── Template: Starter Trial (timeVolume=14, timeVolumePeriod=DAY, features=basic)
├── Template: Starter (timeVolume=1, timeVolumePeriod=MONTH, price=$49, features=basic)
├── Template: Professional (timeVolume=1, timeVolumePeriod=MONTH, price=$149, features=advanced)
└── Template: Enterprise (custom pricing, features=all)
Key Configuration Steps:
timeVolume=1, timeVolumePeriod=MONTHtimeVolume=14, timeVolumePeriod=DAYcustomer.subscription.created: POST /licensee/{id} to create/activate the customer, then issue the appropriate Subscription licensecustomer.subscription.updated: update the license to reflect the new plancustomer.subscription.deleted: deactivate the license (set active=false)invoice.payment_failed: notify the licensee and prepare for suspensionKey Parameters:
timeVolume=1, timeVolumePeriod=MONTH for all production subscription templatestimeVolume=14, timeVolumePeriod=DAY for trial licensesactive=true/false per plan to gate analytics, AI, templatesTrial Signup and Expiry:
User signs up → POST /licensee + POST /license (Try & Buy template)
↓ App grants access, user sees 14-day countdown
↓ Periodic cron job checks: GET /licensee/{id}/validate
↓ At day 11: license response shows remaining_time=3d → send upgrade prompt email
↓ At day 14: trial license expires → validate returns "not valid" → app blocks access
Subscription Lifecycle:
Stripe event: subscription.created → webhook handler
↓ Extract Stripe subscription ID and product ID
↓ POST /licensee {licenseeNumber=stripe_customer_id}
↓ POST /license {licenseeNumber, template=Professional}
↓ App: GET /validate → returns active Professional license
↓ User gains full access to advanced features
↓
Stripe event: subscription.updated (plan change)
↓ Identify old and new License Templates
↓ DELETE /license {old_license_number}
↓ POST /license {new_license_number, template=Enterprise}
↓ App: GET /validate → returns new Enterprise license
↓ User immediately sees expanded feature set
↓
Stripe event: subscription.deleted (cancellation)
↓ PATCH /license {active=false} OR DELETE /license
↓ App: GET /validate → returns "not valid"
↓ At next request, user sees "subscription expired" page with reactivation prompt
Feature Gating: All feature access flows through a single validation call:
GET /licensee/{licenseeNumber}/validate
The response includes active license details:
{
"ttl": 2592000,
"validFrom": "2026-03-19",
"validUntil": "2026-04-19",
"featureAnalytics": { "active": true },
"featureAI": { "active": true },
"featureTemplates": { "active": false }
}
The app denies access to any feature where active=false or the current timestamp is outside validFrom/validUntil.
Each customer account is a Licensee, with licenseeNumber mapped to the Stripe customer_id. This linkage is critical for webhook integration: when Stripe fires a customer.subscription.created event, the webhook handler maps the Stripe customer ID to the corresponding licensee.
Provisioning Flow:
/licensee {licenseeNumber=stripe_customer_id} to NetLicensingnumber for future API calls/license {licenseeNumber, template=trial} to assign a trial licenselicenseeNumber for validation callsPlan Changes: When a Stripe customer.subscription.updated webhook fires (customer upgrades/downgrades), the webhook handler:
/licensee/{licenseeNumber} to fetch the current Licensee/license with the new License Template to activate the upgraded licenseThis ensures clean state transitions with no residual access from old plans.
past_due. Set the license to a limited “suspended” state (e.g., read-only access) rather than immediately deactivating it. This gives customers a chance to update payment info without losing all access.current_period_start and current_period_end as the source of truth when creating licenses.
Wordpress Plugin: DigiPass
DigiPass helps publishers and bloggers monetize digital content with subscription-based access.

Document SaaS: Credit Packs to Subscription Tiers
A document conversion and eSignature service moved from credit packs to subscription tiers with overage pricing.