# 快速开始：5 分钟完成第一次调用

本教程帮助你在 5 分钟内完成平台接口的第一次调用。

## 注册开发者账号

在浏览器中打开开发者门户 `https://dev.${DOMAIN}`。开发者组织有两种来法：

- **自助注册**：在门户的注册页填组织名称、你的名字和邮箱。平台给你建好开发者组织，你是它的管理员，激活码发到这个邮箱。部署不能发邮件时自助注册是关闭的，返回 HTTP 503（`self sign-up is closed on this deployment`），这时请平台运营为你开通。
- **由平台运营开通**：运营建好开发者组织，把你的邮箱设为第一位成员（开发者兼管理员）。激活码发到你的邮箱，或者由运营交给你。

第一次登录：在门户点「登录」，转到平台的登录页，填登录名（邮箱），用激活码设置密码。平台自己的登录页上这一步是「输入激活码 / 验证码，设置密码」；部署用身份服务自己的登录页时，这一页的标题是「E-Mail Verification」，在验证码（Code）一栏填激活码，再填新密码。之后每次登录填登录名和密码。登录页可能请你设置双因素认证（「2-Factor Setup」）；组织没有强制要求时可以跳过，以后再设。登录后回到门户。

组织管理员在门户的「团队」页邀请成员、调整角色：「开发者」可以建应用、传版本、提交审核；「管理员」另外能管理团队和 API 凭证。脚本和命令行不走浏览器登录，而用 API 凭证，见 [认证与凭证](/developers/guide/auth) 的「调用开发者平台接口」。

## 创建一个应用

登录开发者门户，点「新建应用」，在「创建上架条目」对话框里选类型（应用、模板等），填名称和 ID。命令行里用接口建，或者第一次 `k3 publish` 时自动建：

```bash
k3 api POST /api/dev/apps --data '{"id": "my-app", "listing": {"name": "我的应用"}}'
```

应用 ID 在整个平台唯一：3 到 48 个小写字母、数字和 `-`，以字母开头，不能是平台保留的词。已被别的组织占用时创建返回 HTTP 409；ID 格式正确但被保留时也返回 HTTP 409，错误码 `app_id_reserved`，说明里写明这个 ID 是保留的（之前的平台版本对保留的 ID 返回 HTTP 422 `validation_failed`）。`distribution` 这样的目录应用 ID 由平台保留，要接手维护由平台运营转交，见 [本地开发与测试企业](/developers/guide/local-dev) 的「接手目录里的应用」；转交之前怎样跑这类应用的代码，见同一页的「转交之前运行目录应用的代码」。

分发方式不在创建时选，由你的开发者组织决定：

- **商店应用**：独立的开发者组织（自助注册或由运营开通）建的应用。发布到应用市场，供多个企业安装，版本要经平台审核；上架之前要完成门户里的企业认证，测试不需要。
- **自建应用**：属于某个企业的开发者组织建的，只在这个企业里用。版本提交后自动通过，不审核。这样的组织只建集成，而且在工作台的「我的组织 → 开放平台」里建，不在开发者门户。

由谁运行应用也不在创建时选，而由每个版本的清单决定：写了 `runtime.image` 的版本由平台托管运行；没写的由你自己运行，平台按清单的 `opsApi` 与 `web.url` 找它（见 [本地开发与测试企业](/developers/guide/local-dev) 的「开发版本的 opsApi 与 web.url」）。所以创建的回答里 `hosting` 总是 `external`，它不是一个可选的字段。

## 建测试企业并以测试员工身份使用

测试企业是你的组织自己的租户，数据独立、不计费，可以带示例客户、多维表和测试员工，开发调试都在这里。在门户的「测试企业」页点「新建测试企业」，或者用命令行：

```bash
k3 test-enterprise create "我的测试企业" --email sbx-admin@example.com --sample general
```

`--email` 是测试企业的管理员，要一个平台上还没有账号的邮箱：你自己登录门户的邮箱会被拒绝（HTTP 422 `already in use`）。回答里的测试企业 ID 就是下文的 `TENANT`。接着：

1. 把应用的一个版本装进去：门户应用详情的「测试企业」页签点「试装」，草稿也可以装。
2. 以测试员工身份进入：每个测试企业有测试店长、测试前台、测试员工三个人。`k3 test-enterprise users <测试企业 ID>` 列出他们，`k3 test-enterprise signin <测试企业 ID> <员工 ID>` 打开一个一次性的登录地址，进到测试企业的工作台，像企业的员工一样使用应用。门户里每个测试员工旁边也有「以此身份打开」。

数量、期限、试装的规则和命令行的全部用法见 [本地开发与测试企业](/developers/guide/local-dev)。

## 获取测试密钥

进入应用的凭证管理页面。

创建一个环境为 `test` 的应用密钥（命令行：`k3 credentials create <应用 ID> --env test`），获取你的客户端 ID（`client_id`）与客户端密钥（`client_secret`）。测试环境密钥以 `kwo_test_` 为前缀。

