License Development, Debugging & Release Full-Process Guide

v1.0.0

Client Software Development → Debugging → Release Full-Process Guide (v4)

Applies to: developers publishing client software on PowerSoftware.net and integrating the license code system. This document covers the complete lifecycle from "creating a product" to "real user purchase", with the focus on the new dual-environment debugging capability in v4: before release, use the real payment pipeline (Waffo Test environment) to run the full flow on your own computer as a debug machine.

1. Full-Process Overview

┌──────────────┐   ┌──────────────┐   ┌──────────────┐   ┌──────────────┐
│ ① Development│ → │ ② Debugging  │ → │ ③ Release    │ → │ ④ Operations │
│              │   │              │   │              │   │              │
│ Create draft │   │ Register     │   │ Submit for   │   │ View orders  │
│ Integrate SDK│   │ debug machine│   │ review       │   │ Unbind/refund│
│ Save draft   │   │ Real pipeline│   │ Approved     │   │ Version      │
│              │   │ (Test env)   │   │ Sync to prod │   │ iteration    │
└──────────────┘   └──────────────┘   └──────────────┘   └──────────────┘
Stage Product State Where Licenses/Orders Land Real Revenue?
① Development Draft (DRAFT) No
② Debugging Draft / Under review Test tables (license codes with T- prefix) No (test payment channel)
③ Release Published Production tables Yes

Core rule: debugging and production are fully isolated. Everything that happens on debug machines (trials, orders, payments, license issuance, activation, refunds) only affects test tables — it never enters revenue sharing, never enters settlement, and never affects any real user; access from non-debug machines always goes through the production pipeline.

2. Development Stage

2.1 Create a Product

  1. Log in to the PowerSoftware.net Developer Center → Publish product, choose "Client Software" as product type
  2. Choose Try-Before-You-Buy as the sales model and set trial days (7~14 days recommended)
  3. Configure license editions (edition): three tiers by default, BASIC / PRO / ULTIMATE; code, name, price and feature list are customizable
  4. Check "Platform collects license fees" (mandatory for Try-Before-You-Buy)
  5. Upload the software package and introduction materials

2.2 Save Draft (New Capability)

There are two buttons at the bottom of the product form:

Button Behavior
Save Draft Product is saved as DRAFT, does not enter the review queue, and can be edited at any time
Save & Submit for Review Product enters the review queue (PENDING_RELEASE)

After saving as draft / submitting for review, the platform asynchronously syncs the product to the Waffo Test environment (failures only trigger alerts and never block saving). This is the prerequisite for placing orders during debugging. Therefore: during development, just save a draft and you can start debugging — no need to submit for review.

After saving, note down the productUniqueCode (visible on the publish page, not a secret).

2.3 Integrate the License SDK (Two Integration Scenarios)

PowerSoftware.net provides two license integration methods for client software. Choose based on your situation:

Scenario A: Full Platform Pipeline (Try-Before-You-Buy) Scenario B: Orders Outside Platform
Applicable Products Client software + Try-Before-You-Buy sales model Promotion-only software (self-collected payment); or server/client software with Pay-First (PAY_FIRST) sales model or self-collected VIP features
Who Handles Payment PowerSoftware.net platform (Waffo, Alipay, PayPal); debugging uses the Waffo Test environment as payment channel Developer (in-app payment or other channels)
Who Issues Licenses Platform auto-issues after successful payment Software calls platform API to issue (HMAC signed)
Needs licenseApiSecret? ❌ No ✅ Yes (server-side only, never in client)
Needs a Server? ❌ No, pure client is sufficient ✅ Yes (to safeguard licenseApiSecret, forward issuance requests)
Trial Authorization ✅ Supported (claimTrial) ❌ No trial (trial only supports Try-Before-You-Buy products)
Purchase Page Platform-provided, one-line SDK redirect No platform purchase page, developer handles it

About edition configuration: all product types (client/server/promotion-only) can enable license codes and customize editions (BASIC/PRO/ULTIMATE, etc.). Edition prices and feature lists are shown on the purchase page only when the developer checks "Platform collects license fees" (licensePlatformPayment); otherwise the developer handles billing independently and the platform only provides license issuance and verification.

