unit uPinPad;

interface

const
  ctIniFileName = 'tu_ficher_ini.ini';
  // en algún lado tenemos que guardar los datos de conexión al PinPad. En este caso lo hacemos en un .ini
  //  este .ini tiene que tener las siguientes entradas
  //  - TERMINAL: número de terminal
  //  - COMERCIO: número de comercio
  //  - ENCRIPTA: clave de encriptación proporcionada por RedSys
  //  - INICIALIZACION: datos de inicialización de la conexión (puerto, baudios, bits de parada,....  ejemplo: COM9:,19200,N,8,1)
  //  - VERSION: versión del firmware del PinPad (dato proporcionado por RedSys)
  // en mi caso lo tengo todo bajo un grupo llamado GLOBAL

type
  TPinPad = class
  public
    class procedure LoadLibrary;
    class procedure UnloadLibrary;
    class procedure InitializePinPad;
    class procedure UnloadPinpad;
    class function DoRefund(Sesion, Secuencia, Importe, NDoc: AnsiString): Integer; overload;
    class function DoRefund(Importe, NDoc: AnsiString): Integer; overload;
    class function DoConsulta(NDoc, Datai, Dataf, Acc: AnsiString): Integer;
    class function DoPayment(Importe, NDoc, Acc: AnsiString): Integer;
    class function GetHandle: THandle;
    class function GetInitialized: Boolean;
    class function GetWstr: string;
  end;

var
  Handle: THandle;
  Wstr: PAnsiChar;
  Tam: Integer;
  Initialized: Boolean;
  fnDllIniTpvpcLatente: function(cComercio, cTerminal, cClaveFirma, cConfPuerto, cVersion: AnsiString): Integer; stdcall; //safecall;
  fnDllParaTpvpcLatente: function(): Integer; stdcall;
  fnDllOperComContable: function(cNumPedido, cRTSOriginal, cImporte, cFactura, cTipoOper: AnsiString; cXMLResp: PAnsiChar; tam: Integer): Integer; stdcall;
  fnDllOperConsulta: function(cNumPedido, cRTS, cFactura, cFechaIni, cFechaFin, cTipo, cResultado, cNumPagina: AnsiString; cXMLResp: PAnsiChar; tam: Integer): Integer; stdcall;
  fnDllOperPinPad: function(cImporte, cFactura, cTipoOper: AnsiString; cXMLResp: PAnsiChar; tam: Integer): Integer; stdcall;
  fnDllDevSinOrigTrj: function(cImporte, cFactura: AnsiString; cXMLResp: PAnsiChar; tam: Integer): Integer; stdcall;

implementation

uses
  System.Classes, System.SysUtils, System.Variants, System.AnsiStrings, System.IniFiles,
  Winapi.Windows,
  Vcl.Dialogs;

{ TPinPad }

class procedure TPinPad.LoadLibrary;
begin
  try
    Tam := 10240;
    Wstr := PAnsiChar(StrAlloc(Tam + 1));
    Handle := Winapi.Windows.LoadLibrary(PChar(GetEnvironmentVariable('PROGRAMFILES') + '\TpvpcImplantado\dllTpvpcLatente.dll'));
  except
    on E: Exception do
      ShowMessage(E.Message);
  end;
  if Handle = 0 then
    ShowMessage('DLL "dllTpvpcLatente.dll" de PinPad no encontrada');
end;

class procedure TPinPad.InitializePinPad;
var
  Res: Integer;
  Comercio: AnsiString;
  Terminal: AnsiString;
  Encripta: AnsiString;
  Inicializacion: AnsiString;
  Version: AnsiString;
  Ini: TIniFile;
