跳到正文

指南

出售额度

为 API 或 AI 服务提供预付额度:买家自行充值,你的服务器按调用扣减。不从充值中扣取任何金额。

额度适合一切按用量出售的东西:token、图片、分钟数、搜索次数。买家用加密货币购买一个额度包;你在每次调用时从中扣减;额度用完时,买家在自己的页面上充值。绝不会自动扣款 — 永远由买家自己付款 — Perly 也不从充值中抽取任何费用。

设置

  1. 1

    创建商品和额度包

    Dashboard → Products → New,然后 New price → Credit pack:例如 $10 买 1 000 个额度,$40 买 5 000 个。或者 pay.prices.create({ product, model: "credit_pack", unit_amount: 1000, credits: 1000 })。

  2. 2

    决定何时告知买家余额

    在商品页的 Credits 部分:提醒阈值、给买家的邮件,以及 Any amount — 以单个额度包为单位的 N 倍,让买家可以充 $25,而不只是固定额度包。

  3. 3

    为每位买家创建客户

    每个用户创建一个,带上你的 id:pay.customers.create({ email, external_id: user.id })。之后 SDK 直接接受这个 id。

扣减、查询、充值

import { PerlyPay, InvalidRequestError } from "perly-pay";
const pay = new PerlyPay(process.env.PERLY_PAY_SECRET_KEY);

// Before the work: enough left?
const { balance, top_up_url } = await pay.credits.balance(user.id);

// After it: spend. The same idempotencyKey never spends twice.
try {
  await pay.credits.consume(user.id, 3, { action: "chat", idempotencyKey: request.id });
} catch (e) {
  if (e instanceof InvalidRequestError && e.code === "insufficient_credits") {
    return { error: "Out of credits", topUp: await pay.credits.topUpUrl(user.id) };
  }
  throw e;
}
  • 客户可以是 cus_… id、邮箱,或你给它的自有 id(external_id)。如果你为多个商品出售额度,请指定 product。
  • 扣减是原子操作,余额永远不会低于零:两个并发扣减不会同时用掉同一批额度。不足时返回 insufficient_credits(409),且不扣减任何额度。
  • 把你的请求 id 作为幂等键传入。重试 — 无论在超时、故障之后,甚至第二天 — 都会返回第一次的扣减,而不会再扣一次。
  • action 是你自己的标签(chat、image.hd、api/search)。商品页会按操作和按客户汇总用量。

充值页

top_up_url 是买家专属的页面,每次都相同:它以你的结账样式显示余额和你的额度包,买家选好额度包后开出一张普通账单。账单一付清,额度就会入账一次。把链接放在应用里、放在“额度已用完”的错误信息里,或者直接发给买家 — 买家门户里也有充值按钮。

额度用完时

事件时机
credits.low余额降到商品的阈值。每次跨越阈值触发一次:要等充值让余额回到阈值以上后才会再次触发。
credits.depleted余额降到零。每次跨越触发一次。

两个事件都以客户作为 data.object,并附带 data.product 和 data.balance。如果设置了阈值并开启了邮件,买家还会收到一封附充值链接的邮件 — 每天最多一封。测试模式下邮件只发给你的团队。

多个额度包的额度会累加;扣减时先用最早的额度包。