unit uDecaConfig;

interface

uses
  Winapi.Windows, System.SysUtils, System.Classes, System.IniFiles, System.Win.ComObj;

type
  TDecaModoConexion = (dmRemotoApi, dmLocalCli);

  TDecaConfig = record
    Modo: TDecaModoConexion;
    UrlServicio: string;      // ej: https://www.misdeca.es
    ApiKey: string;           // Token API de autenticacion
    TimeoutSeg: Integer;      // Segundos de espera para peticiones HTTP / CLI
    RutaPhp: string;          // Ruta a ejecutable php.exe
    RutaScriptCli: string;    // Ruta a cli_emitir.php
    EmpresaCif: string;       // CIF de la empresa
  end;

function GetDecaIniFileName: string;
function GetDecaConfigPorDefecto: TDecaConfig;
function CargarDecaConfig(out AConfig: TDecaConfig): Boolean;
function GuardarDecaConfig(const AConfig: TDecaConfig): Boolean;
function ProbarConexionDeca(const AConfig: TDecaConfig; out AInfoEmpresa, AErrorMsg: string): Boolean;
function EmitirDocumentoDeca(const AConfig: TDecaConfig; const APayloadJson: string; out ARespJson: string): Boolean;
function ConsultarDocumentoDeca(const AConfig: TDecaConfig; const ACodigo, AReferencia: string; out ARespJson: string): Boolean;
function ObtenerPresetsVehiculos(const AConfig: TDecaConfig; out ARespJson: string): Boolean;
function ObtenerPresetsContactos(const AConfig: TDecaConfig; const ATipo: string; out ARespJson: string): Boolean;
function GuardarPresetVehiculo(const AConfig: TDecaConfig; const APayloadJson: string): Boolean;
function GuardarPresetContacto(const AConfig: TDecaConfig; const APayloadJson: string): Boolean;

implementation

const
  DECA_CRYPT_KEY = 'DeCA_2026_Strato_ControlTime_SecKey#99!';
  DEFAULT_URL_SERVICIO = 'https://www.misdeca.es';
  DEFAULT_API_KEY = 'deca_live_prieto_v53022166_2026_key8811';
  DEFAULT_PHP_EXE = 'C:\xampp\php\php.exe';
  DEFAULT_SCRIPT_CLI = 'c:\fuentes\proyectos delphi\DECA\server\cli_emitir.php';
  DEFAULT_TIMEOUT_SEG = 30;

function CifrarCadena(const ATexto: string): string;
var
  i, kLen: Integer;
  b: Byte;
  kChar: Char;
  LHex: string;
begin
  Result := '';
  kLen := Length(DECA_CRYPT_KEY);
  if (ATexto = '') or (kLen = 0) then Exit;

  for i := 1 to Length(ATexto) do
  begin
    kChar := DECA_CRYPT_KEY[((i - 1) mod kLen) + 1];
    b := (Ord(ATexto[i]) xor Ord(kChar)) and $FF;
    LHex := IntToHex(b, 2);
    Result := Result + LHex;
  end;
end;

function DescifrarCadena(const AHex: string): string;
var
  i, kLen, bVal: Integer;
  kChar, rChar: Char;
  LSub: string;
begin
  Result := '';
  kLen := Length(DECA_CRYPT_KEY);
  if (Length(AHex) < 2) or ((Length(AHex) mod 2) <> 0) or (kLen = 0) then Exit;

  for i := 1 to Length(AHex) div 2 do
  begin
    LSub := '$' + Copy(AHex, (i - 1) * 2 + 1, 2);
    bVal := StrToIntDef(LSub, -1);
    if bVal < 0 then Exit;
    kChar := DECA_CRYPT_KEY[((i - 1) mod kLen) + 1];
    rChar := Chr(bVal xor Ord(kChar));
    Result := Result + rChar;
  end;
end;

function GetDecaIniFileName: string;
begin
  Result := ExtractFilePath(ParamStr(0)) + 'deca.ini';
end;

function GetDecaConfigPorDefecto: TDecaConfig;
begin
  Result.Modo := dmRemotoApi;
  Result.UrlServicio := DEFAULT_URL_SERVICIO;
  Result.ApiKey := DEFAULT_API_KEY;
  Result.TimeoutSeg := DEFAULT_TIMEOUT_SEG;
  Result.RutaPhp := DEFAULT_PHP_EXE;
  Result.RutaScriptCli := DEFAULT_SCRIPT_CLI;
  Result.EmpresaCif := 'V53022166';
