unit ufuncional.gateway.sales;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics,
  Controls, Forms, Dialogs, Registry, ExtCtrls, System.JSON,
  uClasse.JSON.autenticacao,
  uClasse.JSON.Recuperacao,
  uClasse.JSON.Confirmacao;

type
  { -- Registro das Classes -- }
  TpbmFuncionalGatewaySales = class;
  TFuncionalTipoOperacao = (fncNulo, fncAutenticacao, fncRecuperacao, fncConfirmacao);
  TAmbiente = (ambHomologacao, ambProducao);

  TfsOnResposta = procedure(pMensagemOperador: AnsiString; Funcao: TFuncionalTipoOperacao; RetornoOK: Boolean) of object;

  { TIdentificacao }
  TIdentificacao = class(TPersistent)
  private
    Flogin_Senha:         string;
    Flogin_Nome:          string;
    Floja_CNPJ:           string;
    FAmbiente:            TAmbiente;
    FEndPointHomologacao: String;
    FEndPointProducao:    string;
    FToken:               WideString;
    procedure Setlogin_Nome(const Value: string);
    procedure Setlogin_Senha(const Value: string);
    procedure Setloja_CNPJ(const Value: string);
    procedure SetAmbiente(const Value: TAmbiente);
    procedure SetEndPointHomologacao(const Value: String);
    procedure SetEndPointProducao(const Value: string);
    procedure SetToken(const Value: WideString);
  protected
  public
    constructor Create;
    destructor Destroy; override;
  published
    property loja_CNPJ:           string read Floja_CNPJ write Setloja_CNPJ;
    property login_Nome:          string read Flogin_Nome write Setlogin_Nome;
    property login_Senha:         string read Flogin_Senha write Setlogin_Senha;
    property Ambiente:            TAmbiente read FAmbiente write SetAmbiente;
    property EndPointHomologacao: String read FEndPointHomologacao write SetEndPointHomologacao;
    property EndPointProducao:    string read FEndPointProducao write SetEndPointProducao;
    property Token:               WideString read FToken write SetToken;
  end;

  { -- Classe Principal do Componente -- }
  TpbmFuncionalGatewaySales = class(TComponent)
  private
    FAbout: String;
    FRefaz: Boolean;
    // Eventos
    FOnResposta:    TfsOnResposta;
    FIdentificacao: TIdentificacao;
    FAutorizacao:   uClasse.JSON.Recuperacao.TRootDTO;
    FConfirmacao:   uClasse.JSON.Confirmacao.TRootDTO;
    procedure SetAbout(const Value: String);
    procedure SetIdentificacao(const Value: TIdentificacao);
    procedure SetAutorizacao(const Value: uClasse.JSON.Recuperacao.TRootDTO);
    procedure SetConfirmacao(const Value: uClasse.JSON.Confirmacao.TRootDTO);
  protected
    function getExecutaQuery(const Query: string; Variables: TJSONObject): string;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    // Funções e procedures

    procedure autenticacao;
    procedure RecuperaAutorizacao(pCartao, pAutorizacao: string; pData: TDate);
    procedure ConfirmaAutorizacao(pCartao, pAutorizacao: string; pData: TDate);
  published
    // Eventos
    property OnResposta:    TfsOnResposta read FOnResposta write FOnResposta;
    Property About:         String read FAbout write SetAbout;
    property Identificacao: TIdentificacao read FIdentificacao write SetIdentificacao;
    property Autorizacao:   uClasse.JSON.Recuperacao.TRootDTO read FAutorizacao write SetAutorizacao;
    property Confirmacao:   uClasse.JSON.Confirmacao.TRootDTO read FConfirmacao write SetConfirmacao;

  end;

implementation

uses
  System.NetConsts,
  System.Net.HttpClient,
  System.Net.URLClient;

{ TpbmFuncionalGatewaySales }

procedure TpbmFuncionalGatewaySales.autenticacao;
var
  i:                integer;
  Query, AResponse: string;
  objJSONRootDTO:   uClasse.JSON.autenticacao.TJSONRootDTO;
  Retorno:          Boolean;
