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
Metadata
Release files for revshop-license 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| revshop_license-1.1.1.tar.gz | 25.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| revshop_license-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.3 kB
Release files / revshop_license-1.1.1.tar.gz
| Download URL | revshop_license-1.1.1.tar.gz |
|---|---|
| Size | 25.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
adc3bb9437200b44f37ed3eaed65a11e597be465b63640780a242c9bb64042a9
|
|
BLAKE2b-256 checksum How to use checksums |
7ad60f2379ababdab5e13795ea6d16554dabb9556279e225777b58923c7888f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / revshop_license-1.1.1-py3-none-any.whl
| Download URL | revshop_license-1.1.1-py3-none-any.whl |
|---|---|
| Size | 23.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a968619b984f007bbd6d156060e32e9873d3da5780453195a66a571243418574
|
|
BLAKE2b-256 checksum How to use checksums |
6394a283b9f856009ed9d8fc09da0fcfee35de37119c11a127e31f9b71bee92c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|