Giao thức CLV1 đã công khaiĐọc đặc tả
Mục lục thư viện

Thư viện tài liệuBắt đầu theo ngôn ngữ

Bắt đầu với C# và .NET

Bài này dành cho phần mềm WinForms, WPF, console hay dịch vụ chạy trên .NET 8 trở lên: cài SDK, vài dòng mã, chạy thử, xem kết quả trên dashboard, xử lý lỗi và phát hành. Unity và .NET Framework dùng thư viện C, có bài riêng: [Bắt đầu với Unity](/docs/start-unity).

6 phút đọc

Cần có

  • .NET 8 SDK trở lên. Gói chạy trên .NET 8 và các bản mới hơn, kéo theo hai gói từ nuget.org: BouncyCastle.Cryptography và System.Security.Cryptography.ProtectedData.
  • Một ứng dụng trên dashboard. Trang Tích hợp › Kết nối phần mềm của nó có địa chỉ API, App ID, khoá công khai và nút Tạo key thử nghiệm. Địa chỉ API kết thúc bằng /api, không có /v1: https://<trang ClavisAUH của bạn>/api.

1. Cài SDK

Tải ClavisAuh.0.1.0.nupkg ở trang Tải về, kiểm SHA-256 như trang đó hướng dẫn, rồi đặt file vào thư mục packages cạnh file .csproj của bạn. Tạo nuget.config cũng cạnh file .csproj:

<configuration>
  <packageSources>
    <add key="clavisauh" value="packages" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
</configuration>

Rồi thêm gói:

dotnet add package ClavisAuh --version 0.1.0

Đường dẫn trong nuget.config tính từ chỗ đặt file đó, và Visual Studio đọc cùng file này. nuget.org cấp hai gói đi kèm, nên lần restore đầu cần mạng. Nếu file bạn tải có số phiên bản mới hơn, đổi số trong lệnh cho khớp. SDK chưa có trên NuGet; khi có, bài này sẽ ghi thêm cách cài từ đó.

2. Mã tối thiểu

Các khối dưới đây là từng phần của một file; file đầy đủ, kèm hàm phụ, ở bước 3.

Tạo client một lần khi phần mềm khởi động. Địa chỉ API, App ID và khoá công khai là ba giá trị ở trang Kết nối phần mềm: mẫu đọc chúng từ biến môi trường, phần mềm của bạn thì ghi thẳng vào mã. Khoá thiết bị nằm trong một file, trên Windows được niêm phong bằng DPAPI cho người dùng hiện tại; phiên và token offline gắn với khoá này, còn suất trên license đi theo các định danh đã băm của máy. Mẫu đặt phần này trong Settings.cs để năm kịch bản dùng chung, cùng các hàm phụ đọc cài đặt và báo lỗi:

using ClavisAuh;

namespace ClavisSamples;

/// <summary>What every scenario reads from the environment, and the client they all start from.</summary>
internal static class Settings
{
    public static string Env(string name) =>
        Environment.GetEnvironmentVariable(name) ?? throw new InvalidOperationException($"{name} is not set");

    public static string? Optional(string name) => Environment.GetEnvironmentVariable(name);

    public static string Lang => Optional("CLAVIS_LANG") ?? "en";
    public static string State => Optional("CLAVIS_STATE_DIR") ?? ".clavis";

    public static ClavisClient Client(string? appVersion = null) => new(new ClavisOptions
    {
        BaseUrl = Env("CLAVIS_BASE_URL"),
        AppId = Env("CLAVIS_APP_ID"),
        AppPublicKeys = [Env("CLAVIS_APP_KEY")],
        Device = ClavisDevice.Default(),
        // The same key every run: sessions and offline tokens stay with this machine.
        DeviceKey = ClavisDevice.LoadOrCreateKey(Path.Combine(State, "device.key")),
        AppVersion = appVersion,
    });

    /// <summary>The sentence for the person using the software, then a line for the CI to read.</summary>
    public static int Fail(ClavisException e)
    {
        Console.WriteLine(e.UserMessage(Lang));
        Console.WriteLine($"FAIL {e.Code}");
        return 1;
    }
}

Kích hoạt key của khách. Khi không có mạng, token offline lưu lần trước giữ phần mềm chạy tiếp:

JsonObject? grant = null;
try
{
    await client.InitAsync();
    grant = await client.ActivateAsync(Settings.Env("CLAVIS_LICENSE"));
}
catch (ClavisException e)
{
    // No answer (network_error, timeout...): a token saved last time keeps the software working.
    var offline = e.Retryable && File.Exists(saved) ? client.VerifyOffline(File.ReadAllText(saved)) : null;
    if (offline is null) return Settings.Fail(e);
    Console.WriteLine("Offline: using the license saved on this machine");
}