begin
  Query := 'mutation{createToken(login:\"' + Identificacao.login_Nome + '\", password:\"' + Identificacao.login_Senha + '\"){token}}';

  try
    AResponse             := getExecutaQuery(Query, nil);
    objJSONRootDTO        := TJSONRootDTO.Create;
    objJSONRootDTO.AsJson := AResponse;

    Identificacao.Token := objJSONRootDTO.Data.CreateToken.Token;
    objJSONRootDTO.Free;
    Retorno := True;
  except
    Retorno := False;
    i       := Pos('inválida', AResponse);
    if i > 0 then
    begin
      if Assigned(FOnResposta) then
        FOnResposta('Usuário ou Senha inválida', fncAutenticacao, False);
      Exit;
    end;
  end;

  if Assigned(FOnResposta) then
    FOnResposta('', fncAutenticacao, Retorno);

end;

procedure TpbmFuncionalGatewaySales.ConfirmaAutorizacao(pCartao, pAutorizacao: string; pData: TDate);
var
  AResponse: string;
  sResp:     AnsiString;
  oK:        Boolean;
  i:         integer;
  Query:     string;
begin
  sResp := '';
  oK    := True;

  try
    if Autorizacao.Data.Sales_Transaction.Products.Count <= 0 then
    begin
      FRefaz := True;
      RecuperaAutorizacao(pCartao, pAutorizacao, pData);
    end;
  except
    if Assigned(FOnResposta) then
      FOnResposta('Não foram adicionados produtos a lista de confirmação... Recupere novamente!', fncConfirmacao, False);
    Exit;
  end;

  try

    Query := Query + ' mutation{ Sales_confirmPreAuthorizedSale(' +
    '    storeCode: \"' + Identificacao.loja_CNPJ + '\"' +
    '    customerCode: \"' + pCartao + '\"' +
    '    authorizationID: \"' + pAutorizacao + '\" ' +
    '    products:' + '  [';

    for i := 0 to Autorizacao.Data.Sales_Transaction.Products.Count - 1 do
    begin

      Query := Query + '    {' +
                       '     ean: \"' + Autorizacao.Data.Sales_Transaction.Products.Items[i].Ean + '\",' +
                       '     unitPrice: ' + StringReplace(Autorizacao.Data.Sales_Transaction.Products.Items[i].UnitPrice.ToString, ',', '.', [rfReplaceAll]) + ',' +
                       '     maxConsumerPrice: ' + StringReplace(Autorizacao.Data.Sales_Transaction.Products.Items[i].MaxConsumerPrice.ToString, ',', '.',[rfReplaceAll]) + ',' +
                       '     quantity: ' + Autorizacao.Data.Sales_Transaction.Products.Items[i].Quantity.ToString + ',';

      if Autorizacao.Data.Sales_Transaction.Products.Items[i].MedicalPrescription <> nil then
      begin
        Query := Query + '        medicalPrescription: {' +
                         '           date: \"' + FormatDateTime('yyyy-mm-dd', Autorizacao.Data.Sales_Transaction.Products.Items[i].MedicalPrescription.Date) + '\"' +
                         '           quantity: '+ Autorizacao.Data.Sales_Transaction.Products.Items[i].MedicalPrescription.Quantity.ToString +
                         '           prescriber: {' +
                         '              council: ' + Autorizacao.Data.Sales_Transaction.Products.Items[i].MedicalPrescription.Prescriber.Council +
                         '              registerNumber: ' + Autorizacao.Data.Sales_Transaction.Products.Items[i].MedicalPrescription.Prescriber.RegisterNumber.ToString +
                         '              stateAbbr: \"' + Autorizacao.Data.Sales_Transaction.Products.Items[i].MedicalPrescription.Prescriber.StateAbbr + '\"' +
                         '        }' +
                         '      }';
        if i < Autorizacao.Data.Sales_Transaction.Products.Count - 1 then
          Query := Query + '    },'
        else
          Query := Query + '    }';
      end
      else
      begin
        if i < Autorizacao.Data.Sales_Transaction.Products.Count - 1 then
          Query := Query + '        medicalPrescription: null},'
        else
          Query := Query + '        medicalPrescription: null}';
      end;

    end;

    Query := Query + '  ]){' +
                     '    createdAt' +
                     '    authorizationID' +
                     '    totalValue' +
                     '    moneyPaidValue' +
                     '    cardPaidValue' +
                     '    cardBalance' +
                     '    products {' +
                     '      ean' +
                     '      description' +
                     '      quantity' +
                     '    }' +
                     '    status' +
                     '    statusCode' +
                     '    statusMessage' +
                     '    receipt' +
                     '  }' +
                     '}      ';

    try
      AResponse := getExecutaQuery(Query, nil);
      // '{"data":null,"errors":[{"message":"Token error: token_not_provided"}]}'
      if (Pos('ERROR', UpperCase(AResponse)) <= 0) then
      begin
        Confirmacao.AsJson := AResponse;
      end
      else
      begin
        sResp := AnsiString(AResponse);
        oK    := False;
      end;

    except
      on e: exception do
      begin
        sResp := AnsiString(e.Message);
        oK    := False;
      end;

    end;
  finally
    // Query.Free;
  end;

  if Assigned(FOnResposta) then
    FOnResposta(sResp, fncConfirmacao, oK);

