port.js 1.54 KB
/**
 * `port.js` —— `asBillingPort`:把 `deduct` 收窄成 `BillingPort.charge`。
 *
 * llm/tts 在「续写 / 合成语音」时只需要「扣费,不足即中止」这一条能力,不该看到
 * grant / paywall / products。本文件把 deduct 收窄成最小、稳定的 `BillingPort` 面,
 * 供它们注入消费——llm/tts 不 import billing,billing 也不 import 它们(架构 §8)。
 *
 * @typedef {{ charge:(p:{user_id:string,amount:number,reason:string,ref_type?:string,ref_id?:string,idempotency_key:string})=>Promise<{balance_after:number}> }} BillingPort
 */

/**
 * @param {object} deps
 * @param {(p:any)=>Promise<{balance_after:number,ledger_id:string}>} deps.deduct
 * @returns {BillingPort}
 */
export function asBillingPort({ deduct }) {
  return {
    /**
     * 扣费。余额不足时 deduct 抛 `BillingError({ code:'INSUFFICIENT_BALANCE' })`,
     * 原样上抛供调用方据此中止生成。返回面收窄为只含 `balance_after`。
     * @param {object} p
     * @param {string} p.user_id
     * @param {number} p.amount
     * @param {string} p.reason
     * @param {string} [p.ref_type]
     * @param {string} [p.ref_id]
     * @param {string} p.idempotency_key
     * @returns {Promise<{ balance_after:number }>}
     */
    async charge({ user_id, amount, reason, ref_type, ref_id, idempotency_key }) {
      const { balance_after } = await deduct({
        user_id,
        amount,
        reason,
        ref_type,
        ref_id,
        idempotency_key,
      })
      return { balance_after }
    },
  }
}