How to choose:

  • Indie developer / small team without your own server → choose Scenario A (Chapters 4 and 5 of this document use Scenario A as the main line)
  • Already have payment channels (e.g., WeChat/Alipay merchant) and only need platform license issuance & verification → choose Scenario B

2.3.1 SDK Installation

All three languages (Node.js / Python / Java) are zero-dependency — copy the source code directly into your project, no package manager needed. The machine code algorithm is cross-language consistent (the same machine generates the same machineCode). SDK repository: github.com/mizhanchengxi/powersoftware-license-sdk

2.3.2 Scenario A: Full Platform Pipeline (Try-Before-You-Buy)

Prerequisites: the product sales model is Try-Before-You-Buy, trial days and license editions are configured, and "Platform collects license fees" is checked. The license secret licenseApiSecret is NOT required:

# Python
from ps_license_sdk import LicenseClient, machine_code

client = LicenseClient(product_unique_code="PRO-2026-001", api_secret="")  # leave api_secret empty
mc = machine_code()
// Node.js
import { LicenseClient, machineCode } from './index.js';

const client = new LicenseClient({ productUniqueCode: 'PRO-2026-001', apiSecret: "" });  // Scenario A: leave secret empty
const mc = machineCode();
// Java
import com.powersoftware.sdk.LicenseClient;

LicenseClient client = new LicenseClient("PRO-2026-001", "");  // Scenario A: leave secret empty
String mc = LicenseClient.machineCode();

Complete integration flow:

First launch
    │
    ├─ claimTrial(machineCode) ────────────→ Get trial license
    │     ↓ Returns { licenseCode, activationToken, licenseUpgradeMode }
    │     ↓ Persist locally
    │
    ├─ During trial: all features available
    │     │
    │     └─ Click paid feature → verifyCached() 60s cached check → pass
    │
    └─ Trial expired / not activated
          │
          ├─ verifyCached() returns invalid/expired
          ├─ Show dialog "Purchase required to activate"
          └─ purchaseUrl(machineCode) → open platform purchase page
                │
                ↓ User pays on platform → platform issues license + email delivery
                │
          User returns to the software and enters the license code
                │
                ├─ activate(licenseCode, machineCode)
                │     ↓ Returns { activationToken, licenseUpgradeMode }, persist locally
                │
                └─ Subsequent usage → verifyCached() check → pass

To implement: claimTrial (claim trial on first launch) → verifyCached (paid feature check) → purchaseUrl (redirect to purchase page when not licensed) → activate (activate with user-entered license code) → local persistence of licenseCode + activationTokenLicenseError error code handling.

Core code (Python):

import json
from pathlib import Path
from ps_license_sdk import LicenseClient, machine_code

# ---------- Initialization ----------
client = LicenseClient(product_unique_code="PRO-2026-001", api_secret="")
CRED_FILE = Path.home() / ".myapp" / "license.json"

def load_cred():
    if CRED_FILE.exists():
        return json.loads(CRED_FILE.read_text())
    return {}

def save_cred(d):
    CRED_FILE.parent.mkdir(parents=True, exist_ok=True)
    CRED_FILE.write_text(json.dumps(d, ensure_ascii=False))

# ---------- First launch: claim trial ----------
def claim_trial():
    mc = machine_code()
    try:
        result = client.claim_trial(mc)
        save_cred({
            "licenseCode": result["licenseCode"],
            "activationToken": result["activationToken"],
        })
        print(f"Trial activated, license code: {result['licenseCode']}")
    except Exception as e:
        print(f"Failed to claim trial: {e}")

# ---------- License check (call when a paid feature is clicked) ----------
def check_license(required_edition="PRO"):
    cred = load_cred()
    if not cred.get("licenseCode"):
        return {"valid": False, "reason": "Not activated"}

    mc = machine_code()
    try:
        result = client.verify_cached(
            cred["licenseCode"], mc, cred["activationToken"]
        )
        if not result.get("valid"):
            return {"valid": False, "reason": "License invalid or expired"}

        user_edition = result.get("edition", "")
        levels = {"BASIC": 0, "PRO": 1, "ULTIMATE": 2}
        if levels.get(user_edition, 0) < levels.get(required_edition, 0):
            return {"valid": False, "reason": f"Requires edition {required_edition} or above"}

        return {"valid": True, "edition": user_edition,
                "expiryTime": result.get("expiryTime"),
                "trialExpiryTime": result.get("trialExpiryTime")}
    except Exception as e:
        return {"valid": False, "reason": str(e)}