end;

constructor TpbmFuncionalGatewaySales.Create(AOwner: TComponent);
begin
  inherited;
  FRefaz                            := False;
  FIdentificacao                    := TIdentificacao.Create();
  Identificacao.Ambiente            := ambHomologacao;
  Identificacao.loja_CNPJ           := '00000000000010';
  Identificacao.login_Nome          := 'edi@funcionalcorp.com.br';
  Identificacao.login_Senha         := '00000000000010';
  Identificacao.EndPointHomologacao := 'https://stores-hml.funcionalmais.com/graphql';
  Identificacao.EndPointProducao    := 'https://stores.funcionalmais.com/graphql';
  FAbout                            := 'Sobre';
  FAutorizacao                      := uClasse.JSON.Recuperacao.TRootDTO.Create;
  FConfirmacao                      := uClasse.JSON.Confirmacao.TRootDTO.Create;

  //
  // Recuperacao:= uClasse.json.Recuperacao.TRootDTO;

end;

destructor TpbmFuncionalGatewaySales.Destroy;
begin
  FreeAndNil(FIdentificacao);
  FreeAndNil(FAutorizacao);
  FreeAndNil(FConfirmacao);
  inherited;
end;

function TpbmFuncionalGatewaySales.getExecutaQuery(const Query: string; Variables: TJSONObject): string;
var
  sAuthorization, URLBase: String;
  ARequest:                String;
  objHTTP:                 THTTPClient;
  stRequest:               TStream;
  objResponse:             IHTTPResponse;
  _arq:TStringList;
begin

  if Identificacao.Ambiente = ambHomologacao then
    URLBase := Identificacao.EndPointHomologacao
  else
    URLBase := Identificacao.EndPointProducao;

  if Pos('createToken', Query) = 0 then
    sAuthorization := 'Bearer ' + Identificacao.Token;

  ARequest := Query;
  if Variables = nil then
    ARequest := '{"query": "' + Query + '" }'
  else
    ARequest := '{"query": "' + Query + '",' + '"variables": ' + Variables.ToString + ' }';

  try
    _arq := TStringList.Create;
    _arq.Add('URLBase : '+URLBase);
    _arq.Add('Query   : '+ARequest);
    _arq.SaveToFile( ExtractFilePath( Application.exeName) +'bkp_'+FormatDateTime('hhmmss',now)+'.log');
  finally
    _arq.Free;
  end;

  objHTTP := THTTPClient.Create;
  try
    objHTTP.ContentType := 'application/json';
    stRequest           := TStringStream.Create(ARequest);
    try
      if sAuthorization = '' then
        objResponse := objHTTP.Post(URLBase, stRequest, nil, nil)
      else
        objResponse := objHTTP.Post(URLBase, stRequest, nil, TNetHeaders.Create(TNameValuePair.Create('Authorization', sAuthorization)));
      Result        := objResponse.ContentAsString;
    finally
      stRequest.Free;
    end;
  finally
    objHTTP.Free;
  end;

end;

procedure TpbmFuncionalGatewaySales.RecuperaAutorizacao(pCartao, pAutorizacao: string; pData: TDate);
var
  Query, AResponse: string;
  sData:            string;
  sResp:            AnsiString;
  oK:               Boolean;
  iErros:           integer;

