Saltar al contenido
Desarrolladores

SDK de Java

El SDK de Java conecta sus servicios con la API de bb-sign: gestiona los tokens, reintenta de forma segura y verifica las firmas de los webhooks. Las coordenadas son co.binarybridges.sign:bb-sign-sdk-java:0.1.0-SNAPSHOT. Esta página reúne el README.md del SDK y su README-INSTALL.md.

Requiere Java 17 o superior.

El SDK se entrega en cuatro archivos: el jar, su pom, un .sha256 y la guía de instalación. También puede compilarlo desde el código fuente (ver Compilar desde el código fuente). Instale el jar en su repositorio local de Maven y declárelo como dependencia.

  1. Verifique la integridad del archivo recibido:

    Ventana de terminal
    shasum -a 256 -c bb-sign-sdk-java-0.1.0-SNAPSHOT.jar.sha256
  2. Instálelo. -DpomFile es obligatorio:

    Ventana de terminal
    mvn install:install-file \
    -Dfile=bb-sign-sdk-java-0.1.0-SNAPSHOT.jar \
    -DpomFile=bb-sign-sdk-java-0.1.0-SNAPSHOT.pom
  3. Declárelo como dependencia:

    <dependency>
    <groupId>co.binarybridges.sign</groupId>
    <artifactId>bb-sign-sdk-java</artifactId>
    <version>0.1.0-SNAPSHOT</version>
    </dependency>

El build debe tener acceso a Maven Central. El SDK es un jar simple, no shaded: su build resuelve okhttp, Retrofit y Jackson y los unifica con las versiones que ya usa. Esto evita conflictos con Jackson, presente en la mayoría de los servicios Java.

Si su build no tiene acceso a Maven Central, contáctenos antes de comenzar la integración para recibir una variante shaded del SDK.

El ejemplo completo cabe en un solo archivo. Las credenciales se crean en Configuración, pestaña Credenciales de API, de bb-sign; el secreto se muestra una sola vez, al crearlas.

import co.binarybridges.sign.sdk.BbSignClient;
import co.binarybridges.sign.sdk.generated.model.*;
import java.time.OffsetDateTime;
import java.util.List;
try (BbSignClient client = BbSignClient.builder()
.baseUrl(System.getenv("BBSIGN_BASE_URL"))
.tokenUrl(System.getenv("BBSIGN_TOKEN_URL"))
.clientCredentials(System.getenv("BBSIGN_CLIENT_ID"), System.getenv("BBSIGN_CLIENT_SECRET"))
.build()) {
CreateEnvelopeRequest request = new CreateEnvelopeRequest()
.title("Q3 Agreement")
.otpRequired(false)
.expiresAt(OffsetDateTime.now().plusHours(72))
.signers(List.of(new CreateSignerSpec()
.email("signer@example.com")
.name("A Signer")
.signOrder(1)));
EnvelopeResponse envelope = client.execute(client.envelopes().createEnvelope(request));
System.out.println("Created " + envelope.getId());
}

Lea las credenciales desde variables de entorno, fuera del código fuente. El secreto se almacena solo en Keycloak y bb-sign no conserva copia; si se pierde, rótelo, lo que invalida el anterior de inmediato. El cliente expone envelopes(), documents() y verification().

client.eachPage(
page -> client.envelopes().listEnvelopes(page, 50, null, null, null, null, null, null),
PagedEnvelopeResponse::getContent)
.forEach(envelope -> System.out.println(envelope.getTitle()));

Las páginas se obtienen bajo demanda durante la iteración, de modo que recorrer miles de sobres no los carga todos en memoria.

Webhooks.verify comprueba la firma sobre el cuerpo tal como se recibió. Declare el cuerpo como byte[] en su handler:

import co.binarybridges.sign.sdk.Webhooks;
@PostMapping("/webhooks/bb-sign")
public ResponseEntity<Void> receive(
@RequestHeader("X-BBSign-Signature") String signature,
@RequestBody byte[] rawBody) { // byte[], NOT a DTO
if (!Webhooks.verify(signature, rawBody, System.getenv("BBSIGN_WEBHOOK_SECRET"))) {
return ResponseEntity.status(401).build();
}
WebhookEvent event = new ObjectMapper().readValue(rawBody, WebhookEvent.class);
switch (event.getType()) {
case "envelope.completed" -> onCompleted(event.getData().getEnvelope());
case "signer.signed" -> onSigned(event.getData().getSigner());
default -> { /* ignore unknown types: more will be added */ }
}
return ResponseEntity.ok().build();
}