# ---------- Activate license code (user enters it after purchase) ----------
def activate(license_code):
    mc = machine_code()
    try:
        result = client.activate(license_code, mc)
        save_cred({
            "licenseCode": license_code,
            "activationToken": result["activationToken"],
        })
        return True
    except Exception as e:
        print(f"Activation failed: {e}")
        return False

# ---------- Open purchase page ----------
def open_purchase_page():
    mc = machine_code()
    url = client.purchase_url(mc)
    import webbrowser
    webbrowser.open(url)

Feature gating example:

# Define edition requirements per feature
FEATURE_EDITION = {
    "basic_feature":   "BASIC",
    "plus_feature":    "PRO",
    "ultimate_feature": "ULTIMATE",
}

def run_feature(feature_name):
    required = FEATURE_EDITION.get(feature_name, "BASIC")
    if required == "BASIC":
        do_basic_feature()
        return

    result = check_license(required)
    if result["valid"]:
        do_paid_feature(feature_name)
    else:
        print(f"Cannot use this feature: {result['reason']}")
        open_purchase_page()

2.3.3 Scenario B: Orders Outside Platform (Self-Collected Payment)

Applicable products: promotion-only software (self-collected payment), or server/client software with Pay-First (PAY_FIRST) sales model or self-collected VIP features.

Prerequisites: the product has licenseEnabled turned on; obtain licenseApiSecret from the developer console (server-side only, must never be exposed to the client). Architecture:

Client                  Your server                       PowerSoftware platform
  │                       │                                 │
  │ User pays (your       │                                 │
  │ payment channel)      │                                 │
  ├──────────────────────→│                                 │
  │                       │ generateForSoftware(            │
  │                       │   machineCode, edition,         │
  │                       │   clientOrderId)                │
  │                       │ (HMAC signature + timestamp)    │
  │                       ├────────────────────────────────→│
  │                       │ ← returns licenseCode           │
  │ ← returns licenseCode │                                 │
  │                       │                                 │
  │ activate(licenseCode, machineCode)                     │
  ├───────────────────────────────────────────────────────→│
  │ ← returns activationToken                              │
  │                       │                                 │
  │ verifyCached(...)     │                                 │
  ├───────────────────────────────────────────────────────→│
  │ ← { valid, edition, expiryTime }                       │

Server side (safeguards the license secret licenseApiSecret, issues licenses):

from ps_license_sdk import LicenseClient, machine_code

server_client = LicenseClient(
    product_unique_code="PRO-2026-001",
    api_secret="YOUR_LICENSE_API_SECRET_FROM_DEVELOPER_CONSOLE",
)

def issue_license(user_machine_code, edition="PRO", order_id=""):
    """After the user pays, the server calls the platform to issue a license"""
    result = server_client.generate_for_software(
        machine_code_value=user_machine_code,
        edition=edition,
        expiry_days=365,
        client_order_id=order_id,
    )
    return result["licenseCode"]

Client side (licenseApiSecret NOT required, activation and verification):

client = LicenseClient(product_unique_code="PRO-2026-001", api_secret="")

def activate(license_code):
    mc = machine_code()
    result = client.activate(license_code, mc)
    save_cred({"licenseCode": license_code,
               "activationToken": result["activationToken"]})

To implement: server-side generateForSoftware (license issuance) + upgradeForSoftware (upgrade/renewal); client-side activate / verifyCached + local credential persistence + LicenseError error code handling.

2.3.4 SDK Method Quick Reference

Method Scenario A Scenario B Signature Description
machine_code() Generate machine code (cross-language consistent)
claim_trial(mc) Claim trial license (Try-Before-You-Buy products only)
activate(code, mc) Activate license code, bind to machine
verify(code, mc, token) Verify license status
verify_cached(code, mc, token) Verification with local cache (60s)
deactivate(code, mc) Unbind machine (login required, browser scenario)
purchase_url(mc) Generate platform purchase page URL
generate_for_software(mc, edition, ...) HMAC In-app license issuance (server-side only)
upgrade_for_software(code, edition, ...) HMAC In-app upgrade/renewal (server-side only)

