@base316/sdk

中文文档
0.10.0发布于 2026/11/07MITTypeScriptESM + CJS零依赖近 30 天下载 2679

官方基础316 SDK:认证,应享权利和使用量度.

中文使用文档
文档目录20 节
  1. 两个客户,用他们持有的钥匙分割
  2. 服务器
  3. 这个账户能做吗?
  4. 记录所使用的内容
  5. 客户、订阅和发票
  6. 定义销售内容
  7. 审判和反向审判
  8. 用户、租户和角色
  9. 管理下的租户:一家公司对其客户拥有控制权
  10. 收藏:你自己的数据模型
  11. 进出口
  12. 他们付钱后会怎样
  13. 从代码注册的 Webhooks
  14. 保护自己的API
  15. 引号
  16. 报告,以及用来测试
  17. 将环境配置为代码
  18. 浏览器
  19. 网络呼号
  20. 错误

@ base316/sdk (英语)

认证、应享权利和使用计量 基础316。 。 。 .

npm install @base316/sdk

两个客户,用他们持有的钥匙分割

import { Base316, Base316Browser } from "@base316/sdk";

Base316 持有 密钥 属于您的服务器 . Base316Browser 持有 可公布密钥 并且可以安全地向浏览器发送.

拆分是强制执行的,而不是建议: Base316 在浏览器中 抛出,因为泄露出的秘密密钥是环境的完全妥协.

服务器

import { Base316 } from "@base316/sdk";

const base316 = new Base316({ secretKey: process.env.BASE316_SECRET_KEY });

这个账户能做吗?

const decision = await base316.access.check({
    externalId: "acct_42", // your own id for the account
    feature: "projects",
    requested: 1, // what is about to be consumed
});

if (!decision.allowed) {
    throw new Error(decision.reason);
}

requested 之所以这样有用: 支票拒绝 在此之前 工作情况 而不是报告超龄.

记录所使用的内容

await base316.events.record({
    externalId: "acct_42",
    name: "api.call",
    value: 1,
    idempotencyKey: crypto.randomUUID(),
});

通过一个 idempotencyKey。在网络超时后重试是第二次 有个客户两次开票 申请一次是支持票 无法从数据中回复.

客户、订阅和发票

// Look one up by your own id rather than paging the whole list.
const customer = await base316.customers.byExternalId("acct_42");

const subscriptions = await base316.subscriptions.list({ customerId: customer.id });
const invoices = await base316.invoices.list({ customerId: customer.id });

byExternalId 返回时 null 您尚未创建客户。 那个 是处理的正常状态,而不是错误.

发票、信用附注和引文作为PDF下载,作为PDF返回 Uint8Array数字 :

import { writeFile } from "node:fs/promises";

await writeFile(`${invoice.number}.pdf`, await base316.invoices.pdf(invoice.id));
await base316.creditNotes.pdf(creditNoteId);
await base316.quotes.pdf(quoteId);

在浏览器中, billing.invoicePdf(session, id) 返回已签名用户的 同样的发票。 传到一个 Blob 类型 application/pdf 作为下载提供.

定义销售内容

const meter = await base316.catalog.meters.create({
    key: "words",
    name: "Words generated",
    unit: "words",
    eventName: "draft.generated",
    aggregation: "SUM",
    valueProperty: "words",
});

const feature = await base316.catalog.features.create({
    key: "words",
    name: "Words generated",
    type: "METERED",
    meterId: meter.id,
});

const plan = await base316.catalog.plans.create({
    key: "pro",
    name: "Pro",
    currency: "USD",
    interval: "MONTH",
    prices: [
        { model: "FLAT", amount: "29.00" },
        { model: "PER_UNIT", featureId: feature.id, amount: "0.001" },
    ],
    entitlements: [{ featureId: feature.id, limit: 10_000 }],
});

await base316.subscriptions.create({
    customerId: customer.id,
    planVersionId: plan.versions[0].id,
});

以不同的单价收取一个活动字段的值——按AI模型计算, 使用a MATRIX 价钱。 每个行 dimensions 名称相同的密钥, 每个表的 groupBy 字段,计数器必须与 SUM 或者说 COUNT。 。 。 . amount 是未匹配的用法的默认行; 发票上显示的用途为: unpriced 已经 没有充电.

{
    model: "MATRIX",
    featureId: tokens.id,
    amount: "0.00001",
    rows: [
        { dimensions: { model: "opus" }, unitAmount: "0.000015" },
        { dimensions: { model: "haiku" }, unitAmount: "0.000001" },
    ],
}

