跳到正文
返回 Launch 文件

下載 MarkdownGitHub 原始文件

自定義授權系統接入指南

當前源碼支持兩種授權提供方:Licentivo 和 custom_http。商戶在 系統設置 → 授權系統 統一選擇并啟用其中一個,所有商品和 sandbox/live 環境使用同一提供方;自定義連接與套餐映射仍按商品和環境分別配置。此功能不包含自建授權系統;商戶繼續使用自己的授權服務和 SDK。

現有 Licentivo 帳號交換、九語言 SDK 和設備管理繼續使用原流程。自定義商品通過本文協議同步購買狀態,客戶在帳號中心讀取授權交付,不進入 Licentivo SDK 流程。

快速開始

  1. 在自己的服務實現下面四個 HTTP 接口,配置每個連接獨立的 HMAC 簽名密鑰和標識。
  2. 在“授權系統”選擇“自定義授權服務”,新增連接,填寫地址、環境、連接標識、key ID 和標準 Base64 密鑰,驗證后保存,點擊“啟用自定義授權服務”。
  3. 創建商品和銷售套餐草稿。購買類型和價格在 Launch 管理;授權規則在自有系統管理。
  4. 選擇商品及環境,選擇連接,填寫外部商品與套餐引用并保存。提供方由全站選擇決定,不能為其他商品同時啟用 Licentivo。
  5. 在銷售套餐頁發布測試版,完成付款、交付、續費、升級、退款及故障恢復驗收,再獨立配置正式環境。

地址、環境、連接標識和網路訪問策略創建后不可更改;需要時創建新連接。簽名密鑰可輪換,留空保留原密鑰。存在另一授權系統的訂單時,不能直接切換全站提供方,需要先完成遷移。切換后原套餐轉為僅預覽,需要重新發布;保存的連接憑證和映射保留。已有訂單的商品不能直接切換連接,銷售中的映射也不能直接更換;新價格或映射使用套餐新版本。

通過 API 配置時,先調用 PUT /api/v1/admin/licensing/provider,請求體為 {"provider":"custom_http"} 或 {"provider":"licentivo"},再保存商品映射。未啟用的提供方不能保存商品授權綁定或 Licentivo 套餐策略映射。

連接檢查確認協議、鑒權、環境及支持的購買類型,不會替商戶創建或確認外部套餐。正式收款前必須用真實外部套餐完成 sandbox 接入驗收。

3. 商戶接入流程

  1. 在已有授權系統前實現本文四個接口;授權系統已有的許可證邏輯繼續保留。
  2. 在 Launch 新建“自定義授權服務”連接,配置 HTTPS 地址、連接標識、環境及簽名密鑰。
  3. Launch 調用能力檢查接口,確認協議版本、永久/限時購買支持和交付方式。
  4. 給商品選擇該連接;將銷售套餐版本映射到外部 product_ref 和 plan_ref。外部編號允許使用商戶自己的字符串,不強制使用 Licentivo UUID。
  5. 在 sandbox 中完成付款、續費、升級、退款和故障恢復驗收,然后單獨發布 live 配置。
  6. Launch 驗證付款后,在同一資料庫事務內保存訂單、購買覆蓋和待發送任務。
  7. 后臺把完整購買快照發送給自定義授權服務;該服務持久化接收記錄后再確認接收。
  8. 授權服務處理目標狀態。Launch 查詢原事件回執,只有 applied 才確認本次同步完成。
  9. Launch 為已登錄且擁有該購買的客戶讀取交付資訊,在客戶中心展示授權碼、激活入口或純文本說明。應用接入商戶自己的授權系統。
  10. 續費、升級、退款、撤款、暫停和到期均產生新版本的完整快照。授權服務按最新目標狀態處理。

同一商品的 sandbox/live 分別綁定連接。已有購買后,更換提供方需要獨立遷移流程;舊訂單和舊任務不能跟隨后臺選擇被靜默送到新服務。

4. 自定義授權服務需要實現的 API

以下接口均由商戶授權服務接收,Launch 服務端調用。base_url 可以包含固定路徑前綴;表中路徑追加在該前綴之后。

方法與路徑 做什么
GET /launch/v1/capabilities 連接檢查、協議與能力聲明
POST /launch/v1/events 接收完整購買目標狀態;重試沿用原事件
GET /launch/v1/events/{event_id} 查詢該事件是否實際應用,恢復丟失的響應
GET /launch/v1/entitlements/{entitlement_ref}/delivery 讀取已有授權的交付資訊,不創建第二份授權

