Docs
SDKs

SDK PHP

Cliente oficial ForSign para PHP 8.1+ — baseado em Guzzle, com retries automáticos e exceções tipadas.

Pacote forsign/api-php no Packagist. Cliente PSR-3 (logger), PSR-7 (HTTP via Guzzle), com retry exponencial em 5xx/429 e exceções tipadas para erros de validação.

Compatibilidade

  • PHP 8.1+
  • Extensões: ext-json, ext-curl
  • Dependências: guzzlehttp/guzzle ^7.5, psr/log ^3.0

Instalação

composer require forsign/api-php

Configurando o cliente

<?php
use ForSign\Api\Client;

// Mínimo
$client = new Client(getenv('FORSIGN_API_KEY'));

// Com opções e logger PSR-3 (opcional)
$client = new Client(
    apiKey: getenv('FORSIGN_API_KEY'),
    options: [
        'baseUri' => 'https://api.forsign.digital', // padrão
        'timeout' => 30.0,                          // segundos, padrão 30
        'retries' => 3,                             // padrão 3 (exponencial + jitter)
    ],
    logger: $monologLogger // qualquer Psr\Log\LoggerInterface
);

Use variáveis de ambiente (getenv) ou um secret manager. Nunca commite a API Key. Reaproveite a mesma instância de Client ao longo da requisição.

Métodos disponíveis

Todas as operações ficam sob $client->operations() (instância de OperationRepository):

create cria operação com assinantes, anexos e formulários
complete finaliza manualmente uma operação
cancel cancela com motivo obrigatório
setManualCompletion / setAutomaticCompletion alterna o modo de finalização
getMemberAttachments lista anexos enviados por um membro
approveAttachments / rejectAttachments aprova ou rejeita com motivo
downloadZip PDF assinado + trilha de auditoria
downloadAttachment baixa um anexo individual

Upload de arquivos: a versão atual do SDK PHP não expõe upload nativo. Faça o upload via HTTP (POST /api/v2/document/upload, multipart) e use o id retornado em FileInformation. Veja Upload de documento.

1. Upload + criação da operação

<?php
require __DIR__ . '/vendor/autoload.php';

use ForSign\Api\Client;
use ForSign\Api\Enums\Language;
use ForSign\Api\Enums\SignatureType;
use ForSign\Api\Requests\FileInformation;
use ForSign\Api\Requests\Signer;
use ForSign\Api\NotificationTypes\EmailNotification;
use ForSign\Api\AuthenticationTypes\EmailDoubleAuthentication;
use ForSign\Api\SignatureTypes\DefaultSignatureType;

$client = new Client(getenv('FORSIGN_API_KEY'));

// 1. Upload via HTTP direto (o SDK ainda não expõe uploadFile)
$ch = curl_init('https://api.forsign.digital/api/v2/document/upload');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => ['X-Api-Key: ' . getenv('FORSIGN_API_KEY')],
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => ['file' => new CURLFile('./contrato.pdf')],
    CURLOPT_RETURNTRANSFER => true,
]);
$uploaded = json_decode(curl_exec($ch), true)['data'];

$fileInfo = new FileInformation($uploaded['id'], $uploaded['fileName']);

// 2. Assinante com 2FA por email
$signer = new Signer();
$signer->setName('João Silva')
       ->setEmail('[email protected]')
       ->setPhone('+5511987654321')
       ->setNotificationType(new EmailNotification('[email protected]'))
       ->setDoubleAuthenticationMethod(new EmailDoubleAuthentication('[email protected]'))
       ->setSignatureType(new DefaultSignatureType(SignatureType::UserChoice));

$signer->addSignatureInPosition($fileInfo, 1, '70%', '80%');

// 3. Builder fluente
$operationRequest = $client->createOperationBuilder('Contrato de prestação de serviços')
    ->setLanguage(Language::Portuguese)
    ->setSignersOrderRequirement(true)
    ->setExpirationDate((new DateTime())->add(new DateInterval('P15D')))
    ->withExternalId('CRM-12345')
    ->withRedirectUrl('https://app.exemplo.com/sucesso/{operationId}')
    ->addSigner($signer)
    ->build();

$response = $client->operations()->create($operationRequest);

echo "Operação: {$response->getId()}\n";
foreach ($response->getMembers() as $member) {
    echo "  {$member['name']}: {$member['signUrl']}\n";
}

2. Aprovar (ou rejeitar) anexos

Depois que o assinante envia documentos solicitados, sua aplicação inspeciona e decide. O memberId vem de getMembers() ou do webhook AttachmentFilled.

<?php
$attachments = $client->operations()->getMemberAttachments($memberId);

$toApprove = [];
$toReject = [];

foreach ($attachments as $att) {
    if (!$att->hasUploadedFiles()) continue;

    // Aceita PDFs, rejeita JPG ilegível (exemplo de regra de negócio)
    foreach ($att->getUploadedFiles() as $file) {
        if (str_ends_with(strtolower($file['name']), '.pdf')) {
            $toApprove[] = $file['id'];
        } else {
            $toReject[] = [
                'id' => $file['id'],
                'reason' => 'Envie em PDF, não em imagem.',
            ];
        }
    }
}

if ($toApprove) {
    $client->operations()->approveAttachments($operationMemberId, $toApprove);
}
if ($toReject) {
    $client->operations()->rejectAttachments($operationMemberId, $toReject);
}

3. Download após a operação concluída

<?php
$zip = $client->operations()->downloadZip($operationId);

echo "Arquivo: {$zip->getName()} ({$zip->getHumanReadableFileSize()})\n";
$zip->saveToFile(__DIR__ . '/' . $zip->getName());

// Anexo individual
$file = $client->operations()->downloadAttachment($attachmentId);
$file->saveToFile(__DIR__ . '/' . $file->getFileName());

Tratamento de erros

Duas exceções: ValidationException (HTTP 422, com getValidationErrors() estruturado por campo) e ApiException (todos os demais erros HTTP).

<?php
use ForSign\Api\Exceptions\ApiException;
use ForSign\Api\Exceptions\ValidationException;

try {
    $client->operations()->create($operationRequest);
} catch (ValidationException $e) {
    // 422 — campos inválidos
    foreach ($e->getValidationErrors() as $field => $message) {
        error_log("{$field}: {$message}");
    }
} catch (ApiException $e) {
    // 401 / 402 / 403 / 404 / 5xx
    error_log("HTTP {$e->getStatusCode()}: {$e->getMessage()}");
    foreach ($e->getMessages() ?? [] as $m) {
        error_log("  - {$m}");
    }
}

O middleware do Guzzle já faz retry automático em 5xx, 429 (rate limit) e falhas de conexão — backoff exponencial com jitter. Configure o número de tentativas via 'retries' no construtor.

Referências

On this page