来自矩阵的发票和即将发行的发票线 背负行的 dimensions (单位:千美元){} 用于默认行),以及 customers.entitlements() 将周期的 每行内使用量 byDimension。 。 。 .

计划带有身份;a 版本 带着条件,这就是 a 订阅点。 发布新版本 catalog.plans.createVersion 因此,永远不要重新定价 任何人已经在 旧的.

如果你只知道钥匙—— 应用程序代码中的通常案例—— 要求 直接版本 :

const version = await base316.catalog.plans.currentVersion("pro");

null 意味着计划没有发表任何内容. 草案不能签署.

审判和反向审判

一个版本的 trialDays 打开新订阅为 TRIALING; 其编号 trialEndAction 决定如何审判. CONVERT (违约)账单 计划. DOWNGRADE 移动一个没有卡片的付款人到 trialDowngradePlanVersionId - 实际操作,相同的订阅代号,否 发票——并转换一张卡:

const free = await base316.catalog.plans.currentVersion("free");

const team = await base316.catalog.plans.create({
    key: "team",
    name: "Team",
    currency: "USD",
    interval: "MONTH",
    trialDays: 14,
    trialEndAction: "DOWNGRADE",
    trialDowngradePlanVersionId: free!.id,
    prices: [{ model: "FLAT", amount: "49.00" }],
});

// A no-card trial without checkout; `trialDays` overrides the version's (0 = none).
await base316.subscriptions.create({
    externalId: "acct_42",
    planVersionId: team.versions[0].id,
    trialDays: 14,
});

降级是 subscription.updated 与 change: "trial_downgraded"。 。 。 . PublicPlan 包含两个字段,所以一个定价页可以说明 审判.

用户、租户和角色

您的应用程序本身的终端用户——而不是签入Base316的人 仪表板.

const admin = await base316.roles.create({
    key: "admin",
    name: "Administrator",
    // Your strings. Base316 matches them without interpreting them, and a
    // trailing `.*` grants everything under that prefix.
    permissions: ["billing.*", "reports.export"],
});

const tenant = await base316.organizations.create({ name: "Petronella Ltd", externalId: "t_42" });
const user = await base316.users.create({ externalId: "u_7", email: "[email protected]" });

await base316.organizations.addMember(tenant.id, { userId: user.id, roleId: admin.id });

这就是迁徙之路:这里有人和房客 在此之前 任何人签名 输入,所以第一个登录的人 加入他们自己的历史 而不是开始一个 旁边的新账号. emailVerified: true 带着确认书 已经给你的旧系统.

然后把租户与支付这笔钱的账户联系起来,或者每一次许可检查 答题 NOT_A_MEMBER 而账单看起来很健康:

await base316.customers.create({ externalId: "acct_42", runtimeOrganizationId: tenant.id });

await base316.access.check({ customerId, userId: user.id, permission: "billing.refund" });

users.disable 锁入账户并保存记录——离职的雇员,a 疑似妥协, 账户争议. users.erase 还有一件事, 然后它就还了一张收据 上面写着保存了什么和为什么.

管理下的租户:一家公司对其客户拥有控制权

承租人可以经营其他公司——会计师事务所对其客户公司的管理 在它的品牌。 深一关.

const firm = await base316.organizations.create({ name: "Hartley & Co" });
const client = await base316.organizations.create({ name: "Acme Ltd", parentId: firm.id });

// The firm's "accountant" acts as "editor" in every client. Unmapped roles grant nothing.
await base316.organizations.setInheritedRoles(firm.id, [
    { parentRoleId: accountant.id, childRoleId: editor.id },
]);

// The client's account bills to the firm's; this is what each client used this period.
const { data } = await base316.customers.billed(firmCustomer.id);
  • organizations.children, (中文(简体) ), organizations.detach 财务报告和审定财务报表 inheritedRoles 管理 树. access.check 成果记录 permission.source 财务报告和审定财务报表 viaOrganizationId 当通过公司进入.
  • 客户的权利、信用和使用是公司的计划;它的活动 向律所收取 InvoiceLine.breakdown 每个客户。 添加标记 countsManagedTenants 保存在公司的客户数 includedQuantity: 10 头十个客户都是免费的,每个客户都是免费的 以加通的金额开出.
  • 客户自己的会员 算上公司的计划 client_seats 特性,每个客户; 公司人员计算 seats。 。 。 .
  • organizations.setBranding 财务报告和审定财务报表 addDomain 编号 : verifyDomain 把公司 客户端主页上的品牌和签名域名; Base316Browser.billing.tenantBranding 用可出版的密钥读取.
  • Base316Browser.billing.subscription 返回时 managedBy 为客户,和 自服务器退出、 计划更改并取消答案 403 billed_by_parent。 。 。 .

