feat(billing): resolve chat and rectification pricing by feature
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
shouldLoadGeneralDailyPanchanga,
|
||||
} from "@/lib/consultation-entrypoint";
|
||||
import { CreditRpcError } from "@/lib/consultation-billing";
|
||||
import { resolveFeaturePricing } from "@/lib/feature-pricing";
|
||||
import { reserveConsultationModel } from "@/lib/consultation-model-selection";
|
||||
import { resolveSessionLanguageModel } from "@/lib/model-catalog";
|
||||
import { createAdminSupabaseClient } from "@/lib/supabase/admin";
|
||||
@@ -361,12 +362,13 @@ export async function POST(request: Request) {
|
||||
chatSession.model_id,
|
||||
(modelId) => sessionModel?.id === modelId ? sessionModel : null,
|
||||
async (model) => {
|
||||
const pricing = await resolveFeaturePricing(accounting, "chat.standard", model.id);
|
||||
const { data, error } = await accounting.rpc("reserve_consultation_usage", {
|
||||
p_user_id: userId,
|
||||
p_request_id: requestId,
|
||||
p_session_id: sessionId,
|
||||
p_requested_model_id: model.id,
|
||||
p_credit_cost: model.creditCost,
|
||||
p_credit_cost: pricing.credit_cost,
|
||||
});
|
||||
const reservation = consultationReservationSchema.safeParse(first(data ?? []));
|
||||
if (error || !reservation.success) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { safePublicEvent } from "@/lib/rectification-agentic/v9/stream-mapping";
|
||||
import { RECTIFICATION_SKILL_NAME, RECTIFICATION_SKILL_VERSION } from "@/lib/rectification-agentic/v9/case-status";
|
||||
import { blocksPromptExtraction } from "@/lib/consult-safety";
|
||||
import { authorizeUsage, completeUsage, releaseUsage } from "@/lib/consultation-billing";
|
||||
import { resolveFeaturePricing } from "@/lib/feature-pricing";
|
||||
import { loadRuntimeFeatureFlags } from "@/lib/feature-flags";
|
||||
import { isProductEnabled } from "@/lib/product-access";
|
||||
import { resolveSessionLanguageModel } from "@/lib/model-catalog";
|
||||
@@ -564,12 +565,13 @@ export async function POST(request: Request) {
|
||||
}
|
||||
try {
|
||||
const billingRequestId = await rectificationBillingRequestId(accounting, userId, caseId);
|
||||
const pricing = await resolveFeaturePricing(accounting, "rectification", selectedModel.id);
|
||||
const result = await authorizeUsage(accounting, {
|
||||
userId,
|
||||
requestId: billingRequestId,
|
||||
featureKey: "rectification",
|
||||
requestedModelId: selectedModel.id,
|
||||
creditCost: selectedModel.creditCost,
|
||||
creditCost: pricing.credit_cost,
|
||||
});
|
||||
if (result.success) return { success: true, status: 200 };
|
||||
return {
|
||||
@@ -719,8 +721,16 @@ export async function POST(request: Request) {
|
||||
} catch (error) {
|
||||
const code = error instanceof RectificationToolServiceError ? error.code : "run_failed";
|
||||
console.error(`[rectification-v9] run failed case=${caseId} code=${code}`);
|
||||
if (code === "billing_denied" || code.includes("insufficient")) {
|
||||
send({ type: "error", code: "billing_denied", message: "咨询点数不足,请先兑换后再继续。" });
|
||||
if (code === "insufficient_credits" || code.includes("insufficient")) {
|
||||
send({ type: "error", code: "insufficient_credits", message: "校正点数不足,请先兑换后再继续。" });
|
||||
} else if (code === "fair_use_minute") {
|
||||
send({ type: "error", code, message: "请求频率达到安全上限,请稍后再试。" });
|
||||
} else if (code === "fair_use_day") {
|
||||
send({ type: "error", code, message: "今日请求频率达到安全上限,请明天再试。" });
|
||||
} else if (code === "fair_use_billing_period") {
|
||||
send({ type: "error", code, message: "当前账户触发安全熔断,请稍后再试或联系支持。" });
|
||||
} else if (code === "billing_denied") {
|
||||
send({ type: "error", code, message: "暂时无法确认校正点数,请稍后重试。" });
|
||||
} else if (code.includes("legacy_skill_identity_unverifiable")) {
|
||||
send({ type: "error", code: "skill_identity_unverifiable", message: "该校正绑定的是无法核验的历史 Skill,请先采用当前注册版本。" });
|
||||
} else if (code.includes("skill_identity_missing")) {
|
||||
|
||||
@@ -34,7 +34,6 @@ export function FeaturePricingManagement() {
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
setIsLoading(true);
|
||||
adminRequestJson<{ data: Row[] }>("/api/admin/feature-pricing")
|
||||
.then((payload) => { if (active) setRows(payload.data); })
|
||||
.finally(() => { if (active) setIsLoading(false); });
|
||||
|
||||
Reference in New Issue
Block a user