Return field note: the success responses of activate / verify / claimTrial all carry licenseUpgradeMode (product upgrade policy: SAME_CODE = the license code stays unchanged; NEW_CODE = the code is re-bound). Clients use it to decide whether to show a "bind license code" input: with SAME_CODE the code never changes, so never prompt users to re-enter it; with NEW_CODE a new code is issued on upgrade/renewal — overwrite the locally stored licenseCode with the one returned by the API. Defaults to SAME_CODE when unset; verify is cached for about 60s, so a config change takes effect within 60s. The responses also carry trialExpiryTime (trial expiry snapshot, ISO 8601 string; null when the license did not originate from a trial). Clients can use it as a grace window: if the user trials the full product and then buys a lower tier, keep the top-tier features unlocked until trialExpiryTime. The responses also carry trialExpiryTime (trial expiry snapshot, ISO 8601 string; null when the license did not originate from a trial). Clients can use it as a grace window: if the user trials the full product and then buys a lower tier, keep the top-tier features unlocked until trialExpiryTime. The responses also carry trialExpiryTime (trial expiry snapshot, ISO 8601 string; null when the license did not originate from a trial). Clients can use it as a grace window: if the user trials the full product and then buys a lower tier, keep the top-tier features unlocked until trialExpiryTime. The responses also carry trialExpiryTime (trial expiry snapshot, ISO 8601 string; null when the license did not originate from a trial). Clients can use it as a grace window: if the user trials the full product and then buys a lower tier, keep the top-tier features unlocked until trialExpiryTime.

Dual-environment debugging is fully transparent to both scenarios:

  • Scenario A: on debug machines, claimTrial / platform purchase page payment automatically runs through the Test environment for the whole pipeline (see Chapter 4)
  • Scenario B: when a debug machine calls generateForSoftware, the platform also routes by machine code to the Test environment and returns a T- prefixed license code; no change to server code is needed
  • SDK call patterns are identical during debugging and after release — no environment parameters or code branches needed; environment routing is handled automatically by the platform server

3. License Integration Details (Common to Both Scenarios)

3.1 Local Credential Storage Specification

The SDK itself does not handle persistence; developers implement it themselves. What to store:

{
  "licenseCode": "XXXXXXXXXXXX",
  "activationToken": "YYYYYYYYYYYY",
  "lastVerify": {
    "valid": true,
    "edition": "ULTIMATE",
    "expiryTime": 1735689600000,
    "trialExpiryTime": 1735000000000,
    "trialExpiryTime": 1735000000000,
    "trialExpiryTime": 1735000000000,
    "trialExpiryTime": 1735000000000,
    "cachedAt": 1735689600000
  }
}

Principles:

  • Store only licenseCode + activationToken + the latest verify result
  • Do not store decryptable full license information (meaningless against reverse engineering; treat it as cache only)
  • The 60s cache of verifyCached lives inside the SDK process and is lost after restart; call verify again

3.2 Edition Level Comparison (edition)

Developers define their own edition codes (e.g., BASIC / PRO / ULTIMATE) on the platform publish page. All product types (client/server/promotion-only) can customize editions once license codes are enabled. The client compares by level when checking:

EDITION_LEVEL = {"BASIC": 0, "PRO": 1, "ULTIMATE": 2, "TRIAL": 99}

def edition_sufficient(user_edition, required_edition):
    return EDITION_LEVEL.get(user_edition, 0) >= EDITION_LEVEL.get(required_edition, 0)

Common mapping (for reference):

Feature Tier edition code Level Typical Features
Basic BASIC 0 Basic photo editing, format conversion
Pro PRO 1 Batch processing, HD upscaling
Ultimate ULTIMATE 2 AI restoration, cover assistant

Edition names and codes are developer-defined on the platform; they are not forced to be BASIC/PRO/ULTIMATE. Edition prices and feature lists are shown on the purchase page only when "Platform collects license fees" is checked.

3.3 Error Code Handling (LicenseError)