end;

function CargarDecaConfig(out AConfig: TDecaConfig): Boolean;
var
  LIniFile: string;
  LIni: TIniFile;
  LRawData, LDecrypted: string;
  LMemIni: TMemIniFile;
  LStrings: TStringList;
begin
  AConfig := GetDecaConfigPorDefecto;
  LIniFile := GetDecaIniFileName;

  if not FileExists(LIniFile) then
  begin
    GuardarDecaConfig(AConfig);
    Result := True;
    Exit;
  end;

  LIni := TIniFile.Create(LIniFile);
  try
    LRawData := LIni.ReadString('DECA_SECURE_CONFIG', 'Data', '');
  finally
    LIni.Free;
  end;

  if LRawData = '' then
  begin
    GuardarDecaConfig(AConfig);
    Result := True;
    Exit;
  end;

  LDecrypted := DescifrarCadena(LRawData);
  if LDecrypted = '' then
  begin
    Result := False;
    Exit;
  end;

  LStrings := TStringList.Create;
  try
    LStrings.Text := LDecrypted;
    LMemIni := TMemIniFile.Create('');
    try
      LMemIni.SetStrings(LStrings);
      if SameText(LMemIni.ReadString('CONEXION', 'Modo', 'REMOTO_API'), 'LOCAL_CLI') then
        AConfig.Modo := dmLocalCli
      else
        AConfig.Modo := dmRemotoApi;

      AConfig.UrlServicio := LMemIni.ReadString('CONEXION', 'UrlServicio', DEFAULT_URL_SERVICIO);
      AConfig.ApiKey := LMemIni.ReadString('CONEXION', 'ApiKey', DEFAULT_API_KEY);
      AConfig.TimeoutSeg := LMemIni.ReadInteger('CONEXION', 'TimeoutSeg', DEFAULT_TIMEOUT_SEG);
      AConfig.RutaPhp := LMemIni.ReadString('LOCAL', 'RutaPhp', DEFAULT_PHP_EXE);
      AConfig.RutaScriptCli := LMemIni.ReadString('LOCAL', 'RutaScriptCli', DEFAULT_SCRIPT_CLI);
      AConfig.EmpresaCif := LMemIni.ReadString('EMPRESA', 'Cif', 'V53022166');
      Result := True;
    finally
      LMemIni.Free;
    end;
  finally
    LStrings.Free;
  end;
end;

function GuardarDecaConfig(const AConfig: TDecaConfig): Boolean;
var
  LIniFile: string;
  LMemIni: TMemIniFile;
  LStrings: TStringList;
  LPlainText, LEncrypted: string;
  LDiskIni: TIniFile;
begin
  Result := False;
  LIniFile := GetDecaIniFileName;

  LStrings := TStringList.Create;
  try
    LMemIni := TMemIniFile.Create('');
    try
      if AConfig.Modo = dmLocalCli then
        LMemIni.WriteString('CONEXION', 'Modo', 'LOCAL_CLI')
      else
        LMemIni.WriteString('CONEXION', 'Modo', 'REMOTO_API');

      LMemIni.WriteString('CONEXION', 'UrlServicio', Trim(AConfig.UrlServicio));
      LMemIni.WriteString('CONEXION', 'ApiKey', Trim(AConfig.ApiKey));
      LMemIni.WriteInteger('CONEXION', 'TimeoutSeg', AConfig.TimeoutSeg);
      LMemIni.WriteString('LOCAL', 'RutaPhp', Trim(AConfig.RutaPhp));
      LMemIni.WriteString('LOCAL', 'RutaScriptCli', Trim(AConfig.RutaScriptCli));
      LMemIni.WriteString('EMPRESA', 'Cif', Trim(AConfig.EmpresaCif));

      LMemIni.GetStrings(LStrings);
      LPlainText := LStrings.Text;
    finally
      LMemIni.Free;
    end;

    LEncrypted := CifrarCadena(LPlainText);

    LDiskIni := TIniFile.Create(LIniFile);
    try
      LDiskIni.WriteString('DECA_SECURE_CONFIG', 'Data', LEncrypted);
      LDiskIni.WriteString('DECA_SECURE_CONFIG', 'Version', '1.0');
      Result := True;
    finally
      LDiskIni.Free;
    end;
  finally
    LStrings.Free;
  end;