Khoá công khai chỉ dùng để kiểm chữ ký, nhúng vào phần mềm là an toàn. Đừng bao giờ nhúng một khoá bí mật nào.

3. Chạy thử

Cách nhanh nhất là dự án mẫu, đã nối sẵn SDK và cài đặt của ứng dụng:

  1. Ở trang Kết nối phần mềm, chọn C#, bấm Tạo key thử nghiệm rồi Tải dự án mẫu (.zip), và giải nén.
  2. Trong thư mục vừa giải nén, chạy .\run.cmd <key thử nghiệm> (Windows) hoặc ./run.sh <key thử nghiệm> (Linux, macOS). Script chạy dotnet run --project ClavisSamples -c Release -- k1.

Muốn chạy trong dự án của bạn: thêm Settings.cs ở trên và nguyên K1Key.cs dưới đây vào một dự án console đã có gói, rồi cho Program.cs đúng một dòng return await ClavisSamples.K1Key.RunAsync();. Đặt bốn cài đặt lấy ở trang Kết nối phần mềm (CLAVIS_APP_KEY là khoá công khai, không phải bí mật), rồi dotnet run:

$env:CLAVIS_BASE_URL = "<địa chỉ API>"
$env:CLAVIS_APP_ID = "<App ID>"
$env:CLAVIS_APP_KEY = "<khoá công khai>"
$env:CLAVIS_LICENSE = "<key thử nghiệm>"
export CLAVIS_BASE_URL="<địa chỉ API>" CLAVIS_APP_ID="<App ID>" CLAVIS_APP_KEY="<khoá công khai>" CLAVIS_LICENSE="<key thử nghiệm>"

Mẫu in License active until <ngày>, rồi OK k1 active ở dòng cuối. Chạy lại trên cùng máy vẫn thành công và không tốn thêm suất: server nhận ra máy qua các định danh đã băm (Thiết bị, HWID và chuyển máy). Khoá thiết bị, dùng để ký yêu cầu, nằm trong thư mục .clavis dưới thư mục bạn chạy lệnh (hoặc trong CLAVIS_STATE_DIR). Container tạo mới cho mỗi lần chạy thì lần nào cũng giống một máy mới, xem Câu hỏi thường gặp. Toàn bộ kịch bản K1 trong dự án mẫu:

using System.Text.Json.Nodes;
using ClavisAuh;

namespace ClavisSamples;

/// <summary>K1 · License key only: activate on this machine, keep the session alive, work offline for a while.</summary>
internal static class K1Key
{
    public static async Task<int> RunAsync()
    {
        var client = Settings.Client("1.0.0");
        var saved = Path.Combine(Settings.State, "offline.token");

        JsonObject? grant = null;
        try
        {
            await client.InitAsync();
            grant = await client.ActivateAsync(Settings.Env("CLAVIS_LICENSE"));
        }
        catch (ClavisException e)
        {
            // No answer (network_error, timeout...): a token saved last time keeps the software working.
            var offline = e.Retryable && File.Exists(saved) ? client.VerifyOffline(File.ReadAllText(saved)) : null;
            if (offline is null) return Settings.Fail(e);
            Console.WriteLine("Offline: using the license saved on this machine");
        }

        if (grant is not null)
        {
            var license = grant["license"]!;
            Console.WriteLine($"License {license["status"]} until {license["expiresAt"]?.ToString() ?? "forever"}");
            // Every few minutes while the software runs; sessions end after an hour without one.
            await client.HeartbeatAsync();
            if (client.OfflineToken is { } token) File.WriteAllText(saved, token);
            await client.LogoutAsync();
        }
        Console.WriteLine($"OK k1 {(grant is null ? "offline" : grant["license"]!["status"])}");
        return 0;
    }
}

4. Thấy trên dashboard

  • Tích hợp › Kết nối phần mềm: bước 4 chuyển sang Đã kết nối, kèm csharp/0.1.0 và hệ điều hành của máy.
  • Phát hành & bản quyền › License: key chuyển từ Chưa dùng sang Đang dùng.
  • Người dùng cuối › Thiết bị & phiên: máy vừa chạy, cùng phiên nó vừa mở.

5. Xử lý lỗi

Mọi lần bị từ chối là một ClavisException có ba thứ bạn cần:

  • e.Code: mã trong bảng Mã lỗi, ví dụ invalid_credentials hay device_limit.
  • e.UserMessage("vi"): câu cho người dùng phần mềm của bạn, tiếng Việt hoặc "en", hiện thẳng được.
  • e.Retryable: true khi không nhận được câu trả lời (mất mạng, quá chậm, máy chủ bận); để một lúc rồi thử lại có thể thành công.

