Assinatura Digital em Ruby on Rails: Integração via REST Puro
Sejamos diretos: não há SDK oficial da SignDocs para Ruby — os seis SDKs cobrem Node, Python, PHP, Java, C# e Go. A boa notícia é que a API REST foi desenhada para dispensar SDK: endpoints convencionais, JSON simples, OAuth2 padrão. Este guia entrega a integração Rails completa em REST puro — token com cache thread-safe, sessão em uma chamada, webhook com raw_post e a verificação HMAC manual com o esquema exato ({timestamp}.{corpo}, headers X-SignDocs-Signature e X-SignDocs-Timestamp) — sem mistério e sem dependências além da stdlib.
O client de serviço: token com cache + criação de sessão
Dois detalhes que economizam debugging: o endpoint é /oauth2/token (com o "2") em form-urlencoded, não JSON; e o expires_in é 900 — o mutex em torno do cache evita a corrida de threads do Puma renovando em paralelo. O link do signatário é url + "?cs=" + clientSecret.
O webhook: raw_post e o esquema exato do HMAC
A SignDocs assina cada entrega com HMAC-SHA256 sobre a string {timestamp}.{corpo bruto} e envia dois headers: X-SignDocs-Signature (hex) e X-SignDocs-Timestamp (Unix, segundos), com tolerância anti-replay de 5 minutos. Em Rails:
O BaixarDocumentosJob (ActiveJob + Sidekiq/GoodJob/SolidQueue) consulta GET /v1/transactions/{id}/download e /evidence, baixa o PDF assinado e o pacote .p7m pelas URLs temporárias e anexa via ActiveStorage. O controller nunca faz esse trabalho — a plataforma reenvia eventos se você demorar, e sem a checagem de ultimo_evento_id a reentrega processaria em dobro.
O caminho de teste
- Unidade: a verificação HMAC acima é função pura — teste com fixtures: assinatura válida, inválida, timestamp velho, evento duplicado, tipo desconhecido.
- Contrato: uma suíte pequena contra o sandbox (credenciais de HML, dados fictícios; TTL de 7 dias — cada teste cria o que consome).
- Tubulação:
POST /v1/webhooks/{webhookId}/testenvia um payload assinado de verdade ao seu endpoint — em desenvolvimento, exponha o servidor local com um túnel. A estratégia completa está em como testar do sandbox ao CI.
E antes do go-live, os 12 erros mais comuns — o código deste guia já esquiva os cinco piores por construção.
Perguntas Frequentes
Existe SDK oficial da SignDocs para Ruby?
Não — os SDKs oficiais cobrem TypeScript/Node, Python, PHP, Java, C#/.NET e Go. Para Ruby, o caminho é a API REST pura, e a boa notícia é que ela foi desenhada para isso: endpoints convencionais, JSON simples, autenticação OAuth2 padrão e erros estruturados (RFC 7807). A integração completa em Ruby cabe em três blocos de código — token, criação de sessão e webhook — todos mostrados neste guia. A ausência de SDK muda o volume de código em algumas dezenas de linhas, não a viabilidade.
Como obtenho o token OAuth2 em Ruby?
Um POST application/x-www-form-urlencoded para /oauth2/token (atenção ao 2 no caminho) com grant_type=client_credentials, client_id e client_secret — em Ruby puro, Net::HTTP.post_form resolve; com Faraday, um .post com url_encoded. A resposta traz access_token (JWT ES256) e expires_in de 900 segundos: cacheie o token no processo e renove antes de expirar — um mutex simples em torno do cache evita a corrida de múltiplas threads renovando ao mesmo tempo em servidores como o Puma.
Como verifico a assinatura do webhook manualmente em Rails?
O esquema exato: a SignDocs envia os headers X-SignDocs-Signature (HMAC-SHA256 em hex) e X-SignDocs-Timestamp (Unix, em segundos), e a assinatura cobre a string '{timestamp}.{corpo bruto}'. No controller: leia request.raw_post (nunca params re-serializados), rejeite se o timestamp estiver a mais de 300 segundos do relógio (anti-replay), calcule OpenSSL::HMAC.hexdigest('SHA256', segredo, timestamp + '.' + raw) e compare com ActiveSupport::SecurityUtils.secure_compare — comparação timing-safe. Só então faça o JSON.parse e processe.
O webhook em Rails precisa de algum ajuste de CSRF?
Sim — o mesmo da comunidade Django com outro nome: o protect_from_forgery do Rails bloqueia POSTs externos sem token CSRF. No controller do webhook, use skip_before_action :verify_authenticity_token (ou herde de ActionController::API, que não tem CSRF). Isso é seguro porque a verificação HMAC substitui o CSRF com uma garantia mais forte para este caso: prova criptográfica de que a requisição veio da SignDocs. A combinação proibida é pular o CSRF sem verificar o HMAC.
Onde processar o download do PDF assinado no Rails?
Num job — ActiveJob com Sidekiq, GoodJob ou SolidQueue. O controller do webhook confirma rápido (200) e enfileira; o job consulta GET /v1/transactions/{id}/download e /evidence, baixa o PDF assinado e o pacote .p7m pelas URLs temporárias e anexa ao seu registro (ActiveStorage funciona bem). O par transaction_id + event_id no seu modelo dá a idempotência: reentregas do webhook reencontram o registro já processado e retornam 200 sem efeito.
Vale a pena escrever uma gem interna para isso?
Se você tem mais de um app Rails consumindo a API, sim — um pequeno client interno (uma classe com token cache, create_session, transaction e um verificador de webhook) concentra o conhecimento e os testes num lugar. Para um app só, uma classe de serviço em app/services basta. Em ambos os casos, aponte a base URL por credencial de ambiente (api-hml.signdocs.com.br em desenvolvimento) e rode a suíte de contrato contra o sandbox — cada teste criando o que consome, por causa do TTL de 7 dias.
REST puro, primeira assinatura ainda hoje
Credenciais de homologação gratuitas, endpoints convencionais e o esquema de webhook documentado até o byte — a integração Ruby completa no sandbox, sem cartão.
Criar credenciais de homologação Falar com o time comercial