Saltar a contenido

API - Business Central

Objetivo

Esta pagina explica como integrar Business Central con la API de firma para registrar tablets, crear solicitudes y recuperar el PDF firmado.

URLs utiles

  • API base URL: https://apifirma.jdmarquez.dev
  • Swagger UI: https://apifirma.jdmarquez.dev/swagger
  • OpenAPI JSON: https://apifirma.jdmarquez.dev/swagger/v1/swagger.json

Flujo de referencia para BC

  1. registrar o reutilizar una tablet
  2. emitir codigo de activacion si la tablet se instala o se resetea
  3. crear una solicitud de firma con externalRef, deviceId y pdfBase64
  4. consultar el estado hasta signed, cancelled o expired
  5. descargar el PDF firmado

Requisitos previos en BC

  • la API debe estar publicada por HTTPS
  • la extension debe tener permitido HttpClient
  • el token BC debe almacenarse como secreto o en configuracion segura
  • el PDF debe convertirse a Base64 antes de llamar a POST /v1/signature-requests

Autenticacion

Authorization: Bearer <BC_BEARER_TOKEN>

BC no usa X-Device-Token; ese header es solo para la tablet activada.

Inicializacion recomendada en AL

local procedure GetApiBaseUrl(): Text
begin
    exit('https://apifirma.jdmarquez.dev');
end;

local procedure GetApiBearerToken(): Text
begin
    exit('replace_me');
end;

Estructura sugerida en AL

codeunit 50100 "BC PDF Sign Mgt."
{
    procedure RegisterDevice(DeviceId: Text; DeviceName: Text)
    begin
    end;

    procedure IssueActivationCode(DeviceId: Text)
    begin
    end;

    procedure CreateSignatureRequest(PdfBase64: Text)
    begin
    end;

    procedure GetSignatureStatus(RequestId: Guid)
    begin
    end;

    procedure DownloadSignedPdf(RequestId: Guid; var TargetBlob: Codeunit "Temp Blob")
    begin
    end;
}

La recomendacion es centralizar toda la comunicacion HTTP con la API en una sola codeunit.

Helper base para enviar JSON

local procedure SendJson(Method: Text; Url: Text; Token: Text; BodyText: Text; var Response: HttpResponseMessage)
var
    Client: HttpClient;
    Request: HttpRequestMessage;
    Content: HttpContent;
    Headers: HttpHeaders;
begin
    Request.Method := Method;
    Request.SetRequestUri(Url);

    if BodyText <> '' then begin
        Content.WriteFrom(BodyText);
        Content.GetHeaders(Headers);
        Headers.Clear();
        Headers.Add('Content-Type', 'application/json');
        Request.Content := Content;
    end;

    Request.GetHeaders(Headers);
    Headers.Add('Authorization', StrSubstNo('Bearer %1', Token));

    if not Client.Send(Request, Response) then
        Error('No se pudo contactar con la API de firma.');
end;

Ejemplo AL - registrar tablet

procedure RegisterDevice(ApiBaseUrl: Text; Token: Text)
var
    Payload: JsonObject;
    Response: HttpResponseMessage;
    BodyText: Text;
begin
    Payload.Add('deviceId', 'tablet-recepcion-1');
    Payload.Add('name', 'Recepcion 1');
    Payload.WriteTo(BodyText);

    SendJson('POST', ApiBaseUrl + '/v1/devices', Token, BodyText, Response);

    if not Response.IsSuccessStatusCode() then
        Error('Error al registrar tablet. Codigo HTTP: %1', Response.HttpStatusCode());
end;

Ejemplo AL - emitir codigo de activacion

procedure IssueActivationCode(ApiBaseUrl: Text; Token: Text; DeviceId: Text)
var
    Response: HttpResponseMessage;
begin
    SendJson('POST', ApiBaseUrl + '/v1/devices/' + DeviceId + '/activation-code', Token, '', Response);

    if not Response.IsSuccessStatusCode() then
        Error('Error al emitir codigo de activacion. Codigo HTTP: %1', Response.HttpStatusCode());