Câu trả lời không mang đúng chữ ký của ứng dụng bị từ chối bằng ClavisVerificationException (cũng là một ClavisException), với mã unsigned_response, invalid_response_signature, unknown_key hoặc stale_response. Hãy coi đó như không có câu trả lời; thường là địa chỉ API hoặc khoá công khai trong phần mềm bị sai.

Khi phần mềm đang chạy, gửi heartbeat đều đặn để giữ phiên, khoảng mỗi 5 tới 30 phút: phiên hết sau 60 phút không có heartbeat.

// Every few minutes while the software runs; sessions end after an hour without one.
await client.HeartbeatAsync();

Lưu token offline mới nhất, để lần sau không có mạng vẫn mở được:

if (client.OfflineToken is { } token) File.WriteAllText(saved, token);

Mọi gói đều có token offline, mặc định dùng được 7 ngày; Premium tự chọn thời hạn, còn Chế độ chỉ online thì tắt hẳn. Xem License offline.

6. Đóng gói và phát hành

  • Ghi địa chỉ API, App ID và khoá công khai vào mã hoặc vào file cấu hình đi cùng bản build. Máy khách không cần biến môi trường nào.
  • Đặt thư mục trạng thái (khoá thiết bị, token offline) trong Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), thêm tên phần mềm của bạn. Đừng đặt trong thư mục cài đặt.
  • Truyền AppVersion đúng phiên bản bạn phát hành: server dùng nó để chặn bản quá cũ và báo có bản mới, xem Bản phát hành có ký và cập nhật.
  • Trong WinForms và WPF, gọi các hàm …Async bằng await trong sự kiện của giao diện. Đừng chặn bằng .Result hay .Wait(): giao diện sẽ đứng trong lúc chờ mạng.
  • dotnet publish -c Release đóng gói SDK cùng phần mềm như mọi gói NuGet. Chạy thử bản publish tới OK k1 active trên một máy sạch trước khi phát hành.
  • Trước khi bán, bật Đã phát hành công khai ở Tích hợp › Cài đặt ứng dụng. Mọi key TEST ngừng hoạt động, nên key thử nào lỡ lọt ra ngoài cũng không dùng được nữa.

Câu hỏi thường gặp

Phần mềm có cần mạng mỗi lần mở không? Kích hoạt và heartbeat cần mạng. Khi mất mạng, token đã lưu giữ phần mềm chạy tiếp, mặc định 7 ngày hoặc theo thời hạn chọn trên Premium, được kiểm ngay trên máy mà không cần server.

Khách cài lại hệ điều hành hay đổi máy thì sao? Cài lại trên cùng máy, server vẫn nhận ra máy nhờ dấu nhận diện phần cứng đã băm. Máy mới chiếm một suất mới; hết suất thì khách nhận device_limit, và bạn Đặt lại thiết bị trong trang License. Xem Thiết bị, HWID và chuyển máy.

Giới hạn tính năng theo gói license thế nào? client.Has("ten_tinh_nang") cho biết license có quyền tính năng đó không; tạo quyền trong Gói license và quyền tính năng.

Phần mềm của tôi còn chạy .NET Framework 4.x? SDK này cần .NET 8. Với .NET Framework, gọi thư viện C qua P/Invoke đúng như bài Bắt đầu với Unity: cùng mã, không phụ thuộc Unity.

Liên quan

Bạn cần giúp gì?

Câu hỏi thường gặp

ClavisAUH là gì?

Dịch vụ bản quyền và xác thực cho phần mềm desktop: license key, khoá thiết bị, đăng nhập người dùng và phản hồi có chữ ký. Tất cả quản lý trên một dashboard

Vì sao kẻ crack không làm giả được server?

SDK chỉ tin câu trả lời ký bằng khoá bí mật của ứng dụng. Khoá đó không bao giờ rời server của chúng tôi, nên server giả không ký được và bị từ chối

Trong file chạy của tôi có gì?

Mã ứng dụng và khoá công khai. Không có bí mật nào, vì mỗi thiết bị tự tạo cặp khoá riêng ở lần chạy đầu

Có chạy được khi offline không?

Có. Mọi gói đều có token offline có chữ ký dùng được 7 ngày, ứng dụng tự kiểm tra tại máy. Premium cho bạn chọn bao lâu. License đã thu hồi vẫn có thể chạy offline trong khoảng đó, trừ khi Chế độ chỉ online đang bật

Hỗ trợ những ngôn ngữ nào?

Hiện có JavaScript và TypeScript. Tiếp theo là C++, C# và Python, cùng một giao thức công khai

Gói Basic có miễn phí thật không?

Có. Không giới hạn thời gian, không cần thẻ. Cần nhiều hơn thì lên Premium

Liên hệ