/** * `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 } }, } }