Python SDK AI Prompt

Copy/paste this prompt into your AI assistant (e.g., GitHub Copilot Chat, Cursor, ChatGPT) to integrate the ExisOne Python SDK into your application.

Prompt

You're assisting me in integrating the ExisOne Python SDK for software licensing.

GOAL
- Generate a stable hardware ID on first run
- Activate a license with user input (activation key + email) and report app version
- Validate the license on every app start (online or offline) with version checking
- Check for software updates by comparing client version to server version
- Deactivate the license from the same hardware when requested
- Support offline customers with RSA-signed offline activation codes
- Submit support tickets from within the app

CONTEXT
- SDK package: exisone-client (version 0.7.0)
- Python: 3.8+
- I have an API base URL and an access token (format: exo_at_<public>_<secret>)
- Validate requires 'verify' permission on the access token
- For offline validation, I have an RSA public key (PEM format) from the Crypto Keys page
- Server may enforce minimum version requirements - client must handle version_outdated status

REQUIREMENTS
1) Install the package:
   pip install exisone-client

2) Initialize the client:
   from exisone import ExisOneClient, ExisOneClientOptions

   options = ExisOneClientOptions(
       base_url="https://YOUR-API-HOST",
       access_token="exo_at_PUBLIC_SECRET",
       offline_public_key="""-----BEGIN PUBLIC KEY-----
   MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
   -----END PUBLIC KEY-----"""  # Optional: for offline license validation
   )

   client = ExisOneClient(options)

   # Log SDK version
   print(f"ExisOne SDK: {client.get_version()}")

3) Hardware ID:
   - On first run, compute client.generate_hardware_id(), persist locally (e.g., config file, user data)
   - Reuse the persisted value thereafter
   - Display to user so they can request offline licenses if needed

4) Activation flow with version:
   - Ask for Activation Key (or Offline Code) and Email
   - For online keys with version check:
     result = client.activate(
         activation_key=key,
         email=email,
         hardware_id=hardware_id,
         product_name="MyProduct",
         version="1.0.0"
     )
     if not result.success:
         if result.error_code == "version_outdated":
             # User must upgrade before activating
             show_error(f"Please upgrade to version {result.minimum_required_version}")
         else:
             show_error(result.error_message)
     else:
         # Activation successful - optionally notify about updates
         if result.server_version != "1.0.0":
             show_info(f"Update available: v{result.server_version}")
   - For offline codes: use validate_offline() or validate_smart() - no activation needed

5) Validation with version check:
   - Rich validation with version:
     result = client.validate(
         activation_key=key,
         hardware_id=hwid,
         product_name="MyProduct",
         version="1.0.0"
     )

     if result.status == "version_outdated":
         # Force upgrade - license valid but version too old
         show_error(f"Please upgrade to version {result.minimum_required_version} to continue")
         return
     if result.is_valid:
         print(f"Licensed until: {result.expiration_date}")
         print(f"Features: {', '.join(result.features)}")
         # Optionally check for updates
         if result.server_version and result.server_version != "1.0.0":
             show_info(f"Update available: v{result.server_version}")

   - Smart validation (handles online/offline):
     result = client.validate_smart(key_or_code, hardware_id, "MyProduct")
     # result.server_version and result.minimum_required_version available

6) Offline-only validation (no network):
   - result = client.validate_offline(offline_code, hardware_id)
   - Check result.is_valid, result.is_expired, result.hardware_mismatch
   - Use when you know the user has an offline code and want zero network calls

7) Deactivation flow:
   - For smart deactivation (tries server, succeeds locally if offline):
     result = client.deactivate_smart(key_or_code, hardware_id, "MyProduct")
     print(f"Success: {result.success}, Server notified: {result.server_notified}")
   - Legacy online-only: client.deactivate(key, hardware_id, "MyProduct")

8) Support tickets:
   - Provide a form (product, email, subject, message)
   - Call: client.send_support_ticket(product_name, email, subject, message)

9) Offline license workflow for customers without internet:
   - Customer sees their Hardware ID in your app (from generate_hardware_id())
   - Customer contacts you (email/phone) with their Hardware ID
   - You generate an Offline Activation Code in the License Keys dashboard
   - Customer enters the long code into your app
   - Your app validates using validate_offline() or validate_smart()

10) Tenancy:
    - Ensure the access token belongs to the same tenant that owns the activation keys

11) Version enforcement handling:
    - Always pass your app's version during activation and validation
    - Handle "version_outdated" status by prompting user to upgrade
    - Use server_version to notify users when updates are available
    - minimum_required_version tells you the oldest acceptable version

12) Error handling:
    import requests

    try:
        result = client.validate(activation_key, hardware_id)
    except requests.HTTPError as e:
        print(f"HTTP error: {e.response.status_code}")
    except requests.RequestException as e:
        print(f"Network error: {e}")

OFFLINE CODE SECURITY
- Offline codes are RSA-SHA256 signed (Base32 encoded with dashes)
- They embed: product ID, hardware ID, expiration date, email, features
- Cannot be forged, transferred, or modified
- Expiration is checked against local machine time

RESULT TYPES (0.7.0)
@dataclass
class ActivationResult:
    success: bool
    error_code: str | None
    error_message: str | None
    server_version: str | None
    minimum_required_version: str | None
    license_data: str | None

@dataclass
class ValidationResult:
    is_valid: bool
    status: str
    expiration_date: datetime | None
    features: list[str]
    server_version: str | None
    minimum_required_version: str | None

@dataclass
class OfflineValidationResult:
    is_valid: bool
    error_message: str | None
    product_name: str | None
    expiration_date: datetime | None
    features: list[str]
    is_expired: bool
    hardware_mismatch: bool

@dataclass
class SmartValidationResult:
    is_valid: bool
    status: str
    expiration_date: datetime | None
    features: list[str]
    was_offline: bool
    error_message: str | None
    product_name: str | None
    server_version: str | None
    minimum_required_version: str | None

@dataclass
class DeactivationResult:
    success: bool
    server_notified: bool
    error_message: str | None

DELIVERABLES
- Add initialization and configuration code
- Implement activation UI supporting both online keys and offline codes
- Pass app version during activation and validation
- Handle version_outdated status with upgrade prompts
- Optionally notify users when server_version differs from current version
- Use validate_smart() for seamless online/offline handling
- Provide code snippets for persistence of hardware ID and license key/code
- Display hardware ID to users for offline license requests
- Ensure proper exception handling

Please produce idiomatic Python for my stack (PyQt/Tkinter/CLI/etc. as applicable), with concise functions and no extraneous commentary.

Notes