fix(live): refuse billing in pilot mode and explain unparsed documents
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 3s
masterplan-lock / check (push) Successful in 14s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 18s
CI (SIAX Cloud) / security (pull_request) Successful in 23s
CI (SIAX Cloud) / quality (pull_request) Successful in 54s
CI (SIAX Cloud) / integration (pull_request) Successful in 1m2s

- Checkout returns 503 CREDITS_DISABLED while OPT_CREDITS_MODE=off, so a
  customer can never pay for credits that the pipeline will not consume.
- Packs expose purchases_enabled; the portal hides Buy and says billing is off.
- Structuring an unparsed document (e.g. a scanned PDF with no text layer) now
  answers 422 DOCUMENT_NOT_PARSED instead of a generic INVALID_REQUEST.
This commit is contained in:
2026-11-02 20:14:12 +01:00
parent 8822b2adaa
commit a0dd6b7653
4 changed files with 48 additions and 10 deletions
+9
View File
@@ -27,6 +27,15 @@ export async function POST(request: NextRequest) {
if ('response' in verified) return verified.response;
const { tenantId } = verified.scope;
if (process.env.OPT_CREDITS_MODE === 'off') {
return NextResponse.json(
{
error: 'CREDITS_DISABLED',
message: 'Billing is not active yet (pilot mode). No payment can be taken.',
},
{ status: 503 },
);
}
const book = readCreditPriceBook();
if (!book) {
return NextResponse.json(
+1
View File
@@ -27,6 +27,7 @@ export async function GET(request: NextRequest) {
return NextResponse.json({
version: book.version,
currency: book.currency,
purchases_enabled: process.env.OPT_CREDITS_MODE !== 'off',
packs: book.packs.map((pack) => ({
id: pack.id,
credits: pack.credits,
+10 -1
View File
@@ -163,13 +163,22 @@ export async function POST(request: NextRequest) {
const dataClass = body.data_class ?? 'low';
const format = body.format ?? 'json';
if (pipelineStage !== 'parsed') {
return NextResponse.json(
{
error: 'DOCUMENT_NOT_PARSED',
message:
'The document has no verified text layer yet (parse_failed or not parsed). OCR is required before structuring.',
},
{ status: 422 },
);
}
if (
!isValidScopeId(projectId) ||
!isValidScopeId(documentId) ||
!isValidScopeId(versionId) ||
typeof checksum !== 'string' ||
!/^[0-9a-f]{64}$/.test(checksum) ||
pipelineStage !== 'parsed' ||
typeof instruction !== 'string' ||
!instruction.trim() ||
instruction.length > 4000 ||