begin
  sResp := '';
  oK    := True;

  sData := FormatDateTime('yyyy-mm-dd', pData) + 'T' + FormatDateTime('hh:mm:ss', now) + 'Z';
  Query := 'query query_Sales_transact{' +
    '  Sales_transaction(storeCode: \"' + Trim(Identificacao.loja_CNPJ) + '\", ' +
    '                    customerCode: \"' + pCartao + '\", ' +
    '                    authorizationID: \"' + pAutorizacao + '\", ' +
    '                    createdAt: \"' + sData + '\")' +
    '   {' +
    '    createdAt' +
    '    authorizationID' +
    '    totalValue' +
    '    moneyPaidValue' +
    '    cardPaidValue' +
    '    products {' +
    '      ean' +
    '      description' +
    '      maxConsumerPrice' +
    '      unitPrice' +
    '      quantity' +
    '      medicalPrescription {' +
    '        date' +
    '        quantity' +
    '        prescriber {' +
    '          council' +
    '          registerNumber' +
    '          stateAbbr' +
    '        }' +
    '      }' +
    '      status' +
    '      statusCode' +
    '      statusMessage' +
    '    }' +
    '  }' +
    '}';

  try
    AResponse := getExecutaQuery(Query, nil);

    // '{"data":{"Sales_transaction":{"createdAt":null,"authorizationID":"23317","totalValue":null,"moneyPaidValue":null,"cardPaidValue":null,"products":[]}}}'#$A
    // '{"data":null,"errors":[{"message":"Token error: token_not_provided"}]}'
    iErros := Pos('ERROR', UpperCase(AResponse));
    iErros := iErros + Pos('TIMING OUT', UpperCase(AResponse));
    iErros := iErros + Pos('"createdAt":null', AResponse);

    // if (pos('ERROR', UpperCase(AResponse)) <= 0) and (pos('TIMING OUT', UpperCase(AResponse)) <= 0) and (pos('"createdAt":null', AResponse)<=0)  then
    if iErros <= 0 then
    begin
      Autorizacao.AsJson := AResponse;
    end
    else
    begin
      sResp := AResponse;
      oK    := False;
    end;

  except
    on e: exception do
    begin
      sResp := e.Message;
      oK    := False;
    end;

  end;

  if Assigned(FOnResposta) then
    if not FRefaz then
      FOnResposta(sResp, fncRecuperacao, oK);

  FRefaz := False;
end;

procedure TpbmFuncionalGatewaySales.SetAbout(const Value: String);
begin
  FAbout := 'Sobre';
end;

procedure TpbmFuncionalGatewaySales.SetAutorizacao(const Value: uClasse.JSON.Recuperacao.TRootDTO);
begin
  FAutorizacao := Value;
end;

procedure TpbmFuncionalGatewaySales.SetConfirmacao(const Value: uClasse.JSON.Confirmacao.TRootDTO);
begin
  FConfirmacao := Value;
end;

procedure TpbmFuncionalGatewaySales.SetIdentificacao(const Value: TIdentificacao);
begin
  FIdentificacao := Value;
end;

{ TAutenticacao }

constructor TIdentificacao.Create;
begin

end;

destructor TIdentificacao.Destroy;
begin

  inherited;
end;

procedure TIdentificacao.SetAmbiente(const Value: TAmbiente);
begin
  FAmbiente := Value;
end;

procedure TIdentificacao.SetEndPointHomologacao(const Value: String);
begin
  FEndPointHomologacao := Value;
end;

procedure TIdentificacao.SetEndPointProducao(const Value: string);
begin
  FEndPointProducao := Value;
end;

procedure TIdentificacao.Setlogin_Nome(const Value: string);
begin
  Flogin_Nome := Value;
end;

procedure TIdentificacao.Setlogin_Senha(const Value: string);
begin
  Flogin_Senha := Value;
end;

procedure TIdentificacao.Setloja_CNPJ(const Value: string);
begin
  Floja_CNPJ := Value;
end;

procedure TIdentificacao.SetToken(const Value: WideString);
begin
  FToken := Value;
end;

end.