The SDK throws LicenseError with an error_code attribute:

from ps_license_sdk import LicenseError

try:
    result = client.verify_cached(...)
except LicenseError as e:
    if e.error_code == "expired":
        open_purchase_page()
    elif e.error_code == "revoked":
        show_message("License has been revoked, please contact support")
    elif e.error_code == "machineLimit":
        show_message("Machine binding limit reached, please unbind old devices in your account center")
    elif e.error_code == "NETWORK_ERROR":
        show_message("Network error, please check your network and retry")
    else:
        show_message(f"Verification failed: {e}")
Error Code Meaning Suggested Client Handling
codeNotFound License code does not exist Check the input
revoked Revoked Prompt to contact support
expired Expired Guide to purchase/renew
machineLimit Machine binding limit reached Guide to unbind in account center
tooManyAttempts Rate limited Prompt to retry later
trialNotEnabled Trial not enabled for the product Check platform configuration
trialAlreadyPurchased Already purchased this product Show "already purchased" and lead to the purchase page
trialAlreadyPurchased Already purchased this product Show "already purchased" and lead to the purchase page
trialAlreadyPurchased Already purchased this product Show "already purchased" and lead to the purchase page
trialAlreadyPurchased Already purchased this product Show "already purchased" and lead to the purchase page
NETWORK_ERROR Network/timeout Offline grace or prompt retry

3.4 Purchase Page Redirect (Scenario A Only)

3.4.1 Purchase Page URL

https://www.powersoftware.app/product/license/purchase?productUniqueCode={productUniqueCode}&machineCode={machineCode}

SDK method:

url = client.purchase_url(mc)

When a debug machine visits this page, a "Debug Mode (Test Environment)" badge appears at the top and payment goes through the test channel (see Chapter 4). Language is auto-detected by the purchase page from the user browser (URL prefix / Accept-Language); the SDK does not need to care.

3.4.2 Site: Always Use the International Site .app; Developers Never Choose a Purchase Site

powersoftware.app (International) powersoftware.cn (China)
Payment Methods Waffo (card / Apple Pay / Google Pay, etc.) + PayPal + Alipay Alipay only
Country/Currency Auto-detected by Cloudflare via IP (CN→CNY, others→USD) Fixed country=CN, CNY
Language Auto-detected by URL prefix / Accept-Language Fixed zh-CN
Positioning The only purchase entry developers need to use The landing site for Alipay payments from the international site

Developers do not need to choose a purchase site: purchaseUrl always points to the international site .app. Overseas users complete Waffo / PayPal payments directly on .app; when users in China choose Alipay on .app, the platform automatically redirects to the China site .cn to complete Alipay payment (login state is synced automatically, no re-login needed) and returns to the license flow after successful payment. The entire pipeline is transparent to both users and developers.

# Correct: always use the international site; do not switch base by region/language
url = client.purchase_url(mc)

# Not recommended: detecting region yourself and passing a .cn base — the Alipay
# redirect is already handled by the platform, and hardcoding .cn loses
# the Waffo / PayPal payment methods

4. Debugging Stage (Dual Environment, New in v4)

The most reliable acceptance method before release: register your everyday development computer as a "debug device" and run the complete flow through the real payment pipeline.

4.1 Register a Machine Code

  1. Account Center → "My Machine Codes" → register the machine code of your debug computer
    • The machine code can be generated with the SDK machine_code() (the three languages produce identical results on the same machine)
  2. Note down that machine code

4.2 Register a Debug Device

  1. Developer Center → Product List → the "Debug Devices" button of the target product
  2. In the dialog, pick from registered machine codes and add it to this product (an optional note is supported)
  3. Limit: at most 3 debug devices per product; supports a toggle (temporarily disable routing) and removal

4.3 What Happens on a Debug Machine

On a registered and enabled debug machine, the whole pipeline of this product automatically switches to the Waffo Test environment:

Debug machine (your PC)   PowerSoftware platform (server auto-determines env)
    │                  │
    │ claimTrial(machineCode)
    ├─────────────────→│ This machine is a debug device of this product → Test path
    │                  │
    │ ← Trial license (T- prefixed code, written to test license table)
    │                  │
    │ purchaseUrl(machineCode)
    ├─────────────────→│ Purchase page shows the "Debug Mode (Test Environment)" badge
    │                  │ → Pay with a Waffo Test test card (no real charge)
    │                  │ → Order lands in the test order table, platform issues a test code (T- code)
    │ ← Platform issues code (T- code)
    │                  │
    │ activate(T- code, machineCode)
    ├─────────────────→│ Locates the test table directly by the T- prefix
    │ ← activationToken│
    │                  │
    │ verifyCached(...)
    ├─────────────────→│ Verification passes
    │ ← { valid, edition, expiryTime }

Key points:

  • How to recognize: a license code with the T- prefix is a test license; the purchase page shows the "Debug Mode (Test Environment)" badge at the top
  • Payment: goes through the Waffo Test checkout; use a test card (e.g., 4576 ... 0110) to complete payment — no real charge occurs
  • Zero code change: the client needs no change at all; the same code behaves identically on debug machines and real user machines (only the backend tables differ)

4.4 Recommended Debug Checklist

  • First launch on the debug machine → claimTrial succeeds and returns a T- prefixed trial code
  • During trial, verifyCached returns valid and paid features pass
  • purchaseUrl opens the purchase page; confirm the "Debug Mode" badge appears
  • Complete payment with the test card → receive the test license code (email/page)
  • activate succeeds → verifyCached passes
  • Edition gating works correctly (a lower-edition code accessing higher-edition features is blocked)
  • Machine binding limit (machineLimit) and the unbind path work normally
  • (Scenario B only) calling generateForSoftware on the debug machine returns a T- prefixed code with normal activation/verification; calling the same API with an unregistered machine returns a production code
  • Use another unregistered machine to repeat claimTrial and confirm it goes through the production pipeline (control verification)

4.5 Debugging Notes

Item Description
Test data cleanup Test licenses/orders do not affect production; no cleanup needed; to reset, remove and re-add in the debug device panel
Debug toggle To temporarily stop using the test pipeline, just turn off the toggle in the debug device panel; no need to delete the device
Promotion-only products Promotion-only products are not synced to the Waffo product catalog; there is no debug purchase pipeline
No valid price When main/secondary prices and all license editions have no positive price, test product sync fails (DingTalk alert); configure at least one edition price

5. Release Stage

5.1 Submit for Review

  1. After all debug checklist items pass, click "Save & Submit for Review" in the product form
  2. The product enters the review queue (PENDING_RELEASE)

For products saved as draft during debugging, the content submitted for review is exactly the latest draft content; editing again during review automatically rolls back to draft state (to prevent content changes during review) and requires resubmission.

5.2 Review Passed → Auto-Sync to Production

After platform operations approve the review:

  1. The product state becomes published and goes live
  2. The platform automatically syncs the product to the Waffo production environment (writes back waffo_product_id), creating/restoring the production checkout product
  3. The product detail page and search results become visible to all users

5.3 Real User Pipeline (Production)

The pipeline for real users (non-debug machines) is completely identical to debugging, except everything lands in production tables:

First launch → claimTrial gets a trial (production license code, no T- prefix)
        → Trial expires → purchaseUrl opens the purchase page (Alipay / PayPal real payment)
        → Platform auto-issues license after successful payment + email delivery
        → activate → verifyCached passes

Users of Scenario B (self-collected payment) do not use the platform purchase page: after the user pays via your channel, your server calls generateForSoftware to issue a production license code; the subsequent activation/verification pipeline is the same as Scenario A.

5.4 Release Verification Checklist

  • Visit the product detail page from a computer not registered as a debug machine and confirm it displays normally
  • The purchase page has no "Debug Mode" badge
  • A real small-amount payment → license issued → activation succeeds
  • The order appears in the Developer Center order list (test orders never appear there)

6. Post-Release Operations

Operation Entry Description
View orders Developer Center → My Orders Production orders only; test orders do not participate in revenue sharing or settlement
User unbinding User account center / developer manual licensing Rebind quota: cannot unbind again within 30 days after unbinding
Version iteration Product edit → Save Draft / Submit for Review Editing triggers another async sync to Waffo Test; you can keep using debug machines to verify new versions
Refunds Waffo Dashboard Buyer opens a ticket, merchant reviews it in the Dashboard; refunds of test orders only revoke test licenses
Retire debugging Remove the device in the debug device panel It is recommended to remove debug machines after versions are stable to avoid accidentally using the test pipeline

