@ 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
}
}