@tinkerlandai/indiana-sdk 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +52 -264
- package/dist/{api-DDlcUvTv.d.ts → ai-usage-CO0lDavO.d.ts} +3163 -660
- package/dist/chunk-EZFAY4ZL.js +1 -0
- package/dist/chunk-ULRVTA45.js +1 -0
- package/dist/chunk-YOH6DIUN.js +27 -0
- package/dist/{coupon-gncDkBtm.d.ts → customer-page-aZb5Twue.d.ts} +122 -3
- package/dist/funnel-runtime-host-OJJSHKUD.js +1 -0
- package/dist/funnel-runtime-start-DvrWekm0.d.ts +9330 -0
- package/dist/index.d.ts +8677 -1611
- package/dist/index.js +1 -1
- package/dist/native-shell.js +1 -1
- package/dist/openrouter.d.ts +3 -3
- package/dist/openrouter.js +1 -1
- package/dist/paywall-HCADUT22.js +1 -0
- package/dist/popup.d.ts +19 -123
- package/dist/popup.js +5 -4
- package/dist/{funnel-runtime-C05ZECiP.d.ts → screen-DfuSw6Tz.d.ts} +320 -2419
- package/dist/server.d.ts +4 -765
- package/dist/server.js +1 -1
- package/dist/src-3GT6E6XD.js +1 -0
- package/package.json +3 -3
- package/dist/ai-usage-CzA11PV5.d.ts +0 -235
- package/dist/chunk-IUDHYOKC.js +0 -21
- package/dist/chunk-OIWEFX7U.js +0 -1
- package/dist/chunk-TDSKWZTO.js +0 -1
- package/dist/feature-client-CyYevYy2.d.ts +0 -450
- package/dist/funnel-runtime-host-DWRQK5U2.js +0 -4
- package/dist/src-MCXYJ2QR.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Release facts and artifact evidence: [SDK release ledger](../../docs/sdk-releases.md). Earlier personal-package versions are preserved in [@houzhen888/groove-grow history](../../docs/sdk-changelog-groove-grow.md). The two packages have independent version histories.
|
|
4
4
|
|
|
5
|
+
## 0.2.0 — 2026-10-16
|
|
6
|
+
|
|
7
|
+
- Breaking: server Funnel starts and decisions use `customer: { type: "id" | "externalUserId", value }`; optional `customerData` is creation-only and requires an external user reference. Remove start-body `customerToken` and decision `customerId`; no legacy compatibility. Customer-credential starts name no Customer.
|
|
8
|
+
- Export `createFunnelCampaignClient` from `/server` for explicit campaign decisions and starts with durable suppression. Validate returned Funnel and Customer identity before accepting a Run.
|
|
9
|
+
- Use `X-Indiana-Web-View` and `X-Indiana-Telemetry-Subject` response headers. Include current renderer, native shell and Paywall recovery/lifecycle fixes.
|
|
10
|
+
- Route native receipt recovery through independent `subscriptions/native/restore`, including read-only recovery. This package release does not certify or publish Apple/Google store integrations; real store acceptance remains deferred.
|
|
11
|
+
|
|
12
|
+
## 0.1.0 — 2026-10-16
|
|
13
|
+
|
|
14
|
+
- Web Beta with current Indiana factories, Customer two-table identity, current Funnel runtime, Feature generation, Offering hierarchy and independent Paywall sessions. No legacy API or data compatibility.
|
|
15
|
+
- Converge current request/response contracts and regenerate native shell assets. Include canonical Stripe payment setup, subscription administration and License client contracts. Server availability is documented in README; unfinished billing authorization, batch/conflict, Reward Product and native execution remain outside accepted scope.
|
|
16
|
+
- Keep one npm package with browser, server, popup, OpenRouter and native-shell entries. Swift/Kotlin store acceptance and publication are deferred.
|
|
17
|
+
|
|
18
|
+
- Move Local/Remote evaluation policy to each Funnel. New Funnels default to Local; Owner/Admin manage it in Funnel settings. API keys only authenticate and authorize calls; one key can call different modes. Remove `remoteEval`: `configuration()` reads the Funnel mode and the server enforces rule downloads and API decisions.
|
|
19
|
+
- Add `configuration()` with public Funnel topology and versioned resource references. Remote payloads omit rules and commercial parameters; Local payloads include pure evaluation configuration.
|
|
20
|
+
- Keep business actions server-authoritative. Existing Funnels migrate explicitly to Remote; one API key can call both Local and Remote Funnels.
|
|
21
|
+
|
|
5
22
|
## 0.0.1 — 2026-10-13
|
|
6
23
|
|
|
7
24
|
- First organizational Beta under `@tinkerlandai/indiana-sdk`, replacing personal-package imports. Publish with the `beta` tag and pin `0.0.1` when installing.
|
package/README.md
CHANGED
|
@@ -1,307 +1,95 @@
|
|
|
1
1
|
# @tinkerlandai/indiana-sdk
|
|
2
2
|
|
|
3
|
-
Indiana
|
|
3
|
+
Indiana 的 JavaScript/TypeScript SDK,包含浏览器、Node.js、屏幕渲染与可选 AI 用量集成。`0.2.0` 以 `beta` 标签分发;服务端和数据库必须使用匹配的当前协议。本版本有破坏性变更,不提供旧名称、旧输入或旧数据模型的兼容层。
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npm install @tinkerlandai/indiana-sdk@0.0
|
|
6
|
+
npm install @tinkerlandai/indiana-sdk@0.2.0
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## 一个 npm 包,五个入口
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
| 导入入口 | 用途 |
|
|
12
|
+
| ---------------------------------------- | ----------------------------------------------------------- |
|
|
13
|
+
| `@tinkerlandai/indiana-sdk` | 浏览器 Customer、Funnel、Feature 与独立 Paywall 客户端 |
|
|
14
|
+
| `@tinkerlandai/indiana-sdk/server` | 可信 Node.js 后端、Customer Session、业务写入和 Funnel 决策 |
|
|
15
|
+
| `@tinkerlandai/indiana-sdk/popup` | 独立屏幕渲染、清洗和预览 |
|
|
16
|
+
| `@tinkerlandai/indiana-sdk/openrouter` | 可选 OpenRouter 用量集成,需要安装对应 peer dependency |
|
|
17
|
+
| `@tinkerlandai/indiana-sdk/native-shell` | 原生 WebView 的静态 Funnel shell 资源;不代表商店购买验收 |
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
JavaScript 与 TypeScript 共用本包,声明文件随包提供。内部 `@indiana/contracts` 在构建时内联,接入方无需安装私有包。Swift 和 Kotlin 使用各自的包管理生态,不在本次 npm 发布范围内。
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
| ---------------------------------------- | -------------------------------------------------------------- |
|
|
17
|
-
| `@tinkerlandai/indiana-sdk` | 当前客户读取、Funnel 控制器与统一展示宿主 |
|
|
18
|
-
| `@tinkerlandai/indiana-sdk/server` | 客户管理、Session 签发、账本写入与可信 Funnel 启动 |
|
|
19
|
-
| `@tinkerlandai/indiana-sdk/openrouter` | 可选 OpenRouter 用量集成,宿主需安装对应 peer dependency |
|
|
20
|
-
| `@tinkerlandai/indiana-sdk/popup` | 纯屏幕渲染、清洗和编辑预览 |
|
|
21
|
-
| `@tinkerlandai/indiana-sdk/native-shell` | 供 Grow 托管的原生 Funnel 壳生成纯设计资源,不含客户或运行凭据 |
|
|
22
|
-
|
|
23
|
-
## 可信后端与客户身份
|
|
21
|
+
## 从可信后端创建 Customer Session
|
|
24
22
|
|
|
25
23
|
```ts
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
import { createIndianaServer } from "@tinkerlandai/indiana-sdk/server";
|
|
25
|
+
|
|
26
|
+
const indiana = createIndianaServer({
|
|
27
|
+
baseUrl: process.env.INDIANA_BASE_URL!,
|
|
28
|
+
apiKey: process.env.INDIANA_SECRET_KEY!,
|
|
29
|
+
scope: {
|
|
30
|
+
projectId: process.env.INDIANA_PROJECT_ID!,
|
|
31
|
+
environment: "sandbox",
|
|
32
|
+
},
|
|
31
33
|
});
|
|
32
34
|
const user = await requireSignedInUser(request);
|
|
33
|
-
const { customer } = await
|
|
35
|
+
const { customer } = await indiana.getOrCreateCustomer({
|
|
34
36
|
externalUserId: user.id,
|
|
35
37
|
});
|
|
36
|
-
const { customerToken } = await
|
|
38
|
+
const { customerToken } = await indiana.createCustomerSession(customer.id);
|
|
37
39
|
```
|
|
38
40
|
|
|
39
|
-
Secret Key
|
|
40
|
-
|
|
41
|
-
## 统一 Funnel
|
|
41
|
+
Secret Key 仅保存在可信后端。浏览器传入的 Customer ID 不能作为登录证明;将 Customer Token 交给对应的已登录用户。App、Sandbox/Live、Customer、权限和允许来源分别校验。SDK 在内存中持有客户凭据;退出或切换身份时销毁旧视图并清理凭据。
|
|
42
42
|
|
|
43
43
|
```ts
|
|
44
|
-
import {
|
|
45
|
-
const funnels = createFunnelStartClient({
|
|
46
|
-
baseUrl: process.env.GROOVE_BASE_URL!,
|
|
47
|
-
apiKey: process.env.GROOVE_SECRET_KEY!,
|
|
48
|
-
scope: { projectId: process.env.GROOVE_PROJECT_ID!, environment: "sandbox" },
|
|
49
|
-
});
|
|
50
|
-
// 宿主在首次调用前持久保存整个 start 输入;响应丢失后原样重试。
|
|
51
|
-
const result = await funnels.start({ ...retainedStartInput, customerToken });
|
|
52
|
-
```
|
|
44
|
+
import { createIndiana } from "@tinkerlandai/indiana-sdk";
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
import {
|
|
58
|
-
createFunnelRunController,
|
|
59
|
-
showFunnelRun,
|
|
60
|
-
} from "@tinkerlandai/indiana-sdk";
|
|
61
|
-
const client = createFunnelRunController({
|
|
46
|
+
const indiana = createIndiana({
|
|
62
47
|
baseUrl,
|
|
63
48
|
scope: { projectId, environment: "sandbox" },
|
|
64
|
-
runToken,
|
|
65
|
-
});
|
|
66
|
-
const handle = await showFunnelRun(container, {
|
|
67
|
-
client,
|
|
68
|
-
onAuthenticate: async () => obtainCurrentUserCustomerToken(),
|
|
69
|
-
onCheckout: ({ url }) => window.location.assign(url),
|
|
70
|
-
onOpenUrl: ({ url }) => window.location.assign(url),
|
|
71
|
-
onError: reportError,
|
|
72
49
|
});
|
|
50
|
+
indiana.identify(customerToken);
|
|
51
|
+
const { holdings } = await indiana.readHoldings();
|
|
52
|
+
// 退出账号时:
|
|
53
|
+
indiana.reset();
|
|
73
54
|
```
|
|
74
55
|
|
|
75
|
-
|
|
56
|
+
Customer 使用原始业务字段;资料更新是部分 patch,没有 Customer revision。Guest 登录到已有 Customer 时返回 `switched: true`,资源和历史保留原归属,接入方应为目标 Customer 创建新 Session/Funnel;不合并旧 Guest 的财务事实。
|
|
76
57
|
|
|
77
|
-
|
|
58
|
+
## Funnel、Feature 与独立 Paywall
|
|
78
59
|
|
|
79
|
-
|
|
60
|
+
可信后端通过 `createFunnelStartClient().start()` 提交 `funnelId`、持久保留的 `requestId`、`customer`、`locale` 和 `parameters`。`customer` 为 `{ type: "id", value: customerId }` 或 `{ type: "externalUserId", value: verifiedUser.id }`。后者可以同时提供仅用于新建的 `customerData`,需要 `customers:write` 权限;已有 Customer 不因此更新。服务端响应返回 `customerId`,后端再为它签发 Customer Session。旧的 start-body `customerToken` 和 decision `customerId` 明确拒绝。
|
|
80
61
|
|
|
81
|
-
|
|
62
|
+
`createFunnelClient({ customerToken, ...options })` 用于已持有 Customer 凭据的客户端;其 `start()` 不指定 `customer` 或 `customerData`,始终以凭据所属 Customer 运行。响应丢失时保留原编号和输入重试,商业参数放在 `parameters.commerce`。运行有独立的 Run Token,不能据此切换 Customer。
|
|
82
63
|
|
|
83
|
-
|
|
64
|
+
`createFunnelDecisionClient` 的 `submit/decide` 与 `/server` 的 `createFunnelCampaignClient` 同样使用明确的 `customer` 引用。Campaign 客户端提供显式 `decide/start`;受频率或进行中运行限制时,`start` 返回 `suppressed` 和重试时间,不伪造 Run 凭据。Run 实际展示后才提交 `shown`,不能用页面关闭或本地存储代替持久的展示事实。
|
|
84
65
|
|
|
85
|
-
|
|
86
|
-
import { createFunnelDecisionClient } from "@tinkerlandai/indiana-sdk/server";
|
|
87
|
-
const decisions = createFunnelDecisionClient({
|
|
88
|
-
baseUrl,
|
|
89
|
-
scope,
|
|
90
|
-
apiKey,
|
|
91
|
-
funnelId,
|
|
92
|
-
});
|
|
93
|
-
const decision = await decisions.decide({
|
|
94
|
-
requestId: retainedRequestId,
|
|
95
|
-
customerId,
|
|
96
|
-
attributes: {
|
|
97
|
-
tags: ["ad"],
|
|
98
|
-
isNeverPaid: true,
|
|
99
|
-
country: "US",
|
|
100
|
-
channel: "ads",
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
可信后端提交完整 `attributes` JSON,业务可以自行提供付费历史、渠道等属性。结果的 `value` 是规则输出的任意 JSON;首次成功提交的结果(包括 null、false、0 或零优惠对象)固定在当前 allocationScopeId,后续属性变化不会重新计算。`state` 分别表示 active、expired 或 inactive;它随有效期和活动状态变化,value 保持不变。新的分配活动须显式更换 allocationScopeId。parameters、answers、results 是 Funnel 保留的上下文命名空间。
|
|
66
|
+
浏览器使用 `createFunnelRunController` 与 `showFunnelRun`。控制器负责当前协议的命令、读取、重试、恢复和订阅;展示句柄提供 `refresh()` 和 `destroy()`。业务操作按当前发布节点委派给服务端,客户端声明或重定向不构成付款证明。
|
|
106
67
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
const features = decisions.features({
|
|
111
|
-
attributes: { id: customerId, country: "US" },
|
|
112
|
-
});
|
|
113
|
-
await features.init(); // 下载当前 App/环境/Funnel 的配置
|
|
114
|
-
const value = features.getFeatureValue("discount", { starter: 0 }); // 同步、本地判断
|
|
115
|
-
features.setAttributes({ id: customerId, country: "JP" }); // 完整替换属性
|
|
116
|
-
await features.refresh(); // 显式获取新的资源配置
|
|
117
|
-
```
|
|
68
|
+
Local/Remote 求值策略由每个 Funnel 决定。`createFunnelDecisionClient` 支持当前 `configuration()`、`features()`、提交和结果读取;`createFeatureClient` 以 `generation` 标识配置。Local 下载的规则对客户端可见;敏感决策应使用 Remote 和可信业务事实。
|
|
118
69
|
|
|
119
|
-
|
|
70
|
+
Offering 固定 Product Version 的范围与层级。Paywall 固定已发布 Offering Version,并独立选择显示的 Product 子集、分组、顺序和默认项。`createPaywallStartClient`、`createPaywallSession`、`showPaywall` 使用独立 Paywall Session,不创建占位 Funnel。付款完成后重新查询权益,业务后端仍须重新鉴权并幂等扣减用量。
|
|
120
71
|
|
|
121
|
-
|
|
72
|
+
## Web 商业能力与当前服务边界
|
|
122
73
|
|
|
123
|
-
|
|
74
|
+
当前服务端实现了 Customer/Guest、Entitlement 检查、持有与账本读取、用量和余额、Coupon/推荐码、Offering/独立 Paywall,以及配置后的 Stripe Sandbox attach、支付方式收集、订阅验证/更新/退款/Trial 和 License 分配路径。每个操作仍受已配置的支付账号、执行模式、Product/Price 条款和当前实现范围约束;接口存在不表示任意组合已验收。
|
|
124
75
|
|
|
125
|
-
|
|
76
|
+
Stripe 环境的 `autumn` 模式由 Indiana 执行;`record_only` 返回交给商户后端执行的指令。两种模式都从验证后的 Stripe 事实写入财务与权益。支付方式收集本身不产生扣款、Invoice 或额外权益。报价/确认/读取使用各自持久操作 ID;同一请求编号不能更换输入。
|
|
126
77
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
回执可以早于等待节点抵达,但收到确认不代表已经消费。条件和后续任务以 `nodeId.field` 引用声明结果。仅查询优惠的调用方可以继续使用上面的查询接口,不需要增加回执等待。
|
|
130
|
-
|
|
131
|
-
本版包含 `createFunnelOrchestrationClient` 的 `signal` / `readResults` 接口。外部任务可复用已启用的 Sandbox 通知端点,提供签名请求、冻结输入和持久重试预算。接收器必须按 Action Receipt ID 去重;网络调用允许至少一次投递。配置、协议、数据库变更与验收边界见[编排说明](../../docs/funnel-orchestration-2026-10-10.md)。接口存在不代表宿主通知已经配置或验收。
|
|
132
|
-
|
|
133
|
-
## 当前持有与账本
|
|
134
|
-
|
|
135
|
-
### 原生订单恢复
|
|
136
|
-
|
|
137
|
-
Swift/Kotlin 宿主的 `recoverNativeOrders` 通过应用自有 HTTPS 后端提交当前客户会话及受保护的订单绑定。后端使用 `createNativeReceiptClient`,服务器密钥始终留在后端:
|
|
138
|
-
|
|
139
|
-
```ts
|
|
140
|
-
import { createNativeReceiptClient } from "@tinkerlandai/indiana-sdk/server";
|
|
141
|
-
const receipts = createNativeReceiptClient({
|
|
142
|
-
baseUrl: process.env.GROOVE_BASE_URL!,
|
|
143
|
-
apiKey: process.env.GROOVE_SECRET_KEY!,
|
|
144
|
-
scope: { projectId: process.env.GROOVE_PROJECT_ID!, environment: "sandbox" },
|
|
145
|
-
});
|
|
146
|
-
// 先校验应用自身登录会话及当前客户身份;重试保留原 requestId 和输入。
|
|
147
|
-
const result = await receipts.recover(retainedCurrentReceiptInput);
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
该操作为 `/api/v1/funnels` 的 `native_receipt`,需要 `billing:write` 与 `funnels:run` 以及当前 `gg_ct_` 客户会话。它不依赖 run token,不复活或推进已结束、关闭或过期流程。Apple 只 finish 返回的 `nativeCompletion.transactionIds`;Google 的 acknowledge/consume 在服务端持久交付后完成。完整输入、宿主 relay、固定 SKU 席位及商店优惠边界见[当前原生接入](../../docs/native-commercial-current.md)。
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
import { createGroove } from "@tinkerlandai/indiana-sdk";
|
|
154
|
-
const grow = createGroove({
|
|
155
|
-
baseUrl,
|
|
156
|
-
scope: { projectId, environment: "sandbox" },
|
|
157
|
-
});
|
|
158
|
-
grow.identify(customerToken);
|
|
159
|
-
const { holdings } = await grow.readHoldings();
|
|
160
|
-
const { ledger } = await grow.readLedger({
|
|
161
|
-
type: null,
|
|
162
|
-
from: null,
|
|
163
|
-
to: null,
|
|
164
|
-
after: null,
|
|
165
|
-
limit: 50,
|
|
166
|
-
});
|
|
167
|
-
const { quote } = await grow.quoteBalance({
|
|
168
|
-
operation: "quote",
|
|
169
|
-
entitlementId,
|
|
170
|
-
quantity: "1",
|
|
171
|
-
properties: {},
|
|
172
|
-
});
|
|
173
|
-
```
|
|
78
|
+
本 Beta **不认证完整商业功能或生产运行**。当前仍未接通的服务包括:
|
|
174
79
|
|
|
175
|
-
|
|
80
|
+
- 订阅批处理。冲突查询/重试已有对应服务,但仍需匹配部署及业务前置条件。
|
|
81
|
+
- Postpaid Billing Authorization、Billing Coupon 列表和账单调整确认。当前 AutoTopup consent 的准备/确认/撤销已有服务,不能据此推定其他授权类型可用。
|
|
82
|
+
- `claimRewardProduct` 的完整交付。
|
|
83
|
+
- Apple/Google 真实商店验收。当前独立购买/恢复的协议及部分服务实现不代表商店已验收;`createNativeReceiptClient().recover()` 使用 `subscriptions/native/restore`,不再发送旧 Funnel 原生恢复命令。
|
|
176
84
|
|
|
177
|
-
|
|
178
|
-
const { receipt } = await groove.mutateBalance({
|
|
179
|
-
customerId: verifiedCustomer.id,
|
|
180
|
-
command: {
|
|
181
|
-
operation: "consume",
|
|
182
|
-
entitlementId,
|
|
183
|
-
quantity: "1",
|
|
184
|
-
properties: {},
|
|
185
|
-
idempotencyKey: `job:${job.id}`,
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
后端使用 `consume`、`reserve`、`confirm`、`release`、`refund`。每次重试保留原输入和幂等键,确认、释放与退款引用原操作 ID。写入需要 `credits:consume`;退款另外需要 `credits:manage`。当前余额执行仅开放明确的 Sandbox 范围。
|
|
191
|
-
|
|
192
|
-
## 积分、用量与计费
|
|
193
|
-
|
|
194
|
-
以下接口需要与 SDK 协议匹配的 Grow 部署。SDK 包验证与发布不会自动升级既有宿主;数据库、Stripe、原生商店和部署各自的验收结果以对应回执为准。
|
|
195
|
-
|
|
196
|
-
### 周期积分、消费分析与批量用量
|
|
197
|
-
|
|
198
|
-
接口与验收边界见 [积分能力补齐](../../docs/credit-expansion.md)。在可信服务端使用:
|
|
199
|
-
|
|
200
|
-
```ts
|
|
201
|
-
const { entity } = await grow.manageUsageEntity({
|
|
202
|
-
customerId,
|
|
203
|
-
command: { action: "upsert", externalId: "workspace-a", name: "Workspace A" },
|
|
204
|
-
});
|
|
205
|
-
const requestId = crypto.randomUUID(); // 保存这个逻辑批次的身份,重试时复用
|
|
206
|
-
const { batchId } = await grow.submitUsageBatch({
|
|
207
|
-
requestId,
|
|
208
|
-
items: [
|
|
209
|
-
{
|
|
210
|
-
customerId,
|
|
211
|
-
command: {
|
|
212
|
-
operation: "consume",
|
|
213
|
-
entitlementId,
|
|
214
|
-
entityId: entity.id,
|
|
215
|
-
quantity: "1",
|
|
216
|
-
properties: { model: "alpha" },
|
|
217
|
-
idempotencyKey: "generation-001",
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
],
|
|
221
|
-
});
|
|
222
|
-
const { batch } = await grow.readUsageBatch({ batchId, after: -1, limit: 100 });
|
|
223
|
-
// queued 只表示等待执行;逐条检查 accepted / rejected / failed 及 receipt。
|
|
224
|
-
// 后续页使用 batch.nextAfter;整个批次没有跨功能事务语义。
|
|
225
|
-
const { analytics } = await grow.readCreditAnalytics({
|
|
226
|
-
customerId,
|
|
227
|
-
query: {
|
|
228
|
-
range: {
|
|
229
|
-
kind: "absolute",
|
|
230
|
-
from: "2026-10-01T00:00:00Z",
|
|
231
|
-
to: "2026-10-08T00:00:00Z",
|
|
232
|
-
},
|
|
233
|
-
timezone: "UTC",
|
|
234
|
-
filterBy: [],
|
|
235
|
-
interval: "day",
|
|
236
|
-
groupBy: "properties.model",
|
|
237
|
-
entitlementId: null,
|
|
238
|
-
after: null,
|
|
239
|
-
limit: 100,
|
|
240
|
-
},
|
|
241
|
-
});
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
`manageUsageEntity` 需要 `credits:manage`;批量提交需要 `credits:consume`,读取和分析需要 `credits:read`。批次最多 1,000 条,入队后返回 HTTP 202,每条事件保留独立消费凭据。相同 `requestId` 的重试必须保留完整输入;修改内容会冲突。分析保留 `entitlementId` 与 `resourceId`,不同计量单位不能直接相加,`charged` 等字段是积分数量。
|
|
245
|
-
|
|
246
|
-
批量消费可指定 `command.occurredAt`;只允许当前 UTC 月内且不晚于服务器当前时间的事件。该时间用于事件归属,不回溯重算已结算账期或过去套餐的额度与费率。相对分析范围可用 `last_days` 或客户的 `billing_cycles`;读取后续页时,将 `range` 固定为返回的 `analytics.from` / `analytics.to`(`kind: "absolute"`),并传入 `analytics.nextCursor`,避免相对日期窗口在分页时移动。小时分桶使用 UTC,其余分桶使用查询的 IANA 时区。
|
|
247
|
-
|
|
248
|
-
服务端 AI SDK v7 的自动 Token 采集用 `createAiUsageMiddleware`,必须提供每次上游调用的持久幂等键与 usage/quote 存储回调。生成和流式最终用量均使用原报价一次扣减;失败恢复直接重放扣款,避免重跑模型。`readBillingInvoice`、`readCreditStatement` 提供分页逐项账单和精确来源/退款明细。接入代码、时间范围与当前边界见 [积分接入](../../docs/credits-integration.md)。后续协议扩展须单独发布并匹配服务端部署。
|
|
249
|
-
|
|
250
|
-
### 余额规则、状态同步与 AI 配置
|
|
251
|
-
|
|
252
|
-
Server SDK 的 `readBalanceSource` / `adjustBalance` 在读取精确可用余额后以 `expectedAvailable` 增减或 `set_remaining`;保留预扣、已消费及付款依据。独立 `manageBalanceGrant` 的创建/更新使用当前 `rollover`、`nextResetAt` 字段,`amount: null` 表示无限额度,不能与结转组合。没有旧输入兼容。
|
|
253
|
-
|
|
254
|
-
非消耗型的实际状态也通过当前客户的 `readHoldings()` 返回(`entitlements:read`):每行必含可空 `usage`;非消耗型的 `usage` 含实际占用、总容量、带符号剩余量及修订号,其他类型为 null。无限容量的 capacity / remaining 为 null,超容量的 remaining 为负;available 始终非负。指定 Entity 只计算其独立非消耗型容量。
|
|
255
|
-
|
|
256
|
-
非消耗型 metered 权益使用绝对状态同步,读取需要 `credits:read`,设置需要 `credits:manage`:
|
|
257
|
-
|
|
258
|
-
```ts
|
|
259
|
-
const { usage } = await grow.readUsage({
|
|
260
|
-
customerId,
|
|
261
|
-
query: { entitlementId, entityId: null },
|
|
262
|
-
});
|
|
263
|
-
await grow.setUsage({
|
|
264
|
-
customerId,
|
|
265
|
-
command: {
|
|
266
|
-
entitlementId,
|
|
267
|
-
entityId: null,
|
|
268
|
-
quantity: "12",
|
|
269
|
-
expectedRevision: usage.revision,
|
|
270
|
-
idempotencyKey: durableSyncId,
|
|
271
|
-
properties: {},
|
|
272
|
-
},
|
|
273
|
-
});
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
`createConfiguredAiPricing(grow, { entitlementId })` 从已发布权益读取控制台 AI 模型/加价配置,入口为 `@tinkerlandai/indiana-sdk/server`。支持负加价和显式免费价格,免费调用保留真实 Token 的零金额事件。`createOpenRouterUsageClient` 从独立入口 `@tinkerlandai/indiana-sdk/openrouter` 导入,包装当前 `@openrouter/sdk` 的 `chat.send`,自动捕获生成/流式 Token,先持久保存 usage/原报价,再原子扣款;需要宿主提供 `context`、`persist` 和明确的推理计价方式。只有使用该入口的应用需要安装可选 peer dependency `@openrouter/sdk`;普通服务端入口的类型不引用它。示例及缺失价格、取消流、并发冲突和恢复语义见 [当前接入](../../docs/credits-integration.md)。
|
|
277
|
-
|
|
278
|
-
### 新购订阅选择优惠券
|
|
279
|
-
|
|
280
|
-
先通过 `redeemCoupon` 或发放获得持券,再用 `listOffers` 查询;将所选持券的 `id` 传入 `previewAttach.offerId`,不是 Coupon 定义 ID。仅新购 Stripe Checkout 支持此输入,首次结账最多一张券;已有订阅使用现有订阅优惠操作。
|
|
281
|
-
|
|
282
|
-
```ts
|
|
283
|
-
const { quote } = await groove.previewAttach({
|
|
284
|
-
requestId: crypto.randomUUID(),
|
|
285
|
-
plans: [{ productVersionId }],
|
|
286
|
-
provider: "stripe",
|
|
287
|
-
currency: "USD",
|
|
288
|
-
offerId: selectedOffer.id,
|
|
289
|
-
});
|
|
290
|
-
// 向客户展示 quote.discount 的条款、amountMinor 和逐套餐 allocations。
|
|
291
|
-
// quote.catalogAmountMinor 是原价,自动税额在原生 Checkout 中确认。
|
|
292
|
-
const { operation } = await groove.attach({ operationId: quote.operationId });
|
|
293
|
-
```
|
|
85
|
+
Live 商业执行仍有显式服务端门禁。其他 Product/Price、Entity、订阅追加和数量组合,以匹配服务端的实际能力为准;不自动回退到旧实现。
|
|
294
86
|
|
|
295
|
-
|
|
87
|
+
## AI 用量
|
|
296
88
|
|
|
297
|
-
|
|
89
|
+
`/server` 提供 AI SDK 用量中间件;`/openrouter` 提供 OpenRouter 包装器。宿主必须提供每次调用的持久幂等键、usage/quote 存储及恢复回调。恢复时重放原报价的扣款,不重新运行模型。密钥始终留在可信后端。
|
|
298
90
|
|
|
299
|
-
|
|
91
|
+
## 发布与验收
|
|
300
92
|
|
|
301
|
-
|
|
302
|
-
- 本版包含独立商店路径的 Web 契约;Swift/Kotlin 配套包另行发布:`previewAttach`/`previewUpdate` 固定订阅操作后,Swift/Kotlin 的 `purchaseSubscription`/`switchSubscription` 获取实际商店报价并购买;`restorePurchases` 扫描当前购买,通过 `POST /api/v1/subscriptions/native/restore` 恢复,不要求本地保留原订单绑定。后端仍须校验当前客户会话;商店购买链与账户关联决定归属,客户端的意图或客户 ID 不能转移购买。独立恢复要求 `billing:write`,已知 Funnel 订单的回执恢复仍使用上面的协议。候选迁移、本地替身验证与真实验收边界见[第八批记录](../../docs/subscription-core-phase8.md)。
|
|
303
|
-
- 本版的预付 Feature 数量接口:`previewAttach` 的每个 plan 可传 `featureQuantities: { [entitlementVersionId]: quantity }`;`previewUpdate` 使用 `change: { type: "quantity", entitlementVersionId, quantity }`,再通过同一 `update` 确认。Swift/Kotlin 对应 `previewQuantityUpdate`。增加数量可立即付款生效,减少数量使用 `renewal`;重试保留原 requestId。服务端根据不可变套餐条款和实际计费项交付额度。此项支持 Stripe,Apple/Google 仍使用固定已发布 SKU,不能传可变数量替换商店价格。详见[第九批记录](../../docs/subscription-core-phase9.md)。
|
|
304
|
-
- 本版客户资料使用 `attributes`,属性删除使用 `removeAttributes`,标签增减使用 `tagChanges`;旧个人包 `0.0.7` 保留原 `metadata` / `tags` 契约,不能向旧包传入当前新字段。
|
|
305
|
-
- 本版浏览器和服务端提供 `createFeatureClient` 与 Feature 评估契约,见[实施记录](../../docs/experiments-growthbook-port-plan-2026-10-11.md)。该导出不在旧个人包 `0.0.7` 的类型入口中。
|
|
93
|
+
npm 发布不部署 Web 服务、不应用数据库迁移,也不发布 Swift/Kotlin 包。安装消费、类型、离线单元和构建检查,与真实 Auth/RLS、Stripe、原生商店、浏览器和线上部署分别记录;局部通过不代表整套产品验收。
|
|
306
94
|
|
|
307
|
-
|
|
95
|
+
版本、冻结产物哈希和 registry 回读见 [发布总账](../../docs/sdk-releases.md);当前架构与实现边界见 [开发状态](../../docs/development-status.md)。
|