客户端密钥仅在创建时完整展示一次。请将它妥善保存在环境变量中：

```bash
export CLIENT_ID="${CLIENT_ID}"
export CLIENT_SECRET="${CLIENT_SECRET}"
export TENANT="${TENANT}"
export DOMAIN="${DOMAIN}"
```

## 换取访问令牌

调用令牌端点 `POST https://api.${DOMAIN}/open/v1/oauth/token` 换取令牌。认证信息可以通过 HTTP Basic 认证头提供，也可以写在请求正文中。

使用 curl 发起请求：

```bash
curl -sS -X POST "https://api.${DOMAIN}/open/v1/oauth/token" \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&tenant=${TENANT}"
```

请求成功返回 HTTP 200，正文包含有效期为 300 秒的 Bearer 访问令牌：

```json
{
  "access_token": "at_test_example_token_value",
  "token_type": "Bearer",
  "expires_in": 300,
  "scope": "platform:ping",
  "tenant": "test_corp",
  "environment": "test"
}
```

将返回的令牌存入环境变量：

```bash
export ACCESS_TOKEN="at_test_example_token_value"
```

## 调用 whoami 接口

持有访问令牌后，向 `GET https://api.${DOMAIN}/open/v1/whoami` 发送请求以验证身份：

```bash
curl -sS -X GET "https://api.${DOMAIN}/open/v1/whoami" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"
```

接口返回当前令牌关联的租户、应用、凭证与权限范围：

```json
{
  "tenant": "test_corp",
  "app": "app_example_id",
  "credential": "crd_example_id",
  "scopes": ["platform:ping"]
}
```

## 使用 Go SDK 调用

Go SDK 负责自动获取令牌、在到期前 60 秒自动刷新、处理重试与游标分页。

安装 SDK：

```bash
go get github.com/21stware/raise-go
```

`raise-go` 发布在公共的 Go 模块代理上。连不上公共代理时，平台在 `https://dev.${DOMAIN}/dl/go` 也提供一个只读的模块代理，但它只有这个平台构建时带的那个 raise-go 版本，通常是最新版；别的版本答 404，`go` 命令接着试 `GOPROXY` 里的下一个：

```bash
export GOPROXY="https://dev.${DOMAIN}/dl/go,https://proxy.golang.org,direct"
```

只能用平台的代理时，项目要用它提供的那个版本（`k3 sdk update` 升到最新版）。

编写并运行以下代码：

```go
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/21stware/raise-go"
)

func main() {
	c := raise.New(os.Getenv("CLIENT_ID"), os.Getenv("CLIENT_SECRET"),
		raise.WithTenant(os.Getenv("TENANT")),
		raise.WithBaseURL("https://api."+os.Getenv("DOMAIN")+"/open/v1"),
	)
	me, err := c.Platform.Whoami(context.Background())
	if err != nil {
		log.Fatal(err) // *raise.Error：err.Code、err.RequestID
	}
	fmt.Println(me.Tenant, me.App, me.Scopes)
}
```

## 使用 TypeScript SDK 调用

TypeScript SDK 支持 Node.js 18+ 与现代边缘运行时，提供零外部依赖的客户端实现。

安装 SDK：

```bash
npm install "https://dev.${DOMAIN}/dl/sdk/21stware-raise-sdk-0.7.7.tgz"
```

npm 包不在公共的 npm 注册表上，从平台的 `/dl/sdk/` 安装，不用登录（应用 SDK 与套件包见下面的「安装应用 SDK 与套件包」）。`GET https://dev.${DOMAIN}/dl/sdk/` 以 JSON 列出平台提供的 SDK 与套件包（文件名里带版本号），把上面的文件名换成列表里的那个。

编写并运行以下代码：

```typescript
import { Raise } from '@21stware/raise-sdk'

const raise = new Raise({
  clientId: process.env.CLIENT_ID!,
  clientSecret: process.env.CLIENT_SECRET!,
  tenant: process.env.TENANT,
  baseUrl: `https://api.${process.env.DOMAIN}/open/v1`,
})

