> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.6mm.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.6mm.com/_mcp/server.

# 秒合约跳转与内嵌接入

本文对应 **Agent Java SDK `v0.3.0`**、Java 8+ 与该标签的公开契约。示例在合作方后端运行；先按[快速开始](/zh-CN/sdk/agent-sdk/java/quick-start)初始化 `client`。

## 示例前提

Java 示例共用以下导入。按快速开始初始化 `client`，从已保存的绑定取得 `platformUserId`。示例用户和订单 ID 必须替换为实际持久化值。

```java
import com.sixmm.agent.*;
import com.sixmm.agent.model.*;
import java.util.*;
```

## 秒合约页面接入

### 跳转进入

后端签发一次性入口链接，把 `webUrl` 返回前端，由浏览器立即打开：

```java
CreateEntryUrlResponse entry = client.createEntryUrl(
        CreateEntryUrlRequest.of("merchant-user-001")
                .withRedirectPath("/zh-CN/up-down?tradingMode=demo")
                .withReturnUrl("https://merchant.example/markets"));
String webUrl = entry.webUrl;
```

对应接口：`POST /v1/agent/create-entry-url`。本例进入模拟页面；实盘使用 `tradingMode=real`，并让商户钱包接口使用实盘环境。

以下为交易页面路径示例；语言、交易对和站点路径以平台提供的前端配置为准：

| 产品   | 路径                                                 | 钱包       |
| ---- | -------------------------------------------------- | -------- |
| 涨跌预测 | `/zh-CN/up-down?tradingMode=real` 或 `demo`         | Funding  |
| 高低预测 | `/zh-CN/high-low?tradingMode=real` 或 `demo`        | Funding  |
| 网格预测 | `/zh-CN/grid-prediction?tradingMode=real` 或 `demo` | Funding  |
| 永续合约 | `/zh-CN/futures/BTCUSDT?tradingMode=real` 或 `demo` | Contract |

`redirectPath` 是前端内部相对路径，必须以 `/` 开头；`returnUrl` 是商户绝对 HTTP/HTTPS 地址。`expireAt` 为 Unix 秒。一次性 ticket 过期或已消费后重新签发，不能长期缓存或共享给其他用户。Agent 保存目标路径；页面模式由前端处理，不会因签发入口链接自动执行资金划转。

### SDK 内嵌进入

已有合约内嵌的商户可以复用后端签 token 和续签逻辑，并配置秒合约页面地址。前端继续使用 Widget 的 `partner-token + tokenProvider` 机制。

接入顺序：

1. 使用平台提供的 Widget SDK 创建实例，配置目标秒合约页面和实盘/模拟模式。
2. `tokenProvider` 收到该实例的 `channelId` 后，请求商户自己的认证接口。
3. 商户后端根据登录会话取得 `agentUserId`，调用通用 `createEmbedToken`。
4. 商户后端返回 `embedToken`、`expireAt`，并原样带回当前请求的 `channelId`，由前端交给 SDK。
5. 续签使用同一实例通道；重建 Widget 后使用新实例提供的通道。

后端签 token 的调用：

```java
// channelId 必须来自当前 Widget 实例。
String channelId = "tw_example_channel";
CreateEmbedTokenResponse auth = client.createEmbedToken(
        CreateEmbedTokenRequest.of("merchant-user-001", channelId));
String embedToken = auth.embedToken;
long expireAt = auth.expireAt;
```

对应接口：`POST /v1/agent/create-embed-token`。业务入参为 `agentUserId`、`channelId`，以及可选 `symbol`。秒合约不需要借用合约交易对来选择玩法。玩法、页面地址和交易模式由前端配置；token 不负责选择资金环境。

token 为短期一次性凭据，不放入 URL query，不当作用户永久登录凭据。签发或续签 token 不会充值、划转或改变余额。Java Agent SDK 负责后端认证；浏览器端另行加载 Trading Widget SDK。

#### 前端完整示例

