交易记录与花费
每一笔进出组织的余额都会写进一本只能追加的账本。计费页面用两种方式把它呈现出来:一张图表看花钱的形状,一张表格看背后确切的记录。
交易列表
表格按从新到旧排列,一次显示 50 条记录;加载更多会取下一页。
| 列 | 里面装的是什么 |
|---|---|
| 日期 | 这条记录写入的时间,精确到秒 |
| 类型 | 这是哪一类记录 |
| 详情 | 这条记录所属的实例,以及它覆盖了多少秒 |
| 金额 | 余额的带符号变化,充值为正 |
| 余额 | 这条记录之后紧接着的余额 |
你会遇到的类型:
| 类型 | 控制台标签 | 由什么产生 |
|---|---|---|
topup | 余额已添加 | 一笔已确认的 Stripe 付款 |
charge_gpu | GPU 用量 | 一次运行时长的结算 |
charge_storage | 存储 | 一次停止期间磁盘时长的结算 |
adjustment | 调整 | 任何一次既不是充值、也不是计量用量的带符号余额变动 —— 多数是 Stripe 的追回,自动入账。见下文 |
adjustment 不只是手动修正。Stripe 的事件会自己产生它,而这一行的 meta.reason 会说明是哪一种:
meta.reason | 方向 | 发生了什么 |
|---|---|---|
refund | 扣款 | 一笔付款被退款了。只扣这一次事件所涉及的金额,所以第二次部分退款不会把第一次的再扣一遍 |
dispute_opened | 扣款 | 有人发起了拒付,钱已经没了 |
dispute_funds_withdrawn | 扣款 | 一笔先以问询形式到达的争议最终把资金划走了,所以它刚发起时并没有扣款 |
dispute_won | 入账 | 你赢了争议,那次扣款被冲回 |
争议在进行期间还会冻结这个组织:POST /v1/instances 和启动一个已停止的实例都会返回 402 PAYMENTS_FROZEN,这样一笔拒付就没法用来买 GPU 时间。争议结束时无论输赢都会解冻 —— 输了就保留那次扣款,之后由余额来决定你能做什么 —— 但前提是这个组织没有其他争议还开着。而一次问询,也就是发卡行只是在提问、还没有任何资金变动,会冻结账户但根本不产生任何账本记录:为了一个往往不了了之的提问就去扣款,等于从一个什么错都没犯的人手里拿走额度。
详情单元格会链到那个实例的计费标签页。充值和调整没有对应的实例,显示一个短横线。
因为结算大约每分钟跑一次,单个实例每小时大概产生 60 行,一天 1,440 行上下。这个列表是拿来抽样和筛选的,不是拿来从头读到尾的——要看单个实例的合计,就打开那个实例读已结算花费;要看逐日的合计,就用图表。不满一美分的金额会显示到小数点后四位,所以一分钟的存储费用读起来是 $0.0007 之类的数字,而不是 $0.00。
消费图表
消费 —— 最近 30 天每天画一个点,30 天的合计就在标题旁边。把鼠标悬在某个点上,会给出那一天的数字。
图表统计的是什么:
- 只有 GPU 费用和存储费用。充值和调整不计入——这是花费,不是现金流。
- 天是按 UTC 分桶的,所以在 UTC 以西的地方深夜跑的任务,会落到第二天那个点上。
- 没有费用的日子会画成零,而不是跳过,正是这一点让图表在几阵工作之间出现平直的一段段。
端点
两个端点读的都是当前组织的账本。发送一个 bearer 令牌——可以是已登录的会话,也可以是 shk_ 组织 API 密钥。用会话时,由 X-Org-Id 选择组织,不带这个请求头就用个人组织;API 密钥被钉在它自己的组织上,为其他任何组织发送 X-Org-Id 都会被以 ORG_MISMATCH 拒绝。这两个端点都不要求管理员,所以成员或 API 密钥都能读。参见 身份验证。
GET /v1/billing/transactions
curl "$SUPERHEAT_API/v1/billing/transactions?limit=100" \
-H "Authorization: Bearer $SUPERHEAT_TOKEN" \
-H "X-Org-Id: $ORG_ID"
| 参数 | 取值范围 | 默认值 | 作用 |
|---|---|---|---|
limit | 1 到 200 | 50 | 每页的记录数 |
cursor | 一个记录 id | — | 返回比这个 id 更早的记录 |
instance_id | 一个实例 id | — | 把这一页限定在单个实例上 |
记录以从新到旧的顺序装在 items 里返回,旁边还有一个 next_cursor。把那个值作为 cursor 传回去就能取下一页;当它返回 null 时,你就到账本的尽头了。
每条记录都带着自己的 id(它同时充当分页游标)、type、所属的 instance_id(充值或调整则为 null)、created_at 时间戳、带符号的金额,以及由此得出的余额。计量类的费用还带一个 meta 对象,记着计费周期的起止时间和它覆盖的秒数;调整则带一个来自上表的 meta.reason。
要拉取单个实例的费用:
curl "$SUPERHEAT_API/v1/billing/transactions?instance_id=$INSTANCE_ID&limit=200" \
-H "Authorization: Bearer $SUPERHEAT_TOKEN" \
-H "X-Org-Id: $ORG_ID"
GET /v1/billing/spend-daily
curl "$SUPERHEAT_API/v1/billing/spend-daily?days=90" \
-H "Authorization: Bearer $SUPERHEAT_TOKEN" \
-H "X-Org-Id: $ORG_ID"
| 参数 | 取值范围 | 默认值 | 作用 |
|---|---|---|---|
days | 1 到 90 | 30 | 往前聚合多久 |
这就是图表的数据源:按 UTC 日期分组的 GPU 和存储费用,从旧到新,每条记录带一个 date 和那一天的合计。只有有费用的日子才会出现,所以清闲的一周产生的是没有记录,而不是一串零——如果你要画一条稠密的序列,空档得自己填。