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.
forsign/api-php
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-phpConfigurando 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):
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.