end;

function NormalizarUrlBase(const AUrl: string): string;
begin
  Result := Trim(AUrl);
  while (Length(Result) > 0) and (Result[Length(Result)] = '/') do
    Delete(Result, Length(Result), 1);
end;

function EnviarHttpRest(const AUrl, APayloadJson, AApiKey: string; ATimeoutSeg: Integer; out ARespJson: string): Boolean;
var
  LHttp: OleVariant;
  LStatus: Integer;
begin
  Result := False;
  ARespJson := '';
  try
    LHttp := CreateOleObject('WinHttp.WinHttpRequest.5.1');
    LHttp.Open('POST', AUrl, False);
    LHttp.SetTimeouts(5000, 10000, ATimeoutSeg * 1000, ATimeoutSeg * 1000);
    LHttp.SetRequestHeader('Content-Type', 'application/json; charset=utf-8');
    if Trim(AApiKey) <> '' then
    begin
      LHttp.SetRequestHeader('X-API-Key', Trim(AApiKey));
      LHttp.SetRequestHeader('Authorization', 'Bearer ' + Trim(AApiKey));
    end;

    LHttp.Send(APayloadJson);
    LStatus := LHttp.Status;
    ARespJson := Trim(LHttp.ResponseText);

    // 200 = OK, 400 = Error de validacion de campos estructurado en JSON
    Result := (LStatus = 200) or ((LStatus = 400) and (Pos('"error"', ARespJson) > 0));
    if not Result then
    begin
      if ARespJson = '' then
        ARespJson := Format('El servidor retorn'#243' HTTP %d: %s', [LStatus, LHttp.StatusText]);
    end;
  except
    on E: Exception do
      ARespJson := 'Error de comunicaci'#243'n HTTPS: ' + E.Message;
  end;
end;

function ProbarConexionDeca(const AConfig: TDecaConfig; out AInfoEmpresa, AErrorMsg: string): Boolean;
var
  LUrlPing: string;
  LHttp: OleVariant;
  LStatus: Integer;
  LRespText: string;
  LPhpExe, LScriptFile: string;
begin
  Result := False;
  AInfoEmpresa := '';
  AErrorMsg := '';

  if AConfig.Modo = dmRemotoApi then
  begin
    if Trim(AConfig.UrlServicio) = '' then
    begin
      AErrorMsg := 'Indique la URL del servicio DeCA en la configuraci'#243'n.';
      Exit;
    end;

    LUrlPing := NormalizarUrlBase(AConfig.UrlServicio) + '/api/index.php?route=deca/ping&api_key=' + Trim(AConfig.ApiKey);
    try
      LHttp := CreateOleObject('WinHttp.WinHttpRequest.5.1');
      LHttp.Open('GET', LUrlPing, False);
      LHttp.SetTimeouts(4000, 6000, 10000, 10000);
      LHttp.SetRequestHeader('X-API-Key', Trim(AConfig.ApiKey));
      LHttp.SetRequestHeader('Authorization', 'Bearer ' + Trim(AConfig.ApiKey));
      LHttp.Send();

      LStatus := LHttp.Status;
      LRespText := Trim(LHttp.ResponseText);

      if LStatus = 200 then
      begin
        Result := Pos('"success":true', LRespText) > 0;
        if Result then
          AInfoEmpresa := LRespText
        else
          AErrorMsg := 'Respuesta inesperada del servidor: ' + LRespText;
      end
      else if LStatus = 401 then
        AErrorMsg := 'No autorizado (401): Verifique la API Key de la empresa.'
      else
        AErrorMsg := Format('Error HTTP %d: %s'#13#10'%s', [LStatus, LHttp.StatusText, LRespText]);
    except
      on E: Exception do
        AErrorMsg := 'No se pudo conectar con el servidor: ' + E.Message;
    end;
  end
  else
  begin
    // Modo Local CLI
    LPhpExe := Trim(AConfig.RutaPhp);
    if (LPhpExe = '') or not FileExists(LPhpExe) then
    begin
      AErrorMsg := 'No se encontr'#243' el ejecutable PHP en: ' + LPhpExe;
      Exit;
    end;

    LScriptFile := Trim(AConfig.RutaScriptCli);
    if (LScriptFile = '') or not FileExists(LScriptFile) then
    begin
      AErrorMsg := 'No se encontr'#243' el script emisor en: ' + LScriptFile;
      Exit;
    end;

    Result := True;
    AInfoEmpresa := 'PHP y script CLI local disponibles correctamente.';
  end;
end;

function EjecutarCliLocal(const AConfig: TDecaConfig; const APayloadJson: string; out ARespJson: string): Boolean;
var
  LTempDir, LReqFile, LRespFile, LScriptFile, LPhpExe: string;
  LReqLines, LRespLines: TStringList;
  LStartupInfo: TStartupInfo;
  LProcessInfo: TProcessInformation;
  LSecAttr: TSecurityAttributes;
  LOutHandle, LInHandle: THandle;
  LCmdLine: string;
  LExitCode: DWORD;
  LCreatedOk: Boolean;
begin
  Result := False;
  ARespJson := '';
  LExitCode := 0;

  LTempDir := IncludeTrailingPathDelimiter(GetEnvironmentVariable('TEMP'));
  if (LTempDir = '') or not DirectoryExists(LTempDir) then
    LTempDir := ExtractFilePath(ParamStr(0));

  LReqFile := LTempDir + 'deca_req_' + IntToStr(GetTickCount) + '.json';
  LRespFile := LTempDir + 'deca_resp_' + IntToStr(GetTickCount) + '.json';
  LScriptFile := Trim(AConfig.RutaScriptCli);
  LPhpExe := Trim(AConfig.RutaPhp);

  if not FileExists(LPhpExe) then
  begin
    ARespJson := 'No se encontr'#243' el int'#233'rprete PHP en ' + LPhpExe;
    Exit;
  end;

  if not FileExists(LScriptFile) then
  begin
    ARespJson := 'No se encontr'#243' el script emisor en ' + LScriptFile;
    Exit;
  end;

  LReqLines := TStringList.Create;
  try
    LReqLines.Text := APayloadJson;
    LReqLines.SaveToFile(LReqFile, TEncoding.UTF8);
  finally
    LReqLines.Free;
  end;

  FillChar(LSecAttr, SizeOf(LSecAttr), 0);
  LSecAttr.nLength := SizeOf(SECURITY_ATTRIBUTES);
  LSecAttr.bInheritHandle := True;
  LSecAttr.lpSecurityDescriptor := nil;

  LOutHandle := CreateFile(PChar(LRespFile), GENERIC_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE, @LSecAttr, CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL, 0);

  if LOutHandle = INVALID_HANDLE_VALUE then
  begin
    ARespJson := 'No se pudo crear archivo de respuesta: ' + SysErrorMessage(GetLastError);
    DeleteFile(LReqFile);
    Exit;
  end;

  LInHandle := CreateFile('NUL', GENERIC_READ,
    FILE_SHARE_READ or FILE_SHARE_WRITE, @LSecAttr, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, 0);

  try
    FillChar(LStartupInfo, SizeOf(LStartupInfo), 0);
    LStartupInfo.cb := SizeOf(LStartupInfo);
    LStartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    LStartupInfo.wShowWindow := SW_HIDE;
    LStartupInfo.hStdOutput := LOutHandle;
    LStartupInfo.hStdError := LOutHandle;
    LStartupInfo.hStdInput := LInHandle;

    LCmdLine := Format('"%s" "%s" "%s"', [LPhpExe, LScriptFile, LReqFile]);
    UniqueString(LCmdLine);

    LCreatedOk := CreateProcess(nil, PChar(LCmdLine), nil, nil, True, CREATE_NO_WINDOW, nil, nil, LStartupInfo, LProcessInfo);

    if LCreatedOk then
    begin
      WaitForSingleObject(LProcessInfo.hProcess, AConfig.TimeoutSeg * 1000);
      GetExitCodeProcess(LProcessInfo.hProcess, LExitCode);
      CloseHandle(LProcessInfo.hProcess);
      CloseHandle(LProcessInfo.hThread);
    end
    else
      ARespJson := 'Fallo al iniciar proceso PHP: ' + SysErrorMessage(GetLastError);
  finally
    CloseHandle(LOutHandle);
    if LInHandle <> INVALID_HANDLE_VALUE then
      CloseHandle(LInHandle);
  end;

  if LCreatedOk then
  begin
    if FileExists(LRespFile) then
    begin
      LRespLines := TStringList.Create;
      try
        LRespLines.LoadFromFile(LRespFile, TEncoding.UTF8);
        ARespJson := Trim(LRespLines.Text);
        Result := Pos('"success":true', ARespJson) > 0;
        if (not Result) and (ARespJson = '') then
          ARespJson := Format('El proceso PHP finaliz'#243' con c'#243'digo %d sin devolver datos.', [LExitCode]);
      finally
        LRespLines.Free;
        DeleteFile(LRespFile);
      end;
    end
    else
      ARespJson := Format('El proceso emisor no gener'#243' archivo de respuesta (c'#243'digo %d).', [LExitCode]);
  end
  else
    DeleteFile(LRespFile);

  DeleteFile(LReqFile);
end;

function InyectarApiKeyEnJson(const APayloadJson, AApiKey: string): string;
var
  LTrimmed, LKeyPart: string;
begin
  LTrimmed := Trim(APayloadJson);
  if (Trim(AApiKey) = '') or (LTrimmed = '') or (LTrimmed[1] <> '{') then
  begin
    Result := APayloadJson;
    Exit;
  end;

  // Inyectar "api_key":"..." tras la primera llave de apertura
  LKeyPart := '"api_key":"' + Trim(AApiKey) + '",';
  Result := '{' + LKeyPart + Copy(LTrimmed, 2, Length(LTrimmed) - 1);
end;

function EmitirDocumentoDeca(const AConfig: TDecaConfig; const APayloadJson: string; out ARespJson: string): Boolean;
var
  LUrlEmitir, LPayloadConKey: string;
begin
  if AConfig.Modo = dmRemotoApi then
  begin
    LUrlEmitir := NormalizarUrlBase(AConfig.UrlServicio) + '/api/index.php?route=deca/emitir&api_key=' + Trim(AConfig.ApiKey);
    LPayloadConKey := InyectarApiKeyEnJson(APayloadJson, AConfig.ApiKey);
    Result := EnviarHttpRest(LUrlEmitir, LPayloadConKey, AConfig.ApiKey, AConfig.TimeoutSeg, ARespJson);
    if Result then
      Result := Pos('"success":true', ARespJson) > 0;
  end
  else
  begin
    LPayloadConKey := InyectarApiKeyEnJson(APayloadJson, AConfig.ApiKey);
    Result := EjecutarCliLocal(AConfig, LPayloadConKey, ARespJson);
  end;
end;

function ConsultarDocumentoDeca(const AConfig: TDecaConfig; const ACodigo, AReferencia: string; out ARespJson: string): Boolean;
var
  LUrl, LParamCod, LParamRef: string;
  LHttp: OleVariant;
  LStatus: Integer;
begin
  Result := False;
  ARespJson := '';

  if (Trim(ACodigo) = '') and (Trim(AReferencia) = '') then Exit;

  if AConfig.Modo = dmRemotoApi then
  begin
    LParamCod := StringReplace(Trim(ACodigo), ' ', '%20', [rfReplaceAll]);
    LParamRef := StringReplace(Trim(AReferencia), ' ', '%20', [rfReplaceAll]);
    LParamRef := StringReplace(LParamRef, '/', '%2F', [rfReplaceAll]);

    LUrl := NormalizarUrlBase(AConfig.UrlServicio) + '/api/index.php?route=deca/consultar&api_key=' + Trim(AConfig.ApiKey);
    if LParamCod <> '' then
      LUrl := LUrl + '&codigo=' + LParamCod;
    if LParamRef <> '' then
      LUrl := LUrl + '&referencia=' + LParamRef;

    try
      LHttp := CreateOleObject('WinHttp.WinHttpRequest.5.1');
      LHttp.Open('GET', LUrl, False);
      LHttp.SetTimeouts(4000, 6000, AConfig.TimeoutSeg * 1000, AConfig.TimeoutSeg * 1000);
      if Trim(AConfig.ApiKey) <> '' then
      begin
        LHttp.SetRequestHeader('X-API-Key', Trim(AConfig.ApiKey));
        LHttp.SetRequestHeader('Authorization', 'Bearer ' + Trim(AConfig.ApiKey));
      end;
      LHttp.Send();

      LStatus := LHttp.Status;
      ARespJson := Trim(LHttp.ResponseText);

      Result := (LStatus = 200) and (Pos('"success":true', ARespJson) > 0) and (Pos('"encontrado":true', ARespJson) > 0);
    except
      on E: Exception do
        ARespJson := 'Error de comunicaci'#243'n HTTPS: ' + E.Message;
    end;
  end;
end;

function ObtenerPresetsVehiculos(const AConfig: TDecaConfig; out ARespJson: string): Boolean;
var
  LUrl: string;
  LHttp: OleVariant;
  LStatus: Integer;
begin
  Result := False;
  ARespJson := '';
  LUrl := NormalizarUrlBase(AConfig.UrlServicio) + '/api/index.php?route=presets/vehiculos&api_key=' + Trim(AConfig.ApiKey);
  try
    LHttp := CreateOleObject('WinHttp.WinHttpRequest.5.1');
    LHttp.Open('GET', LUrl, False);
    LHttp.SetTimeouts(4000, 6000, AConfig.TimeoutSeg * 1000, AConfig.TimeoutSeg * 1000);
    if Trim(AConfig.ApiKey) <> '' then
    begin
      LHttp.SetRequestHeader('X-API-Key', Trim(AConfig.ApiKey));
      LHttp.SetRequestHeader('Authorization', 'Bearer ' + Trim(AConfig.ApiKey));
    end;
    LHttp.Send();
    LStatus := LHttp.Status;
    ARespJson := Trim(LHttp.ResponseText);
    Result := (LStatus = 200) and (Pos('"success":true', ARespJson) > 0);
  except
    on E: Exception do
      ARespJson := 'Error: ' + E.Message;
  end;
end;

function ObtenerPresetsContactos(const AConfig: TDecaConfig; const ATipo: string; out ARespJson: string): Boolean;
var
  LUrl: string;
  LHttp: OleVariant;
  LStatus: Integer;
begin
  Result := False;
  ARespJson := '';
  LUrl := NormalizarUrlBase(AConfig.UrlServicio) + '/api/index.php?route=presets/contactos&api_key=' + Trim(AConfig.ApiKey);
  if Trim(ATipo) <> '' then
    LUrl := LUrl + '&tipo=' + Trim(ATipo);
  try
    LHttp := CreateOleObject('WinHttp.WinHttpRequest.5.1');
    LHttp.Open('GET', LUrl, False);
    LHttp.SetTimeouts(4000, 6000, AConfig.TimeoutSeg * 1000, AConfig.TimeoutSeg * 1000);
    if Trim(AConfig.ApiKey) <> '' then
    begin
      LHttp.SetRequestHeader('X-API-Key', Trim(AConfig.ApiKey));
      LHttp.SetRequestHeader('Authorization', 'Bearer ' + Trim(AConfig.ApiKey));
    end;
    LHttp.Send();
    LStatus := LHttp.Status;
    ARespJson := Trim(LHttp.ResponseText);
    Result := (LStatus = 200) and (Pos('"success":true', ARespJson) > 0);
  except
    on E: Exception do
      ARespJson := 'Error: ' + E.Message;
  end;
end;

function GuardarPresetVehiculo(const AConfig: TDecaConfig; const APayloadJson: string): Boolean;
var
  LUrl, LResp: string;
begin
  LUrl := NormalizarUrlBase(AConfig.UrlServicio) + '/api/index.php?route=presets/vehiculos&api_key=' + Trim(AConfig.ApiKey);
  Result := EnviarHttpRest(LUrl, APayloadJson, AConfig.ApiKey, AConfig.TimeoutSeg, LResp);
end;

function GuardarPresetContacto(const AConfig: TDecaConfig; const APayloadJson: string): Boolean;
var
  LUrl, LResp: string;
begin
  LUrl := NormalizarUrlBase(AConfig.UrlServicio) + '/api/index.php?route=presets/contactos&api_key=' + Trim(AConfig.ApiKey);
  Result := EnviarHttpRest(LUrl, APayloadJson, AConfig.ApiKey, AConfig.TimeoutSeg, LResp);
end;

end.