7. Frequently Asked Questions (FAQ)

Q1: Do I need to change client code or configuration during debugging? No. Environment routing is automatically determined by the platform server based on "whether the machine code is in the debug device list"; SDK calls are completely identical.

Q2: Can license codes obtained on a debug machine be used by real users? No, and it is not recommended. T- codes are only valid in test tables, and debug data never participates in any production logic; sending test codes to real users means they cannot obtain after-sales support through the normal verification path.

Q3: Does debugging cost money? No. The test payment channel uses test cards with no real charges; test orders do not participate in revenue sharing/settlement.

Q4: Does debugging affect my product review? No. Debug data is fully decoupled from review; you can debug in draft state, and whether to submit for review is up to you.

Q5: What if I want to debug on multiple computers? Each product can register at most 3 debug devices; add/remove them in the debug device panel.

Q6: Can I debug purchases if the product type is "promotion-only" or has no price configured? No. Promotion-only products are not synced to the Waffo product catalog; without a valid price, test product sync fails. Such products can only debug the trial and verification pipeline.

Q7: What happens if I forget to turn off the debug toggle before release? The impact is limited to the machine you registered — it still goes through the test pipeline; all real users are unaffected. Once you confirm stability, simply remove it in the debug device panel.

8. Integration Checklist (Verify Item by Item Before Release)

Scenario A (Full Platform Pipeline)

  • Publish the product on PowerSoftware.net with the "Try-Before-You-Buy" sales model
  • Configure trial days (7~14 days recommended)
  • Configure license editions (edition code + name + price + feature list)
  • Confirm "Platform collects license fees" is checked (mandatory for Try-Before-You-Buy)
  • Note down the productUniqueCode
  • Copy the SDK source code into your project (see 2.3.1)
  • Implement claimTrial → claim trial on first launch
  • Implement activate → activate with user-entered license code
  • Implement verifyCached → check when a paid feature is clicked
  • Implement purchaseUrl → redirect to the purchase page when not licensed (always use the international site .app; no site selection needed)
  • Implement local credential persistence (licenseCode + activationToken)
  • Implement edition level comparison logic (see 3.2)
  • Implement LicenseError error code handling (see 3.3)
  • Register a debug machine and complete the dual-environment debug checklist (see 4.4)
  • Submit for review → approved → release verification (see 5.4)

Scenario B (Orders Outside Platform)

  • Publish the product on PowerSoftware.net with licenseEnabled turned on
  • Configure license editions (edition code + name); if platform collection is needed, check "Platform collects license fees" and fill in prices
  • Obtain licenseApiSecret from the developer console
  • Note down the productUniqueCode
  • Set up a server to safeguard the license secret licenseApiSecret and implement the license issuance API (the secret must never be delivered to the client)
  • Client: copy the SDK source code into the project
  • Server: implement generateForSoftware (license issuance)
  • Server: implement upgradeForSoftware (upgrade/renewal)
  • Client: implement activate / verifyCached
  • Implement local credential persistence
  • Implement edition level comparison logic (see 3.2)
  • Implement LicenseError error code handling (see 3.3)
  • Register a debug machine to verify the T- code pipeline (see 4.4)
  • Submit for review → approved → release verification (see 5.4)

Appendix: SDK Repository (GitHub)

github.com/mizhanchengxi/powersoftware-license-sdk
├── node/      SDK source (ESM, zero-dependency, single file)
├── python/    SDK source (py3, zero-dependency, 3 files)
├── java/      SDK source (Java 8+, zero-dependency, 3 files)
└── docs/      SDK specification documents

All three packages provide: machineCode() / sign() / LicenseClient (including activate / verify / deactivate / claimTrial / generateForSoftware / upgradeForSoftware / verifyCached / purchaseUrl).

This document is the v4 upgrade of the Client Software Authorization Integration Guide (CLIENT_SOFTWARE_GUIDE), fully covering all of its content while adding dual-environment debugging and draft/submit-for-review capabilities.