首版采用主動查詢回執,不新增外部回調修改訂單的入口。自定義服務不具有確認付款、修改價格或改寫購買期限的權限。

4.1 通用約定與鑒權

  • JSON 使用 UTF-8;時間使用 UTC RFC 3339;version 使用正整數。
  • 成功響應為 {"data": {...}, "request_id": "..."};錯誤響應為 {"error": {"code": "...", "message": "..."}, "request_id": "..."}。
  • 每個環境和連接使用獨立密鑰。密鑰為至少 32 個隨機位元組,在 Launch 中引用現有加密密鑰存儲,不放進前端配置、示例或應用安裝包。
  • 每個請求攜帶 X-Launch-Key-Id、X-Launch-Timestamp、X-Launch-Nonce、X-Launch-Signature。
  • 時間戳為十進制 Unix 秒。nonce 為至少 128 位隨機值的無填充 base64url 編碼,每次網路嘗試生成新值。

簽名輸入是下列七項用單個換行符連接的 UTF-8 位元組,末尾不添加換行:

v1
<key_id>
<timestamp>
<nonce>
<UPPERCASE HTTP method>
<encoded request path and raw query, including the base_url path prefix>
<lowercase hexadecimal SHA-256 digest of the exact request body>

X-Launch-Signature 由 v1= 前綴與 HMAC-SHA256 結果的無填充 base64url 編碼串接而成。GET 請求本文為空。接收端先驗證簽章和時間窗口,再反序列化;使用固定時間比較,預設允許 ±300 秒偏差,並至少保留 nonce 600 秒以防重播。重試不可重複使用 nonce,但事件 ID 和業務請求本文必須保持不變。

簽名不替代 HTTPS。請求不攜帶客戶密碼、支付密鑰或 Licentivo 管理密鑰;預設僅發送匿名帳號引用,不發送郵箱。服務端 HTTP 客戶端需要超時、響應限長和禁止攜帶憑據跟隨重定向。配置自定義地址時校驗目標地址;訪問內網應是管理員明確配置的部署能力。

4.2 能力檢查

GET /launch/v1/capabilities 返回 HTTP 200,例如:

{
  "data": {
    "protocol": "launch-external-licensing-v1",
    "connection_ref": "merchant-01-sandbox",
    "environment": "sandbox",
    "purchase_types": ["perpetual", "fixed_term"],
    "delivery_types": ["license_key", "activation_url", "instructions"],
    "devices": false,
    "identity_exchange": false
  },
  "request_id": "check-001"
}

protocol、connection_ref、environment、purchase_types、delivery_types 必填。后兩項必須為非空數組,未支持的購買類型不能發布銷售。首版 devices 和 identity_exchange 必須為 false;只有擴展契約和實現完成后才允許聲明支持。

4.3 購買快照

POST /launch/v1/events 的請求示例:

{
  "schema": "launch-external-licensing-v1",
  "event_id": "7bf84b91-2749-4b01-a6a7-bd527c488ee8",
  "connection_ref": "merchant-01-sandbox",
  "environment": "sandbox",
  "type": "entitlement.snapshot",
  "change_reason": "purchase",
  "account_ref": "customer_opaque_001",
  "entitlement_ref": "purchase_opaque_001",
  "launch_product_id": "c6dd463e-50f7-4b76-8239-0a8ec6fc304b",
  "launch_plan_version_id": "5459a073-9362-4b46-949d-9b0c5d84421a",
  "product_ref": "desktop-editor",
  "plan_ref": "professional-yearly-v1",
  "version": 1,
  "desired_state": "active",
  "valid_from": "2026-10-25T00:00:00Z",
  "valid_until": "2027-10-25T00:00:00Z"
}

上例所有字段均必填。valid_until 為 null 表示永久購買;其他值必須晚于 valid_from。

欄位 約束和含義
event_id Launch 生成的 UUID;同一事件重試不變
connection_ref / environment 必須與簽名密鑰所屬連接、環境一致
account_ref 穩定匿名帳號引用;接收方以連接、環境和該引用共同識別客戶
entitlement_ref 一次購買關系的穩定引用;全部撤銷后重新購買使用新引用
launch_product_id / launch_plan_version_id Launch 的商品與不可變套餐版本 UUID
product_ref / plan_ref 接入方配置的外部引用;1–128 個 ASCII 字符,允許字母、數字、_、-、.;不自動創建外部套餐
version 同一連接、環境、購買引用下單調遞增的目標版本
desired_state active、suspended、expired、revoked
change_reason purchase、renewal、upgrade、refund、reversal、suspension、resumption、expiry、reconciliation