begin
  Initialized := False;

  if Handle = 0 then
  begin
    ShowMessage('DLL "dllTpvpcLatente.dll" de PinPad no encontrada');
    Exit;
  end;

  try
    @fnDllIniTpvpcLatente := GetProcAddress(Handle, 'fnDllIniTpvpcLatente');
  except
    on E: Exception do
      ShowMessage('Error en getProcAddress'#13 + E.Message);
  end;
  if @fnDllIniTpvpcLatente = nil then
  begin
    ShowMessage('La función "fnDllIniTpvpcLatente" de la DLL "dllTpvpcLatente.dll" no existe.');
    Exit;
  end;

  Ini := TIniFile.Create(ctIniFileName);
  try
    Terminal := AnsiString(Ini.ReadString('GLOBAL', 'TERMINAL', ''));
    Comercio := AnsiString(Ini.ReadString('GLOBAL', 'COMERCIO', ''));
    Encripta := AnsiString(Ini.ReadString('GLOBAL', 'ENCRIPTA', ''));
    Inicializacion := AnsiString(Ini.ReadString('GLOBAL', 'INICIALIZACION', ''));
    Version := AnsiString(Ini.ReadString('GLOBAL', 'VERSION', ''));
  finally
    Ini.Free;
  end;

  try
    Res := fnDllIniTpvpcLatente(Comercio, Terminal, Encripta, Inicializacion, Version);
  except
    on E: Exception do
    begin
      ShowMessage('Error inicializando PinPaD'#13 + E.Message);
      Exit;
    end;
  end;

  if Res <> 0 then
    ShowMessage('PinPad no inicializado')
  else
    Initialized := True;
end;

class procedure TPinPad.UnloadLibrary;
begin
  try
    if Assigned(Wstr) then
      System.AnsiStrings.StrDispose(Wstr);
    if Handle <> 0 then
      FreeLibrary(Handle);
  except
    on E: Exception do
      ShowMessage('Error UnloadLibrary: ' + E.Message);
  end;
end;

class procedure TPinPad.UnloadPinpad;
begin
  try
    @fnDllParaTpvpcLatente := getProcAddress(Handle, 'fnDllParaTpvpcLatente');
    if @fnDllParaTpvpcLatente <> nil then
      fnDllParaTpvpcLatente;
  except
    on E: Exception do
      ShowMessage('ERROR UnloadPinpad: ' + E.Message);
  end;
end;

class function TPinPad.GetHandle: THandle;
begin
  Result := Handle;
end;

class function TPinPad.GetInitialized: Boolean;
begin
  Result := Initialized;
end;

class function TPinPad.DoRefund(Sesion, Secuencia, Importe, NDoc: AnsiString): Integer;
begin
  Result := -999999999;
  @fnDllOperComContable := nil;
  try
    @fnDllOperComContable := GetProcAddress(Handle, 'fnDllOperComContable');
  except
    on E: Exception do
      ShowMessage('Error en la devolucion'#13 + E.Message);
  end;

  if @fnDllOperComContable = nil then
    Exit;

  try
    Result := fnDllOperComContable(Sesion, Secuencia, Importe, NDoc, 'DEVOLUCION', Wstr, Tam);
  except
    on E: Exception do
      ShowMessage('Error en la devolucion'#13 + E.Message);
  end;
end;

class function TPinPad.DoConsulta(NDoc, Datai, Dataf, Acc: AnsiString): Integer;
begin
  Result := 0;

  try
    @fnDllOperConsulta := GetProcAddress(Handle, 'fnDllOperConsulta');
    if @fnDllOperConsulta <> nil then
      Result := fnDllOperConsulta('', '', NDoc, Datai, Dataf, Acc, '', '', UPinPad.Wstr, UPinPad.Tam);
  except
    on E: Exception do
      ShowMessage('Error en consulta'#13 + E.Message);
  end;
end;

class function TPinPad.GetWstr: string;
begin
  Result := string(Wstr);
end;

class function TPinPad.DoPayment(Importe, NDoc, Acc: AnsiString): Integer;
begin
  Result := -999999999;
  try
    @fnDllOperPinPad := GetProcAddress(Handle, 'fnDllOperPinPad');
  except
    on E: Exception do
      ShowMessage('Error realizando cargo'#13 + E.Message);
  end;

  if @fnDllOperPinPad = nil then
    Exit;

  try
    Result := fnDllOperPinPad(Importe, NDoc, Acc, Wstr, Tam);
  except
    on E: Exception do
      ShowMessage('Error realizando cargo'#13 + E.Message);
  end;
end;

class function TPinPad.DoRefund(Importe, NDoc: AnsiString): Integer;
begin
  Result := -999999999;
  try
    @fnDllDevSinOrigTrj := GetProcAddress(Handle, 'fnDllDevSinOrigTrj');
  except
    on E: Exception do
      ShowMessage('Error realizando devolución'#13 + E.Message);
  end;

  if @fnDllDevSinOrigTrj = nil then
    Exit;

  try
    Result := fnDllDevSinOrigTrj(Importe, NDoc, Wstr, Tam);
  except
    on E: Exception do
      ShowMessage('Error realizando devolución'#13 + E.Message);
  end;
end;

end.
