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
- registrar o reutilizar una tablet
- emitir codigo de activacion si la tablet se instala o se resetea
- crear una solicitud de firma con
externalRef,deviceIdypdfBase64 - consultar el estado hasta
signed,cancelledoexpired - 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 ausente404 NOT_FOUND: solicitud o tablet inexistente409 INVALID_STATE: la solicitud ya no admite esa operacion413 PDF_TOO_LARGE: el PDF supera el limite configurado422 PDF_INVALID: Base64 o PDF invalidos500 INTERNAL_ERROR: error inesperado; revisartraceId
Tabla ampliada: API - Errores de integracion
Recomendaciones practicas para BC
- guardar
requestIdyexternalRefpara trazabilidad - registrar siempre
traceIdcuando 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