Интеграция электронной подписи SignNow на сайт
SignNow — конкурент DocuSign от airSlate, ориентированный на SMB и API-интеграции. Отличается более простым ценообразованием и REST API без обязательного SDK — все операции через стандартные HTTP-запросы. Документация: docs.signnow.com.
Аутентификация
SignNow использует OAuth 2.0 с client_credentials для серверных интеграций:
class SignNowAuthService
{
private string $baseUrl = 'https://api.signnow.com';
public function getToken(): string
{
$response = Http::withBasicAuth(
config('services.signnow.client_id'),
config('services.signnow.client_secret')
)->asForm()->post("{$this->baseUrl}/oauth2/token", [
'grant_type' => 'client_credentials',
'scope' => '*',
]);
$token = $response->json('access_token');
// Кешируем — токен живёт час
Cache::put('signnow_token', $token, now()->addMinutes(55));
return $token;
}
public function token(): string
{
return Cache::get('signnow_token') ?? $this->getToken();
}
}
Для флоу с подписью конкретного пользователя (user-level operations) нужен Password Grant с логином/паролем пользователя SignNow. Для большинства API-интеграций достаточно client_credentials.
Загрузка документа и простановка полей
class SignNowDocumentService
{
public function __construct(private SignNowAuthService $auth)
{}
public function uploadDocument(string $pdfPath, string $fileName): string
{
$response = Http::withToken($this->auth->token())
->attach('file', file_get_contents($pdfPath), $fileName)
->post('https://api.signnow.com/document');
return $response->json('id'); // document ID
}
public function addSignatureFields(string $documentId, array $signers): void
{
$fields = [];
$roleIndex = 0;
foreach ($signers as $signer) {
$fields[] = [
'type' => 'signature',
'role' => $signer['role'] ?? "Signer {$roleIndex}",
'role_id' => (string)$roleIndex,
'required' => true,
'height' => 40,
'width' => 200,
'x' => 100,
'y' => 600,
'page_number' => 0,
];
$roleIndex++;
}
Http::withToken($this->auth->token())
->put("https://api.signnow.com/document/{$documentId}", [
'fields' => $fields,
]);
}
}
SignNow поддерживает smart fields через anchor-теги в PDF: если в документе есть текст [[sig|req|signer1]], SignNow автоматически ставит поле подписи. Это удобнее фиксированных координат.
Создание invite для подписантов
public function sendInvite(string $documentId, array $signers): string
{
$recipients = [];
foreach ($signers as $index => $signer) {
$recipients[] = [
'email' => $signer['email'],
'role' => $signer['role'] ?? "Signer {$index}",
'role_id' => (string)$index,
'order' => $index + 1,
'reminder' => [
'remind_before' => 0,
'remind_after' => 3, // напомнить через 3 дня
'remind_repeat' => 2, // повторять каждые 2 дня
],
'expiration_days' => 30,
'subject' => 'Пожалуйста, подпишите документ',
'message' => "Здравствуйте, {$signer['name']}! Прошу вас подписать прикреплённый документ.",
];
}
$response = Http::withToken($this->auth->token())
->post("https://api.signnow.com/document/{$documentId}/invite", [
'to' => $recipients,
'from' => config('services.signnow.sender_email'),
]);
return $response->json('status'); // 'success'
}
Embedded signing
Для подписи без перехода на SignNow — получение ссылки для вставки в iframe или редиректа:
public function getSigningLink(string $documentId, string $email): string
{
$response = Http::withToken($this->auth->token())
->post("https://api.signnow.com/link", [
'document_id' => $documentId,
]);
// link действителен один раз и ограниченное время
return $response->json('url');
}
Embedded signing работает через <iframe src="..."> или window.open. После подписания SignNow вызывает postMessage или редирект на указанный URL.
Webhook: события документа
// Регистрация webhook
Http::withToken($this->auth->token())
->post('https://api.signnow.com/api/v2/events', [
'event' => 'document.complete',
'entity_id' => $documentId,
'action' => 'callback',
'callback_url' => route('webhooks.signnow'),
]);
// Обработчик
public function handleSignNowWebhook(Request $request): Response
{
$data = $request->json()->all();
if (($data['event'] ?? '') === 'document.complete') {
$documentId = $data['meta']['document_id'];
DownloadSignedDocumentJob::dispatch($documentId);
}
return response()->noContent();
}
Скачивание подписанного документа
public function downloadSigned(string $documentId): string
{
$response = Http::withToken($this->auth->token())
->get("https://api.signnow.com/document/{$documentId}/download", [
'type' => 'collapsed', // единый PDF со всеми подписями
]);
$path = storage_path("app/contracts/signed_{$documentId}.pdf");
file_put_contents($path, $response->body());
return $path;
}
Отличия от DocuSign
SignNow работает через чистый REST без обязательного SDK, что упрощает интеграцию на любом языке. Нет встроенного фискального хранилища документов — если нужно хранить подписанные договоры с аудит-треком, это организуется на стороне приложения. Функциональность embedded signing менее гибкая по кастомизации UI. При этом API более прямолинеен и документирован.
Сроки
OAuth, загрузка документа, отправка invite, webhook и скачивание подписанного файла: 2–3 рабочих дня. С embedded signing и полным UX-флоу внутри сайта: 3–4 рабочих дня.