account_ref、entitlement_ref 使用 1–128 位字母、數字、下劃線或短橫線。schema、type 固定為示例中的值。接收方按 desired_state 和完整絕對期限應用結果,不根據原因自行加天數或撤銷整份授權。

特別是退款:退掉續費或升級訂單后仍可能保留有效基礎購買,此時快照仍可為 active,但期限縮短或套餐改變。只有賬本重算后不再有有效購買覆蓋,才發送對應終止狀態。

4.4 接收、回執與恢復

接收方必須在持久化事件 ID、原始請求體摘要及待執行任務之后,才返回 HTTP 202;已經同步完成的重復請求可以返回 HTTP 200。兩者都返回下述回執結構。

{
  "data": {
    "event_id": "7bf84b91-2749-4b01-a6a7-bd527c488ee8",
    "connection_ref": "merchant-01-sandbox",
    "environment": "sandbox",
    "entitlement_ref": "purchase_opaque_001",
    "version": 1,
    "payload_sha256": "<64-character lowercase hexadecimal SHA-256 digest of the exact event body>",
    "status": "pending",
    "error_code": null
  },
  "request_id": "receipt-001"
}

摘要占位符需要替換為真實摘要。回執中上述字段均必填;error_code 無錯誤時為 null。查詢 GET /launch/v1/events/{event_id} 返回相同結構,HTTP 200,狀態可為:

status 含義 Launch 的處理
pending 已持久化,尚未完成實際授權變更 延后查詢,不能標記同步完成
applied 授權服務確認目標狀態已實際應用 核對全部關聯字段和摘要后確認同步完成
rejected 確定無法應用,例如外部套餐不存在 停止自動重試,顯示明確錯誤
manual_review 結果不確定或狀態沖突,需要人工核對 保留原任務和事件,等待可審計處理

交付狀態獨立于同步回執;applied 不能直接當成客戶應用的運行許可。

可靠性要求:

  • 至少一次投遞,接收方必須保證業務冪等;不能承諾網路層“只調用一次”。
  • 同一事件 ID、同一原始請求體返回已有結果;同一 ID、不同請求體返回 HTTP 409 IDEMPOTENCY_CONFLICT。
  • 舊版本不得覆蓋新版本。同一購買版本的不同事件或不同目標不能靜默接受,應返回 HTTP 409 STATE_VERSION_CONFLICT 供核對。
  • 丟失 POST 響應或進程重啟后,Launch 先 GET 原事件;明確返回 EVENT_NOT_FOUND 后才重發原 ID、原請求體。
  • 遠端實際發放結果不明確時,接入方也應使用自己的持久化冪等引用核對,不能重發一次就再生成一份授權。
  • HTTP 429 遵循 Retry-After;超時和 5xx 退避恢復;超過上限進入人工核對。鑒權、配置和業務拒絕不得被當成同步成功。

建議錯誤碼:SIGNATURE_INVALID(401)、SIGNATURE_EXPIRED(401)、NONCE_REPLAYED(409)、SCOPE_MISMATCH(403)、EVENT_NOT_FOUND(404)、IDEMPOTENCY_CONFLICT(409)、STATE_VERSION_CONFLICT(409)、PLAN_UNKNOWN(422)、PURCHASE_TYPE_UNSUPPORTED(422)、RATE_LIMITED(429)、SERVICE_UNAVAILABLE(503)。錯誤消息不得包含憑據。

4.5 授權交付

GET /launch/v1/entitlements/{entitlement_ref}/delivery 只能讀取該連接及環境中的已有交付結果,不得把查詢當作新發放命令。HTTP 200 示例:

{
  "data": {
    "connection_ref": "merchant-01-sandbox",
    "environment": "sandbox",
    "entitlement_ref": "purchase_opaque_001",
    "version": 1,
    "status": "ready",
    "delivery": {
      "type": "license_key",
      "value": "EXAMPLE-ONLY-NOT-A-REAL-LICENSE",
      "expires_at": null
    }
  },
  "request_id": "delivery-001"
}

status 為 pending、ready 或 unavailable;非 ready 時 delivery 為 null。ready 時 type 必須為已聲明的 license_key、activation_url 或 instructions,value 最多 4096 個 UTF-8 位元組,expires_at 為 UTC 時間或 null。所有響應關聯字段必須與 Launch 當前購買版本一致,舊版本交付不能當作當前結果。

