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
- Log in to the PowerSoftware.net Developer Center → Publish product, choose "Client Software" as product type
- Choose Try-Before-You-Buy as the sales model and set trial days (7~14 days recommended)
- Configure license editions (edition): three tiers by default,
BASIC/PRO/ULTIMATE; code, name, price and feature list are customizable - Check "Platform collects license fees" (mandatory for Try-Before-You-Buy)
- 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 + activationToken → LicenseError 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/claimTrialall carrylicenseUpgradeMode(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: withSAME_CODEthe code never changes, so never prompt users to re-enter it; withNEW_CODEa new code is issued on upgrade/renewal — overwrite the locally storedlicenseCodewith the one returned by the API. Defaults toSAME_CODEwhen unset;verifyis cached for about 60s, so a config change takes effect within 60s. The responses also carrytrialExpiryTime(trial expiry snapshot, ISO 8601 string;nullwhen 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 untiltrialExpiryTime. The responses also carrytrialExpiryTime(trial expiry snapshot, ISO 8601 string;nullwhen 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 untiltrialExpiryTime. The responses also carrytrialExpiryTime(trial expiry snapshot, ISO 8601 string;nullwhen 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 untiltrialExpiryTime. The responses also carrytrialExpiryTime(trial expiry snapshot, ISO 8601 string;nullwhen 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 untiltrialExpiryTime.
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 aT-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
verifyCachedlives inside the SDK process and is lost after restart; callverifyagain
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
- 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)
- The machine code can be generated with the SDK
- Note down that machine code
4.2 Register a Debug Device
- Developer Center → Product List → the "Debug Devices" button of the target product
- In the dialog, pick from registered machine codes and add it to this product (an optional note is supported)
- 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 →
claimTrialsucceeds and returns aT-prefixed trial code - During trial,
verifyCachedreturnsvalidand paid features pass -
purchaseUrlopens the purchase page; confirm the "Debug Mode" badge appears - Complete payment with the test card → receive the test license code (email/page)
-
activatesucceeds →verifyCachedpasses - 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
generateForSoftwareon the debug machine returns aT-prefixed code with normal activation/verification; calling the same API with an unregistered machine returns a production code - Use another unregistered machine to repeat
claimTrialand 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
- After all debug checklist items pass, click "Save & Submit for Review" in the product form
- 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:
- The product state becomes published and goes live
- The platform automatically syncs the product to the Waffo production environment (writes back
waffo_product_id), creating/restoring the production checkout product - 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
generateForSoftwareto 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
LicenseErrorerror 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
licenseEnabledturned on - Configure license editions (edition code + name); if platform collection is needed, check "Platform collects license fees" and fill in prices
- Obtain
licenseApiSecretfrom the developer console - Note down the
productUniqueCode - Set up a server to safeguard the license secret
licenseApiSecretand 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
LicenseErrorerror 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.