TON 钱包绑定接口对接文档

概述

本文档描述了如何通过 TON Connect 协议与后端的钱包授权接口对接,实现 TON 钱包的绑定和解绑功能。接口使用 JWT 认证,需在请求头中携带有效令牌。后端使用 TON 的 ton-proof 协议验证 Ed25519 签名,确保绑定请求的安全性。

基础信息

  • 接口前缀/auth/wallet
  • 认证方式:JWT(在 Authorization 请求头中传递 Bearer <token>
  • 响应格式
  • 成功:HTTP 200,无响应体
  • 失败:HTTP 400,返回 JSON: json { "code": 400, "error": "错误信息(如:参数错误)" }

接口详情

1. 获取 Nonce

用于生成绑定钱包所需的唯一 nonce。

  • URLGET /auth/wallet/nonce
  • 请求头Authorization: Bearer <jwt-token>
  • 响应
  • 成功(HTTP 200): json "uuid-string" // 例如 "550e8400-e29b-41d4-a716-446655440000"
  • 失败(HTTP 400,示例): json { "code": 400, "error": "未登录" }

  • 说明

  • Nonce 有效期 5 分钟,需在绑定接口中使用。
  • 需确保用户已登录(JWT 有效)。

2. 绑定钱包

将用户的 TON 钱包地址绑定到账户。

  • URLPUT /auth/wallet/bind
  • 请求头Authorization: Bearer <jwt-token> Content-Type: application/json
  • 请求体json { "externalAddress": "string", "signature": "string", "timestamp": number, "domain": "string", "payload": "string", "publicKey": "string" }
  • 字段说明

    • externalAddress:TON 钱包地址,格式为 EQ...Uf...(Base64 编码,48 字节)。
    • signature:对消息的 Ed25519 签名(Base64 编码,64 字节)。
    • timestamp:签名生成时间(Unix 时间戳,秒),需在 10 分钟内。
    • domain:应用域名,必须为 yourapp.com(请替换为实际域名)。
    • payload:从 /nonce 接口获取的 nonce。
    • publicKey:钱包的 Ed25519 公钥(16 进制编码,32 字节)。
  • 响应

  • 成功(HTTP 200):无响应体。
  • 失败(HTTP 400,示例): json { "code": 400, "error": "参数错误" // 或 "Nonce 无效或已过期", "签名验证失败", "该钱包地址已被绑定" }

  • 说明

  • 需先调用 /nonce 获取 nonce。
  • 使用 TON Connect 生成 ton_proof,确保签名和公钥正确。

3. 解绑钱包

解除用户账户与 TON 钱包的绑定。

  • URLPUT /auth/wallet/unbind
  • 请求头Authorization: Bearer <jwt-token>
  • 请求体:无
  • 响应
  • 成功(HTTP 200):无响应体。
  • 失败(HTTP 400,示例): json { "code": 400, "error": "未绑定钱包" }

  • 说明

  • 需确保用户已绑定钱包。

TON Connect 集成

前端需使用 TON Connect SDK 生成 ton-proof,以提供签名和相关字段。以下是实现步骤:

依赖

安装 @ton/tonconnect

npm install @ton/tonconnect

示例代码

import { TonConnect } from '@ton/tonconnect';
import axios from 'axios';

const connector = new TonConnect({
    manifestUrl: 'https://yourapp.com/tonconnect-manifest.json' // 替换为实际 manifest 文件地址
});

// 获取 Nonce
async function getNonce() {
    try {
        const response = await axios.get('http://your-backend.com/auth/wallet/nonce', {
            headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
        });
        return response.data;
    } catch (error) {
        console.error('获取 Nonce 失败:', error.response?.data?.error);
        throw error;
    }
}

// 绑定钱包
async function bindWallet() {
    try {
        const userId = "123"; // 从登录状态获取用户 ID
        const nonce = await getNonce();
        const message = `Sign to bind wallet for user: ${userId}`;

        // 生成 ton_proof
        const tonProof = await connector.requestTonProof({
            payload: nonce,
            message: message
        });

        const response = await axios.put(
            'http://your-backend.com/auth/wallet/bind',
            {
                externalAddress: tonProof.address,
                signature: tonProof.proof.signature,
                timestamp: tonProof.proof.timestamp,
                domain: tonProof.proof.domain.value,
                payload: tonProof.proof.payload,
                publicKey: tonProof.public_key
            },
            { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } }
        );

        if (response.status === 200) {
            alert('钱包绑定成功');
        }
    } catch (error) {
        alert('绑定失败: ' + (error.response?.data?.error || '未知错误'));
    }
}

// 解绑钱包
async function unbindWallet() {
    try {
        const response = await axios.put(
            'http://your-backend.com/auth/wallet/unbind',
            {},
            { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } }
        );
        if (response.status === 200) {
            alert('钱包解绑成功');
        }
    } catch (error) {
        alert('解绑失败: ' + (error.response?.data?.error || '未知错误'));
    }
}

注意事项

  1. TON Connect 配置
  2. 创建 tonconnect-manifest.json,包含应用信息(如域名、名称)。
  3. 示例: json { "url": "https://yourapp.com", "name": "Your App Name", "iconUrl": "https://yourapp.com/icon.png" }
  4. 部署到可公开访问的地址(如 https://yourapp.com/tonconnect-manifest.json)。

  5. 域名

  6. 确保 tonProof.proof.domain.valueyourapp.com(与后端配置一致)。

  7. 公钥

  8. TON Connect 的 tonProof.public_key 提供 16 进制编码的 Ed25519 公钥,直接传递。

  9. 错误处理

  10. 处理 400 错误,显示后端返回的 error 信息。

  11. 钱包支持

  12. 测试支持的钱包(如 Tonkeeper、TON Wallet)。
  13. 确保用户已安装兼容的 TON 钱包扩展或应用。

测试指南

测试环境

  • 后端 URLhttp://your-backend.com(替换为实际地址)
  • 域名yourapp.com(与后端配置一致)
  • 钱包:Tonkeeper 或其他支持 TON Connect 的钱包

测试用例

  1. 获取 Nonce
  2. 调用 GET /auth/wallet/nonce,验证返回有效 UUID 字符串。
  3. 确保未登录时返回 400 错误(由全局处理器处理)。

  4. 绑定钱包

  5. 使用 TON Connect 生成 ton_proof,调用 PUT /auth/wallet/bind
  6. 测试有效签名(正确地址、签名、公钥、nonce、时间戳、域名)。
  7. 测试无效场景:

    • 无效地址(非 TON 格式)
    • 无效签名(篡改或错误公钥)
    • 过期 nonce(超过 5 分钟)
    • 过期时间戳(超过 10 分钟)
    • 错误域名
    • 已绑定的地址
  8. 解绑钱包

  9. 调用 PUT /auth/wallet/unbind,验证成功解绑。
  10. 测试未绑定钱包时的错误。

常见问题

  1. 签名验证失败
  2. 检查 publicKey 是否正确(16 进制,32 字节)。
  3. 确保 messageSign to bind wallet for user: <userId>
  4. 验证 payload/nonce 返回的 nonce 一致。