收藏:你自己的数据模型

定义收藏是服务器操作;读取和写入其记录是 一个浏览器 。 分头行动就是为什么他们靠不同的客户生活.

await base316.collections.create({
    key: "tasks",
    name: "Task",
    fields: [
        { key: "title", label: "Title", type: "TEXT", required: true },
        { key: "status", label: "Status", type: "SELECT", options: ["open", "done"] },
        { key: "due", label: "Due", type: "DATE" },
    ],
});

以 tasks.read 财务报告和审定财务报表 tasks.write 权限已经 已经到位,所以 <DataTable collection="tasks" /> 丢入页面执行 a 规则是存在的,而不是一个人必须记住.

一个字段可以重新被贴上标签,重新排序,并且需要或不需要制作. 内容 type 无法 更改: 已存储的每个值都被验证为旧值 .

进出口

// Find out what a file will do before doing it.
const preview = await base316.transfer.importCsv("customers", { csv, dryRun: true });

console.log(preview.mapping.providerName); // "Chargebee", detected from the header
console.log(preview.mapping.unmapped); // columns nothing read
console.log(preview.failed); // rows that will not land

const summary = await base316.transfer.importCsv("customers", { csv });

不良行会单独失败,而不是拒绝文件,所以有一行不正确 五千元就花你一行钱 奥米 provider 并且从中检测到 页眉; 已解决 mapping 随便哪条路都回来,你可以检查一下 在信任它之前就决定了.

transfer.exportCsv("customers") 返回 CSV 文本,而不是 JSON —— 将其写入 打开它.

他们付钱后会怎样

// A coupon on every invoice this subscription raises.
await base316.subscriptions.applyDiscount(subscriptionId, { code: "half-off" });

// A seat added mid-month.
await base316.addons.attach({ subscriptionId, addonId, quantity: 3 });

// A callout, billed on the next invoice.
await base316.pendingCharges.create({
    customerId,
    description: "Emergency callout",
    currency: "USD",
    amount: "250.00",
});

// A check that bounced. The invoice reopens.
await base316.invoices.reversePayment(invoiceId, paymentId, { reason: "bounced" });

名片过期或自动充电需要的客户,代用 在史戴克主机的网页上,从收费门户的付款方法部分或 信用页面。 您的服务器可以看到并清理文件上的卡片 :

// A single-use link straight to the portal's payment methods section.
const { url } = await base316.billingPortal.createSession(customerId, {
    page: "payment-methods",
});

const { data: cards } = await base316.paymentMethods.list(customerId);
await base316.paymentMethods.setDefault(customerId, cards[1]!.id);

两项规则值得你先知道,然后才能建立起来:

钱包上加需要一个 idempotencyKey。 。 。 . 网络超时后重试 一次付款的两次贷记账户是 没有人能从数据中回答. duplicate: true 结果报告 你是如何知道重试被认出来的.

开具的发票从不重写. 这是客户可能持有的文件 支付,税务机关可能已经看到。 减少一个, 提出一个信用记录—— creditNotes.create ——这是相反的条目而不是编辑. void 只有一张本不该存在的发票.

从代码注册的 Webhooks

const endpoint = await base316.webhooks.create({
    url: "https://yourapp.com/hooks/base316",
    eventTypes: ["invoice.paid", "subscription.canceled"],
});

// Store `endpoint.secret` now. Only a hash is kept here.

然后在您的操作器中验证一个交付:

const event = await constructWebhookEvent({
    payload: rawBody, // the exact bytes, not a re-serialized object
    signature: request.headers.get("base316-signature"),
    secret: process.env.BASE316_WEBHOOK_SECRET,
});

当一个融合变得平静, webhooks.deliveries() 说什么是寄来的 远的结局是怎样的. webhooks.events() 是另一个问题——什么 发生的事情,是否有人在听,以及你是如何向 通过投票,接生员无法接触到的机器 cursor 从 上页.

保护自己的API

const api = await base316.apiResources.create({
    name: "Your API",
    audience: "https://api.yourapp.com",
});

