Одобрение и подписание документов
Помимо согласия на cookie, SDK может записать, что человек одобрил конкретную версию документа — условия использования, договор дистанционной продажи, уведомление о конфиденциальности или документ, сформированный вашей собственной системой.
Код одобрения не входит в cmp.js. Он загружается автоматически при первом вызове одного из двух методов, описанных ниже (или заранее — с помощью data-modules="documents"); см. Файлы сборки и модули по требованию.
Два способа получить одобрение
Заголовок раздела «Два способа получить одобрение»1. CMP.showDocumentApproval(slug, options) — диалог HashEntry
Заголовок раздела «1. CMP.showDocumentApproval(slug, options) — диалог HashEntry»Загружает активную версию документа, показывает её в диалоге с кнопками «Принять» и «Отклонить» и записывает одобрение, когда человек принимает документ.
const result = await CMP.showDocumentApproval('distance-sales-agreement', { language: 'en', // необязательно, по умолчанию — язык SDK metadata: { order_id: 'ORD-54321' }, // необязательно approvalToken: tokenFromYourBackend // необязательно / обязательно, см. ниже});
if (result.success && result.decision === 'accepted') { continueCheckout(result.consent_token);} else if (result.decision === 'rejected') { // человек отклонил документ} else { showError(result.error); // { code, status, message }}| Параметр | Описание |
|---|---|
language | Язык версии документа, которую нужно показать. |
metadata | Пары «ключ — значение», сохраняемые вместе с записью (объединяются с CMP.setMetadata). |
approvalToken | Одноразовый токен одобрения, выпущенный вашим бэкендом. |
Для обратной совместимости простой объект без какого-либо из этих ключей рассматривается как metadata.
Отклонение закрывает диалог и вызывает событие document:rejected; запись на сервер не отправляется. Если сохранение завершается временной ошибкой, диалог остаётся открытым и человек может снова нажать «Принять» — повторная попытка использует тот же ключ идемпотентности (и тот же токен).
2. CMP.approveDocument(slug, options) — ваш собственный интерфейс
Заголовок раздела «2. CMP.approveDocument(slug, options) — ваш собственный интерфейс»Записывает одобрение, ничего не показывая, — для флажка в вашей форме, вашего собственного средства просмотра договоров или панели для подписи, которую вы отображаете сами.
const result = await CMP.approveDocument('terms-of-service', { approvalToken: tokenFromYourBackend, metadata: { order_id: 'ORD-54321' }});| Параметр | Отправляется как | Описание |
|---|---|---|
version_id | document_version_id | Версия, которую нужно одобрить. Если параметр не указан, SDK определяет активную версию для language. |
version | — | Обозначение версии, возвращаемое в результате, когда вы передаёте version_id. |
language | language | Язык версии. |
decision | decision | 'accepted' (по умолчанию) или 'rejected'. |
visitor_id | visitor_id | Ваш идентификатор человека (не более 100 символов). Игнорируется, когда используется токен одобрения, — вместо него сохраняется subject токена. |
approvalToken | approval_token | Одноразовый токен одобрения. |
document_content_base64 | custom_document_content | Содержимое документа, сформированного вашей системой (HTML, обычный текст или PDF; примерно до 500 КБ в декодированном виде). |
document_mime_type | custom_document_mime_type | text/html, text/plain или application/pdf. |
signature | поля верхнего уровня | См. Поля подписи. |
metadata | metadata | Пары «ключ — значение», сохраняемые вместе с записью. |
Два вызова для одного и того же документа, версии и решения, которые перекрываются по времени, используют один общий запрос и один общий результат, поэтому двойной щелчок не создаёт двух записей.
Одноразовые токены одобрения
Заголовок раздела «Одноразовые токены одобрения»Браузерный SDK подписывает свои запросы API-ключом сайта, который виден в исходном коде вашей страницы. Поэтому одобрение документа, отправленное из браузера, доказывает лишь то, что его отправил кто-то, имеющий доступ к вашей странице; имя или адрес электронной почты в нём — это то, что было введено.
Токен одобрения позволяет вашему бэкенду поручиться за то, кто именно одобряет документ:
- Ваш бэкенд, которому известен вошедший в систему пользователь, вызывает
POST /tool/v1/s2s/approval-tokens(см. REST API) и получает токен (hat_…). - Вы передаёте токен на страницу и отдаёте его SDK как
approvalToken. - SDK отправляет его как поле верхнего уровня
approval_token. Сервер атомарно погашает токен, берёт сведения о личности человека из токена, а не из браузера, и сохраняет запись сidentity_assurance: tenant_attested.
Правила:
- Токен действителен для одного сайта и одного документа (при необходимости — для одной версии), в течение ограниченного времени (по умолчанию 10 минут, не более 1 часа) и один раз.
- Документ можно настроить в панели управления так, чтобы он требовал токен. Для такого документа SDK сразу, не отправляя запроса, возвращает ошибку, если токен не передан:
error.code = "APPROVAL_TOKEN_REQUIRED". Сервер применяет то же правило. - Коды сервера передаются без изменений:
APPROVAL_TOKEN_REQUIRED,APPROVAL_TOKEN_INVALID(неизвестен, истёк, уже использован или выпущен для другого сайта/документа),APPROVAL_TOKEN_CONFLICT(поле подписанта или содержимое документа, отправленные браузером, противоречат тому, за что ручается токен). - При сбое сети или ответе 5xx SDK повторяет попытку с тем же токеном и тем же ключом идемпотентности; сервер рассматривает повтор как тот же самый запрос. После окончательного ответа 4xx повторных попыток нет, а одобрения никогда не сохраняются в офлайн-очереди. Чтобы попробовать снова, выпустите новый токен.
Поля подписи
Заголовок раздела «Поля подписи»approveDocument принимает объект signature для одобрений, полученных с нарисованной подписью или явным заявлением. SDK не содержит панели для подписи: вы сами отображаете панель (или флажок с заявлением) и передаёте результат.
await CMP.approveDocument('service-contract', { approvalToken: tokenFromYourBackend, signature: { image_base64: canvas.toDataURL('image/png'), mime_type: 'image/png', signer_name: 'Jane Doe', declaration_text: 'I have read and accept the service contract.', input_type: 'touch', stroke_count: 14, drawing_duration_ms: 3200, screen_resolution: '390x844' }});| Поле | Отправляется как | Примечания |
|---|---|---|
image_base64 | signature_image | PNG, JPEG или WebP; примерно до 150 КБ в декодированном виде. |
mime_type | signature_mime_type | |
signer_name, signer_email, signer_phone, signer_id_number | те же имена | То, что подписант сообщает о себе, если только за эти сведения не ручается токен одобрения. |
declaration_text | declaration_text | Фраза, с которой человек согласился (не более 1000 символов). |
input_type | input_type | touch, mouse или pen. |
stroke_count, drawing_duration_ms, strokes_data | те же имена | Телеметрия рисования. |
screen_resolution | screen_resolution |
Все эти значения отправляются как поля верхнего уровня запроса, а не внутри metadata. Поля otp_verified / otp_reference из браузера не принимаются; они учитываются только на маршруте server-to-server или через токен одобрения.
Что здесь означает «простая электронная подпись»
Заголовок раздела «Что здесь означает «простая электронная подпись»»Запись, созданная таким образом, является простой электронной подписью: это данные в электронной форме (нарисованное изображение, заявление, технический контекст), которые присоединены к конкретной версии документа и которые подписант использовал для подписания. Это не усиленная и не квалифицированная электронная подпись — подписанту не выдаётся сертификат, и HashEntry не проверяет личность подписанта.
Что показывает доказательство:
- какая версия документа (по хешу содержимого) была одобрена, когда, с какого IP-адреса и user agent и с какими заявленными сведениями о подписанте;
- что запись не изменялась с момента её создания (см. Офлайн-проверка доказательств);
- насколько надёжно подкреплена личность — в поле
identity_assurance:
| Значение | Смысл |
|---|---|
self_declared | Сведения о подписанте поступили из браузера. Их никто не проверял. |
tenant_attested | За человека поручился ваш бэкенд — либо с помощью одноразового токена одобрения, либо заявив о выполненной проверке OTP на маршруте server-to-server. Это заверение — ваше: HashEntry фиксирует, что вы это засвидетельствовали, а не то, что это проверил HashEntry. |
Достаточно ли простой электронной подписи для конкретного договора, зависит от применимого права и от вида сделки; для некоторых сделок требуется определённая форма. Уточните у своего юриста, какой уровень вам нужен.
Когда версия документа меняется
Заголовок раздела «Когда версия документа меняется»Если сервер отвечает 409 CONFIG_OUTDATED, SDK заново читает активную версию документа:
- не изменилась (кеш сервера был холодным) → тот же запрос отправляется ещё один раз;
- изменилась → одобрение не отправляется повторно. Результат —
success: falseсerror.code = "DOCUMENT_VERSION_CHANGED", и срабатывает событиеdocument:error.
Во втором случае покажите документ снова (человек не видел новую версию) и получите новое одобрение — с новым токеном, если вы используете токены.
Результаты, события и ошибки
Заголовок раздела «Результаты, события и ошибки»CMP.on('document:approved', ({ slug, version, consentId }) => {});CMP.on('document:rejected', ({ slug, version }) => {});CMP.on('document:error', ({ slug, version, code, status, message }) => {});error.code | Что делать |
|---|---|
MODULE_LOAD_FAILED | Файл модуля не удалось загрузить (сеть, блокировщик рекламы, CSP). Вызовите метод ещё раз, чтобы повторить попытку. |
DOCUMENT_NOT_FOUND, VERSION_NOT_FOUND | Проверьте slug, а также то, что документ прикреплён к сайту и что у него есть активная версия на запрошенном языке. |
APPROVAL_TOKEN_REQUIRED | Выпустите токен на своём бэкенде и передайте его. |
APPROVAL_TOKEN_INVALID, APPROVAL_TOKEN_CONFLICT | Выпустите новый токен; убедитесь, что поля подписанта соответствуют токену. |
DOCUMENT_VERSION_CHANGED | Покажите документ снова. |
CONFIG_OUTDATED | Переданная вами версия не является активной, и перепроверить её не удалось. |
NETWORK_ERROR, TIMEOUT, RATE_LIMIT_EXCEEDED, QUEUE_OVERLOADED | Временная ошибка. Дайте человеку возможность попробовать ещё раз. |
VALIDATION_ERROR и ошибки размера (SIGNATURE_IMAGE_TOO_LARGE, CUSTOM_DOCUMENT_TOO_LARGE, …) | Исправьте запрос. |
Считайте одобрение данным только тогда, когда result.success === true. Сохраняйте result.consent_token: он идентифицирует запись для последующей проверки и экспорта доказательств.