REVSHOP License Manager - Python Client for Software License Protection
Project description
REVSHOP License Manager - Python Client
A secure Python client for REVSHOP license protection system. Protect your Python applications with hardware-bound license keys, real-time monitoring, and automatic termination on violations.
Features
- 🔐 Hardware ID Binding - Binds to CPU, motherboard, or machine ID
- 🔄 Real-time Monitoring - Automatic status checks every 30 seconds
- 🚫 Auto-terminate - Program exits immediately on license violations
- 📴 No Grace Period - Strict enforcement, internet required
- 🔒 Secure Storage - Encrypted local configuration
- ✅ Signature Verification - HMAC-SHA256 response validation
- 🎯 Simple API - Easy integration with flexible callbacks
Installation
pip install revshop-license
Quick Start
Basic Usage
from revshop_license import LicenseManager
from datetime import datetime
# Initialize
lm = LicenseManager(
base_url="https://your-shop.com",
product_id="your_product_id",
product_secret="your_product_secret"
)
# Validate license - this returns all license data
result = lm.validate("REV-XXXX-XXXX-XXXX-XXXX")
if result["valid"]:
# Get license data from result (NOT from Library directly)
print("License valid!")
print(f"Product: {result['license_info']['product']['name']}")
print(f"Activated: {result['activated_at']}")
print(f"Expires: {result['expires_at']}")
# Calculate days remaining
if result['expires_at']:
expires = datetime.fromisoformat(result['expires_at'].replace('Z', '+00:00'))
days_left = (expires - datetime.now(expires.tzinfo)).days
print(f"Days remaining: {days_left}")
else:
print(f"Error: {result['message']}")
Important Notes
- First activation requires internet - Cannot use offline mode for first-time activation
- Auto-exit on violations - The program will automatically exit if license is revoked/suspended/expired
- No grace period - Strict enforcement requires internet connection
- Must call validate() first - All license data comes from the
validate()result
Understanding the Flow
1. Create LicenseManager
↓
2. Call validate("REV-XXXX...")
↓ (Server validates and returns data)
3. Get result with all license info:
- activated_at: when license was first activated
- expires_at: when license expires
- license_info: full product details
- first_activation: True if this is first use
↓
4. Use the data to display in your UI
Advanced Usage
Custom Callbacks (Optional)
By default, the library will exit the program when a license violation is detected without printing anything. You can customize this behavior:
def on_revoked(data):
# This is called when license is revoked
# You have about 1 second to show your message before exit
print("License revoked! Contact support.")
# Program will exit automatically after callback returns
# Set callbacks BEFORE validate()
lm.set_callbacks(
on_revoked=on_revoked,
on_suspended=on_suspended,
on_expired=on_expired
)
result = lm.validate("REV-XXXX-XXXX-XXXX-XXXX")
Auto Lookup Mode
If you don't know the product_id, you can use the auto-lookup feature:
lm, lookup_result = LicenseManager.from_license_key(
base_url="https://your-shop.com",
license_key="REV-XXXX-XXXX-XXXX-XXXX",
product_secret="your_product_secret"
)
if lookup_result["found"]:
result = lm.validate()
print(f"Product: {lookup_result['product_name']}")
Complete Example
import sys, time
from datetime import datetime
from revshop_license import LicenseManager
# Configuration (should be encoded in production)
BASE_URL = "https://your-shop.com"
PRODUCT_ID = "your_product_id"
PRODUCT_SECRET = "your_product_secret"
# Create LicenseManager
lm = LicenseManager(
base_url=BASE_URL,
product_id=PRODUCT_ID,
product_secret=PRODUCT_SECRET
)
# Get license key from user
license_key = input("Enter License Key: ").strip()
if not license_key:
print("License key required!")
sys.exit(1)
# Validate - this returns ALL license data
result = lm.validate(license_key)
if not result["valid"]:
print(f"Validation failed: {result['message']}")
sys.exit(1)
# ✅ Success! Display license information from RESULT
print("\n" + "="*60)
print("LICENSE ACTIVATED")
print("="*60)
# Get product name from result
info = result.get("license_info", {})
print(f"Product: {info.get('product', {}).get('name', 'N/A')}")
print(f"Status: {info.get('status', 'N/A')}")
# Get dates from result (NOT from Library)
if result.get("activated_at"):
activated = datetime.fromisoformat(result["activated_at"].replace('Z', '+00:00'))
print(f"Activated: {activated.strftime('%Y-%m-%d %H:%M')}")
if result.get("expires_at"):
expires = datetime.fromisoformat(result["expires_at"].replace('Z', '+00:00'))
print(f"Expires: {expires.strftime('%Y-%m-%d %H:%M')}")
# Calculate days remaining
days_left = (expires - datetime.now(expires.tzinfo)).days
print(f"Days remaining: {days_left}")
else:
print("License type: Lifetime")
print("="*60)
print("\nProgram is now running with license protection.")
print("The program will exit automatically if license is revoked.\n")
# Your main application loop
try:
while True:
# Your application logic here
time.sleep(1)
except KeyboardInterrupt:
lm.stop_monitoring()
print("\nProgram terminated.")
Common Mistakes
❌ Wrong: Trying to get data before validate()
lm = LicenseManager(...)
# This is WRONG - validate() hasn't been called yet!
dates = lm.get_license_dates() # Returns None or old data
print(dates)
result = lm.validate(license_key) # Data comes from here!
✅ Correct: Get data from validate() result
lm = LicenseManager(...)
result = lm.validate(license_key) # Get data here
if result["valid"]:
# Use data from result
print(f"Activated: {result['activated_at']}")
print(f"Expires: {result['expires_at']}")
API Reference
LicenseManager
Constructor
LicenseManager(
base_url: str, # License server URL
product_id: str, # Product ID from REVSHOP
product_secret: str # Product Secret from REVSHOP Admin
)
Methods
| Method | Description |
|---|---|
validate(license_key=None, force_online=False) |
Validate license key. Returns all license data |
validate_force_online(license_key=None) |
Force online validation |
check_status_now() |
Check status immediately |
get_saved_license() |
Get saved license key |
save_license(key) |
Save license key |
clear_license() |
Clear saved license |
deactivate(reason) |
Deactivate on this device |
get_status() |
Get current status |
get_license_dates() |
Get dates from cached config (call validate() first!) |
stop_monitoring() |
Stop monitoring threads |
set_callbacks(...) |
Set event callbacks |
Validation Result
The validate() method returns a dictionary with these keys:
{
"valid": bool, # Is license valid
"message": str, # Status message
"offline": bool, # Using offline cache
"first_activation": bool, # Is this the first activation
"activated_at": str, # Activation timestamp (ISO format)
"expires_at": str, # Expiration timestamp (ISO format)
"license_info": dict # Full license details including product name
}
Callbacks
Set custom callbacks for license events:
on_revoked- Called when license is revokedon_suspended- Called when license is suspendedon_expired- Called when license expireson_device_removed- Called when device is removedon_failed- Called on validation failureon_status_changed- Called on status change
Note: Callbacks are called BEFORE the program exits. You have about 1 second to show your message.
Configuration
The client stores configuration in:
- Windows:
%USERPROFILE%\.revshop_license\.config.dat - Linux/Mac:
~/.revshop_license/.config.dat
Configuration is encrypted with hardware-based key.
Error Codes
| Error Code | Description |
|---|---|
NOT_ACTIVATED |
License not activated yet (first use) |
LICENSE_NOT_FOUND |
License key doesn't exist |
LICENSE_REVOKED |
License has been revoked |
LICENSE_SUSPENDED |
License has been suspended |
LICENSE_EXPIRED |
License has expired |
DEVICE_NOT_AUTHORIZED |
This device was removed |
DEVICE_LIMIT_EXCEEDED |
Maximum devices reached |
INVALID_PRODUCT_SECRET |
Wrong product secret |
NETWORK_ERROR |
Cannot connect to server |
Requirements
- Python 3.8+
requestslibrary (installed automatically)
Security Notes
- Encode your credentials - Use base85 or similar encoding to hide
product_idandproduct_secretin your code - Hardware binding - License is tied to specific hardware (CPU/Mainboard)
- No grace period - Strict enforcement requires internet connection
- Auto-terminate - Program exits immediately on violations
Example: Encoding Configuration
import base64
# Encode your credentials
product_id = "your_product_id"
product_secret = "your_product_secret"
base_url = "https://your-shop.com"
encoded_id = base64.b85encode(product_id.encode()).decode()
encoded_secret = base64.b85encode(product_secret.encode()).decode()
encoded_url = base64.b85encode(base_url.encode()).decode()
print(f"_ENCODED_PRODUCT_ID = \"{encoded_id}\"")
print(f"_ENCODED_SECRET = \"{encoded_secret}\"")
print(f"_ENCODED_URL = \"{encoded_url}\"")
# In your program, decode at runtime
def decode_config(encoded: str) -> str:
return base64.b85decode(encoded).decode('utf-8')
PRODUCT_ID = decode_config(_ENCODED_PRODUCT_ID)
PRODUCT_SECRET = decode_config(_ENCODED_SECRET)
BASE_URL = decode_config(_ENCODED_URL)
License
MIT License - see LICENSE file for details.
Support
For support, contact: support@revshop.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file revshop_license-1.1.1.tar.gz.
File metadata
- Download URL: revshop_license-1.1.1.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adc3bb9437200b44f37ed3eaed65a11e597be465b63640780a242c9bb64042a9
|
|
| MD5 |
7bf7679fb15e609aa4e84701644172af
|
|
| BLAKE2b-256 |
7ad60f2379ababdab5e13795ea6d16554dabb9556279e225777b58923c7888f5
|
File details
Details for the file revshop_license-1.1.1-py3-none-any.whl.
File metadata
- Download URL: revshop_license-1.1.1-py3-none-any.whl
- Upload date:
- Size: 23.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a968619b984f007bbd6d156060e32e9873d3da5780453195a66a571243418574
|
|
| MD5 |
6f943a3281111473af904277335948d7
|
|
| BLAKE2b-256 |
6394a283b9f856009ed9d8fc09da0fcfee35de37119c11a127e31f9b71bee92c
|