end;

Ejemplo AL - crear solicitud de firma

procedure CreateSignatureRequest(ApiBaseUrl: Text; Token: Text; PdfBase64: Text)
var
    Payload: JsonObject;
    Response: HttpResponseMessage;
    BodyText: Text;
begin
    Payload.Add('externalRef', 'BC-1001');
    Payload.Add('deviceId', 'tablet-recepcion-1');
    Payload.Add('documentName', 'autorizacion.pdf');
    Payload.Add('pdfBase64', PdfBase64);
    Payload.Add('guestName', 'John Doe');
    Payload.Add('reservationRef', 'RES-1001');
    Payload.WriteTo(BodyText);

    SendJson('POST', ApiBaseUrl + '/v1/signature-requests', Token, BodyText, Response);

    if not Response.IsSuccessStatusCode() then
        Error('Error al crear solicitud. Codigo HTTP: %1', Response.HttpStatusCode());
end;

Ejemplo AL - consultar estado

procedure GetSignatureStatus(ApiBaseUrl: Text; Token: Text; RequestId: Guid)
var
    Response: HttpResponseMessage;
begin
    SendJson('GET', ApiBaseUrl + '/v1/signature-requests/' + Format(RequestId), Token, '', Response);

    if not Response.IsSuccessStatusCode() then
        Error('Error al consultar estado. Codigo HTTP: %1', Response.HttpStatusCode());
end;

Ejemplo AL - descargar PDF final

procedure DownloadSignedPdf(ApiBaseUrl: Text; Token: Text; RequestId: Guid; var TargetBlob: Codeunit "Temp Blob")
var
    Client: HttpClient;
    Request: HttpRequestMessage;
    Response: HttpResponseMessage;
    Headers: HttpHeaders;
    InStr: InStream;
    OutStr: OutStream;
begin
    Request.Method := 'GET';
    Request.SetRequestUri(ApiBaseUrl + '/v1/signature-requests/' + Format(RequestId) + '/result');
    Request.GetHeaders(Headers);
    Headers.Add('Authorization', StrSubstNo('Bearer %1', Token));

    if not Client.Send(Request, Response) then
        Error('No se pudo descargar el PDF firmado.');

    if not Response.IsSuccessStatusCode() then
        Error('Error al descargar resultado. Codigo HTTP: %1', Response.HttpStatusCode());

    Response.Content().ReadAs(InStr);
    TargetBlob.CreateOutStream(OutStr);
    CopyStream(OutStr, InStr);
end;

Ejemplo AL - flujo minimo de negocio

procedure StartCheckInSignature(PdfBase64: Text)
begin
    RegisterDevice(GetApiBaseUrl(), GetApiBearerToken());
    CreateSignatureRequest(GetApiBaseUrl(), GetApiBearerToken(), PdfBase64);
end;

Ejemplo de payload JSON desde BC

{
  "externalRef": "BC-1001",
  "deviceId": "tablet-recepcion-1",
  "documentName": "autorizacion.pdf",
  "pdfBase64": "JVBERi0xLjQKJ...",
  "guestName": "John Doe",
  "reservationRef": "RES-1001"
}

Errores que BC debe manejar

  • 401 UNAUTHENTICATED: token BC incorrecto o ausente
  • 404 NOT_FOUND: solicitud o tablet inexistente
  • 409 INVALID_STATE: la solicitud ya no admite esa operacion
  • 413 PDF_TOO_LARGE: el PDF supera el limite configurado
  • 422 PDF_INVALID: Base64 o PDF invalidos
  • 500 INTERNAL_ERROR: error inesperado; revisar traceId

Tabla ampliada: API - Errores de integracion

Recomendaciones practicas para BC

  • guardar requestId y externalRef para trazabilidad
  • registrar siempre traceId cuando una llamada falle
  • no hacer polling agresivo; para BC basta con consultar estado segun el proceso de negocio
  • usar Swagger para validar el contrato antes de escribir codigo AL

Siguiente lectura recomendada