授權碼屬于敏感資料;交付響應使用 Cache-Control: no-store,不記錄正文,需要持久化時使用加密存儲。激活 URL 僅接受連接配置允許的 HTTPS 來源;instructions 按純文本展示,不執行 HTML 或腳本。已撤銷、全額退款或無有效購買的帳號不能繼續通過 Launch 取得交付資訊;已交付憑據的實際失效由外部系統執行。

Launch 管理與客戶 API

這些接口位于商戶部署的 Launch origin 下,不使用外部服務的 HMAC 密鑰鑒權。管理員使用現有網站會話,寫請求必須帶 X-CSRF-Token。客戶只能讀取自己的購買。

方法與路徑 做什么
GET /api/v1/admin/licensing/connections 返回 connections、bindings、mappings 和 default_provider;連接列表只包含自定義連接,Licentivo 沿用原管理接口
POST /api/v1/admin/licensing/connections 驗證并新增自定義連接
PUT /api/v1/admin/licensing/connections/{id} 編輯名稱、允許的激活來源及簽名配置;歷史接收地址不變
POST /api/v1/admin/licensing/connections/{id}/check 重新檢查;HTTP 200 中的 last_check_error 非 null 表示檢查失敗
PUT /api/v1/admin/products/{id}/licensing 為商品所選環境配置提供方、連接與外部套餐映射
GET /api/v1/me/products/{id}/delivery?environment=sandbox 當前客戶讀取當前有效、自定義購買的授權交付;Cookie 或 desktop Bearer 鑒權
GET /api/v1/licensing/documentation 此文件,無需登錄
GET /api/v1/licensing/protocol 自定義服務需要實現的 OpenAPI,無需登錄

新增自定義連接的請求體:

{
  "provider": "custom_http",
  "name": "My licensing service",
  "base_url": "https://licenses.example.com",
  "environment": "sandbox",
  "connection_ref": "merchant-01-sandbox",
  "key_id": "key-1",
  "signing_key": "<standard Base64 encoding of 32 to 128 random bytes>",
  "allow_private_network": false,
  "allow_local_http": false,
  "activation_origins": ["https://activate.example.com"]
}

響應 data 是脫敏后的連接對象,不返回簽名密鑰或 secret ID。allow_local_http 僅允許 sandbox 的 localhost/回環地址;私網 HTTPS 需要管理員顯式允許。密鑰留空僅限編輯已有連接。Licentivo 仍通過 POST /api/v1/admin/connections/licentivo 設置。

配置商品的請求體:

{
  "provider": "custom_http",
  "environment": "sandbox",
  "connection_id": "<id returned when creating the connection>",
  "mappings": [
    {
      "version_id": "<Launch plan version UUID>",
      "product_ref": "desktop-editor",
      "plan_ref": "professional-yearly-v1"
    }
  ]
}

選擇 Licentivo 時發送 {"provider":"licentivo","environment":"sandbox","mappings":[]},其產品/策略映射仍使用原接口。首次切換使已有發布變為預覽,需要按新提供方重新檢查并發布。mappings 可僅包含要新增或保留的版本;省略不會刪除舊映射。

客戶交付為 HTTP 200 的標準 data envelope;無購買返回 404,Licentivo 商品返回 409 LICENSING_DELIVERY_UNSUPPORTED,未同步、暫停、到期或全額退款返回 409 ENTITLEMENT_NOT_READY,外部暫不可用或回執不匹配返回 503。客戶中心的“查看授權交付”會重新校驗歸屬和狀態,不把憑據寫入 URL 或日志。

自定義商品的 Licentivo identity、device、integration-check 接口返回 409 CUSTOM_LICENSING_SDK_REQUIRED。舊 /credential 繼續返回 410。應用應使用商戶自己的 SDK,而不是把 Launch 的 ready 狀態當作運行授權。

示例與驗收

Python 薄適配器示例 提供簽名、nonce、事件去重和回執持久化的接收框架;實際授權處理需要接入商戶系統。

自定義協議的 OpenAPI 與 Launch 自身的 OpenAPI 分別描述兩個方向。接收方無需實現 Licentivo 的產品目錄、策略目錄或身份信任接口。

本地驗證使用隔離 MySQL、真實 HTTP 適配器和模擬授權/支付服務,覆蓋響應丟失、重啟、續費、升級、退款、帳號停用、回執不匹配及交付隔離。生產驗收還需使用商戶自己的授權服務和應用。未聲明的設備、共享帳號登錄及離線能力不由 Launch 補齊。