跳到正文
Indiana
控制台
文档
章节目录 / 快速开始

快速开始

NEXT.JS APP ROUTER · TYPESCRIPT · SANDBOX

从安装 SDK 到读取第一份客户权益。

下载接入示例
SDK 0.0.1更新于

开始前准备

本例用一个 AI 创作 App 接入客户身份和 generations 权益。示例宿主是 Next.js App Router;SDK 本身不依赖框架。

  • 服务端运行 Node.js 22 或更高版本,使用 ESM。按下方命令固定安装当前组织包;它与旧个人包的版本空间独立。
  • 一个已有服务端登录校验的 Web App。示例中的 getSignedInUser 由你的登录系统提供,返回稳定 user.id 或 null。
  • 一个 Indiana App,选择 Sandbox,在 SDK 接入页保存 base URL、App ID 和服务端 Key,并设置网站来源。
  • Key 至少授权 customers:write、sessions:create、entitlements:read。客户 Token 继承签发 Key 的客户权限。
  • 若要看到可用的 generations:保存并启用这个计量功能,再发布含额度的商品,通过 Funnel 领取或购买。只有已配置且符合资格的默认免费套餐/自动试用政策才会分配合同;未配置时空持有是正常结果。

相关:已发布 SDK 与源码支持范围·服务端 API Key 鉴权·查看 Key 权限图解·创建功能和套餐

安装并配置 SDK

本指南使用已发布的 npm 0.0.1 beta,直接执行下面的安装命令,无需构建源码包。下载 ZIP 提供宿主接入文件。在应用根目录创建被 Git 忽略的 .env.local,填写 SDK 接入页显示的值,然后重启开发服务。

浏览器和服务端必须使用同一个 App ID、base URL 和 Sandbox。GROOVE_SECRET_KEY 只出现在服务端模块,绝不能加 NEXT_PUBLIC_ 前缀。

接入当前 App 与 Sandbox

接入 → SDK 接入

当前组件的隔离示例截图;未持久化、未访问真实服务。

  1. 准备可信身份与来源

    有当前 App apiKeys:write 权限的 Owner、Admin 或 Developer 配置服务端 Key 与 app.example.com 的完整 HTTPS 来源。后端识别自己的用户,浏览器只接收 customer token。

完成后检查真实 App 的进度由持久事实决定;示例空状态不代表服务已连通。

完成后检查

依赖已安装,环境变量已填写,Secret Key 没有出现在任何浏览器代码中。

TerminalShell
# 安装文档使用的 npm 0.0.1 beta 版本npm install @tinkerlandai/indiana-sdk@0.0.1

建立服务端客户端

新建 lib/groove-server.ts。这个模块集中保存 Key 和范围,供你的会话接口和受保护的业务操作使用。

用户身份以你自己的登录系统为准。相同 App、环境、externalUserId 再次识别会返回同一个客户,不按邮箱合并,也不覆盖已有资料。

完成后检查

缺少配置时模块明确报错;Key 只在服务端读取。下一步会使用真实登录会话识别客户。

lib/groove-server.tsTypeScript
import "server-only";import { createGrooveServer } from "@tinkerlandai/indiana-sdk/server"; function required(name: string): string {  const value = process.env[name];  if (!value) throw new Error(`Missing ${name}`);  return value;} export const groove = createGrooveServer({  baseUrl: required("GROOVE_BASE_URL"),  apiKey: required("GROOVE_SECRET_KEY"),  scope: {    projectId: required("GROOVE_PROJECT_ID"),    environment: "sandbox",  },});

为当前登录用户签发会话

在你自己的应用里新增 GET /api/groove-session。它先校验登录,再识别客户,最后返回这个客户的短期 Token。浏览器不提交 customerId 或 externalUserId。

将 @/lib/auth 替换为你现有的服务端认证模块。第二个选项卡只说明它的类型约定,需要你提供真实实现。

完成后检查

已登录请求返回 customerToken 和 expiresAt,响应为 no-store;未登录请求返回 401。Token 默认 10 分钟过期,权限每次请求重新核对。

app/api/groove-session/route.tsTypeScript
import { groove } from "@/lib/groove-server";import { getSignedInUser } from "@/lib/auth"; export async function GET(request: Request) {  // 使用你现有的服务端登录校验。不要从查询参数取用户 ID。  const user = await getSignedInUser(request);  if (!user) return Response.json({ error: "Unauthenticated" }, { status: 401 });   const { customer } = await groove.getOrCreateCustomer({    externalUserId: user.id,  });  const { customerToken, expiresAt } =    await groove.createCustomerSession(customer.id);   return Response.json(    { customerToken, expiresAt },    { headers: { "Cache-Control": "no-store" } },  );}

在浏览器读取并显示权益

新建 lib/groove-client.ts,再将 GenerationAccess 放进你的已登录页面。先获取会话并 identify,然后读取当前客户的权益。

持有按服务端时间和实际账本计算。accessible、unlimited 和 available 用于界面展示;真正执行生成任务时,仍需在可信后端授权并原子消费或预占积分。

完成后检查

页面显示 generations 可用或不可用;浏览器网络请求携带客户 Token,代码里没有 Secret Key。登出前调用 grow.reset()。

lib/groove-client.tsTypeScript
import { createGroove } from "@tinkerlandai/indiana-sdk"; export const grow = createGroove({  baseUrl: process.env.NEXT_PUBLIC_GROOVE_BASE_URL!,  scope: {    projectId: process.env.NEXT_PUBLIC_GROOVE_PROJECT_ID!,    environment: "sandbox",  },}); export async function connectGroove(signal?: AbortSignal) {  const response = await fetch("/api/groove-session", {    credentials: "same-origin",    cache: "no-store",    signal,  });  if (!response.ok) throw new Error(`Session failed: ${response.status}`);  const { customerToken } = await response.json();  if (typeof customerToken !== "string") throw new Error("Invalid session");  signal?.throwIfAborted();  grow.identify(customerToken);  return grow.readHoldings();} // 登出或切换用户前调用;不要把 Token 存入 localStorage。export function disconnectGroove() {  grow.reset();}

验证接入与常见问题

从网络面板和控制台核对
现象检查方法
UNAUTHENTICATED先确认宿主会话接口返回 200;Token 过期时重新签发一次,再重试。不要无限重试。
FORBIDDENKey 是否有 entitlements:read?是否已撤销、过期或缩减权限?
SCOPE_MISMATCH前后端的 App ID 和环境是否一致?Header 不能切换 Key 的范围。
unassigned / allowed: false是否已发布并通过 Funnel 授予商品?权益 key 是否为 generations?已有客户保留其原合同版本。
浏览器跨域失败SDK 接入里的来源是否与网站协议、域名和端口完全一致?

从网络面板和控制台核对

  • UNAUTHENTICATED

    检查方法
    先确认宿主会话接口返回 200;Token 过期时重新签发一次,再重试。不要无限重试。
  • FORBIDDEN

    检查方法
    Key 是否有 entitlements:read?是否已撤销、过期或缩减权限?
  • SCOPE_MISMATCH

    检查方法
    前后端的 App ID 和环境是否一致?Header 不能切换 Key 的范围。
  • unassigned / allowed: false

    检查方法
    是否已发布并通过 Funnel 授予商品?权益 key 是否为 generations?已有客户保留其原合同版本。
  • 浏览器跨域失败

    检查方法
    SDK 接入里的来源是否与网站协议、域名和端口完全一致?

相关:完整代码示例·积分消费·HTTP 参数与响应