以下参数已按公开 [Trading Widget SDK](https://app.6mm.com/sdk/trading-widget/v1/index.js) 核对（2026-10-10，线上版本 `1.8.0`）。Widget 版本与 Java SDK `v0.3.0` 是两个独立版本。

```html
<div id="seconds-widget" style="width: 100%; height: 720px"></div>
<script src="https://app.6mm.com/sdk/trading-widget/v1/index.js"></script>
<script>
  const page = 'up-down';
  const tradingMode = 'demo';
  const capabilities = TradingWidget.capabilities;
  if (!capabilities?.partnerToken ||
      !capabilities.pages?.includes(page) ||
      !capabilities.tradingModes?.includes(tradingMode)) {
    throw new Error('Trading Widget SDK does not support the requested page or mode');
  }

  const widget = TradingWidget.create('#seconds-widget', {
    baseUrl: 'https://app.6mm.com',
    page,
    tradingMode,
    theme: 'dark',
    locale: 'zh-CN',
    auth: {
      mode: 'partner-token',
      tokenProvider: async ({ channelId }) => {
        // 这是商户自行实现的接口；后端从登录会话取得用户身份。
        const response = await fetch('/api/seconds/embed-token', {
          method: 'POST',
          credentials: 'same-origin',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ channelId }),
        });
        if (!response.ok) {
          throw new Error('Failed to request embed token');
        }
        const result = await response.json();
        if (typeof result.embedToken !== 'string' || !result.embedToken.trim() ||
            !Number.isFinite(result.expireAt) || result.channelId !== channelId) {
          throw new Error('Invalid embed token response');
        }
        return {
          embedToken: result.embedToken,
          expireAt: result.expireAt,
          channelId: result.channelId,
        };
      },
    },
    onReady() {
      console.log('Trading widget ready');
    },
    onError(error) {
      console.error('Trading widget error', error.code);
    },
  });
  window.addEventListener('beforeunload', () => widget.destroy());
</script>
```

| 选项            | 取值                                         | 说明                                           |
| ------------- | ------------------------------------------ | -------------------------------------------- |
| `page`        | `up-down` / `high-low` / `grid-prediction` | 涨跌 / 高低 / 网格预测；这是页面选项，不是 token 入参            |
| `tradingMode` | `real` / `demo`                            | 实盘 / 模拟盘；省略时默认为 `real`，建议始终显式传入              |
| `baseUrl`     | `https://app.6mm.com`                      | 平台前端地址；SDK 自动生成 `/embed/trading` 及页面、模式和通道参数 |

商户后端 `/api/seconds/embed-token` 接收 `{ "channelId": "tw_xxx" }`，校验当前登录会话并调用前面的 `createEmbedToken`，成功后返回以下**顶层结构**：

```json
{
  "embedToken": "short-one-time-token",
  "expireAt": 1791590700,
  "channelId": "tw_xxx"
}
```

`expireAt` 使用 Agent 返回的实际 Unix 秒时间戳，不要使用示例值；`channelId` 由商户后端原样带回当前请求值，Agent 的 token 响应本身不包含该字段。如果商户接口使用 `{ "data": ... }` 包装，前端须先取 `data` 再返回上述对象给 `tokenProvider`。业务失败应返回失败 HTTP 状态或在解析包装层时显式抛错。

SDK 在初始化和续签时调用 `tokenProvider`，每次都应请求新的 token；不要缓存并重复兑换同一个 token。用户切换或实盘/模拟盘切换时销毁旧实例，再以目标模式创建新实例。模拟页面的资金接口另行传 `environment=SIMULATED`；设置 `tradingMode` 或签 token 都不会替商户切换钱包请求的环境。

## 来源与相关指南

* [官方 v0.3.0 源码](https://github.com/6mm-com/agent-java-sdk/tree/54aa3c1)
* [合约查询与分页](/zh-CN/sdk/agent-sdk/java/perpetual-queries)
* [Funding 钱包](/zh-CN/sdk/agent-sdk/java/funding-wallet)
* [升级指南](/zh-CN/sdk/agent-sdk/java/upgrade)