const me = await raise.platform.whoami()
console.log(me.tenant, me.app, me.scopes)
```

## 用模板创建一个应用

命令行工具 `k3` 可以生成一个完整的应用项目：Go 服务端、React 页面和清单 `manifest.json`。项目依赖公开发布的 Go SDK `github.com/21stware/raise-go`（v0.7.7），构建时从 Go 模块代理下载，项目里不带 SDK 的副本，`go.mod` 也没有 `replace`。

```bash
k3 app init my-app --module example.com/my-app
cd my-app
go build ./...
```

服务端用 SDK 的应用工具包 `appkit` 写（`api/app`）。`appkit.Serve` 是整个应用：调用者来自宿主网关转来的页面令牌（`K3-App-Token`），用宿主的公钥核验，请求里自带的身份头一律不认；人员、权限、审计经 `appkit.Client` 调宿主的 `/app/v1`；助理的草稿经 `appkit.MountDrafts`；op 只接受宿主签名的断言。进程只读宿主规范规定的应用环境：`K3_PLATFORM_URL`、`K3_APP_CLIENT_ID`、`K3_APP_CLIENT_SECRET`，以及 `PORT` 和 `K3_BASE_DOMAIN`。所以同一个程序既能由平台托管运行，也能放在别的宿主后面运行，例如参考宿主（见 脱离平台运行）。在本机接测试企业的做法见 [本地开发与测试企业](/developers/guide/local-dev) 的「平台根地址：RAISE_BASE_URL 与 K3_PLATFORM_URL」。

清单由代码生成：改了 `api/app/app.go` 里的清单之后，运行 `go run ./api manifest > manifest.json`。页面在 `web/`，用 `@21stware/raise-app-kit` 0.4.1 的 `bootstrap`，构建到 `api/app/webdist`，由服务端在 `/` 提供。0.4.1 的页面只能在工作台里打开；脱离平台打开页面、按宿主的能力隐藏入口，要等 `@21stware/raise-app-kit` 0.5 发布。

模板把数据放在内存里。应用有自己的数据时，版本在清单里用 `data.migrations` 声明自己的表结构，用 `runtime.image` 声明服务端镜像（按摘要固定），由平台托管运行，平台为它准备专属的数据库。带 `data.migrations` 的版本只能托管运行，数据库连接只交给平台运行的进程。在平台之外运行的应用拿不到平台的数据库，数据放在应用自己的存储里。开发时怎样在测试企业里跑这样的应用，见 [本地开发与测试企业](/developers/guide/local-dev) 的「有自己数据的应用」。

模板固定的是 v0.7.7，即目前最新发布的版本。以后的版本发布后，升级到 SDK 的最新版本：

```bash
k3 sdk update
```

它在项目目录里依次运行 `go get github.com/21stware/raise-go@latest` 和 `go mod tidy`。

## 安装应用 SDK 与套件包

应用的 React 页面用应用 SDK `@21stware/raise-app-sdk` 与套件包 `@21stware/raise-app-kit`。它们和服务端 SDK 一样从平台的 `/dl/sdk/` 安装，不用登录，也不用令牌。`/dl/sdk/` 保留每个已发布的版本，钉在旧版本上的项目也能从这里安装。

每个包写明了它依赖的其他 `@21stware/*` 包的确切版本，这些包要一起装：

- `@21stware/raise-app-kit` 依赖 `@21stware/raise-design`、`@21stware/raise-platform`、`@21stware/raise-app-sdk`；
- `@21stware/raise-app-sdk` 依赖 `@21stware/raise-bridge`。

先把 tarball 下载到项目里，再从文件安装。直接 `npm install https://…/x.tgz` 装不上：npm 会去公共注册表找这些依赖，那里没有。下面是 SDK 0.7.7 发布的版本：

```bash
mkdir -p vendor
for f in 21stware-raise-app-kit-0.4.1.tgz 21stware-raise-app-sdk-1.2.0.tgz \
         21stware-raise-design-0.2.3.tgz 21stware-raise-platform-0.2.2.tgz 21stware-raise-bridge-1.1.0.tgz; do
  curl -fsSL -o "vendor/$f" "https://dev.${DOMAIN}/dl/sdk/$f"
done
npm install ./vendor/*.tgz
```

`GET https://dev.${DOMAIN}/dl/sdk/` 答一个 JSON：`{"npm": […], "go": […]}`。`npm` 的每项是一个 tarball，有 `name`、`version`、`file`、`size`、`sha256` 和 `url`；`go` 的每项是平台的 Go 模块代理（`/dl/go/`）里的一个 `module` 和 `version`。部署没有带这些包时答 HTTP 404。版本不同时按列表换文件名。

`/dl/sdk/` 上的 npm 包和 `/dl/go/` 上的 raise-go 都保留发布过的每个版本。在这之前构建的部署，旧的 npm 版本会得到 404：按列表换成现在的版本，或者用提交在仓库里的 `vendor/`。每个版本同时发布哪些套件版本，见 [App API](/developers/guide/app-api) 的「兼容矩阵」。`react` 等其他依赖仍从公共 npm 注册表安装。

把 `vendor/` 提交到应用的仓库，CI 和别的机器就不必访问平台。

这些包没有发布到 npmjs.org，也没有发布到 GitHub Packages（`npm.pkg.github.com`）。

## 下一步

- [本地开发与测试企业](/developers/guide/local-dev)：安装 `k3`，建测试企业，让本机的进程接到测试企业，收发事件，看日志。
- [凭证与认证机制](/developers/guide/auth)：了解三种凭证、测试与正式环境、客户令牌及密钥轮换。
- [接口约定与错误处理](/developers/guide/conventions)：错误信封、分页规则、幂等键与限流响应头。
- [错误码与请求 ID](/developers/guide/errors)：每个错误码的含义与处理方式，用请求 ID 查一次调用。
- [Webhooks 与事件订阅](/developers/guide/webhooks)：配置事件推送、校验 HMAC 签名与处理重试。