await base316.apiResources.addScope(api.id, {
    key: "invoices:read",
    // Gated on a plan feature: a customer without it gets a token without the
    // scope, rather than a token that works and a refusal deeper in.
    featureKey: "invoice_api",
});

const credential = await base316.apiClients.create({
    customerId,
    name: "Nightly sync",
    audience: "https://api.yourapp.com",
    scopes: ["invoices:read"],
});

rateLimits.set 第一场比赛是客户自己的政策 计划,然后资源默认——所以一个账户可以被解除,超过其计划 没有为他们设计计划.

引号

const quote = await base316.quotes.create({ customerId, planVersionId });

await base316.quotes.send(quote.id);

const accepted = await base316.quotes.accept(quote.id);
// accepted.subscriptionId — acceptance is what creates it

开票时不要开票 条件被冻结. 引用 lines 而不是一个 planVersionId 是一个引用工作 接受开具发票草稿而不是订阅.

读取 status没有 storedStatus:第一个适用过期,所以引用其 日期改为: EXPIRED 扫荡是否注意到.

报告,以及用来测试

const { byCurrency } = await base316.reports.mrr();
const aging = await base316.reports.ageing({ worstCustomerLimit: 10 });

宏观经济风险不转换为一种货币——需要汇率和 日期, 和发明 要么使数字 错误的方式没有人能看到。 过 at 以复制过去的数字.

在沙盒中,你可以移动时间:

await base316.sandbox.seed();
await base316.testClocks.create({ name: "Renewal rehearsal" });

const { effects } = await base316.testClocks.advance("2027-01-01T00:00:00Z");
// effects.subscriptionsRenewed — assert on this instead of polling

这就是"在发货前证明这个续约"的呼唤而不是 月等来. 两者都拒绝生产,因为提前时间将延长 每一个订阅和收费真正的卡.

将环境配置为代码

authConfig, (中文(简体) ), email 财务报告和审定财务报表 paymentAccounts 仅覆盖原设定 在仪表板中发生——一种环境之间的区别,即 可复制的 也是有人点击的记录 每个保存是一个 上升,所以部署 运行两次汇合而不是失败.

auditLogs.list 仅按构造读取。 没有写法和 将不会有: API 可以编辑的日志不是审计日志 .

浏览器

import { Base316Browser } from "@base316/sdk";

const base316 = new Base316Browser({ publishableKey: "b316_pk_live_..." });
const result = await base316.auth.signIn({ email, password });

网络呼号

在你信任之前先核实 未经证实的网钩身体是未经认证的 任何了解你终点的人的请求.

import { constructWebhookEvent } from "@base316/sdk";

const event = constructWebhookEvent({
    payload: rawBody, // the raw bytes, not the parsed JSON
    signature: request.headers.get("base316-signature"),
    secret: process.env.BASE316_WEBHOOK_SECRET,
});

首先解析身体并重排它 改变字节和 签名将不匹配 .

错误

每一次失败都是 Base316Error 拥有稳定 code,这样你就可以在 原因,而不是可能重新措辞的信息.

import { Base316Error } from "@base316/sdk";

try {
  await base316.events.record({ ... });
} catch (error) {
  if (error instanceof Base316Error && error.code === "rate_limited") {
    // back off
  }
}

版本与发布记录

@base316/sdk 共发布 10 个版本,首次发布于 2026/09/13;过去 12 个月发布了 10 个版本。

版本发布日期距今
0.10.0latest2026/11/07今天
0.9.02026/10/299 天前
0.8.02026/10/2612 天前
0.7.12026/10/2216 天前
0.7.02026/10/1920 天前
0.6.02026/10/1424 天前

关于 @base316/sdk 的常见问题

@base316/sdk 是什么?

官方基础316 SDK:认证,应享权利和使用量度.

如何安装 @base316/sdk?

在项目目录运行 npm install @base316/sdk,或使用 pnpm add / yarn add / bun add。

@base316/sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@base316/sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@base316/sdk 可以商用吗?

@base316/sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@base316/sdk 的下载量有多少?

根据 npm 官方下载统计,@base316/sdk 在 2026/10/08 – 2026/11/06 这 30 天内被下载 2,679 次,日均约 89 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@base316/sdk 还在维护吗?

最新版本 0.10.0 发布于 2026/11/07(今天);至今共发布 10 个版本,其中过去 12 个月发布了 10 个;首次发布于 2026/09/13。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。