La firma cubre los bytes exactos recibidos. Si el parámetro se declara como un DTO, Spring interpreta el JSON y entrega un objeto; volver a serializarlo produce bytes distintos (cambian el orden de las claves y los espacios) y la verificación falla aunque el objeto sea correcto.

  • Spring MVC: @RequestBody byte[] o @RequestBody String, o un ContentCachingRequestWrapper si además necesita la forma interpretada.
  • Jakarta Servlet: lea request.getInputStream() una sola vez, antes que cualquier otro componente.

El SDK recibe byte[] en lugar de String de forma intencional: el tipo orienta hacia el uso correcto.

Devuelva 2xx en cuanto la firma sea válida y procese el evento de forma asíncrona. bb-sign reintenta con una secuencia que abarca aproximadamente 15 horas, y tras 3 secuencias consecutivas agotadas desactiva el endpoint; el procesamiento asíncrono mantiene la respuesta rápida bajo carga.

X-BBSign-Event-Id es estable entre reintentos y reentregas del mismo evento. La entrega es al menos una vez, de modo que un evento puede llegar dos veces; use el id como clave de idempotencia.

Mientras la ventana de rotación está abierta, el encabezado lleva dos valores v1=. Webhooks.verify acepta cualquiera de los dos, lo que le permite desplegar el nuevo secreto sin interrupción y sin configuración adicional.

La verificación depende del tiempo: una firma con más de 300 s de antigüedad se rechaza, de modo que un fixture fijo en una prueba unitaria deja de ser válido. Use la sobrecarga que recibe el reloj:

Webhooks.verify(signature, rawBody, secret, Duration.ofSeconds(300), Instant.ofEpochSecond(1760000000L));

Las solicitudes GET se reintentan ante errores 5xx y timeouts de conexión, con backoff exponencial y jitter completo.

Un POST cuya respuesta no se recibió no se reintenta. Si createEnvelope agota el tiempo de espera, el resultado es desconocido: el sobre puede existir ya, y un reintento crearía un segundo acuerdo enviado a las mismas personas. La API no usa claves de idempotencia.

Si una creación agota el tiempo de espera, liste antes de reintentar:

try {
client.execute(client.envelopes().createEnvelope(request));
} catch (BbSignException e) {
if (e.statusCode() == 0) {
// Never reached bb-sign, or the answer was lost. Check before sending again.
}
}

429 y 503 se reintentan también en POST, porque indican que el servidor no procesó la solicitud.

Toda respuesta distinta de 2xx lanza BbSignException, que expone statusCode(). 0 indica que la solicitud no obtuvo respuesta.

Estado Significado
400 Solicitud mal formada o con errores de validación
401 Credenciales rechazadas. El SDK renueva el token y reintenta una vez antes de reportarlo
403 Autenticado, pero esta credencial no tiene el permiso de la organización
404 El recurso no existe, o pertenece a otra organización o a un espacio de trabajo que la credencial no puede ver. No se distinguen, de forma intencional
409 El recurso no está en un estado que permita la operación, por ejemplo enviar un sobre ya enviado
429 Una cuota de la organización está agotada

Los mensajes de las excepciones nunca incluyen su secreto ni su token de acceso, aunque la respuesta de bb-sign los contenga. Errores incluye la tabla completa y el cuerpo del error.

  • Credenciales fuera de los logs. El SDK no incluye interceptor de logging ni punto para instalar uno. Evite agregar por su cuenta el HttpLoggingInterceptor de okhttp en nivel BODY: imprime el encabezado Authorization y el cuerpo del formulario del token tal cual.
  • TLS siempre verificado. La verificación de certificados no se puede desactivar.
  • Escrituras sin duplicados. Una escritura con resultado desconocido no se reintenta (ver Reintentos).
Ventana de terminal
cd sdk-java
mvn verify # regenerates the client, compiles, runs the tests
./verify.sh # the CI gate: drift, Jackson-not-Gson, plugin survival
mvn package # writes the handover artifact to target/handover/

El cliente en src/main/java/co/binarybridges/sign/sdk/generated/ es código generado y versionado: regenérelo en lugar de editarlo. Se versiona para que cada cambio en la especificación aparezca como un diff revisable.