Add monetization spine + Build Your Own Case (Min Sak)

- Stripe: StripeClient.php, checkout/portal/webhook endpoints, idempotent event handling
- FreeTier: tier-aware credits (free/light/pro/pro_plus), bonus_balance, hourly caps per tier
- pricing.php + billing.php: 4-tier cards, 3 topups, Customer Portal, balance breakdown
- Min Sak: CaseStore.php, AzureDocIntelligence.php, AzureSearchAdmin.php — per-user hybrid RAG
- api/case/: upload, list, delete, ingest-callback (HMAC-auth'd from n8n)
- award-survey-credits: inter-site HMAC endpoint for dobetternorge.no survey bonus
- dashboard.php: tier badge, balance breakdown card, Min Sak CTA, survey CTA
- KorrespondAgent + all 3 other agents: use_my_case toggle wired to dbnToolsCaseContext()
- bootstrap.php: dbnToolsCaseContext(), dbnToolsIntersiteSecret(), dbnToolsCurrentTier()

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 20:52:54 +02:00
parent ed5489d174
commit ba9cddf9a1
30 changed files with 2804 additions and 133 deletions
+50
View File
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../includes/bootstrap.php';
require_once __DIR__ . '/../../includes/FreeTier.php';
require_once __DIR__ . '/../../includes/CaseStore.php';
dbnToolsRequireMethod('POST');
dbnToolsRequireAuth();
$userId = (int)($_SESSION['dbn_tools_sso_uid'] ?? 0);
if ($userId <= 0) {
dbnToolsError('Auth required.', 401, 'auth_required');
}
if (empty($_FILES['file']) || ($_FILES['file']['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
dbnToolsError('No file uploaded or upload error.', 400, 'no_file');
}
$file = $_FILES['file'];
$size = (int)$file['size'];
$tmp = (string)$file['tmp_name'];
$name = (string)$file['name'];
if ($size <= 0 || $size > 25 * 1024 * 1024) {
dbnToolsError('Filen må være mellom 1 byte og 25 MB.', 413, 'bad_size');
}
// Validate it's actually a PDF (magic number check)
$fh = @fopen($tmp, 'rb');
if ($fh === false) {
dbnToolsError('Kunne ikke lese filen.', 500, 'read_fail');
}
$head = (string)fread($fh, 5);
fclose($fh);
if (strncmp($head, '%PDF-', 5) !== 0) {
dbnToolsError('Filen er ikke en gyldig PDF.', 415, 'not_pdf');
}
try {
$doc = CaseStore::registerUpload($userId, $name, $tmp, $size);
CaseStore::caseEnqueueIngest((int)$doc['doc_id'], $userId);
dbnToolsRespond([
'ok' => true,
'doc_id' => $doc['doc_id'],
'filename' => $doc['filename'],
]);
} catch (Throwable $e) {
dbnToolsError($e->getMessage(), 400, 'upload_failed');
}