diff --git a/docs/releases/2026-09-21-SUPPORT-FORM.md b/docs/releases/2026-09-21-SUPPORT-FORM.md
index cd55687..aa630fe 100644
--- a/docs/releases/2026-09-21-SUPPORT-FORM.md
+++ b/docs/releases/2026-09-21-SUPPORT-FORM.md
@@ -1,6 +1,6 @@
# Contact support from Help
-Recipient is exactly `support@katerion.ru` (explicit user instruction; do not silently rewrite to caterium.ru). The Help dialog offers a human support tab and an unanswered-question call to action. It also works without login. The AI assistant stays separate.
+Recipient is exactly `support@caterium.ru` (address explicitly corrected by the user on 2026-09-21). The Help dialog offers a human support tab and an unanswered-question call to action. It also works without login. The AI assistant stays separate.
Submission uses same-origin `/api/support.php`, not mailto. Required: name, reply email, topic, subject and message. Diagnostics are opt-in and contain only browser, viewport, current section and release strings. No SDK, session, token, order, customer record or URL query/hash is serialized. Drafts remain in memory on errors/close and are cleared on account/workspace changes. Only confirmed `202 accepted` clears the message. No autoresponder, attachments, arbitrary recipient or database access.
diff --git a/public/api/support-lib.php b/public/api/support-lib.php
index 0d2f57b..c62ca27 100644
--- a/public/api/support-lib.php
+++ b/public/api/support-lib.php
@@ -2,7 +2,7 @@
/** Fixed-recipient contact form. No customer database access and no mail relay. */
declare(strict_types=1);
namespace Caterium\Support;
-const RECIPIENT = 'support@katerion.ru';
+const RECIPIENT = 'support@caterium.ru';
const SENDER = 'no-reply@caterium.ru';
const TOPICS = ['question'=>'Вопрос по приложению','problem'=>'Ошибка или проблема','access'=>'Вход и подписка','suggestion'=>'Предложение','other'=>'Другое'];
final class Problem extends \RuntimeException {
diff --git a/public/core/help-center.js b/public/core/help-center.js
index b721512..a5d4123 100644
--- a/public/core/help-center.js
+++ b/public/core/help-center.js
@@ -6,7 +6,7 @@
const norm=s=>String(s).toLowerCase().replace(/ё/g,'е');
const BOT={origin:"https://ai-staff-alpha.vercel.app",agent:"e9cc0f28-aaa5-48d4-a020-ae9633988faf"};
let dialog,data,loading=false,returnFocus;
- const supportUrl=new URL('support-form.js?v=20260921-support-mail',document.currentScript.src);
+ const supportUrl=new URL('support-form.js?v=20260921-support-recipient',document.currentScript.src);
function loadContactForm(){
if(window.CateriumSupportForm){window.CateriumSupportForm.attach(dialog);return;}
if(document.getElementById('ctSupportFormScript'))return;
@@ -51,7 +51,7 @@
function ensureDialog(){
if(dialog)return;
dialog=document.createElement('dialog');dialog.id='ctHelpDialog';dialog.setAttribute('aria-labelledby','ctHelpTitle');
- dialog.innerHTML=`
Помощь в Caterium
Руководство пользователя и поддержка
ИИ-помощник
Ответы по работе с приложением
Помощник отвечает на вопросы «как это сделать» по руководству Caterium. Он не видит данные вашей компании, но ваш вопрос обрабатывает внешний сервис, поэтому не пишите пароли, коды подтверждения и данные клиентов. Поиск по руководству остаётся на этом устройстве и ничего не отправляет.
Если инструкция не помогла
Почта поддержки: support@katerion.ru. Вкладка «Написать в поддержку» открывает форму обращения.
Подготовьте название раздела, последовательность действий, точный текст ошибки, устройство и браузер. На снимке экрана закройте лишние телефоны и адреса. Не передавайте пароли и коды подтверждения.
`;
+ dialog.innerHTML=`
Помощь в Caterium
Руководство пользователя и поддержка
ИИ-помощник
Ответы по работе с приложением
Помощник отвечает на вопросы «как это сделать» по руководству Caterium. Он не видит данные вашей компании, но ваш вопрос обрабатывает внешний сервис, поэтому не пишите пароли, коды подтверждения и данные клиентов. Поиск по руководству остаётся на этом устройстве и ничего не отправляет.
Если инструкция не помогла
Почта поддержки: support@caterium.ru. Вкладка «Написать в поддержку» открывает форму обращения.
Подготовьте название раздела, последовательность действий, точный текст ошибки, устройство и браузер. На снимке экрана закройте лишние телефоны и адреса. Не передавайте пароли и коды подтверждения.
`;
document.body.appendChild(dialog);
dialog.querySelector('#ctHelpClose').onclick=()=>dialog.close();
dialog.addEventListener('keydown',e=>{if(e.key==='Escape'){e.preventDefault();e.stopPropagation();dialog.close();}});
diff --git a/public/core/support-form.js b/public/core/support-form.js
index 21094ac..61bf169 100644
--- a/public/core/support-form.js
+++ b/public/core/support-form.js
@@ -2,7 +2,7 @@
(()=>{
'use strict';
if(window.CateriumSupportForm)return;
- const EMAIL='support@katerion.ru',ENDPOINT=new URL('../api/support.php',document.currentScript.src).href;
+ const EMAIL='support@caterium.ru',ENDPOINT=new URL('../api/support.php',document.currentScript.src).href;
const identity=()=>`${window.SunCloudV2?.getSession?.()?.user?.id||''}:${window.SunCloudV2?.getWorkspace?.()?.id||''}`;
let root,form,tab,csrf='',busy=false,controller=null,scope=identity(),generation=0,lastPayload='',requestId='';
const $=id=>root?.querySelector('#'+id);
diff --git a/public/service-worker.js b/public/service-worker.js
index 20028c4..619bf0b 100644
--- a/public/service-worker.js
+++ b/public/service-worker.js
@@ -1,7 +1,7 @@
-const CACHE='sun-catering-pwa-v110-20260918-ui-stability-20260919-client-menu-support-bot-training-catalog-banquet-onepage-employee-session-mfa-recovery-promo-entry-support-mail';
+const CACHE='sun-catering-pwa-v110-20260918-ui-stability-20260919-client-menu-support-bot-training-catalog-banquet-onepage-employee-session-mfa-recovery-promo-entry-support-mail-correct-recipient';
const VERSION='20260918-ui-stability';
const CORE=[
- './core/support-form.js?v=20260921-support-mail',
+ './core/support-form.js?v=20260921-support-recipient',
'./core/trial-promo-developer-v181.js?v=20260921-promo-entry',
'./core/banquet-client-menu.js?v=20260920-onepage',
'./core/training-catalog.js?v=20260920-training',
diff --git a/tests/production-support-form.mjs b/tests/production-support-form.mjs
index ef228a6..854bca1 100644
--- a/tests/production-support-form.mjs
+++ b/tests/production-support-form.mjs
@@ -15,10 +15,10 @@ try{
await page.goto(base.href,{waitUntil:'domcontentloaded'});
await page.getByRole('button',{name:'Помощь со входом',exact:true}).click();
await page.locator('#ctContactTab').click();await expect(page.locator('#ctContactForm')).toBeVisible();
- await expect(page.locator('#ctContactSection')).toContainText('support@katerion.ru');
+ await expect(page.locator('#ctContactSection')).toContainText('support@caterium.ru');
assert(await page.locator('#ctContactForm').evaluate(el=>el.scrollWidth<=el.clientWidth));
await page.screenshot({path:`${output}/support-form-${width}.png`,animations:'disabled'});
- results.push({width,form:true,recipient:'support@katerion.ru',draftOnly:true});
+ results.push({width,form:true,recipient:'support@caterium.ru',draftOnly:true});
}finally{await context.close();}
}
const context=await browser.newContext();
@@ -31,7 +31,7 @@ try{
// cross-origin request cannot reach mail(). Inbox delivery is a separate check.
const response=await context.request.get(new URL('api/support.php',base).href);
assert.equal(response.status(),200);assert.match(response.headers()['cache-control'],/no-store/);
- const data=await response.json();assert.equal(data.recipient,'support@katerion.ru');assert.match(data.csrf,/^[a-f0-9]{64}$/);
+ const data=await response.json();assert.equal(data.recipient,'support@caterium.ru');assert.match(data.csrf,/^[a-f0-9]{64}$/);
const rejected=await context.request.post(new URL('api/support.php',base).href,{headers:{Origin:'https://example.invalid'},data:{}});assert.equal(rejected.status(),403);
await fs.writeFile(`${output}/support-form.json`,JSON.stringify({checkedAt:new Date().toISOString(),results,phpSessionEndpoint:true,crossOriginBlocked:true,realEmailSent:false,inboxDeliveryVerified:false},null,2));
}finally{await context.close();}
diff --git a/tests/support-form.spec.mjs b/tests/support-form.spec.mjs
index 74d3a5f..9394491 100644
--- a/tests/support-form.spec.mjs
+++ b/tests/support-form.spec.mjs
@@ -1,7 +1,7 @@
import {test,expect} from '@playwright/test';
import fs from 'node:fs';
import {spawnSync} from 'node:child_process';
-const EMAIL='support@katerion.ru';
+const EMAIL='support@caterium.ru';
async function fixture(page){
await page.route('**/index.html',r=>r.fulfill({contentType:'text/html',body:''}));
await page.goto('/index.html');await page.addStyleTag({url:'/core/help-center.css'});await page.addScriptTag({url:'/core/help-center.js'});
diff --git a/tests/support-mail.php b/tests/support-mail.php
index 5e376e1..145249f 100644
--- a/tests/support-mail.php
+++ b/tests/support-mail.php
@@ -16,7 +16,7 @@ try{
$answer=submit($row,'127.0.0.1',$dir,$send,1000000);check($answer['status']==='accepted','Mail accepted');
$repeat=submit($row,'127.0.0.2',$dir,$send,1000001);check($repeat===$answer&&$count===1,'Retries must not send duplicates');
$changed=$row;$changed['message']='Changed';denies(static function()use($changed,$dir,$send){submit($changed,'127.0.0.1',$dir,$send,1000002);},409);
- [$to,$subject,$body,$headers,$params]=$GLOBALS['capturedMail'];check($to==='support@katerion.ru','Recipient exactly matches user request');check($headers['Reply-To']===$input['email'],'Reply to sender');check($params==='-fno-reply@caterium.ru','Fixed envelope');
+ [$to,$subject,$body,$headers,$params]=$GLOBALS['capturedMail'];check($to==='support@caterium.ru','Recipient exactly matches user request');check($headers['Reply-To']===$input['email'],'Reply to sender');check($params==='-fno-reply@caterium.ru','Fixed envelope');
check(strpos(base64_decode($body),$input['message'])!==false,'UTF-8 message is intact');check(strpos($subject,'SUP-')!==false,'Request ID in subject');
$saved=file_get_contents($dir.'/limits.json');check(strpos($saved,$input['email'])===false&&strpos($saved,$input['message'])===false,'Do not store message/email in rate-limit file');
for($i=2;$i<=3;$i++){$next=$row;$next['request_id']=sprintf('10000000-0000-4000-8000-%012d',$i);submit($next,'127.0.0.1',$dir,$send,1000000+$i);}