├── .coveralls.yml ├── .travis.yml ├── .gitignore ├── phpunit.xml ├── composer.json ├── src ├── PropriedadesExportaveisParaArrayInterface.php ├── PropriedadesImportaveisPorXMLInterface.php ├── LeitorSimplesXML.php ├── Boleto.php ├── Convenio.php ├── config.ini ├── Util.php ├── Ticket.php ├── ComunicadorCurlSOAP.php ├── Pagador.php ├── Config.php ├── Titulo.php ├── BoletoSantanderServico.php └── InstrucoesDeTitulo.php ├── tests ├── ConvenioTest.php ├── ConfigTest.php ├── ComunicadorCurlSOAPTest.php ├── TicketTest.php ├── BoletoTest.php ├── UtilTest.php ├── PagadorTest.php ├── TituloTest.php ├── InstrucoesDeTituloTest.php └── BoletoSantanderServicoTest.php ├── README.md └── LICENSE /.coveralls.yml: -------------------------------------------------------------------------------- 1 | coverage_clover: logs/clover.xml 2 | json_path: logs/coveralls-upload.json 3 | service_name: travis-ci 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: php 3 | php: 4 | - '5.6' 5 | - '7.0' 6 | - '7.1' 7 | - hhvm 8 | install: composer install 9 | after_script: 10 | - php vendor/bin/php-coveralls -v 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | /vendor/ 3 | 4 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file 5 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 6 | # composer.lock 7 | 8 | # Ignorando a pasta gerada pelos logs do PHPUnit 9 | /logs/ 10 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | src/ 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tiexpert/ws-boleto-santander", 3 | "description": "WS Boletos Santander são classes criadas para facilitar a integração entre aplicativos feitos em PHP e a geração de boletos online no banco Santander.", 4 | "homepage": "https://github.com/DenysXavier/WSBoletoSantander", 5 | "type": "library", 6 | "license": "Apache-2.0", 7 | "authors": [ 8 | { 9 | "name": "Denys Xavier", 10 | "email": "equipe@tiexpert.net", 11 | "homepage": "http://www.tiexpert.net" 12 | } 13 | ], 14 | "support": { 15 | "issues": "https://github.com/DenysXavier/WSBoletoSantander/issues", 16 | "source": "https://github.com/DenysXavier/WSBoletoSantander" 17 | }, 18 | "require": { 19 | "php": ">=5.6", 20 | "ext-dom": "*", 21 | "ext-xmlwriter": "*", 22 | "ext-curl": "*" 23 | }, 24 | "require-dev": { 25 | "fzaninotto/faker": "1.6.*", 26 | "phpunit/phpunit": "5.6.*", 27 | "php-coveralls/php-coveralls": "^2.1" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "TIExpert\\WSBoletoSantander\\": "src/" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/PropriedadesExportaveisParaArrayInterface.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | interface PropriedadesExportaveisParaArrayInterface { 29 | 30 | /** Exporta um array associativo no qual as chaves são as propriedades representadas como no WebService do Santander 31 | * 32 | * @return array 33 | */ 34 | public function exportarArray(); 35 | } 36 | -------------------------------------------------------------------------------- /src/PropriedadesImportaveisPorXMLInterface.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | interface PropriedadesImportaveisPorXMLInterface { 29 | 30 | /** Carrega as propriedades da instância da classe que implementa esta interface usando a estrutura XML 31 | * 32 | * @param \DOMDocument $xml Estrutura XML legível 33 | */ 34 | public function carregarPorXML(\DOMDocument $xml); 35 | } 36 | -------------------------------------------------------------------------------- /src/LeitorSimplesXML.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class LeitorSimplesXML { 27 | 28 | /** property \DOMDocument $xml Referência ao objeto contendo toda a estrutura XML a ser lida. */ 29 | private $xml; 30 | 31 | /** Cria uma nova instância de LeitorSimplesXML 32 | * 33 | * @param \DOMDocument $xml Objeto contendo toda estrutura XML a ser lida 34 | */ 35 | public function __construct(\DOMDocument $xml) { 36 | $this->xml = $xml; 37 | } 38 | 39 | /** Obtém o valor do primeiro nó com o nome informado 40 | * 41 | * @param string $tagName Nome do nó a ser pesquisado 42 | * @return string 43 | * @throws \Exception 44 | */ 45 | public function getValorNo($tagName) { 46 | try { 47 | return $this->xml->getElementsByTagName($tagName)->item(0)->nodeValue; 48 | } catch (\Exception $e) { 49 | throw $e; 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /tests/ConvenioTest.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | class ConvenioTest extends \PHPUnit_Framework_TestCase { 28 | 29 | private $convenioObj; 30 | 31 | /** 32 | * @author Denys Xavier 33 | * @test 34 | */ 35 | public function seConvenioForInstanciadoComParametrosNulosEntaoSuasPropriedadesDevemSerValoresPadroesDeConfiguracao() { 36 | $this->convenioObj = new Convenio(NULL, NULL); 37 | $config = Config::getInstance(); 38 | 39 | $this->assertEquals($config->getConvenio("banco_padrao"), $this->convenioObj->getCodigoBanco()); 40 | $this->assertEquals($config->getConvenio("convenio_padrao"), $this->convenioObj->getCodigoConvenio()); 41 | } 42 | 43 | /** 44 | * @author Denys Xavier 45 | * @test 46 | */ 47 | public function testeAcessorDaPropriedadeCodigoBanco() { 48 | $validParam = "0033"; 49 | 50 | $this->convenioObj = new Convenio("CÓDIGO ERRADO", NULL); 51 | $this->convenioObj->setCodigoBanco($validParam); 52 | $this->assertEquals($validParam, $this->convenioObj->getCodigoBanco()); 53 | } 54 | 55 | /** 56 | * @author Denys Xavier 57 | * @test 58 | */ 59 | public function testeAcessorDaPropriedadeCodigoConvenio() { 60 | $validParam = 123456; 61 | 62 | $this->convenioObj = new Convenio(NULL, "CONVÊNIO ERRADO"); 63 | $this->convenioObj->setCodigoConvenio($validParam); 64 | $this->assertEquals($validParam, $this->convenioObj->getCodigoConvenio()); 65 | } 66 | 67 | /** 68 | * @author Denys Xavier 69 | * @test 70 | */ 71 | public function oArrayExportadoDevePossuirAsMesmasChavesUtilizadasPeloWSdoBanco(){ 72 | $chaveConvenioBanco = array("CONVENIO.COD-BANCO", "CONVENIO.COD-CONVENIO"); 73 | 74 | $this->convenioObj = new Convenio(); 75 | $export = $this->convenioObj->exportarArray(); 76 | 77 | foreach ($chaveConvenioBanco as $chave) { 78 | $this->assertArrayHasKey($chave, $export); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Boleto.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class Boleto implements PropriedadesExportaveisParaArrayInterface, PropriedadesImportaveisPorXMLInterface { 27 | 28 | private $convenio; 29 | private $pagador; 30 | private $titulo; 31 | 32 | public function __construct(Convenio $convenio = NULL, Pagador $pagador = NULL, Titulo $titulo = NULL) { 33 | $this->convenio = $convenio; 34 | $this->pagador = $pagador; 35 | $this->titulo = $titulo; 36 | } 37 | 38 | public function getConvenio() { 39 | return $this->convenio; 40 | } 41 | 42 | public function getPagador() { 43 | return $this->pagador; 44 | } 45 | 46 | public function getTitulo() { 47 | return $this->titulo; 48 | } 49 | 50 | public function setConvenio(Convenio $convenio) { 51 | $this->convenio = $convenio; 52 | return $this; 53 | } 54 | 55 | public function setPagador(Pagador $pagador) { 56 | $this->pagador = $pagador; 57 | return $this; 58 | } 59 | 60 | public function setTitulo(Titulo $titulo) { 61 | $this->titulo = $titulo; 62 | return $this; 63 | } 64 | 65 | /** Exporta um array associativo no qual as chaves são as propriedades representadas como no WebService do Santander 66 | * 67 | * @return array 68 | */ 69 | public function exportarArray() { 70 | return array_merge($this->convenio->exportarArray(), $this->pagador->exportarArray(), $this->titulo->exportarArray(), $this->titulo->getInstrucoes()->exportarArray()); 71 | } 72 | 73 | /** Carrega as propriedades da instância usando a estrutura XML 74 | * 75 | * @param \DOMDocument $xml Estrutura XML legível 76 | */ 77 | public function carregarPorXML(\DOMDocument $xml) { 78 | $convenio = new Convenio(); 79 | $convenio->carregarPorXML($xml); 80 | $this->convenio = $convenio; 81 | 82 | $pagador = new Pagador(); 83 | $pagador->carregarPorXML($xml); 84 | $this->pagador = $pagador; 85 | 86 | $titulo = new Titulo(); 87 | $titulo->carregarPorXML($xml); 88 | $this->titulo = $titulo; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /tests/ConfigTest.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class ConfigTest extends \PHPUnit_Framework_TestCase { 27 | 28 | private static $caminhoArquivoINI = "./config.ini"; 29 | private $caminhoPadraoArquivoINI = "src/config.ini"; 30 | private $configObj; 31 | 32 | public static function setUpBeforeClass() { 33 | parent::setUpBeforeClass(); 34 | if (file_exists(self::$caminhoArquivoINI)) { 35 | unlink(self::$caminhoArquivoINI); 36 | } 37 | } 38 | 39 | public static function tearDownAfterClass() { 40 | parent::tearDownAfterClass(); 41 | unlink(self::$caminhoArquivoINI); 42 | } 43 | 44 | /** 45 | * @author Denys Xavier 46 | * @test 47 | */ 48 | public function oCaminhoDoArquivoDeConfiguracaoPadraoFicaNaRaiz() { 49 | $this->assertStringEndsWith($this->caminhoPadraoArquivoINI, Config::getCaminhoArquivoConfig()); 50 | } 51 | 52 | /** 53 | * @author Denys Xavier 54 | * @test 55 | */ 56 | public function seNaoExistirArquivoDeConfiguracaoNoPrimeiroAcessoDaClasseConfigEntaoUmDeveSerCriado() { 57 | Config::setCaminhoArquivoConfig(self::$caminhoArquivoINI); 58 | $this->configObj = Config::getInstance(); 59 | 60 | $this->assertFileExists(self::$caminhoArquivoINI); 61 | } 62 | 63 | /** 64 | * @author Denys Xavier 65 | * @test 66 | */ 67 | public function aoAlterarCaminhoDoArquivoDeConfiguracaoONovoArquivoDeveSerCarregado() { 68 | $caminhoArquivoConfigTemp = "./configTemp.ini"; 69 | 70 | $this->configObj = Config::getInstance(); 71 | 72 | try { 73 | $this->configObj->getOpcao("grupoTeste1", "chaveTeste1"); 74 | } catch (\InvalidArgumentException $e) { 75 | $handler = fopen($caminhoArquivoConfigTemp, "w"); 76 | fwrite($handler, "[grupoTeste1]" . PHP_EOL . "chaveTeste1=1234567890"); 77 | fclose($handler); 78 | 79 | Config::setCaminhoArquivoConfig($caminhoArquivoConfigTemp); 80 | 81 | $this->assertEquals("1234567890", $this->configObj->getOpcao("grupoTeste1", "chaveTeste1")); 82 | 83 | unlink($caminhoArquivoConfigTemp); 84 | Config::setCaminhoArquivoConfig(self::$caminhoArquivoINI); 85 | return; 86 | } 87 | 88 | $this->fail("Uma exceção deveria ser lançada para opções que não tem grupo nem chaves existentes."); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/Convenio.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class Convenio implements PropriedadesExportaveisParaArrayInterface, PropriedadesImportaveisPorXMLInterface { 27 | 28 | /** property string $codigoBanco Código do banco */ 29 | private $codigoBanco; 30 | 31 | /** property string $codigoConvenio Número do convênio informado pelo banco */ 32 | private $codigoConvenio; 33 | 34 | /** Cria uma nova instância de Convenio. 35 | * 36 | * Se nenhum parâmetro for informado, então, o código do banco e o convênio são carregados a partir da configuração do arquivo config.ini 37 | * 38 | * @param string $codigoBanco Código do banco 39 | * @param string $codigoConvenio Número do convênio informado pelo banco 40 | */ 41 | public function __construct($codigoBanco = NULL, $codigoConvenio = NULL) { 42 | $this->codigoBanco = $codigoBanco; 43 | $this->codigoConvenio = $codigoConvenio; 44 | 45 | if (is_null($codigoBanco) && is_null($codigoConvenio)) { 46 | $config = Config::getInstance(); 47 | $this->codigoBanco = $config->getConvenio("banco_padrao"); 48 | $this->codigoConvenio = $config->getConvenio("convenio_padrao"); 49 | } 50 | } 51 | 52 | /** Obtém o código do banco 53 | * 54 | * @return string 55 | */ 56 | public function getCodigoBanco() { 57 | return $this->codigoBanco; 58 | } 59 | 60 | /** Obtém o número do convênio 61 | * 62 | * @return string 63 | */ 64 | public function getCodigoConvenio() { 65 | return $this->codigoConvenio; 66 | } 67 | 68 | /** Determina o código do banco 69 | * 70 | * @param string $codigoBanco Código do banco 71 | * @return \TIExpert\WSBoletoSantander\Convenio 72 | */ 73 | public function setCodigoBanco($codigoBanco) { 74 | $this->codigoBanco = $codigoBanco; 75 | return $this; 76 | } 77 | 78 | /** Determina o número do convênio 79 | * 80 | * @param string $codigoConvenio 81 | * @return \TIExpert\WSBoletoSantander\Convenio 82 | */ 83 | public function setCodigoConvenio($codigoConvenio) { 84 | $this->codigoConvenio = $codigoConvenio; 85 | return $this; 86 | } 87 | 88 | /** Exporta um array associativo no qual as chaves são as propriedades representadas como no WebService do Santander 89 | * 90 | * @return array 91 | */ 92 | public function exportarArray() { 93 | $array["CONVENIO.COD-BANCO"] = $this->codigoBanco; 94 | $array["CONVENIO.COD-CONVENIO"] = $this->codigoConvenio; 95 | 96 | return $array; 97 | } 98 | 99 | /** Carrega as propriedades da instância usando a estrutura XML 100 | * 101 | * @param \DOMDocument $xml Estrutura XML legível 102 | */ 103 | public function carregarPorXML(\DOMDocument $xml) { 104 | $leitor = new LeitorSimplesXML($xml); 105 | 106 | $this->setCodigoBanco($leitor->getValorNo("codBanco")); 107 | $this->setCodigoConvenio($leitor->getValorNo("codConv")); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /tests/ComunicadorCurlSOAPTest.php: -------------------------------------------------------------------------------- 1 | 32 | * @test 33 | */ 34 | public function metodoGetCurlConfiguracaoArrayDeveVirComAsConfiguracoesMinimasParaFazerUmChamado() { 35 | $itensNecessarios = array(CURLOPT_TIMEOUT, CURLOPT_RETURNTRANSFER, CURLOPT_POST, CURLOPT_POSTFIELDS, CURLOPT_HTTPHEADER, CURLOPT_SSL_VERIFYPEER, CURLOPT_SSL_VERIFYHOST); 36 | 37 | $configResultante = self::$comunicadorCurlSoap->prepararConfiguracaoEndpoint(); 38 | 39 | foreach ($itensNecessarios as $chave) { 40 | $this->assertArrayHasKey($chave, $configResultante); 41 | } 42 | } 43 | 44 | /** 45 | * @author Denys Xavier 46 | * @test 47 | * @expectedException \InvalidArgumentException 48 | */ 49 | public function umaExceptionDeveSerLancadaAoTentarConverterSOAPFaultStringParaExceptionUmSOAPFaultQueNaoEhString() { 50 | $SOAPFaultXML = ' 51 | 52 | SOAP-1_2-ENV:Sender 53 | 54 | 55 | There was an error in the incoming SOAP request packet: Sender, InvalidXml 56 | 57 | '; 58 | 59 | self::$comunicadorCurlSoap->converterSOAPFaultStringParaException($SOAPFaultXML); 60 | } 61 | 62 | /** 63 | * @author Denys Xavier 64 | * @test 65 | */ 66 | public function umaSOAPFaultStringPodeSerConvertidaParaException() { 67 | $faultMessage = 'java.lang.StringIndexOutOfBoundsException'; 68 | $SoapFaultString = 'faultcode=&faultstring=' . $faultMessage . '&detail='; 69 | 70 | $excecao = self::$comunicadorCurlSoap->converterSOAPFaultStringParaException($SoapFaultString); 71 | 72 | $this->assertEquals($faultMessage, $excecao->getMessage()); 73 | } 74 | 75 | /** 76 | * @author Denys Xavier 77 | * @test 78 | */ 79 | public function chamarQualquerURLValidaDeveRetornarUmaResposta() { 80 | // Google.com será usado como parâmetro de URL sempre válida 81 | $endpointConfig = array(CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true); 82 | $resposta = self::$comunicadorCurlSoap->chamar("http://google.com", $endpointConfig); 83 | 84 | $this->assertContains(" 89 | * @test 90 | * @expectedException \Exception 91 | */ 92 | public function chamarQualquerURLInvalidaDeveLancarUmaExcecao() { 93 | $endpointConfig = array(CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 1); 94 | self::$comunicadorCurlSoap->chamar("testedeurlinvalida", $endpointConfig); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/config.ini: -------------------------------------------------------------------------------- 1 | ; Copyright 2016 Denys. 2 | ; 3 | ; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ; you may not use this file except in compliance with the License. 5 | ; You may obtain a copy of the License at 6 | ; 7 | ; http://www.apache.org/licenses/LICENSE-2.0 8 | ; 9 | ; Unless required by applicable law or agreed to in writing, software 10 | ; distributed under the License is distributed on an "AS IS" BASIS, 11 | ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ; See the License for the specific language governing permissions and 13 | ; limitations under the License. 14 | 15 | ; --------------------------------------------- 16 | ; Arquivo de configurações do WSBoletoSantander 17 | ; --------------------------------------------- 18 | 19 | ; Informações relacionadas ao Convênio com o Banco 20 | [convenio] 21 | 22 | ; Número padrão do banco Santander frente ao Banco Central do Brasil 23 | ; http://www.bcb.gov.br/pom/spb/estatistica/port/ASTR003.pdf 24 | ; Usar 4 caracteres, completando com zeros à esquerda 25 | banco_padrao = "0033" 26 | 27 | ; Número do convênio informado pelo Banco 28 | convenio_padrao = "" 29 | 30 | ; Informações relacionadas às instruções do boleto destinadas ao banco 31 | [instrucoes_banco] 32 | 33 | ; Tipo de desconto padrão a ser aplicado pelo banco 34 | ; 0 = isento 35 | ; 1 = Valor fixo até a data informada 36 | ; 2 = Percentual até a data informada 37 | ; 3 = Valor por antecipação dia corrido 38 | tipo_desconto = 0 39 | 40 | ; Valor ou percentual de desconto padrão, com 2 casas decimais 41 | valor_desconto = 0 42 | 43 | ; Data limite padrão para o desconto 44 | ; A data deve seguir o mesmo formato do parâmetro [geral]/formato_data 45 | ; Para determinar que a data limite para para desconto seja o dia atual, use a string "now" 46 | data_limite_desconto = "now" 47 | 48 | ; Tipo de protesto padrão a ser executado pelo banco 49 | ; 0 = Nao Protestar 50 | ; 1 = Protestar dias corridos 51 | ; 2 = Protestar dias úteis 52 | ; 3 = Utilizar Perfil Cedente 53 | tipo_protesto = 0 54 | 55 | ; Quantidade padrão de dias após o vencimento para o protesto ser executado 56 | baixar_apos = 0 57 | 58 | ; Informações gerais sobre o ambiente de execução 59 | [geral] 60 | 61 | ; Garantir que o endpoint seja realmente o Santander 62 | ; 0 = Não 63 | ; 1 = Sim 64 | ; ATENÇÃO: Apenas use 0 em ambiente de desenvolvimento para evitar ataques como, por exemplo, man-in-the-middle. 65 | ; https://pt.wikipedia.org/wiki/Ataque_man-in-the-middle 66 | assegurar_endpoint = 1 67 | 68 | ; Tempo de espera por resposta do serviço 69 | timeout = 30 70 | 71 | ; Tipo de ambiente do banco a ser usado 72 | ; P = Produção 73 | ; T = Teste 74 | ambiente = "T" 75 | 76 | ; Formato de data padrão a ser usado na interpretação e comunicação com o serviço 77 | formato_data = "dmY" 78 | 79 | ; Código da Estação gerado pelo Banco Santander 80 | estacao = "" 81 | 82 | ; Informações relacionadas aos certificados digitais usados 83 | [certificado] 84 | 85 | ; Caminho absoluto para o arquivo do certificado digital da máquina que irá se comunicar com o WebService 86 | ; Ex.: /var/www/html/meu_cert.pem 87 | arquivo = "" 88 | 89 | ; Senha do certificado digital informado no parâmetro [certificado]/arquivo 90 | senha = "" 91 | 92 | ; Formato do certificado digital informado no parâmetro [certificado]/arquivo 93 | ; Os tipos de certificados suportados são: 94 | ; - PEM 95 | ; - DER 96 | ; - ENG 97 | tipo_arquivo = "PEM" 98 | 99 | ; Caminho absoluto para o diretório contendo os certificados das autoridades certificadoras (CA) 100 | ; Se nada for indicado, será usado o diretório padrão configurado no cURL do PHP. 101 | diretorio_cas = "" 102 | 103 | ; Caminho absoluto para o arquivo contendo um ou mais certificados da cadeia de certificação do peer 104 | ; Se seu sistema não tiver o certificado da autoridade certificadora (CA) do endpoint do banco Santander 105 | ; será necessário baixá-lo e indicar seu caminho neste parâmetro 106 | ; Ex.: /var/www/html/entrust_g2_ca.cer 107 | arquivo_ca = "" 108 | -------------------------------------------------------------------------------- /src/Util.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class Util { 26 | 27 | /** Tenta converter um parâmetro para um objeto \DateTime. Se o parâmetro for um objeto DateTime ou NULL, o próprio parâmetro é retornado. 28 | * 29 | * @param mixed $param Parâmetro que será convertido 30 | * @return \DateTime 31 | * @throws \Exception 32 | * @throws \InvalidArgumentException 33 | */ 34 | public static function converterParaDateTime($param) { 35 | try { 36 | if ($param == "now") { 37 | return new \DateTime(); 38 | } else if (is_string($param)) { 39 | return self::converterParaDateTimeUmaString($param); 40 | } else if ($param instanceof \DateTime || is_null($param)) { 41 | return $param; 42 | } 43 | 44 | throw new \InvalidArgumentException("Não é possível converter parâmetro " . gettype($param) . " para uma data válida."); 45 | } catch (\Exception $ex) { 46 | throw $ex; 47 | } 48 | } 49 | 50 | /** Tenta converter um parâmetro do tipo string para um objeto \DateTime. 51 | * 52 | * @param string $param String que será convertida 53 | * @return \DateTime 54 | * @throws \InvalidArgumentException 55 | */ 56 | private static function converterParaDateTimeUmaString($param) { 57 | $formato_data = Config::getInstance()->getGeral("formato_data"); 58 | $data = \DateTime::createFromFormat($formato_data, $param); 59 | 60 | if (!$data) { 61 | throw new \InvalidArgumentException("Não é possível converter '" . $param . "' em uma data válida."); 62 | } 63 | 64 | return $data; 65 | } 66 | 67 | /** Formata um número em uma string específica baseada em sua precisão. 68 | * 69 | * Este método diferencia-se da função number_format nativa do PHP por não arredondar o número. 70 | * 71 | * @param float $numero Número a ser formatado 72 | * @param int $precisao Quantidade de casas decimais 73 | * @param string $separadorDecimal Caracter usado como separador decimal 74 | * @param string $separadorMilhar Caracter usado como separador de milhar 75 | */ 76 | public static function formatarNumero($numero, $precisao = 0, $separadorDecimal = '.', $separadorMilhar = '') { 77 | $partes = explode('.', $numero); 78 | 79 | $numeroFormatado = self::agruparMilhares($partes[0], $separadorMilhar); 80 | 81 | if ($precisao > 0) { 82 | if (!isset($partes[1])) { 83 | $partes[1] = '0'; 84 | } 85 | 86 | $parteFracionadaFormatada = str_pad($partes[1], $precisao, '0', STR_PAD_RIGHT); 87 | $numeroFormatado .= $separadorDecimal . substr($parteFracionadaFormatada, 0, $precisao); 88 | } 89 | 90 | return $numeroFormatado; 91 | } 92 | 93 | /** Formata um número inteiro como uma string separando grupos de 3 algarismos com algum caracter especificado 94 | * 95 | * @param int $numero Número inteiro a ser formatado 96 | * @param string $caracterDeAgrupamento Caracter a ser usado para marcar o agrupamento dos número 97 | */ 98 | private static function agruparMilhares($numero, $caracterDeAgrupamento = '') { 99 | $algarismosInvertidos = strrev($numero); 100 | $grupoAlgarismosInvertidos = str_split($algarismosInvertidos, 3); 101 | $numeroFormatadoInvertido = implode($caracterDeAgrupamento, $grupoAlgarismosInvertidos); 102 | 103 | return strrev($numeroFormatadoInvertido); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /tests/TicketTest.php: -------------------------------------------------------------------------------- 1 | 35 | * @test 36 | */ 37 | public function testeConstrutorPadrao() { 38 | $obj = new Ticket(); 39 | 40 | $dtTeste = new \DateTime(); 41 | 42 | $this->assertEquals($dtTeste, $obj->getData(), "", 3600); 43 | $this->assertEquals(Config::getInstance()->getGeral("ambiente"), $obj->getAmbiente()); 44 | $this->assertEquals(Config::getInstance()->getGeral("estacao"), $obj->getEstacao()); 45 | } 46 | 47 | /** 48 | * @author Denys Xavier 49 | * @test 50 | */ 51 | public function testeAcessorDaPropriedadeNsuQuandoOAmbienteForTeste() { 52 | $validParam = mt_rand(1000000000, 9999999999); 53 | 54 | self::$ticket->setAmbiente("T"); 55 | self::$ticket->setNsu($validParam); 56 | 57 | $validParam = "TST" . $validParam; 58 | 59 | $this->assertEquals($validParam, self::$ticket->getNsu()); 60 | } 61 | 62 | /** 63 | * @author Denys Xavier 64 | * @test 65 | */ 66 | public function testeAcessorDaPropriedadeNsuQuandoOAmbienteForProducao() { 67 | $validParam = mt_rand(1000000000, 9999999999); 68 | 69 | self::$ticket->setAmbiente("P"); 70 | self::$ticket->setNsu($validParam); 71 | 72 | $this->assertEquals($validParam, self::$ticket->getNsu()); 73 | } 74 | 75 | /** 76 | * @author Denys Xavier 77 | * @test 78 | */ 79 | public function testeAcessorDaPropriedadeData() { 80 | $validParam = new \DateTime(); 81 | 82 | self::$ticket->setData($validParam); 83 | 84 | $this->assertEquals($validParam, self::$ticket->getData()); 85 | } 86 | 87 | /** 88 | * @author Denys Xavier 89 | * @test 90 | */ 91 | public function testeAcessorDaPropriedadeAmbiente() { 92 | $validParam = "P"; 93 | 94 | self::$ticket->setAmbiente($validParam); 95 | 96 | $this->assertEquals($validParam, self::$ticket->getAmbiente()); 97 | } 98 | 99 | /** 100 | * @author Denys Xavier 101 | * @test 102 | */ 103 | public function testeAcessorDaPropriedadeEstacao() { 104 | $validParam = self::$faker->regexify('[A-Z0-9]{5}'); 105 | 106 | self::$ticket->setEstacao($validParam); 107 | 108 | $this->assertEquals($validParam, self::$ticket->getEstacao()); 109 | } 110 | 111 | /** 112 | * @author Denys Xavier 113 | * @test 114 | */ 115 | public function testeAcessorDaPropriedadeAutenticacao() { 116 | $validParam = self::$faker->regexify('[A-Za-z0-9/=]{100}'); 117 | 118 | self::$ticket->setAutenticacao($validParam); 119 | 120 | $this->assertEquals($validParam, self::$ticket->getAutenticacao()); 121 | } 122 | 123 | /** 124 | * @author Denys Xavier 125 | * @test 126 | */ 127 | public function aClasseDeveSerExportavelParaXmlDeAcordoComADocumentacao() { 128 | $xmlEstrutura = array("nsu" => self::$ticket->getNsu(), 129 | "dtNsu" => self::$ticket->getData()->format(Config::getInstance()->getGeral("formato_data")), 130 | "tpAmbiente" => self::$ticket->getAmbiente(), 131 | "estacao" => self::$ticket->getEstacao(), 132 | "ticket" => self::$ticket->getAutenticacao()); 133 | 134 | $xmlString = self::$ticket->exportarParaXml("ticketRootNode"); 135 | 136 | $dom = new \DOMDocument(); 137 | $dom->loadXML($xmlString); 138 | 139 | foreach ($xmlEstrutura as $seletor => $conteudo) { 140 | $this->assertEquals($conteudo, $dom->getElementsByTagName($seletor)->item(0)->nodeValue); 141 | } 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /tests/BoletoTest.php: -------------------------------------------------------------------------------- 1 | name, self::$faker->streetAddress, self::$faker->city, self::$faker->city, self::$faker->stateAbbr, self::$faker->postcode); 41 | self::$titulo = new Titulo(mt_rand(0, 9999999), mt_rand(100000000, 999999999), mt_rand(100000000, 999999999), "now", NULL, NULL, NULL, new InstrucoesDeTitulo()); 42 | self::$boletoObj = new Boleto(new Convenio(), new Pagador(), new Titulo()); 43 | } 44 | 45 | /** 46 | * @author Denys Xavier 47 | * @test 48 | */ 49 | public function testeConstrutorBoleto() { 50 | $faker = Factory::create("pt_BR"); 51 | 52 | $convenio = new Convenio(mt_rand(0, 9999), mt_rand(1000000, 9999999)); 53 | $pagador = new Pagador(1, mt_rand(1000000, 9999999), $faker->name, $faker->streetAddress, $faker->city, $faker->city, $faker->stateAbbr, $faker->postcode); 54 | $titulo = new Titulo(mt_rand(0, 9999999), mt_rand(100000000, 999999999), mt_rand(100000000, 999999999), "now"); 55 | 56 | $obj = new Boleto($convenio, $pagador, $titulo); 57 | 58 | $this->assertEquals($convenio, $obj->getConvenio()); 59 | $this->assertEquals($pagador, $obj->getPagador()); 60 | $this->assertEquals($titulo, $obj->getTitulo()); 61 | } 62 | 63 | /** 64 | * @author Denys Xavier 65 | * @test 66 | */ 67 | public function testeAcessorDaPropriedadeConvenio() { 68 | self::$boletoObj->setConvenio(self::$convenio); 69 | $this->assertEquals(self::$convenio, self::$boletoObj->getConvenio()); 70 | } 71 | 72 | /** 73 | * @author Denys Xavier 74 | * @test 75 | */ 76 | public function testeAcessorDaPropriedadePagador() { 77 | self::$boletoObj->setPagador(self::$pagador); 78 | $this->assertEquals(self::$pagador, self::$boletoObj->getPagador()); 79 | } 80 | 81 | /** 82 | * @author Denys Xavier 83 | * @test 84 | */ 85 | public function testeAcessorDaPropriedadeTitulo() { 86 | self::$boletoObj->setTitulo(self::$titulo); 87 | $this->assertEquals(self::$titulo, self::$boletoObj->getTitulo()); 88 | } 89 | 90 | /** 91 | * @author Denys Xavier 92 | * @test 93 | */ 94 | public function oArrayExportadoDevePossuirAsMesmasChavesUtilizadasPeloWSdoBanco() { 95 | $chaveBoletoBanco = array("CONVENIO.COD-BANCO", 96 | "CONVENIO.COD-CONVENIO", 97 | "PAGADOR.TP-DOC", 98 | "PAGADOR.NUM-DOC", 99 | "PAGADOR.NOME", 100 | "PAGADOR.ENDER", 101 | "PAGADOR.BAIRRO", 102 | "PAGADOR.CIDADE", 103 | "PAGADOR.UF", 104 | "PAGADOR.CEP", 105 | "TITULO.NOSSO-NUMERO", 106 | "TITULO.SEU-NUMERO", 107 | "TITULO.DT-VENCTO", 108 | "TITULO.DT-EMISSAO", 109 | "TITULO.ESPECIE", 110 | "TITULO.VL-NOMINAL", 111 | "MENSAGEM", 112 | "TITULO.PC-MULTA", 113 | "TITULO.QT-DIAS-MULTA", 114 | "TITULO.PC-JURO", 115 | "TITULO.TP-DESC", 116 | "TITULO.VL-DESC", 117 | "TITULO.DT-LIMI-DESC", 118 | "TITULO.VL-ABATIMENTO", 119 | "TITULO.TP-PROTESTO", 120 | "TITULO.QT-DIAS-PROTESTO", 121 | "TITULO.QT-DIAS-BAIXA"); 122 | 123 | $export = self::$boletoObj->exportarArray(); 124 | 125 | foreach ($chaveBoletoBanco as $chave) { 126 | $this->assertArrayHasKey($chave, $export); 127 | } 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /tests/UtilTest.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class UtilTest extends \PHPUnit_Framework_TestCase { 27 | 28 | /** 29 | * @author Denys Xavier 30 | * @test 31 | */ 32 | public function umaStringDeDataFormatadaCorretamenteDeveGerarUmObjetoDateTime() { 33 | $str = date(\TIExpert\WSBoletoSantander\Config::getInstance()->getGeral("formato_data")); 34 | 35 | $dataEsperada = new \DateTime(); 36 | $dataEsperada->setDate(date("Y"), date("m"), date("d")); 37 | 38 | $resultado = Util::converterParaDateTime($str); 39 | 40 | $this->assertEquals($dataEsperada, $resultado, '', 60); 41 | } 42 | 43 | /** 44 | * @author Denys Xavier 45 | * @test 46 | */ 47 | public function seOParametroUsadoAoConverterParaDateTimeForTambemDateTimeEntaoOProprioObjetoDeveSerRetornado() { 48 | $dataEsperada = new \DateTime("2016-11-28 12:00:00"); 49 | 50 | $resultado = Util::converterParaDateTime($dataEsperada); 51 | 52 | $this->assertEquals($dataEsperada, $resultado, '', 60); 53 | } 54 | 55 | /** 56 | * @author Denys Xavier 57 | * @test 58 | */ 59 | public function seOParametroUsadoAoConverterParaDateTimeForNuloEntaoOProprioParametroEhRetornado() { 60 | $this->assertNull(Util::converterParaDateTime(NULL)); 61 | } 62 | 63 | /** 64 | * @author Denys Xavier 65 | * @test 66 | * @expectedException InvalidArgumentException 67 | */ 68 | public function seOParametroUsadoAoConverterParaDateTimeNaoForStringOuDateTimeEntaoUmaExcecaoDeveSerLancada() { 69 | $array = array("2016-11-28"); 70 | 71 | Util::converterParaDateTime($array); 72 | } 73 | 74 | /** 75 | * @author Denys Xavier 76 | * @test 77 | * @expectedException Exception 78 | */ 79 | public function umaStringDeDataFormatadaIncorretamenteDeveLancarUmaExcecao() { 80 | $string = "11#28#2016"; 81 | 82 | Util::converterParaDateTime($string); 83 | } 84 | 85 | /** 86 | * @author Denys Xavier 87 | * @test 88 | */ 89 | public function umNumeroFormatadoComZeroPrecisaoSempreSeraApenasSuaParteInteira() { 90 | $float = 12.25; 91 | $resultadoEsperado = "12"; 92 | 93 | $stringFormatada = Util::formatarNumero($float, 0); 94 | 95 | $this->assertEquals($resultadoEsperado, $stringFormatada); 96 | } 97 | 98 | /** 99 | * @author Denys Xavier 100 | * @test 101 | */ 102 | public function aoFormatarUmNumeroComGrandePrecisaoAParteDecimalNaoDeveSerArredondada() { 103 | $bigFloat = 1.789; 104 | $resultadoEsperado = "1.78"; 105 | 106 | $stringFormatada = Util::formatarNumero($bigFloat, 2, '.'); 107 | 108 | $this->assertEquals($resultadoEsperado, $stringFormatada); 109 | } 110 | 111 | /** 112 | * @author Denys Xavier 113 | * @test 114 | */ 115 | public function aoFormatarUmNumeroComBaixaPrecisaoAParteDecimalDeveSerCompletadaComZeros() { 116 | $smallFloat = 1.789; 117 | $resultadoEsperado = "1.78900"; 118 | 119 | $stringFormatada = Util::formatarNumero($smallFloat, 5, '.'); 120 | 121 | $this->assertSame($resultadoEsperado, $stringFormatada); 122 | } 123 | 124 | /** 125 | * @author Denys Xavier 126 | * @test 127 | */ 128 | public function aoFormatarUmNumeroMaiorQue999ACada3AlgarismosDeveVirUmSeparadorDeterminado() { 129 | $bigNumber = 12345678; 130 | $resultadoEsperado = "12@345@678"; 131 | 132 | $stringFormatada = Util::formatarNumero($bigNumber, 0, '.', '@'); 133 | 134 | $this->assertEquals($resultadoEsperado, $stringFormatada); 135 | } 136 | 137 | /** 138 | * @author Denys Xavier 139 | * @test 140 | */ 141 | public function aoFormatarUmNumeroInteiroAPrecisaoDeveVirCompostaDeZeros() { 142 | $number = 1; 143 | $resultadoEsperado = "1.00"; 144 | 145 | $stringFormatada = Util::formatarNumero($number, 2, '.'); 146 | 147 | $this->assertSame($resultadoEsperado, $stringFormatada); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /tests/PagadorTest.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class PagadorTest extends PHPUnit_Framework_TestCase { 27 | 28 | private static $faker; 29 | private static $pagadorObj; 30 | 31 | public static function setUpBeforeClass() { 32 | parent::setUpBeforeClass(); 33 | 34 | self::$faker = \Faker\Factory::create("pt_BR"); 35 | 36 | self::$pagadorObj = new Pagador(); 37 | } 38 | 39 | /** 40 | * @author Denys Xavier 41 | * @test 42 | */ 43 | public function testeConstrutorComTodosOsParametrosInformados() { 44 | $tipoDoc = 1; 45 | $numeroDoc = mt_rand(100000, 999999); 46 | $nome = self::$faker->name; 47 | $endereco = self::$faker->streetAddress; 48 | $bairro = self::$faker->city; 49 | $cidade = self::$faker->city; 50 | $UF = self::$faker->stateAbbr; 51 | $CEP = self::$faker->postcode; 52 | 53 | $obj = new Pagador($tipoDoc, $numeroDoc, $nome, $endereco, $bairro, $cidade, $UF, $CEP); 54 | 55 | $this->assertEquals($tipoDoc, $obj->getTipoDoc()); 56 | $this->assertEquals($numeroDoc, $obj->getNumeroDoc()); 57 | $this->assertEquals($nome, $obj->getNome()); 58 | $this->assertEquals($endereco, $obj->getEndereco()); 59 | $this->assertEquals($bairro, $obj->getBairro()); 60 | $this->assertEquals($cidade, $obj->getCidade()); 61 | $this->assertEquals($UF, $obj->getUF()); 62 | $this->assertEquals($CEP, $obj->getCEP()); 63 | } 64 | 65 | /** 66 | * @author Denys Xavier 67 | * @test 68 | */ 69 | public function testeAcessorDaPropriedadeBairro() { 70 | $validParam = self::$faker->city; 71 | 72 | self::$pagadorObj->setBairro($validParam); 73 | $this->assertEquals($validParam, self::$pagadorObj->getBairro()); 74 | } 75 | 76 | /** 77 | * @author Denys Xavier 78 | * @test 79 | */ 80 | public function testeAcessorDaPropriedadeCEP() { 81 | $validParam = self::$faker->postcode; 82 | 83 | self::$pagadorObj->setCEP($validParam); 84 | $this->assertEquals($validParam, self::$pagadorObj->getCEP()); 85 | } 86 | 87 | /** 88 | * @author Denys Xavier 89 | * @test 90 | */ 91 | public function testeAcessorDaPropriedadeCidade() { 92 | $validParam = self::$faker->city; 93 | 94 | self::$pagadorObj->setCidade($validParam); 95 | $this->assertEquals($validParam, self::$pagadorObj->getCidade()); 96 | } 97 | 98 | /** 99 | * @author Denys Xavier 100 | * @test 101 | */ 102 | public function testeAcessorDaPropriedadeEndereco() { 103 | $validParam = self::$faker->streetAddress; 104 | 105 | self::$pagadorObj->setEndereco($validParam); 106 | $this->assertEquals($validParam, self::$pagadorObj->getEndereco()); 107 | } 108 | 109 | /** 110 | * @author Denys Xavier 111 | * @test 112 | */ 113 | public function testeAcessorDaPropriedadeNome() { 114 | $validParam = self::$faker->name; 115 | 116 | self::$pagadorObj->setNome($validParam); 117 | $this->assertEquals($validParam, self::$pagadorObj->getNome()); 118 | } 119 | 120 | /** 121 | * @author Denys Xavier 122 | * @test 123 | */ 124 | public function testeAcessorDaPropriedadeNumeroDoc() { 125 | $validParam = "123456789"; 126 | 127 | self::$pagadorObj->setNumeroDoc($validParam); 128 | $this->assertEquals($validParam, self::$pagadorObj->getNumeroDoc()); 129 | } 130 | 131 | /** 132 | * @author Denys Xavier 133 | * @test 134 | */ 135 | public function testeAcessorDaPropriedadeTipoDoc() { 136 | $validParam = 1; 137 | 138 | self::$pagadorObj->setTipoDoc($validParam); 139 | $this->assertEquals($validParam, self::$pagadorObj->getTipoDoc()); 140 | } 141 | 142 | /** 143 | * @author Denys Xavier 144 | * @test 145 | */ 146 | public function testeAcessorDaPropriedadeUF() { 147 | $validParam = self::$faker->stateAbbr; 148 | 149 | self::$pagadorObj->setUF($validParam); 150 | $this->assertEquals($validParam, self::$pagadorObj->getUF()); 151 | } 152 | 153 | /** 154 | * @author Denys Xavier 155 | * @test 156 | */ 157 | public function oArrayExportadoDevePossuirAsMesmasChavesUtilizadasPeloWSdoBanco() { 158 | $chavePagador = array("PAGADOR.TP-DOC", "PAGADOR.NUM-DOC", "PAGADOR.NOME", "PAGADOR.ENDER", "PAGADOR.BAIRRO", "PAGADOR.CIDADE", "PAGADOR.UF", "PAGADOR.CEP"); 159 | 160 | $export = self::$pagadorObj->exportarArray(); 161 | 162 | foreach ($chavePagador as $chave) { 163 | $this->assertArrayHasKey($chave, $export); 164 | } 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /src/Ticket.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class Ticket { 26 | 27 | /** @property int $nsu Número seqüencial único por Convênio / Data */ 28 | private $nsu; 29 | 30 | /** @property \DateTime $data Data do NSU gerado */ 31 | private $data; 32 | 33 | /** @property string $ambiente Tipo de ambiente que está sendo usado. T = teste, P = produção */ 34 | private $ambiente; 35 | 36 | /** @property string $estacao Código da Estação gerado pelo Banco Santander */ 37 | private $estacao; 38 | 39 | /** @property string $autenticacao Código de autenticação retornado de uma solicitação ao serviço */ 40 | private $autenticacao; 41 | 42 | /** 43 | * Cria uma nova instância de Ticket 44 | */ 45 | public function __construct() { 46 | $this->data = new \DateTime(); 47 | $this->ambiente = Config::getInstance()->getGeral("ambiente"); 48 | $this->estacao = Config::getInstance()->getGeral("estacao"); 49 | } 50 | 51 | /** Obtém o número seqüencial único por Convênio / Data 52 | * 53 | * @return int 54 | */ 55 | public function getNsu() { 56 | return $this->nsu; 57 | } 58 | 59 | /** Obtém a data do NSU gerado 60 | * 61 | * @return \DateTime 62 | */ 63 | public function getData() { 64 | return $this->data; 65 | } 66 | 67 | /** Obtém o tipo de ambiente que está sendo usado 68 | * 69 | * @return string 70 | */ 71 | public function getAmbiente() { 72 | return $this->ambiente; 73 | } 74 | 75 | /** Obtém o código da Estação gerado pelo Banco Santander 76 | * 77 | * @return string 78 | */ 79 | public function getEstacao() { 80 | return $this->estacao; 81 | } 82 | 83 | /** Obtém o código de autenticação retornado de uma solicitação ao serviço 84 | * 85 | * @return string 86 | */ 87 | public function getAutenticacao() { 88 | return $this->autenticacao; 89 | } 90 | 91 | /** Determina o número seqüencial único por Convênio / Data 92 | * 93 | * @param string $nsu Número seqüencial único por Convênio / Data 94 | * @return \TIExpert\WSBoletoSantander\Ticket 95 | */ 96 | public function setNsu($nsu) { 97 | if ($this->ambiente == "T") { 98 | $nsu = "TST" . $nsu; 99 | } 100 | 101 | $this->nsu = $nsu; 102 | return $this; 103 | } 104 | 105 | /** Determina a data do NSU gerado 106 | * 107 | * @param \DateTime $data Data do NSU gerado 108 | * @return \TIExpert\WSBoletoSantander\Ticket 109 | * @throws \Exception 110 | */ 111 | public function setData(\DateTime $data) { 112 | try { 113 | $this->data = Util::converterParaDateTime($data); 114 | } catch (\Exception $e) { 115 | throw $e; 116 | } 117 | return $this; 118 | } 119 | 120 | /** Determina o tipo de ambiente que está sendo usado 121 | * 122 | * @param string $ambiente Tipo de ambiente que está sendo usado. T = teste, P = produção 123 | * @return \TIExpert\WSBoletoSantander\Ticket 124 | */ 125 | public function setAmbiente($ambiente) { 126 | $this->ambiente = $ambiente; 127 | return $this; 128 | } 129 | 130 | /** Determina o código da Estação gerado pelo Banco Santander 131 | * 132 | * @param string $estacao Código da Estação gerado pelo Banco Santander 133 | * @return \TIExpert\WSBoletoSantander\Ticket 134 | */ 135 | public function setEstacao($estacao) { 136 | $this->estacao = $estacao; 137 | return $this; 138 | } 139 | 140 | /** Determina o código de autenticação retornado de uma solicitação ao serviço 141 | * 142 | * @param string $autenticacao Código de autenticação retornado de uma solicitação ao serviço 143 | * @return \TIExpert\WSBoletoSantander\Ticket 144 | */ 145 | public function setAutenticacao($autenticacao) { 146 | $this->autenticacao = $autenticacao; 147 | return $this; 148 | } 149 | 150 | /** Exporta as propriedades da classe para um formato XML de acordo com a documentação do banco Santander 151 | * 152 | * @param string $nomeNoRaiz Nome do nó raiz das propriedades 153 | * @return string 154 | */ 155 | public function exportarParaXml($nomeNoRaiz = NULL) { 156 | $xml = new \XMLWriter(); 157 | $xml->openMemory(); 158 | 159 | if (!is_null($nomeNoRaiz)) { 160 | $xml->startElement($nomeNoRaiz); 161 | } 162 | 163 | $xml->writeElement("nsu", $this->nsu); 164 | $xml->writeElement("dtNsu", $this->data->format(Config::getInstance()->getGeral("formato_data"))); 165 | $xml->writeElement("tpAmbiente", $this->ambiente); 166 | $xml->writeElement("estacao", $this->estacao); 167 | $xml->writeElement("ticket", $this->autenticacao); 168 | 169 | if (!is_null($nomeNoRaiz)) { 170 | $xml->endElement(); 171 | } 172 | 173 | return $xml->outputMemory(); 174 | } 175 | 176 | } 177 | -------------------------------------------------------------------------------- /src/ComunicadorCurlSOAP.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class ComunicadorCurlSOAP { 27 | 28 | /** Executa uma comunicação com o endpoint enviando uma string em formato XML 29 | * 30 | * @param string $endpoint Url que deve ser atingida para executar a ação do WebService 31 | * @param array $endpointConfig Array contendo os parâmetros de configurações a serem usados na execução do cURL para que ele alcance o $endpoint 32 | * @return string 33 | * @throws Exception 34 | */ 35 | public function chamar($endpoint, $endpointConfig) { 36 | $ch = curl_init($endpoint); 37 | curl_setopt_array($ch, $endpointConfig); 38 | 39 | $response = curl_exec($ch); 40 | 41 | if (!$response) { 42 | $error_message = curl_error($ch); 43 | curl_close($ch); 44 | 45 | throw new \Exception($error_message); 46 | } 47 | 48 | curl_close($ch); 49 | 50 | return $response; 51 | } 52 | 53 | /** Gera um array contendo todas as configurações e parâmetros necessários para um recurso de cURL 54 | * 55 | * @param string $conteudoPost String no formato XML contendo os dados que devem ser informados ao WebService 56 | * @return array 57 | */ 58 | public function prepararConfiguracaoEndpoint($conteudoPost = "") { 59 | $arrayConfig = array(); 60 | 61 | $this->criarCabecalhosHTTP($arrayConfig, $conteudoPost); 62 | $this->configurarCertificadosDeSeguranca($arrayConfig); 63 | 64 | $arrayConfig[CURLOPT_TIMEOUT] = Config::getInstance()->getGeral("timeout"); 65 | $arrayConfig[CURLOPT_RETURNTRANSFER] = true; 66 | 67 | return $arrayConfig; 68 | } 69 | 70 | /** Cria os cabeçalhos HTTP para envio de informações via POST para o endpoint. 71 | * 72 | * @param array $arrayConfig Array contendo as configurações atuais do cURL 73 | * @param string $conteudoPost Conteúdo que será enviado ao endpoint 74 | */ 75 | public function criarCabecalhosHTTP(&$arrayConfig, $conteudoPost) { 76 | $arrayConfig[CURLOPT_POST] = true; 77 | $arrayConfig[CURLOPT_POSTFIELDS] = $conteudoPost; 78 | 79 | $headers = array("Content-type: text/xml;charset=\"utf-8\"", 80 | "Accept: text/xml", 81 | "Content-length: " . strlen($conteudoPost)); 82 | 83 | $arrayConfig[CURLOPT_HTTPHEADER] = $headers; 84 | } 85 | 86 | /** Configura e anexa os certificados digitais a serem usados durante a comunicação entre a origem e o endpoint 87 | * 88 | * @param array $arrayConfig Array contendo as configurações atuais do cURL 89 | */ 90 | public function configurarCertificadosDeSeguranca(&$arrayConfig) { 91 | $conf = Config::getInstance(); 92 | 93 | $arrayConfig[CURLOPT_SSL_VERIFYPEER] = $conf->getGeral("assegurar_endpoint"); 94 | $arrayConfig[CURLOPT_SSL_VERIFYHOST] = $conf->getGeral("assegurar_endpoint") ? 2 : 0; 95 | 96 | $arrayConfig[CURLOPT_SSLCERT] = $conf->getCertificado("arquivo"); 97 | $arrayConfig[CURLOPT_SSLCERTPASSWD] = $conf->getCertificado("senha"); 98 | 99 | if ((bool) $conf->getGeral("assegurar_endpoint")) { 100 | if ($conf->getCertificado("arquivo_ca") != "") { 101 | $arrayConfig[CURLOPT_CAINFO] = $conf->getCertificado("arquivo_ca"); 102 | } 103 | 104 | if ($conf->getCertificado("diretorio_cas") != "") { 105 | $arrayConfig[CURLOPT_CAPATH] = $conf->getCertificado("diretorio_cas"); 106 | } 107 | } 108 | } 109 | 110 | /** Indica se a resposta de uma chamada ao endpoint pode ser um SOAP Fault que está formatado como string 111 | * 112 | * @param string $response String de resposta a ser analisada 113 | * @return boolean 114 | */ 115 | public function ehSOAPFaultComoString($response) { 116 | $isFault = false; 117 | 118 | $hasTags = preg_match("/[<>]/i", $response); 119 | 120 | if ($hasTags === 0) { 121 | $isFault = preg_match("/(?=.*faultcode)(?=.*faultstring)/i", $response) >= 1; 122 | } 123 | 124 | return $isFault; 125 | } 126 | 127 | /** Tenta converter uma resposta de uma chamada ao endpoint que é um SOAP Fault formatado como string para um Exception 128 | * 129 | * @param string $string Resposta da chamada ao endpoint 130 | * @return \Exception 131 | * @throws \InvalidArgumentException 132 | */ 133 | public function converterSOAPFaultStringParaException($string) { 134 | if ($this->ehSOAPFaultComoString($string)) { 135 | $variaveis = explode("&", $string); 136 | 137 | foreach ($variaveis as $variavel) { 138 | list($chave, $valor) = explode("=", $variavel); 139 | 140 | if ($chave == "faultstring") { 141 | return new \Exception($valor); 142 | } 143 | } 144 | } 145 | throw new \InvalidArgumentException("O parâmetro informado não é um SOAP Fault em formato de string."); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/Pagador.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class Pagador implements PropriedadesExportaveisParaArrayInterface, PropriedadesImportaveisPorXMLInterface { 27 | 28 | /** property int $tipoDoc Tipo de Documento do Pagador */ 29 | private $tipoDoc; 30 | 31 | /** property string $numeroDoc CPF/CNPJ do Pagador */ 32 | private $numeroDoc; 33 | 34 | /** property string $nome Nome do Pagador */ 35 | private $nome; 36 | 37 | /** property string $endereco Endereço do Pagador */ 38 | private $endereco; 39 | 40 | /** property string $bairro Bairro do Pagador */ 41 | private $bairro; 42 | 43 | /** property string $cidade Cidade do Pagador */ 44 | private $cidade; 45 | 46 | /** property string $UF UF do Pagador */ 47 | private $UF; 48 | 49 | /** property string $CEP CEP do Pagador */ 50 | private $CEP; 51 | 52 | /** Cria uma nova instância de Pagador 53 | * 54 | * @param int $tipoDoc Tipo de Documento do Pagador 55 | * @param string $numeroDoc CPF/CNPJ do Pagador 56 | * @param string $nome Nome do Pagador 57 | * @param string $endereco Endereço do Pagador 58 | * @param string $bairro Bairro do Pagador 59 | * @param string $cidade Cidade do Pagador 60 | * @param string $UF UF do Pagador 61 | * @param string $CEP CEP do Pagador 62 | */ 63 | public function __construct($tipoDoc = NULL, $numeroDoc = NULL, $nome = NULL, $endereco = NULL, $bairro = NULL, $cidade = NULL, $UF = NULL, $CEP = NULL) { 64 | $this->tipoDoc = $tipoDoc; 65 | $this->numeroDoc = $numeroDoc; 66 | $this->nome = $nome; 67 | $this->endereco = $endereco; 68 | $this->bairro = $bairro; 69 | $this->cidade = $cidade; 70 | $this->UF = $UF; 71 | $this->CEP = $CEP; 72 | } 73 | 74 | /** Obtém o tipo de documento do Pagador 75 | * 76 | * @return string 77 | */ 78 | public function getTipoDoc() { 79 | return $this->tipoDoc; 80 | } 81 | 82 | /** Obtém o CPF/CNPJ do Pagador 83 | * 84 | * @return string 85 | */ 86 | public function getNumeroDoc() { 87 | return $this->numeroDoc; 88 | } 89 | 90 | /** Obtém o nome do Pagador 91 | * 92 | * @return string 93 | */ 94 | public function getNome() { 95 | return $this->nome; 96 | } 97 | 98 | /** Obtém o endereço do Pagador 99 | * 100 | * @return string 101 | */ 102 | public function getEndereco() { 103 | return $this->endereco; 104 | } 105 | 106 | /** Obtém o bairro do Pagador 107 | * 108 | * @return string 109 | */ 110 | public function getBairro() { 111 | return $this->bairro; 112 | } 113 | 114 | /** Obtém a cidade do Pagador 115 | * 116 | * @return string 117 | */ 118 | public function getCidade() { 119 | return $this->cidade; 120 | } 121 | 122 | /** Obtém a UF do Pagador 123 | * 124 | * @return string 125 | */ 126 | public function getUF() { 127 | return $this->UF; 128 | } 129 | 130 | /** Obtém o CEP do Pagador 131 | * 132 | * @return string 133 | */ 134 | public function getCEP() { 135 | return $this->CEP; 136 | } 137 | 138 | /** Determina o tipo de documento do Pagador 139 | * 140 | * @param string $tipoDoc Tipo de Documento do Pagador 141 | * @return \TIExpert\WSBoletoSantander\Pagador 142 | */ 143 | public function setTipoDoc($tipoDoc) { 144 | $this->tipoDoc = $tipoDoc; 145 | return $this; 146 | } 147 | 148 | /** Determina o CPF/CNPJ do Pagador 149 | * 150 | * @param string $numeroDoc CPF/CNPJ do Pagador 151 | * @return \TIExpert\WSBoletoSantander\Pagador 152 | */ 153 | public function setNumeroDoc($numeroDoc) { 154 | $this->numeroDoc = $numeroDoc; 155 | return $this; 156 | } 157 | 158 | /** Determina o nome do Pagador 159 | * 160 | * @param string $nome Nome do Pagador 161 | * @return \TIExpert\WSBoletoSantander\Pagador 162 | */ 163 | public function setNome($nome) { 164 | $this->nome = $nome; 165 | return $this; 166 | } 167 | 168 | /** Determina o endereço do Pagador 169 | * 170 | * @param string $endereco Endereço do Pagador 171 | * @return \TIExpert\WSBoletoSantander\Pagador 172 | */ 173 | public function setEndereco($endereco) { 174 | $this->endereco = $endereco; 175 | return $this; 176 | } 177 | 178 | /** Determina o bairro do Pagador 179 | * 180 | * @param string $bairro Bairro do Pagador 181 | * @return \TIExpert\WSBoletoSantander\Pagador 182 | */ 183 | public function setBairro($bairro) { 184 | $this->bairro = $bairro; 185 | return $this; 186 | } 187 | 188 | /** Determina a Cidade do Pagador 189 | * 190 | * @param string $cidade Cidade do Pagador 191 | * @return \TIExpert\WSBoletoSantander\Pagador 192 | */ 193 | public function setCidade($cidade) { 194 | $this->cidade = $cidade; 195 | return $this; 196 | } 197 | 198 | /** Determina a UF do Pagador 199 | * 200 | * @param string $UF UF do Pagador 201 | * @return \TIExpert\WSBoletoSantander\Pagador 202 | */ 203 | public function setUF($UF) { 204 | $this->UF = $UF; 205 | return $this; 206 | } 207 | 208 | /** Determina o CEP do Pagador 209 | * 210 | * @param string $CEP CEP do Pagador 211 | * @return \TIExpert\WSBoletoSantander\Pagador 212 | */ 213 | public function setCEP($CEP) { 214 | $this->CEP = $CEP; 215 | return $this; 216 | } 217 | 218 | /** Exporta um array associativo no qual as chaves são as propriedades representadas como no WebService do Santander 219 | * 220 | * @return array 221 | */ 222 | public function exportarArray() { 223 | $array["PAGADOR.TP-DOC"] = $this->tipoDoc; 224 | $array["PAGADOR.NUM-DOC"] = $this->numeroDoc; 225 | $array["PAGADOR.NOME"] = $this->nome; 226 | $array["PAGADOR.ENDER"] = $this->endereco; 227 | $array["PAGADOR.BAIRRO"] = $this->bairro; 228 | $array["PAGADOR.CIDADE"] = $this->cidade; 229 | $array["PAGADOR.UF"] = $this->UF; 230 | $array["PAGADOR.CEP"] = $this->CEP; 231 | return $array; 232 | } 233 | 234 | /** Carrega as propriedades da instância usando a estrutura XML 235 | * 236 | * @param \DOMDocument $xml Estrutura XML legível 237 | */ 238 | public function carregarPorXML(\DOMDocument $xml) { 239 | $leitor = new LeitorSimplesXML($xml); 240 | 241 | $this->setBairro($leitor->getValorNo("bairro")); 242 | $this->setCEP($leitor->getValorNo("cep")); 243 | $this->setCidade($leitor->getValorNo("cidade")); 244 | $this->setEndereco($leitor->getValorNo("ender")); 245 | $this->setNome($leitor->getValorNo("nome")); 246 | $this->setNumeroDoc($leitor->getValorNo("numDoc")); 247 | $this->setTipoDoc($leitor->getValorNo("tpDoc")); 248 | $this->setUF($leitor->getValorNo("uf")); 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class Config { 27 | 28 | /** @property string $caminhoArquivoConfig Caminho para o arquivo de configuração utilizado. */ 29 | private static $caminhoArquivoConfig = __DIR__ . "/config.ini"; 30 | 31 | /** @property array $config Array de configurações unificado. */ 32 | private static $config = array( 33 | "convenio" => array( 34 | "banco_padrao" => "0033", 35 | "convenio_padrao" => ""), 36 | "instrucoes_banco" => array( 37 | "tipo_desconto" => 0, 38 | "valor_desconto" => 0, 39 | "data_limite_desconto" => "now", 40 | "tipo_protesto" => 0, 41 | "baixar_apos" => 0), 42 | "geral" => array( 43 | "assegurar_endpoint" => true, 44 | "timeout" => 30, 45 | "ambiente" => "T", 46 | "formato_data" => "dmY", 47 | "estacao" => ""), 48 | "certificado" => array( 49 | "arquivo" => "", 50 | "senha" => "", 51 | "tipo_arquivo" => "PEM", 52 | "diretorio_cas" => "", 53 | "arquivo_ca" => "") 54 | ); 55 | 56 | /** @property Config $instance Ponteiro para a instância única de Config */ 57 | private static $instance; 58 | 59 | /** Cria uma nova instância de Config 60 | * 61 | * @param string $caminhoArquivoConfig Caminho para o arquivo de configuração utilizado. Se nada for informado, então, será considerado o arquivo config.ini da mesma pasta que o arquivo da classe Config. 62 | */ 63 | private function __construct() { 64 | $this->carregarConfiguracao(); 65 | } 66 | 67 | /** Obtém uma instância de Config 68 | * 69 | * @return Config 70 | */ 71 | public static function getInstance() { 72 | if (is_null(self::$instance)) { 73 | self::$instance = new Config(); 74 | } 75 | return self::$instance; 76 | } 77 | 78 | /** Obtém o caminho do arquivo de configuração que está sendo usado 79 | * 80 | * @return string 81 | */ 82 | public static function getCaminhoArquivoConfig() { 83 | return self::$caminhoArquivoConfig; 84 | } 85 | 86 | /** Define qual o caminho do arquivo de configuração a ser usado. 87 | * 88 | * Caso uma instância de Config já tenha sido criada com o método getInstance(), então, não é mais possível alterar esta propriedade e o novo valor é ignorado. 89 | * 90 | * @param string $caminhoArquivoConfig Caminho para o arquivo de configuração a ser utilizado ou criado 91 | */ 92 | public static function setCaminhoArquivoConfig($caminhoArquivoConfig) { 93 | $caminhoAnterior = self::$caminhoArquivoConfig; 94 | self::$caminhoArquivoConfig = $caminhoArquivoConfig; 95 | 96 | if (!is_null(self::$instance) && $caminhoAnterior != $caminhoArquivoConfig) { 97 | self::carregarConfiguracao(); 98 | } 99 | } 100 | 101 | /** Obtém o valor da chave informada dentro do grupo convenio 102 | * 103 | * @param string $chave Nome da chave da qual o valor deve ser retornado 104 | * @return mixed 105 | */ 106 | public function getConvenio($chave) { 107 | return $this->getOpcao("convenio", $chave); 108 | } 109 | 110 | /** Obtém o valor da chave informada dentro do grupo instrucoes_banco 111 | * 112 | * @param string $chave Nome da chave da qual o valor deve ser retornado 113 | * @return mixed 114 | */ 115 | public function getInstrucao($chave) { 116 | return $this->getOpcao("instrucoes_banco", $chave); 117 | } 118 | 119 | /** Obtém o valor da chave informada dentro do grupo geral 120 | * 121 | * @param string $chave Nome da chave da qual o valor deve ser retornado 122 | * @return mixed 123 | */ 124 | public function getGeral($chave) { 125 | return $this->getOpcao("geral", $chave); 126 | } 127 | 128 | /** Obtém o valor da chave informada dentro do grupo certificado 129 | * 130 | * @param string $chave Nome da chave da qual o valor deve ser retornado 131 | * @return mixed 132 | */ 133 | public function getCertificado($chave) { 134 | return $this->getOpcao("certificado", $chave); 135 | } 136 | 137 | /** Obtém o valor da chave informada dentro do grupo informado 138 | * 139 | * 140 | * @return mixed 141 | */ 142 | public function getOpcao($grupo, $chave) { 143 | if (isset(self::$config[$grupo][$chave])) { 144 | return self::$config[$grupo][$chave]; 145 | } else { 146 | throw new \InvalidArgumentException("O valor da chave " . $chave . " pertencente ao grupo " . $grupo . " não existe."); 147 | } 148 | } 149 | 150 | /** Carrega as configurações a partir de um arquivo INI */ 151 | private static function carregarConfiguracao() { 152 | if (file_exists(self::$caminhoArquivoConfig)) { 153 | self::$config = parse_ini_file(self::$caminhoArquivoConfig, true); 154 | } else { 155 | self::criarArquivoDeConfiguracao(); 156 | } 157 | } 158 | 159 | /** Cria um novo arquivo de configuração no caminho especificado baseado nos dados padrões do array $config 160 | * 161 | * @throws \Exception se acontecer algum problema ao tentar manipular o arquivo. 162 | */ 163 | private static function criarArquivoDeConfiguracao() { 164 | $handler = @fopen(self::$caminhoArquivoConfig, "w"); 165 | if ($handler) { 166 | $ini = self::converterArrayDeConfiguracaoParaINI(); 167 | 168 | fwrite($handler, $ini); 169 | fclose($handler); 170 | } else { 171 | $err = error_get_last(); 172 | throw new \Exception("Não foi possível encontrar o arquivo de configuração e, ao tentar criá-lo, ocorreu o seguinte erro: " . $err["message"]); 173 | } 174 | } 175 | 176 | /** Converte o array $config da instância atual para uma string formatada pronta para ser gravada em um arquivo .ini 177 | * 178 | * @return string 179 | */ 180 | private static function converterArrayDeConfiguracaoParaINI() { 181 | $ini = "; Este arquivo foi criado automaticamente ao tentar acessar as configurações do WSBoletoSantander 182 | ; Se quiser uma versão com as opções comentadas, acesse: https://github.com/DenysXavier/WSBoletoSantander 183 | ; Arquivo de configuração gerado em " . date("d/m/Y H:i:s") . PHP_EOL; 184 | 185 | foreach (self::$config as $grupo => $chaves) { 186 | $ini .= PHP_EOL . self::converterStringEmGrupoDeConfiguracaoINI($grupo); 187 | foreach ($chaves as $chave => $valor) { 188 | $ini .= self::converterChaveEValorEmLinhaDeOpcaoINI($chave, $valor); 189 | } 190 | } 191 | 192 | return $ini; 193 | } 194 | 195 | /** Formata uma string qualquer em um grupo de configuração. 196 | * 197 | * Por exemplo, uma string "XPTO" seria formatada como [XPTO]. 198 | * 199 | * @param string $string String a ser convertida em um formato de grupo 200 | * @return string 201 | */ 202 | private static function converterStringEmGrupoDeConfiguracaoINI($string) { 203 | return "[" . $string . "]" . PHP_EOL; 204 | } 205 | 206 | /** Formata duas strings quaisquer em uma linha de opção de configuração. 207 | * 208 | * Por exemplo: uma string de chave "ordenar" e outra string de valor "ASC" resulta na linha de configuração ordernar = "ASC". 209 | * 210 | * @param string $chave A string que será usada como chave de configuração 211 | * @param string $valor A string que será usada como valor da chave de configuração 212 | * @return string 213 | */ 214 | private static function converterChaveEValorEmLinhaDeOpcaoINI($chave, $valor) { 215 | $str = $chave . " = "; 216 | if (is_string($valor)) { 217 | $valor = '"' . $valor . '"'; 218 | } 219 | $str .= $valor . PHP_EOL; 220 | return $str; 221 | } 222 | 223 | } 224 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Build Status 3 | Coverage Status 4 | Latest Stable Version 5 | Total Downloads 6 | License 7 |
8 | SensioLabsInsight 9 |

10 | 11 | # WSBoletoSantander 12 | 13 | WS Boleto Santander é um conjunto de classes criadas para facilitar a integração entre aplicativos feitos em PHP e a geração de boletos online no banco Santander. 14 | 15 | ## Índice 16 | 17 | > * [Funcionalidades](#funcionalidades) 18 | > * [Requisitos](#requisitos) 19 | > * [Guia Básico de Uso](#guia-básico-de-uso) 20 | > * [Instalação da Biblioteca](#instalação-da-biblioteca) 21 | > * [Montagem do Boleto](#montagem-do-boleto) 22 | > * [Registrando o Boleto](#registrando-o-boleto) 23 | > * [Questões Importantes](#questões-importantes) 24 | > * [Mais Documentação](#mais-documentação) 25 | > * [Licença](#licença) 26 | > * [Aviso Legal](#aviso-legal) 27 | 28 | ## Funcionalidades 29 | 30 | - [x] Inclusão/Registro de boletos 31 | - [x] Sondagem de boletos registrados 32 | - [x] Tratamento de erros de comunicação com o serviço do Santander 33 | 34 | ## Requisitos 35 | 36 | * PHP 5.6 ou superior 37 | 38 | > *Suporte para PHP 5.6 e 7.0 terminará em dezembro de 2018.* 39 | > 40 | > **É fortemente recomendável que se migre para versões superiores.** 41 | 42 | Com as seguintes extensões ativas: 43 | 44 | * cURL 45 | * DOM 46 | * XmlWriter 47 | 48 | ## Guia Básico de Uso 49 | 50 | ### Instalação da Biblioteca 51 | 52 | WS Boleto Santander pode ser instalado via [Composer](https://getcomposer.org) usando o comando: 53 | 54 | ```console 55 | composer require tiexpert/ws-boleto-santander 56 | ``` 57 | 58 | O Composer automaticamente verificará seu ambiente para determinar se seu servidor pode rodar a biblioteca WSBoletoSantander. 59 | 60 | ### Montagem do Boleto 61 | 62 | Para registrar o boleto serão necessárias as seguintes classes: 63 | 64 | ```php 65 | use TIExpert\WSBoletoSantander\Boleto; 66 | use TIExpert\WSBoletoSantander\BoletoSantanderServico; 67 | use TIExpert\WSBoletoSantander\ComunicadorCurlSOAP; 68 | use TIExpert\WSBoletoSantander\Convenio; 69 | use TIExpert\WSBoletoSantander\InstrucoesDeTitulo; 70 | use TIExpert\WSBoletoSantander\Pagador; 71 | use TIExpert\WSBoletoSantander\Titulo; 72 | ``` 73 | Agora, em seu script, defina o convênio. 74 | 75 | ```php 76 | $convenio = new Convenio(); 77 | ``` 78 | 79 | Se o arquivo de configuração estiver já pronto, as informações de convênio já estarão corretas. Caso não, você pode definí-las agora. 80 | 81 | ```php 82 | $convenio->setCodigoBanco("0033"); 83 | $convenio->setCodigoConvenio("123456"); 84 | ``` 85 | 86 | Então, defina as informações do pagador do boleto. 87 | 88 | ```php 89 | $pagador = new Pagador($tipoDoc, $numeroDoc, $nome, $endereco, $bairro, $cidade, $UF, $CEP); 90 | ``` 91 | 92 | Se não desejar instanciar Pagador já com os dados é possível. Pagador tem um construtor padrão sem argumentos e cada propriedade tem um método set para definir valor como, por exemplo, setNome, setCidade, setCEP,etc. 93 | 94 | Por fim, um objeto composto que são as informações do título bancário. 95 | 96 | Comece definindo as instruções do título. 97 | 98 | ```php 99 | $instrucoes = new InstrucoesDeTitulo(); 100 | ``` 101 | 102 | As instruções mais corriqueiras são configuráveis via config.ini. Mas, todas as propriedades como `$multa`, `$multarApos`, `$juros`, `$tipoDesconto`, `$valorDesconto`, `$dataLimiteDesconto`, `$valorAbatimento`, `$tipoProtesto`, `$protestarApos`, `$baixarApos` tem métodos set. 103 | 104 | Propriedades que são representações de data devem ser usadas instâncias de DateTime, ou uma string no formato "dmY". Exemplo: `$instrucao->setDataLimiteDesconto("28032017")`, ou seja, o desconto deve ser aplicado até 28/03/2017. 105 | 106 | Enfim, usaremos essas instruções para compor as informações do título na classe Titulo. 107 | 108 | ```php 109 | $titulo = new Titulo($valor, $nossoNumero, $seuNumero, $dataVencimento, $mensagem, $dataEmissao, $especie, $instrucoes); 110 | ``` 111 | 112 | Assim como as demais classes, todas as propriedades têm seus respectivos set. 113 | 114 | Importante salientar que toda instância de Título deve conter uma instância de InstrucoesDeTitulo. Caso contrário, um erro acontecerá na exportação do XML. 115 | 116 | Agora, com todas as partes prontas, basta montar o boleto. 117 | 118 | ```php 119 | $boleto = new Boleto($convenio, $pagador, $titulo); 120 | ``` 121 | 122 | ### Registrando o Boleto 123 | 124 | Com o boleto já montado, ou seja, com seus objetos e campos populados, deve-se fazer o registro em dois passos: solicitar um tíquete de registro de boleto e depois ratificá-lo. 125 | 126 | Primeiramente, vamos preparar o serviço injetando um comunicador no cliente do serviço. 127 | 128 | ```php 129 | $comunicador = new ComunicadorCurlSOAP (); 130 | $svc = new BoletoSantanderServico($comunicador); 131 | ``` 132 | 133 | Agora, devemos solicitar um tíquete com o método **`solicitarTicketInclusao`**. 134 | 135 | > **Qualquer erro que o WebService retornar será lançado como um Exception pelo método.** 136 | 137 | ```php 138 | $ticket = $svc->solicitarTicketInclusao($boleto); 139 | ``` 140 | 141 | Se nada deu errado, então, uma instância de Ticket é criada com uma autenticação de segurança do banco. Nesse momento, será necessário determinar um número sequencial único (NSU) que será a identificação de seu boleto. Para cada registro de boleto, este NSU deverá ser único por dia e por convênio, ou seja, não se pode usar o mesmo NSU no mesmo dia para o mesmo convênio. 142 | 143 | ```php 144 | $ticket->setNsu(1); 145 | ``` 146 | 147 | Com o tíquete pronto, basta passá-lo como parâmetro no método **`incluirTitulo`**. 148 | 149 | $resultado = $svc->incluirTitulo($ticket); 150 | 151 | Este método retorna `true` em caso de registro com sucesso, ou `false`. Embora, em casos de falha, o mais provável é que seja lançado um Exception com o motivo da falha. 152 | 153 | ## Questões Importantes 154 | 155 | Antes de qualquer tentativa de comunicação com o banco, deve-se primeiro pedir para eles cadastrarem seu certificado digital lá. Sem isso, não tem como o serviço do banco saber a autenticidade de quem o está requisitando. 156 | 157 | Outra coisa, seu certificado digital também deve respeitar algumas regras. 158 | 159 | Primeiro, ele deve ser do tipo cliente ou ambos, ou seja, ele deve de qualquer forma prover meios de comprovar sua identidade. 160 | 161 | ![Aba de informações gerais do certificado](http://i65.tinypic.com/wb2czc.jpg) 162 | 163 | Além disso, seu certificado deve ter 4 informações importantes: 164 | 165 | 1) O tamanho da chave chave-pública deve ser de 2048 bits. 166 | 167 | ![Tamanho da chave-pública](http://i67.tinypic.com/2wecfad.jpg) 168 | 169 | 2) Deve conter número de série. 170 | 171 | ![Chave Serial](http://i67.tinypic.com/xen054.jpg) 172 | 173 | 3) Possuir uma impressão digital. 174 | 175 | ![Impressão Digital do Certificado](http://i67.tinypic.com/25iwtc1.jpg) 176 | 177 | 4) E, um Common Name. 178 | 179 | ![Common Name](http://i67.tinypic.com/ml49ar.jpg) 180 | 181 | Para facilitar o processo de comunicação com o serviço do Santander, é interessante baixar o certificado da CA deles, que atualmente é Entrust Root Certificate Authority—G2. 182 | 183 | Ele pode ser encontrado aqui: https://www.entrust.com/get-support/ssl-certificate-support/root-certificate-downloads/ 184 | 185 | Também será necessário exportar seu certificado digital para o formato PEM. 186 | 187 | Com ambos os arquivos, configure-os no arquivo *config.ini* do WSBoletoSantander. 188 | 189 | Exemplo: 190 | 191 | ```ini 192 | [certificado] 193 | arquivo = "/var/www/html/meu_certificado_digital.pem" 194 | senha = "Senha do meu certificado" 195 | tipo_arquivo = "PEM" 196 | arquivo_ca = "/var/www/html/entrust_g2_ca.cer" 197 | ``` 198 | 199 | ## Mais Documentação 200 | 201 | Em breve, na Wiki do projeto. 202 | 203 | ## Licença 204 | 205 | WS Boleto Santander é distribuído sob a Licença Apache 2.0 e não pode ser usado de forma diferente que a expressa por essa licença. 206 | 207 | Maiores informações, acesse http://www.apache.org/licenses/LICENSE-2.0. 208 | 209 | ## Aviso Legal 210 | 211 | O autor deste projeto não tem nenhuma afiliação, vínculo ou qualquer outra relação com o banco Santander S.A. 212 | 213 | O software é oferecido aqui "como está" e nenhuma garantia é proferida. Portanto, o uso deste software é de inteira responsabilidade do utilizador. 214 | -------------------------------------------------------------------------------- /tests/TituloTest.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | class TituloTest extends PHPUnit_Framework_TestCase { 28 | 29 | private static $tituloObj; 30 | 31 | public static function setUpBeforeClass() { 32 | parent::setUpBeforeClass(); 33 | self::$tituloObj = new Titulo(100, "000000000"); 34 | } 35 | 36 | /** 37 | * @author Denys Xavier 38 | * @test 39 | */ 40 | public function osValoresPadroesParaDataDeEmissaoEDataDeVencimentoSaoOProprioDia() { 41 | $obj = new Titulo(); 42 | 43 | $dataEsperada = new \DateTime(); 44 | 45 | $this->assertEquals($dataEsperada->format("Y-m-d"), $obj->getDataEmissao()->format("Y-m-d")); 46 | $this->assertEquals($dataEsperada->format("Y-m-d"), $obj->getDataVencimento()->format("Y-m-d")); 47 | } 48 | 49 | /** 50 | * @author Denys Xavier 51 | * @test 52 | */ 53 | public function testeAcessorDaPropriedadeNossoNumero() { 54 | $validParam = "123456789"; 55 | self::$tituloObj->setNossoNumero($validParam); 56 | $this->assertEquals($validParam, self::$tituloObj->getNossoNumero()); 57 | } 58 | 59 | /** 60 | * @author Denys Xavier 61 | * @test 62 | */ 63 | public function testeAcessorDaPropriedadeSeuNumero() { 64 | $validParam = "0123456789ABCDE"; 65 | self::$tituloObj->setSeuNumero($validParam); 66 | $this->assertEquals($validParam, self::$tituloObj->getSeuNumero()); 67 | } 68 | 69 | /** 70 | * @author Denys Xavier 71 | * @test 72 | */ 73 | public function testeAcessorDaPropriedadeDataVencimento() { 74 | $validParam = new \DateTime("2016-11-28"); 75 | self::$tituloObj->setDataVencimento($validParam); 76 | $this->assertEquals($validParam, self::$tituloObj->getDataVencimento()); 77 | } 78 | 79 | /** 80 | * @author Denys Xavier 81 | * @test 82 | * @expectedException \Exception 83 | */ 84 | public function testeAcessorDaPropriedadeDataVencimentoComParametroInvalido() { 85 | $invalidParam = array(); 86 | self::$tituloObj->setDataVencimento($invalidParam); 87 | } 88 | 89 | /** 90 | * @author Denys Xavier 91 | * @test 92 | */ 93 | public function testeAcessorDaPropriedadeDataEmissao() { 94 | $validParam = new \DateTime("2016-11-28"); 95 | self::$tituloObj->setDataEmissao($validParam); 96 | $this->assertEquals($validParam, self::$tituloObj->getDataEmissao()); 97 | } 98 | 99 | /** 100 | * @author Denys Xavier 101 | * @test 102 | * @expectedException \Exception 103 | */ 104 | public function testeAcessorDaPropriedadeDataEmissaoComParametroInvalido() { 105 | $invalidParam = array(); 106 | self::$tituloObj->setDataEmissao($invalidParam); 107 | } 108 | 109 | /** 110 | * @author Denys Xavier 111 | * @test 112 | */ 113 | public function testeAcessorDaPropriedadeEspecie() { 114 | $validParam = 99; 115 | self::$tituloObj->setEspecie($validParam); 116 | $this->assertEquals($validParam, self::$tituloObj->getEspecie()); 117 | } 118 | 119 | /** 120 | * @author Denys Xavier 121 | * @test 122 | */ 123 | public function testeAcessorDaPropriedadeValor() { 124 | $validParam = 25.50; 125 | self::$tituloObj->setValor($validParam); 126 | $this->assertEquals($validParam, self::$tituloObj->getValor()); 127 | } 128 | 129 | /** 130 | * @author Denys Xavier 131 | * @test 132 | */ 133 | public function testeAcessorDaPropriedadeMensagem() { 134 | $validParam = "Mensagem de teste\r\nLinha 1\r\nLinha 2\r\n..."; 135 | self::$tituloObj->setMensagem($validParam); 136 | $this->assertEquals($validParam, self::$tituloObj->getMensagem()); 137 | } 138 | 139 | /** 140 | * @author Denys Xavier 141 | * @test 142 | */ 143 | public function testeAcessorDaPropriedadeInstrucoes() { 144 | $validParam = new InstrucoesDeTitulo(); 145 | self::$tituloObj->setInstrucoes($validParam); 146 | $this->assertEquals($validParam, self::$tituloObj->getInstrucoes()); 147 | } 148 | 149 | /** 150 | * @author Denys Xavier 151 | * @test 152 | */ 153 | public function oArrayExportadoDevePossuirAsMesmasChavesUtilizadasPeloWSdoBanco() { 154 | $chaveTitulo = array("TITULO.NOSSO-NUMERO", "TITULO.SEU-NUMERO", "TITULO.DT-VENCTO", "TITULO.DT-EMISSAO", "TITULO.ESPECIE", "TITULO.VL-NOMINAL", "MENSAGEM"); 155 | 156 | $export = self::$tituloObj->exportarArray(); 157 | 158 | foreach ($chaveTitulo as $chave) { 159 | $this->assertArrayHasKey($chave, $export); 160 | } 161 | } 162 | 163 | /** 164 | * @author Denys Xavier 165 | * @test 166 | */ 167 | public function oValorMonetarioDaChaveTITULO_VL_NOMINALNaoPossuiVirgula() { 168 | $valorNominal = 100.123; 169 | $valorExportado = 10012; 170 | 171 | $titulo = new Titulo($valorNominal); 172 | $export = $titulo->exportarArray(); 173 | 174 | $this->assertEquals($valorExportado, $export["TITULO.VL-NOMINAL"]); 175 | } 176 | 177 | /** 178 | * @author Denys Xavier 179 | * @test 180 | */ 181 | public function calculoDeDigitoVerificadorDeNossoNumeroComMenosDe8Algarismos() { 182 | $nossoNumero = 12345; 183 | $nossoNumeroComDigito = 123455; 184 | 185 | $titulo = new Titulo(); 186 | $titulo->setNossoNumero($nossoNumero); 187 | 188 | $this->assertEquals($nossoNumeroComDigito, $titulo->getNossoNumeroComDigito()); 189 | } 190 | 191 | /** 192 | * @author Denys Xavier 193 | * @test 194 | */ 195 | public function calculoDeDigitoVerificadorDeNossoNumeroComMaisDe8Algarismos() { 196 | $nossoNumero = 123456789012; 197 | $nossoNumeroComDigito = 1234567890123; 198 | 199 | $titulo = new Titulo(); 200 | $titulo->setNossoNumero($nossoNumero); 201 | 202 | $this->assertEquals($nossoNumeroComDigito, $titulo->getNossoNumeroComDigito()); 203 | } 204 | 205 | /** 206 | * @author Denys Xavier 207 | * @test 208 | */ 209 | public function sempreQueOModulo11DaSomatoriaDosAlgarismosForMenorQue1EntaoDeveRetornarZero() { 210 | $titulo = new Titulo(); 211 | 212 | $nossoNumeroModulo11Igual0 = 2023; 213 | $nossoNumeroModulo11Igual0ComDigito = 20230; 214 | 215 | $titulo->setNossoNumero($nossoNumeroModulo11Igual0); 216 | $this->assertEquals($nossoNumeroModulo11Igual0ComDigito, $titulo->getNossoNumeroComDigito()); 217 | 218 | $nossoNumeroModulo11Igual1 = 2001; 219 | $nossoNumeroModulo11Igual1ComDigito = 20010; 220 | 221 | $titulo->setNossoNumero($nossoNumeroModulo11Igual1); 222 | $this->assertEquals($nossoNumeroModulo11Igual1ComDigito, $titulo->getNossoNumeroComDigito()); 223 | } 224 | 225 | /** 226 | * @author Denys Xavier 227 | * @test 228 | */ 229 | public function aoSondarUmTituloODigitoVerificadorDoNossoNumeroDeveSerRemovido() { 230 | $nossoNumeroSondado = "1234567890123"; 231 | $nossoNumeroEsperado = "123456789012"; 232 | 233 | $xml = new DOMDocument(); 234 | $xml->loadXML(" 235 | 236 | 237 | 17072017 238 | 17072017 239 | 17072017 240 | 30072017 241 | 099 242 | 243 | 244 | " . $nossoNumeroSondado . " 245 | 00000 246 | 00000 247 | 00 248 | 00 249 | 00 250 | 000000000123456 251 | 0 252 | 0 253 | 0 254 | 1 255 | 00 256 | 00000000000000000 257 | 00000000000000000 258 | 000000000000000 259 | 000000000000000 260 | 000000000000110 261 | "); 262 | 263 | $titulo = new Titulo(); 264 | $titulo->carregarPorXML($xml); 265 | 266 | $this->assertEquals($nossoNumeroEsperado, $titulo->getNossoNumero()); 267 | } 268 | 269 | } 270 | -------------------------------------------------------------------------------- /src/Titulo.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class Titulo implements PropriedadesExportaveisParaArrayInterface, PropriedadesImportaveisPorXMLInterface { 26 | 27 | /** @property string $nossoNumero Número do Título no Banco. */ 28 | private $nossoNumero; 29 | 30 | /** @property string $seuNumero Número do Título no cliente. Opcional. */ 31 | private $seuNumero; 32 | 33 | /** @property \DateTime $dataVencimento Data de vencimento do título. */ 34 | private $dataVencimento; 35 | 36 | /** @property \DateTime $dataEmissao Data de emissão do Título. */ 37 | private $dataEmissao; 38 | 39 | /** @property int $especie Código da Espécie do Documento. */ 40 | private $especie; 41 | 42 | /** @property float $valor Valor nominal do título, com 2 casas decimais. */ 43 | private $valor; 44 | 45 | /** @property string $mensagem Mensagem do boleto. */ 46 | private $mensagem; 47 | 48 | /** @property InstrucoesDeTitulo $instrucoes Instruções do que o banco Santander deve fazer com o título bancário. */ 49 | private $instrucoes; 50 | 51 | /** Cria uma nova instância de Titulo 52 | * 53 | * @param float $valor Valor nominal do título com 2 casas decimais. 54 | * @param string $nossoNumero Número do Título no Banco. 55 | * @param string $seuNumero Número do Título no cliente. 56 | * @param \DateTime $dataVencimento Data de vencimento do título. 57 | * @param string $mensagem Mensagem do boleto. 58 | * @param \DateTime $dataEmissao Data de emissão do Título. 59 | * @param int $especie Código da Espécie do Documento. 60 | * @param \TIExpert\WSBoletoSantander\InstrucoesDeTitulo $instrucoes Instruções do que o banco Santander deve fazer com o título bancário. 61 | */ 62 | public function __construct($valor = 0, $nossoNumero = "0", $seuNumero = NULL, $dataVencimento = NULL, $mensagem = NULL, $dataEmissao = NULL, $especie = NULL, InstrucoesDeTitulo $instrucoes = NULL) { 63 | if (is_null($dataVencimento)) { 64 | $dataVencimento = new \DateTime(); 65 | } 66 | 67 | if (is_null($dataEmissao)) { 68 | $dataEmissao = new \DateTime(); 69 | } 70 | 71 | $this->setNossoNumero($nossoNumero); 72 | $this->setSeuNumero($seuNumero); 73 | $this->setDataVencimento($dataVencimento); 74 | $this->setMensagem($mensagem); 75 | $this->setDataEmissao($dataEmissao); 76 | $this->setEspecie($especie); 77 | $this->setValor($valor); 78 | $this->setInstrucoes($instrucoes); 79 | } 80 | 81 | /** Obtém o número do título no banco. 82 | * 83 | * @return string 84 | */ 85 | public function getNossoNumero() { 86 | return $this->nossoNumero; 87 | } 88 | 89 | /** Obtém o número do título no banco com seu dígito verificador 90 | * 91 | * @return string 92 | */ 93 | public function getNossoNumeroComDigito() { 94 | return $this->nossoNumero . $this->calcularDigitoVerificador($this->nossoNumero); 95 | } 96 | 97 | /** Obtém o número do Título no cliente. 98 | * 99 | * @return string 100 | */ 101 | public function getSeuNumero() { 102 | return $this->seuNumero; 103 | } 104 | 105 | /** Obtém a data de vencimento do título. 106 | * 107 | * @return \DateTime 108 | */ 109 | public function getDataVencimento() { 110 | return $this->dataVencimento; 111 | } 112 | 113 | /** Obtém a data de emissão do Título. 114 | * 115 | * @return \DateTime 116 | */ 117 | public function getDataEmissao() { 118 | return $this->dataEmissao; 119 | } 120 | 121 | /** Obtém o código da Espécie do Documento. 122 | * 123 | * @return int 124 | */ 125 | public function getEspecie() { 126 | return $this->especie; 127 | } 128 | 129 | /** Obtém o valor nominal do título, com 2 casas decimais. 130 | * 131 | * @return float 132 | */ 133 | public function getValor() { 134 | return $this->valor; 135 | } 136 | 137 | /** Obtém a mensagem do boleto. 138 | * 139 | * @return string 140 | */ 141 | public function getMensagem() { 142 | return $this->mensagem; 143 | } 144 | 145 | /** Obtém as instruções do que o banco Santander deve fazer com o título bancário. 146 | * 147 | * @return InstrucoesDeTitulo 148 | */ 149 | public function getInstrucoes() { 150 | return $this->instrucoes; 151 | } 152 | 153 | /** Determina o número do título no banco. 154 | * 155 | * @param string $nossoNumero Número do Título no Banco. 156 | * @return \TIExpert\WSBoletoSantander\Titulo 157 | */ 158 | public function setNossoNumero($nossoNumero) { 159 | $this->nossoNumero = $nossoNumero; 160 | return $this; 161 | } 162 | 163 | /** Determina o número do Título no cliente. 164 | * 165 | * @param string $seuNumero Número do Título no cliente. 166 | * @return \TIExpert\WSBoletoSantander\Titulo 167 | */ 168 | public function setSeuNumero($seuNumero) { 169 | $this->seuNumero = $seuNumero; 170 | return $this; 171 | } 172 | 173 | /** Determina a data de vencimento do título. 174 | * 175 | * @param \DateTime $dataVencimento Data de vencimento do título. 176 | * @return \TIExpert\WSBoletoSantander\Titulo 177 | */ 178 | public function setDataVencimento($dataVencimento) { 179 | try { 180 | $this->dataVencimento = Util::converterParaDateTime($dataVencimento); 181 | } catch (\Exception $ex) { 182 | throw $ex; 183 | } 184 | return $this; 185 | } 186 | 187 | /** Determina a data de emissão do Título. 188 | * 189 | * @param \DateTime $dataEmissao Data de emissão do Título. 190 | * @return \TIExpert\WSBoletoSantander\Titulo 191 | */ 192 | public function setDataEmissao($dataEmissao) { 193 | try { 194 | $this->dataEmissao = Util::converterParaDateTime($dataEmissao); 195 | } catch (\Exception $ex) { 196 | throw $ex; 197 | } 198 | return $this; 199 | } 200 | 201 | /** Determina o código da Espécie do Documento. 202 | * 203 | * @param int $especie Código da Espécie do Documento. 204 | * @return \TIExpert\WSBoletoSantander\Titulo 205 | */ 206 | public function setEspecie($especie) { 207 | $this->especie = $especie; 208 | return $this; 209 | } 210 | 211 | /** Determina o valor nominal do título, com 2 casas decimais. 212 | * 213 | * @param float $valor Valor nominal do título, com 2 casas decimais. 214 | * @return \TIExpert\WSBoletoSantander\Titulo 215 | */ 216 | public function setValor($valor) { 217 | $this->valor = $valor; 218 | return $this; 219 | } 220 | 221 | /** Determina as instruções do que o banco Santander deve fazer com o título bancário. 222 | * 223 | * @param \TIExpert\WSBoletoSantander\InstrucoesDeTitulo $instrucoes 224 | * @return \TIExpert\WSBoletoSantander\Titulo 225 | */ 226 | public function setInstrucoes(InstrucoesDeTitulo $instrucoes = NULL) { 227 | $this->instrucoes = $instrucoes; 228 | return $this; 229 | } 230 | 231 | /** Determina a mensagem do boleto. 232 | * 233 | * @param string $mensagem Mensagem do boleto. 234 | * @return \TIExpert\WSBoletoSantander\Titulo 235 | */ 236 | public function setMensagem($mensagem) { 237 | $this->mensagem = $mensagem; 238 | return $this; 239 | } 240 | 241 | /** Calcula o dígito do campo nosso número 242 | * 243 | * return int 244 | */ 245 | private function calcularDigitoVerificador($numero) { 246 | $digito = 0; 247 | $multiplicador = 2; 248 | $total = 0; 249 | $algarismosInvertidos = array_reverse(str_split($numero)); 250 | 251 | foreach ($algarismosInvertidos as $algarismo) { 252 | $total += $multiplicador * $algarismo; 253 | 254 | if (++$multiplicador > 9) { 255 | $multiplicador = 2; 256 | } 257 | } 258 | 259 | $modulo = $total % 11; 260 | if ($modulo > 1) { 261 | $digito = 11 - $modulo; 262 | } 263 | 264 | return $digito; 265 | } 266 | 267 | /** Exporta um array associativo no qual as chaves são as propriedades representadas como no WebService do Santander 268 | * 269 | * @return array 270 | */ 271 | public function exportarArray() { 272 | $formatoDataPadrao = Config::getInstance()->getGeral("formato_data"); 273 | 274 | $array["TITULO.NOSSO-NUMERO"] = str_pad($this->getNossoNumeroComDigito(), 13, "0", STR_PAD_LEFT); 275 | $array["TITULO.SEU-NUMERO"] = str_pad($this->getSeuNumero(), 15, "0", STR_PAD_LEFT); 276 | $array["TITULO.DT-VENCTO"] = $this->getDataVencimento()->format($formatoDataPadrao); 277 | $array["TITULO.DT-EMISSAO"] = $this->getDataEmissao()->format($formatoDataPadrao); 278 | $array["TITULO.ESPECIE"] = $this->getEspecie(); 279 | $array["TITULO.VL-NOMINAL"] = number_format($this->getValor() * 100, 0, "", ""); 280 | $array["MENSAGEM"] = wordwrap($this->getMensagem(), 100, "\r\n"); 281 | return $array; 282 | } 283 | 284 | /** Carrega as propriedades da instância usando a estrutura XML 285 | * 286 | * @param \DOMDocument $xml Estrutura XML legível 287 | */ 288 | public function carregarPorXML(\DOMDocument $xml) { 289 | $leitor = new LeitorSimplesXML($xml); 290 | 291 | $instrucoes = new InstrucoesDeTitulo(); 292 | $instrucoes->carregarPorXML($xml); 293 | $this->setInstrucoes($instrucoes); 294 | 295 | $this->setDataEmissao($leitor->getValorNo("dtEmissao")); 296 | $this->setDataVencimento($leitor->getValorNo("dtVencto")); 297 | $this->setEspecie($leitor->getValorNo("especie")); 298 | $this->setMensagem($leitor->getValorNo("mensagem")); 299 | $this->setNossoNumero($this->removerUltimoCaracter($leitor->getValorNo("nossoNumero"))); 300 | $this->setSeuNumero($leitor->getValorNo("seuNumero")); 301 | $this->setValor($leitor->getValorNo("vlNominal") / 100); 302 | } 303 | 304 | /** Remove o último caracter da string 305 | * 306 | * @return string 307 | */ 308 | private function removerUltimoCaracter($string) { 309 | $corte = strlen($string) - 1; 310 | $stringSemUltimoCaracter = substr($string, 0, $corte); 311 | return $stringSemUltimoCaracter; 312 | } 313 | 314 | } 315 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tests/InstrucoesDeTituloTest.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | class InstrucoesDeTituloTest extends PHPUnit_Framework_TestCase { 28 | 29 | private static $faker; 30 | private static $instrucoesObj; 31 | 32 | public static function setUpBeforeClass() { 33 | parent::setUpBeforeClass(); 34 | self::$instrucoesObj = new InstrucoesDeTitulo(); 35 | self::$faker = \Faker\Factory::create("pt_BR"); 36 | } 37 | 38 | /** 39 | * @author Denys Xavier 40 | * @test 41 | */ 42 | public function testeAcessorDaPropriedadeMulta() { 43 | $validParam = 100.99; 44 | 45 | self::$instrucoesObj->setMulta($validParam); 46 | $this->assertEquals($validParam, self::$instrucoesObj->getMulta()); 47 | } 48 | 49 | /** 50 | * @author Denys Xavier 51 | * @test 52 | */ 53 | public function testeAcessorDaPropriedadeMultarApos() { 54 | $validParam = 99; 55 | 56 | self::$instrucoesObj->setMultarApos($validParam); 57 | $this->assertEquals($validParam, self::$instrucoesObj->getMultarApos()); 58 | } 59 | 60 | /** 61 | * @author Denys Xavier 62 | * @test 63 | */ 64 | public function testeAcessorDaPropriedadeJuros() { 65 | $validParam = 105.02; 66 | 67 | self::$instrucoesObj->setJuros($validParam); 68 | $this->assertEquals($validParam, self::$instrucoesObj->getJuros()); 69 | } 70 | 71 | /** 72 | * @author Denys Xavier 73 | * @test 74 | */ 75 | public function testeAcessorDaPropriedadeTipoDesconto() { 76 | $validParam = 0; 77 | 78 | self::$instrucoesObj->setTipoDesconto($validParam); 79 | $this->assertEquals($validParam, self::$instrucoesObj->getTipoDesconto()); 80 | } 81 | 82 | /** 83 | * @author Denys Xavier 84 | * @test 85 | */ 86 | public function aoTentarSetarTipoDescontoComoNuloEntaoOValorConfiguradoDeveSerCarregado() { 87 | self::$instrucoesObj->setTipoDesconto(NULL); 88 | $this->assertEquals(Config::getInstance()->getInstrucao("tipo_desconto"), self::$instrucoesObj->getTipoDesconto()); 89 | } 90 | 91 | /** 92 | * @author Denys Xavier 93 | * @test 94 | */ 95 | public function testeAcessorDaPropriedadeValorDesconto() { 96 | $validParam = 12000.01; 97 | 98 | self::$instrucoesObj->setValorDesconto($validParam); 99 | $this->assertEquals($validParam, self::$instrucoesObj->getValorDesconto()); 100 | } 101 | 102 | /** 103 | * @author Denys Xavier 104 | * @test 105 | */ 106 | public function aoTentarSetarValorDescontoComoNuloEntaoOValorConfiguradoDeveSerCarregado() { 107 | self::$instrucoesObj->setValorDesconto(NULL); 108 | $this->assertEquals(Config::getInstance()->getInstrucao("valor_desconto"), self::$instrucoesObj->getValorDesconto()); 109 | } 110 | 111 | /** 112 | * @author Denys Xavier 113 | * @test 114 | */ 115 | public function testeAcessorDaPropriedadeDataLimiteDesconto() { 116 | $validParam = new \DateTime("2016-11-28"); 117 | 118 | self::$instrucoesObj->setDataLimiteDesconto($validParam); 119 | $this->assertEquals($validParam, self::$instrucoesObj->getDataLimiteDesconto()); 120 | } 121 | 122 | /** 123 | * @author Denys Xavier 124 | * @test 125 | * @expectedException \Exception 126 | */ 127 | public function testeAcessorDaPropriedadeDataLimiteDescontoComParametroInvalido() { 128 | $invalidParam = array(); 129 | 130 | self::$instrucoesObj->setDataLimiteDesconto($invalidParam); 131 | } 132 | 133 | /** 134 | * @author Denys Xavier 135 | * @test 136 | */ 137 | public function aoTentarSetarDataLimiteDescontoComoNuloEntaoOValorConfiguradoDeveSerCarregado() { 138 | self::$instrucoesObj->setDataLimiteDesconto(NULL); 139 | $dataEsperadaObj = new \DateTime(Config::getInstance()->getInstrucao("data_limite_desconto")); 140 | 141 | $dataEsperada = $dataEsperadaObj->format("Y-m-d"); 142 | $resultado = self::$instrucoesObj->getDataLimiteDesconto()->format("Y-m-d"); 143 | 144 | $this->assertEquals($dataEsperada, $resultado); 145 | } 146 | 147 | /** 148 | * @author Denys Xavier 149 | * @test 150 | */ 151 | public function testeAcessorDaPropriedadeValorAbatimento() { 152 | $validParam = 12000.01; 153 | 154 | self::$instrucoesObj->setValorAbatimento($validParam); 155 | $this->assertEquals($validParam, self::$instrucoesObj->getValorAbatimento()); 156 | } 157 | 158 | /** 159 | * @author Denys Xavier 160 | * @test 161 | */ 162 | public function testeAcessorDaPropriedadeTipoProtesto() { 163 | $validParam = 0; 164 | 165 | self::$instrucoesObj->setTipoProtesto($validParam); 166 | $this->assertEquals($validParam, self::$instrucoesObj->getTipoProtesto()); 167 | } 168 | 169 | /** 170 | * @author Denys Xavier 171 | * @test 172 | */ 173 | public function aoTentarSetarTipoProtestoComoNuloEntaoOValorConfiguradoDeveSerCarregado() { 174 | self::$instrucoesObj->setTipoProtesto(NULL); 175 | $this->assertEquals(Config::getInstance()->getInstrucao("tipo_protesto"), self::$instrucoesObj->getTipoProtesto()); 176 | } 177 | 178 | /** 179 | * @author Denys Xavier 180 | * @test 181 | */ 182 | public function testeAcessorDaPropriedadeProtestarApos() { 183 | $validParam = 99; 184 | 185 | self::$instrucoesObj->setProtestarApos($validParam); 186 | $this->assertEquals($validParam, self::$instrucoesObj->getProtestarApos()); 187 | } 188 | 189 | /** 190 | * @author Denys Xavier 191 | * @test 192 | */ 193 | public function testeAcessorDaPropriedadeBaixarApos() { 194 | $validParam = 99; 195 | 196 | self::$instrucoesObj->setBaixarApos($validParam); 197 | $this->assertEquals($validParam, self::$instrucoesObj->getBaixarApos()); 198 | } 199 | 200 | /** 201 | * @author Denys Xavier 202 | * @test 203 | */ 204 | public function aoTentarSetarBaixarAposComoNuloEntaoOValorConfiguradoDeveSerCarregado() { 205 | self::$instrucoesObj->setBaixarApos(NULL); 206 | $this->assertEquals(Config::getInstance()->getInstrucao("baixar_apos"), self::$instrucoesObj->getBaixarApos()); 207 | } 208 | 209 | /** 210 | * @author Denys Xavier 211 | * @test 212 | */ 213 | public function testeAcessorDaPropriedadeTipoPagamento() { 214 | $validParam = 2; 215 | 216 | self::$instrucoesObj->setTipoPagamento($validParam); 217 | $this->assertEquals($validParam, self::$instrucoesObj->getTipoPagamento()); 218 | } 219 | 220 | /** 221 | * @author Denys Xavier 222 | * @test 223 | */ 224 | public function oValorPadraoDaPropriedadeTipoPagamentoEh1() { 225 | $obj = new InstrucoesDeTitulo(); 226 | $this->assertEquals(1, $obj->getTipoPagamento()); 227 | } 228 | 229 | /** 230 | * @author Denys Xavier 231 | * @test 232 | */ 233 | public function testeAcessorDaPropriedadeQtdParciais() { 234 | $validParam = mt_rand(1, 99); 235 | 236 | self::$instrucoesObj->setQtdParciais($validParam); 237 | $this->assertEquals($validParam, self::$instrucoesObj->getQtdParciais()); 238 | } 239 | 240 | /** 241 | * @author Denys Xavier 242 | * @test 243 | */ 244 | public function oValorPadraoDaPropriedadeQtdParciaisEhZero() { 245 | $obj = new InstrucoesDeTitulo(); 246 | $this->assertEquals("0", $obj->getQtdParciais()); 247 | } 248 | 249 | /** 250 | * @author Denys Xavier 251 | * @test 252 | */ 253 | public function testeAcessorDaPropriedadeTipoValor() { 254 | $validParam = 2; 255 | 256 | self::$instrucoesObj->setTipoValor($validParam); 257 | $this->assertEquals($validParam, self::$instrucoesObj->getTipoValor()); 258 | } 259 | 260 | /** 261 | * @author Denys Xavier 262 | * @test 263 | */ 264 | public function testeAcessorDaPropriedadePercentualMinimo() { 265 | $validParam = self::$faker->randomFloat(); 266 | 267 | self::$instrucoesObj->setPercentualMinimo($validParam); 268 | $this->assertEquals($validParam, self::$instrucoesObj->getPercentualMinimo()); 269 | } 270 | 271 | /** 272 | * @author Denys Xavier 273 | * @test 274 | */ 275 | public function testeAcessorDaPropriedadePercentualMaximo() { 276 | $validParam = self::$faker->randomFloat(); 277 | 278 | self::$instrucoesObj->setPercentualMaximo($validParam); 279 | $this->assertEquals($validParam, self::$instrucoesObj->getPercentualMaximo()); 280 | } 281 | 282 | /** 283 | * @author Denys Xavier 284 | * @test 285 | */ 286 | public function propriedadesPercentualMinimoEPercentualMaximoDevemTer5CasasAposAVirgulaQuandoExportados() { 287 | $maisDe5DigitosDecimais = 12345.123456789; 288 | $maisDe5DigitosDecimaisEsperado = "12345,12345"; 289 | $menosDe5DigitosDecimais = 54321.12; 290 | $menosDe5DigitosDecimaisEsperado = "54321,12000"; 291 | 292 | $obj = new InstrucoesDeTitulo(); 293 | 294 | $obj->setPercentualMinimo($maisDe5DigitosDecimais); 295 | $obj->setPercentualMaximo($maisDe5DigitosDecimais); 296 | 297 | $exportacao = $obj->exportarArray(); 298 | $this->assertEquals($maisDe5DigitosDecimaisEsperado, $exportacao["TITULO.VL-PERC-MINIMO"]); 299 | $this->assertEquals($maisDe5DigitosDecimaisEsperado, $exportacao["TITULO.VL-PERC-MAXIMO"]); 300 | 301 | $obj->setPercentualMinimo($menosDe5DigitosDecimais); 302 | $obj->setPercentualMaximo($menosDe5DigitosDecimais); 303 | 304 | $exportacao = $obj->exportarArray(); 305 | $this->assertEquals($menosDe5DigitosDecimaisEsperado, $exportacao["TITULO.VL-PERC-MINIMO"]); 306 | $this->assertEquals($menosDe5DigitosDecimaisEsperado, $exportacao["TITULO.VL-PERC-MAXIMO"]); 307 | } 308 | 309 | /** 310 | * @author Denys Xavier 311 | * @test 312 | */ 313 | public function oArrayExportadoDevePossuirAsMesmasChavesUtilizadasPeloWSdoBanco() { 314 | $chaveInstrucao = array("TITULO.PC-MULTA", "TITULO.QT-DIAS-MULTA", "TITULO.PC-JURO", "TITULO.TP-DESC", "TITULO.VL-DESC", "TITULO.DT-LIMI-DESC", "TITULO.VL-ABATIMENTO", "TITULO.TP-PROTESTO", "TITULO.QT-DIAS-PROTESTO", "TITULO.QT-DIAS-BAIXA", "TITULO.TP-PAGAMENTO", "TITULO.QT-PARCIAIS", "TITULO.TP-VALOR", "TITULO.VL-PERC-MINIMO", "TITULO.VL-PERC-MAXIMO"); 315 | 316 | $export = self::$instrucoesObj->exportarArray(); 317 | 318 | foreach ($chaveInstrucao as $chave) { 319 | $this->assertArrayHasKey($chave, $export); 320 | } 321 | } 322 | 323 | } 324 | -------------------------------------------------------------------------------- /src/BoletoSantanderServico.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class BoletoSantanderServico { 27 | 28 | const TICKET_ENDPOINT = "https://ymbdlb.santander.com.br/dl-ticket-services/TicketEndpointService"; 29 | const COBRANCA_ENDPOINT = "https://ymbcash.santander.com.br/ymbsrv/CobrancaV2EndpointService"; 30 | 31 | /** @property \TIExpert\WSBoletoSantander\ComunicadorCurlSOAP $comunicador Referência ao objeto a ser usado como ponte de comunicação entre o serviço e a extensão cURL do PHP. */ 32 | private $comunicador; 33 | 34 | /** Cria uma nova instância de BoletoSantanderServico 35 | * 36 | * @param \TIExpert\WSBoletoSantander\ComunicadorCurlSOAP $comunicadorCurlSOAP Referência ao objeto a ser usado como ponte de comunicação entre o serviço e a extensão cURL do PHP. 37 | */ 38 | public function __construct(ComunicadorCurlSOAP $comunicadorCurlSOAP) { 39 | $this->comunicador = $comunicadorCurlSOAP; 40 | } 41 | 42 | /** Solicita um tíquete de segurança para inclusão do boleto no Santander 43 | * 44 | * @param \TIExpert\WSBoletoSantander\Boleto $boleto Boleto que deverá ser validado e pré-cadastrado no Santander 45 | * @return \TIExpert\WSBoletoSantander\Ticket 46 | */ 47 | public function solicitarTicketInclusao(Boleto $boleto) { 48 | return $this->gerarTicket($boleto); 49 | } 50 | 51 | /** Solicita um tíquete de segurança para sondagem de um boleto no Santander 52 | * 53 | * @param \TIExpert\WSBoletoSantander\Convenio $convenio 54 | * @return \TIExpert\WSBoletoSantander\Ticket 55 | */ 56 | public function solicitarTicketSondagem(Convenio $convenio) { 57 | return $this->gerarTicket($convenio); 58 | } 59 | 60 | /** Faz chamada a um serviço gerador de tíquete baseado no objeto informado 61 | * 62 | * @param \TIExpert\WSBoletoSantander\PropriedadesExportaveisParaArrayInterface $objeto Objeto passível de ter suas propriedades exportadas. 63 | * @return \TIExpert\WSBoletoSantander\Ticket 64 | */ 65 | private function gerarTicket(PropriedadesExportaveisParaArrayInterface $objeto) { 66 | $xml = $this->iniciarXmlSoapEnvelope(); 67 | 68 | $xml->startElementNs("impl", "create", "http://impl.webservice.dl.app.bsbr.altec.com/"); 69 | $xml->startElement("TicketRequest"); 70 | 71 | $xml->startElement("dados"); 72 | $this->anexarArrayMapeado($xml, $objeto); 73 | $xml->endElement(); 74 | 75 | $xml->writeElement("expiracao", 100); 76 | $xml->writeElement("sistema", "YMB"); 77 | $xml->endDocument(); 78 | 79 | $retorno = $this->executarServico(self::TICKET_ENDPOINT, $xml); 80 | 81 | $retornoDocumentoXML = $this->converterRespostaParaDOMDocument($retorno); 82 | return $this->processarRetornoParaTicket($retornoDocumentoXML); 83 | } 84 | 85 | /** Inclui um título a partir de um tíquete de segurança de inclusão 86 | * 87 | * @param \TIExpert\WSBoletoSantander\Ticket $ticket Tíquete de segurança de inclusão. 88 | * @return boolean 89 | */ 90 | public function incluirTitulo(Ticket $ticket) { 91 | $respostaXML = $this->procederTicket($ticket, "registraTitulo"); 92 | 93 | return $this->tituloFoiIncluidoComSucesso($respostaXML); 94 | } 95 | 96 | /** Sonda um título a partir de um tíquete de segurança de sondagem 97 | * 98 | * @param \TIExpert\WSBoletoSantander\Ticket $ticket Tíquete de segurança de sondagem. 99 | * @return \TIExpert\WSBoletoSantander\Boleto 100 | */ 101 | public function sondarTitulo(Ticket $ticket) { 102 | $respostaXML = $this->procederTicket($ticket, "consultaTitulo"); 103 | return $this->converterRespostaParaBoleto($respostaXML); 104 | } 105 | 106 | /** Encaminha um tíquete de segurança para endpoint do serviço do Santander 107 | * 108 | * @param \TIExpert\WSBoletoSantander\Ticket $ticket Tíquete a ser enviado. 109 | * @param string $acao Nome do método a ser executado no endpoint 110 | * @return \DOMDocument 111 | */ 112 | private function procederTicket(Ticket $ticket, $acao) { 113 | $xml = $this->criarEnvelopeParaTicket($ticket, $acao); 114 | $resposta = $this->executarServico(self::COBRANCA_ENDPOINT, $xml); 115 | return $this->converterRespostaParaDOMDocument($resposta); 116 | } 117 | 118 | /** Cria todo o XML do envelope SOAP contendo as informações do tíquete 119 | * 120 | * @param \TIExpert\WSBoletoSantander\Ticket $ticket Tíquete a ser envelopado 121 | * @param string $nomeAcao Nome do nó que será o método a ser chamado no endpoint 122 | * @return \XMLWriter 123 | */ 124 | private function criarEnvelopeParaTicket(Ticket $ticket, $nomeAcao) { 125 | $xml = $this->iniciarXmlSoapEnvelope(); 126 | $xml->startElement($nomeAcao); 127 | $xml->writeRaw($ticket->exportarParaXml("dto")); 128 | $xml->endDocument(); 129 | return $xml; 130 | } 131 | 132 | /** Inicia um novo objeto XMLWriter com o nó raiz Envelope, o nó Header e o nó Body aberto para receber conteúdo. 133 | * 134 | * @return \XMLWriter 135 | */ 136 | private function iniciarXmlSoapEnvelope() { 137 | $xml = new \XMLWriter(); 138 | $xml->openMemory(); 139 | 140 | $xml->startElementNs("soapenv", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/"); 141 | $xml->writeElementNs("soapenv", "Header", NULL); 142 | $xml->startElementNs("soapenv", "Body", NULL); 143 | 144 | return $xml; 145 | } 146 | 147 | /** Anexa um array associativo das propriedades exportáveis de uma classe no atual nó aberto do XMLWriter informado 148 | * 149 | * @param \XMLWriter $xml Objeto XMLWriter com o nó aberto onde será anexado o array 150 | * @param \TIExpert\WSBoletoSantander\PropriedadesExportaveisParaArrayInterface $objeto Instância que terá suas propriedades anexadas ao XML como um array 151 | */ 152 | private function anexarArrayMapeado(\XMLWriter $xml, PropriedadesExportaveisParaArrayInterface $objeto) { 153 | foreach ($objeto->exportarArray() as $key => $value) { 154 | $xml->startElement("entry"); 155 | $xml->writeElement("key", $key); 156 | $xml->writeElement("value", $value); 157 | $xml->endElement(); 158 | } 159 | } 160 | 161 | /** Executa o serviço usando o comunicador 162 | * 163 | * @param string $url URL do endpoint onde está localizado o serviço 164 | * @param \XMLWriter $xmlObject Objeto usado na escrita do XML que deve ser transmitido para o endpoint 165 | * @return string 166 | */ 167 | private function executarServico($url, \XMLWriter $xmlObject) { 168 | $endpointConfig = $this->comunicador->prepararConfiguracaoEndpoint($xmlObject->outputMemory()); 169 | return $this->comunicador->chamar($url, $endpointConfig); 170 | } 171 | 172 | /** Converte toda o xml respondido em forma de string pelo serviço em um objeto DOMDocument 173 | * 174 | * @param string $respostaString String de resposta 175 | * @return \DOMDocument 176 | * @throws \Exception 177 | */ 178 | private function converterRespostaParaDOMDocument($respostaString) { 179 | if ($this->comunicador->ehSOAPFaultComoString($respostaString)) { 180 | throw $this->comunicador->converterSOAPFaultStringParaException($respostaString); 181 | } 182 | 183 | $XML = new \DOMDocument(); 184 | $XML->loadXML($respostaString); 185 | 186 | return $XML; 187 | } 188 | 189 | /** Processa a resposta de uma chamada a um serviço de solicitação de tíquete de segurança 190 | * 191 | * @param \DOMDocument $resposta Resposta de uma chamada a um serviço de solicitação de tíquete de segurança 192 | * @return \TIExpert\WSBoletoSantander\Ticket 193 | * @throws \Exception 194 | */ 195 | private function processarRetornoParaTicket(\DOMDocument $resposta) { 196 | try { 197 | $ticket = $this->criarTiqueteAPartirDaResposta($resposta); 198 | return $ticket; 199 | } catch (\Exception $ex) { 200 | throw $ex; 201 | } 202 | } 203 | 204 | /** Cria um tíquete a partir de um XML de resposta de uma chamada a um serviço de solicitação de tíquete de segurança 205 | * 206 | * @param \DOMDocument $dom XML de resposta de uma chamada a um serviço de solicitação de tíquete de segurança 207 | * @return \TIExpert\WSBoletoSantander\Ticket 208 | * @throws \Exception 209 | */ 210 | private function criarTiqueteAPartirDaResposta(\DOMDocument $dom) { 211 | $leitor = new LeitorSimplesXML($dom); 212 | $retCode = $leitor->getValorNo("retCode"); 213 | 214 | if ($retCode === "0") { 215 | $ticket = new Ticket(); 216 | $ticket->setAutenticacao($leitor->getValorNo("ticket")); 217 | return $ticket; 218 | } 219 | 220 | throw new \Exception("O serviço de inclusão de título do Santander retornou um erro. Código: " . $retCode); 221 | } 222 | 223 | /** Verifica se um título foi incluído com sucesso no Santander a partir da resposta do serviço de inclusão de título 224 | * 225 | * @param \DOMDocument $dom Documento XML representando a resposta do serviço 226 | * @return boolean 227 | * @throws \Exception 228 | */ 229 | private function tituloFoiIncluidoComSucesso(\DOMDocument $dom) { 230 | try { 231 | $this->lancarExceptionSeRespostaForSOAPFault($dom); 232 | $this->processarErros($dom); 233 | } catch (\Exception $e) { 234 | throw $e; 235 | } 236 | 237 | return true; 238 | } 239 | 240 | /** Tenta converter um documento XML de reposta em uma instância da classe Boleto 241 | * 242 | * @param \DOMDocument $respostaXML Documento XML respondido pelo serviço 243 | * @return \TIExpert\WSBoletoSantander\Boleto 244 | */ 245 | private function converterRespostaParaBoleto(\DOMDocument $respostaXML) { 246 | $this->lancarExceptionSeRespostaForSOAPFault($respostaXML); 247 | $this->processarErros($respostaXML); 248 | 249 | $boleto = new Boleto(); 250 | $boleto->carregarPorXML($respostaXML); 251 | return $boleto; 252 | } 253 | 254 | /** Verifica se a resposta de um serviço é um SOAPFault. Em caso verdadeiro, uma Exception é lançada com a mensagem contendo a faultstring. 255 | * 256 | * @param \DOMDocument $dom Documento XML representando a resposta do serviço 257 | * @return NULL 258 | * @throws \Exception 259 | */ 260 | private function lancarExceptionSeRespostaForSOAPFault(\DOMDocument $dom) { 261 | $leitor = new LeitorSimplesXML($dom); 262 | try { 263 | $faultString = $leitor->getValorNo("faultstring"); 264 | throw new \Exception($faultString); 265 | } catch (\Exception $e) { 266 | return; 267 | } 268 | } 269 | 270 | /** Lança uma exceção contendo todos os erros informados na resposta do serviço de inclusão de título 271 | * 272 | * @param \DOMDocument $dom Documento XML representando a resposta do serviço 273 | * @throws \Exception 274 | */ 275 | private function processarErros(\DOMDocument $dom) { 276 | $leitor = new LeitorSimplesXML($dom); 277 | $errosStr = $leitor->getValorNo("descricaoErro"); 278 | 279 | $errorDesc = $this->gerarArrayDeErrosAPartirDaString($errosStr); 280 | 281 | if (count($errorDesc) > 0) { 282 | throw new \Exception("Serviço do Santander retornou os seguintes erros: " . implode("; ", $errorDesc)); 283 | } 284 | } 285 | 286 | /** Gera um array a partir de uma string com layout definido pelo Santander contendo a descrição de todos os erros encontrados na requisição do serviço 287 | * 288 | * @param string $errosStr String de formato determinado contendo os erros encontrados na requisição do serviço 289 | * @return array 290 | */ 291 | private function gerarArrayDeErrosAPartirDaString($errosStr) { 292 | $errosRetornados = array(); 293 | 294 | if ($errosStr != "") { 295 | $erros = explode("\n", $errosStr); 296 | foreach ($erros as $erro) { 297 | list($codigo, $descricao) = explode("-", $erro); 298 | 299 | if (trim($codigo) == "00000") { 300 | break; 301 | } 302 | 303 | $errosRetornados[] = trim($descricao); 304 | } 305 | } 306 | 307 | return $errosRetornados; 308 | } 309 | 310 | } 311 | -------------------------------------------------------------------------------- /src/InstrucoesDeTitulo.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class InstrucoesDeTitulo implements PropriedadesExportaveisParaArrayInterface, PropriedadesImportaveisPorXMLInterface { 26 | 27 | /** @property float $multa Percentual da multa com 2 decimais. Opcional. */ 28 | private $multa; 29 | 30 | /** @property int $multarApos Quantidade de dias após o vencimento do título para incidência da multa. Opcional. */ 31 | private $multarApos; 32 | 33 | /** @property float $juros Percentual de juros com 2 decimais. Opcional. */ 34 | private $juros; 35 | 36 | /** @property int $tipoDesconto Tipo de desconto a ser aplicado. Sendo: 0 = isento; 1 = Valor fixo até a data informada; 2 = Valor por antecipação dia corrido; 3 = Valor por antecipação dia útil. */ 37 | private $tipoDesconto; 38 | 39 | /** @property float $valorDesconto Valor ou percentual de desconto, com 2 casas decimais. */ 40 | private $valorDesconto; 41 | 42 | /** @property \DateTime $dataLimiteDesconto Data limite para Desconto. */ 43 | private $dataLimiteDesconto; 44 | 45 | /** @property float $valorAbatimento Valor do abatimento. Opcional. */ 46 | private $valorAbatimento; 47 | 48 | /** @property int $tipoProtesto Tipo de protesto a ser adotado. Sendo: 0 = Nao Protestar; 1 = Protestar dias corridos; 2 = Protestar dias úteis; 3 = Utilizar Perfil Cedente. */ 49 | private $tipoProtesto; 50 | 51 | /** @property int $protestarApos Quantidade de dias após o vencimento para protesto. */ 52 | private $protestarApos; 53 | 54 | /** @property int $baixarApos Quantidade de dias após o vencimento para baixa/devolução do título. */ 55 | private $baixarApos; 56 | 57 | /** @property int $tipoPagamento Identificação do tipo de pagamento. Sendo: 1 = Conforme Registro; 2 = Divergente; 3 = Parcial. */ 58 | private $tipoPagamento; 59 | 60 | /** @property int $qtdParciais Quantidade de pagamentos possíveis. */ 61 | private $qtdParciais; 62 | 63 | /** @property int $tipoValor Tipo de valor informado. Sendo: 1 = Percentual; 2 = Valor. */ 64 | private $tipoValor; 65 | 66 | /** @property float $percentualMinimo Valor mínimo ou percentual mínimo do título. */ 67 | private $percentualMinimo; 68 | 69 | /** @property float $percentualMaximo Valor máximo ou percentual máximo do título. */ 70 | private $percentualMaximo; 71 | 72 | /** Cria uma nova instância de InstrucoesDeTitulo 73 | * 74 | * @param float $multa Percentual da multa com 2 decimais. 75 | * @param int $multarApos Quantidade de dias após o vencimento do título para incidência da multa. 76 | * @param float $juros Percentual de juros com 2 decimais. 77 | * @param int $tipoDesconto Tipo de desconto a ser aplicado. Sendo: 0 = isento; 1 = Valor fixo até a data informada; 2 = Percentual até a data informada; 3 = Valor por antecipação dia corrido. 78 | * @param float $valorDesconto Valor ou percentual de desconto, com 2 casas decimais. 79 | * @param \DateTime $dataLimiteDesconto Data limite para Desconto. 80 | * @param float $valorAbatimento Valor do abatimento. 81 | * @param int $tipoProtesto Tipo de protesto a ser adotado. Sendo: 0 = Nao Protestar; 1 = Protestar dias corridos; 2 = Protestar dias úteis; 3 = Utilizar Perfil Cedente. 82 | * @param int $protestarApos Quantidade de dias após o vencimento para protesto. 83 | * @param int $baixarApos Quantidade de dias após o vencimento para baixa/devolução do título. 84 | * @param int $tipoPagamento Identificação do tipo de pagamento. Sendo: 1 = Conforme Registro; 2 = Divergente; 3 = Parcial. 85 | * @param int $qtdParciais Quantidade de pagamentos possíveis. 86 | * @param int $tipoValor Tipo de valor informado. Sendo: 1 = Percentual; 2 = Valor. 87 | * @param float $percentualMinimo Valor mínimo ou percentual mínimo do título. 88 | * @param float $percentualMaximo Valor máximo ou percentual máximo do título. 89 | */ 90 | public function __construct($multa = NULL, $multarApos = NULL, $juros = NULL, $tipoDesconto = NULL, $valorDesconto = NULL, $dataLimiteDesconto = NULL, $valorAbatimento = NULL, $tipoProtesto = NULL, $protestarApos = NULL, $baixarApos = NULL, $tipoPagamento = 1, $qtdParciais = 0, $tipoValor = 1, $percentualMinimo = 0, $percentualMaximo = 0) { 91 | $this->setMulta($multa); 92 | $this->setMultarApos($multarApos); 93 | $this->setJuros($juros); 94 | $this->setTipoDesconto($tipoDesconto); 95 | $this->setValorDesconto($valorDesconto); 96 | $this->setDataLimiteDesconto($dataLimiteDesconto); 97 | $this->setValorAbatimento($valorAbatimento); 98 | $this->setTipoProtesto($tipoProtesto); 99 | $this->setProtestarApos($protestarApos); 100 | $this->setBaixarApos($baixarApos); 101 | $this->setTipoPagamento($tipoPagamento); 102 | $this->setQtdParciais($qtdParciais); 103 | $this->setTipoValor($tipoValor); 104 | $this->setPercentualMinimo($percentualMinimo); 105 | $this->setPercentualMaximo($percentualMaximo); 106 | } 107 | 108 | /** Obtém o percentual da multa, com 2 decimais. 109 | * 110 | * @return float 111 | */ 112 | public function getMulta() { 113 | return $this->multa; 114 | } 115 | 116 | /** Obtém a quantidade de dias após o vencimento do título para incidência da multa. 117 | * 118 | * @return int 119 | */ 120 | public function getMultarApos() { 121 | return $this->multarApos; 122 | } 123 | 124 | /** Obtém o percentual de juros com 2 decimais. 125 | * 126 | * @return float 127 | */ 128 | public function getJuros() { 129 | return $this->juros; 130 | } 131 | 132 | /** Obtém o tipo de desconto a ser aplicado. 133 | * 134 | * @return int 135 | */ 136 | public function getTipoDesconto() { 137 | return $this->tipoDesconto; 138 | } 139 | 140 | /** Obtém o valor ou percentual de desconto com 2 casas decimais. 141 | * 142 | * @return type 143 | */ 144 | public function getValorDesconto() { 145 | return $this->valorDesconto; 146 | } 147 | 148 | /** Obtém a data limite para Desconto. 149 | * 150 | * @return \DateTime 151 | */ 152 | public function getDataLimiteDesconto() { 153 | return $this->dataLimiteDesconto; 154 | } 155 | 156 | /** Obtém o valor do abatimento. 157 | * 158 | * @return float 159 | */ 160 | public function getValorAbatimento() { 161 | return $this->valorAbatimento; 162 | } 163 | 164 | /** Obtém o tipo de protesto a ser adotado. 165 | * 166 | * @return int 167 | */ 168 | public function getTipoProtesto() { 169 | return $this->tipoProtesto; 170 | } 171 | 172 | /** Obtém a quantidade de dias após o vencimento para protesto. 173 | * 174 | * @return int 175 | */ 176 | public function getProtestarApos() { 177 | return $this->protestarApos; 178 | } 179 | 180 | /** Obtém a quantidade de dias após o vencimento para baixa/devolução do título. 181 | * 182 | * @return int 183 | */ 184 | public function getBaixarApos() { 185 | return $this->baixarApos; 186 | } 187 | 188 | /** Obtém a identificação do tipo de pagamento. 189 | * 190 | * @return int 191 | */ 192 | public function getTipoPagamento() { 193 | return $this->tipoPagamento; 194 | } 195 | 196 | /** Obtém a quantidade de pagamentos possíveis. 197 | * 198 | * @return int 199 | */ 200 | public function getQtdParciais() { 201 | return $this->qtdParciais; 202 | } 203 | 204 | /** Obtém o tipo de valor informado 205 | * 206 | * @return int 207 | */ 208 | public function getTipoValor() { 209 | return $this->tipoValor; 210 | } 211 | 212 | /** Obtém o valor mínimo ou percentual mínimo do título. 213 | * 214 | * @return float 215 | */ 216 | public function getPercentualMinimo() { 217 | return $this->percentualMinimo; 218 | } 219 | 220 | /** Obtém o valor máximo ou percentual máximo do título. 221 | * 222 | * @return float 223 | */ 224 | public function getPercentualMaximo() { 225 | return $this->percentualMaximo; 226 | } 227 | 228 | /** Determina o percentual da multa, com 2 decimais. 229 | * 230 | * @param float $multa Percentual da multa, com 2 decimais. 231 | * @return \TIExpert\WSBoletoSantander\Titulo 232 | */ 233 | public function setMulta($multa) { 234 | $this->multa = $multa; 235 | return $this; 236 | } 237 | 238 | /** Determina a quantidade de dias após o vencimento do título para incidência da multa. 239 | * 240 | * @param int $multarApos Quantidade de dias após o vencimento do título para incidência da multa. 241 | * @return \TIExpert\WSBoletoSantander\Titulo 242 | */ 243 | public function setMultarApos($multarApos) { 244 | $this->multarApos = $multarApos; 245 | return $this; 246 | } 247 | 248 | /** Determina o percentual de juros, com 2 decimais. 249 | * 250 | * @param float $juros Percentual de juros com 2 decimais. 251 | * @return \TIExpert\WSBoletoSantander\Titulo 252 | */ 253 | public function setJuros($juros) { 254 | $this->juros = $juros; 255 | return $this; 256 | } 257 | 258 | /** Determina o tipo de desconto a ser aplicado. 259 | * 260 | * Se NULL for informado, então, o valor de tipo será o mesmo que está configurado no arquivo config.ini 261 | * 262 | * @param int $tipoDesconto Tipo de desconto a ser aplicado. Sendo: 0 = isento; 1 = Valor fixo até a data informada; 2 = Percentual até a data informada; 3 = Valor por antecipação dia corrido. 263 | * @return \TIExpert\WSBoletoSantander\Titulo 264 | */ 265 | public function setTipoDesconto($tipoDesconto) { 266 | if (is_null($tipoDesconto)) { 267 | $tipoDesconto = Config::getInstance()->getInstrucao("tipo_desconto"); 268 | } 269 | 270 | $this->tipoDesconto = $tipoDesconto; 271 | return $this; 272 | } 273 | 274 | /** Determina o valor ou percentual de desconto com 2 casas decimais. 275 | * 276 | * Se NULL for informado, então, o valor do desconto será o mesmo que está configurado no arquivo config.ini 277 | * 278 | * @param float $valorDesconto Valor ou percentual de desconto com 2 casas decimais. 279 | * @return \TIExpert\WSBoletoSantander\Titulo 280 | */ 281 | public function setValorDesconto($valorDesconto) { 282 | if (is_null($valorDesconto)) { 283 | $valorDesconto = Config::getInstance()->getInstrucao("valor_desconto"); 284 | } 285 | 286 | $this->valorDesconto = $valorDesconto; 287 | return $this; 288 | } 289 | 290 | /** Determina a data limite para Desconto. 291 | * 292 | * Se NULL for informado, então, a data será o mesmo que está configurada no arquivo config.ini 293 | * 294 | * @param \DateTime $dataLimiteDesconto Data limite para Desconto. 295 | * @return \TIExpert\WSBoletoSantander\Titulo 296 | */ 297 | public function setDataLimiteDesconto($dataLimiteDesconto) { 298 | if (is_null($dataLimiteDesconto)) { 299 | $dataLimiteDesconto = Config::getInstance()->getInstrucao("data_limite_desconto"); 300 | } 301 | 302 | try { 303 | $this->dataLimiteDesconto = Util::converterParaDateTime($dataLimiteDesconto); 304 | } catch (\Exception $ex) { 305 | throw $ex; 306 | } 307 | return $this; 308 | } 309 | 310 | /** Determina o valor do abatimento. 311 | * 312 | * @param float $valorAbatimento Valor do abatimento. 313 | * @return \TIExpert\WSBoletoSantander\Titulo 314 | */ 315 | public function setValorAbatimento($valorAbatimento) { 316 | $this->valorAbatimento = $valorAbatimento; 317 | return $this; 318 | } 319 | 320 | /** Determina o tipo de protesto a ser adotado. 321 | * 322 | * Se NULL for informado, então, o valor de tipo de protesto será o mesmo que está configurado no arquivo config.ini 323 | * 324 | * @param int $tipoProtesto Tipo de protesto a ser adotado. Sendo: 0 = Nao Protestar; 1 = Protestar dias corridos; 2 = Protestar dias úteis; 3 = Utilizar Perfil Cedente. 325 | * @return \TIExpert\WSBoletoSantander\Titulo 326 | */ 327 | public function setTipoProtesto($tipoProtesto) { 328 | if (is_null($tipoProtesto)) { 329 | $tipoProtesto = Config::getInstance()->getInstrucao("tipo_protesto"); 330 | } 331 | 332 | $this->tipoProtesto = $tipoProtesto; 333 | return $this; 334 | } 335 | 336 | /** Determina a quantidade de dias após o vencimento para protesto. 337 | * 338 | * @param int $protestarApos Quantidade de dias após o vencimento para protesto. 339 | * @return \TIExpert\WSBoletoSantander\Titulo 340 | */ 341 | public function setProtestarApos($protestarApos) { 342 | $this->protestarApos = $protestarApos; 343 | return $this; 344 | } 345 | 346 | /** Determina a quantidade de dias após o vencimento para baixa/devolução do título. 347 | * 348 | * Se NULL for informado, então, o valor de baixar após será o mesmo que está configurado no arquivo config.ini 349 | * 350 | * @param int $baixarApos Quantidade de dias após o vencimento para baixa/devolução do título. 351 | * @return \TIExpert\WSBoletoSantander\Titulo 352 | */ 353 | public function setBaixarApos($baixarApos) { 354 | if (is_null($baixarApos)) { 355 | $baixarApos = Config::getInstance()->getInstrucao("baixar_apos"); 356 | } 357 | 358 | $this->baixarApos = $baixarApos; 359 | return $this; 360 | } 361 | 362 | /** Determina a identificação do tipo de pagamento. 363 | * 364 | * @param int $tipoPagamento Código do tipo de pagamento. Sendo: 1 = Conforme Registro; 2 = Divergente; 3 = Parcial. 365 | */ 366 | public function setTipoPagamento($tipoPagamento) { 367 | $this->tipoPagamento = $tipoPagamento; 368 | } 369 | 370 | /** Determina a quantidade de pagamentos possíveis. 371 | * 372 | * @param int $qtdParciais Quantidade de pagamentos possíveis 373 | */ 374 | public function setQtdParciais($qtdParciais = 0) { 375 | $this->qtdParciais = $qtdParciais; 376 | } 377 | 378 | /** Determina o tipo de valor informado 379 | * 380 | * @param int $tipoValor Tipo de valor informado. Sendo: 1 = Percentual; 2 = Valor. 381 | */ 382 | public function setTipoValor($tipoValor) { 383 | $this->tipoValor = $tipoValor; 384 | } 385 | 386 | /** Determina o valor mínimo ou percentual mínimo do título. 387 | * 388 | * @param type $percentualMinimo Valor mínimo ou percentual mínimo do título. 389 | */ 390 | public function setPercentualMinimo($percentualMinimo) { 391 | $this->percentualMinimo = $percentualMinimo; 392 | } 393 | 394 | /** Determina o valor máximo ou percentual máximo do título. 395 | * 396 | * @param float $percentualMaximo Valor máximo ou percentual máximo do título. 397 | */ 398 | public function setPercentualMaximo($percentualMaximo) { 399 | $this->percentualMaximo = $percentualMaximo; 400 | } 401 | 402 | /** Exporta um array associativo no qual as chaves são as propriedades representadas como no WebService do Santander 403 | * 404 | * @return array 405 | */ 406 | public function exportarArray() { 407 | $formatoDataPadrao = Config::getInstance()->getGeral("formato_data"); 408 | 409 | $array["TITULO.PC-MULTA"] = $this->getMulta(); 410 | $array["TITULO.QT-DIAS-MULTA"] = $this->getMultarApos(); 411 | $array["TITULO.PC-JURO"] = $this->getJuros(); 412 | $array["TITULO.TP-DESC"] = $this->getTipoDesconto(); 413 | $array["TITULO.VL-DESC"] = $this->getValorDesconto(); 414 | $array["TITULO.DT-LIMI-DESC"] = $this->getDataLimiteDesconto()->format($formatoDataPadrao); 415 | $array["TITULO.VL-ABATIMENTO"] = $this->getValorAbatimento(); 416 | $array["TITULO.TP-PROTESTO"] = $this->getTipoProtesto(); 417 | $array["TITULO.QT-DIAS-PROTESTO"] = $this->getProtestarApos(); 418 | $array["TITULO.QT-DIAS-BAIXA"] = $this->getBaixarApos(); 419 | $array["TITULO.TP-PAGAMENTO"] = $this->getTipoPagamento(); 420 | $array["TITULO.QT-PARCIAIS"] = $this->getQtdParciais(); 421 | $array["TITULO.TP-VALOR"] = $this->getTipoValor(); 422 | $array["TITULO.VL-PERC-MINIMO"] = Util::formatarNumero($this->getPercentualMinimo(), 5, ',', ''); 423 | $array["TITULO.VL-PERC-MAXIMO"] = Util::formatarNumero($this->getPercentualMaximo(), 5, ',', ''); 424 | 425 | return $array; 426 | } 427 | 428 | /** Carrega as propriedades da instância usando a estrutura XML 429 | * 430 | * @param \DOMDocument $xml Estrutura XML legível 431 | */ 432 | public function carregarPorXML(\DOMDocument $xml) { 433 | $leitor = new LeitorSimplesXML($xml); 434 | 435 | $this->setBaixarApos($leitor->getValorNo("qtDiasBaixa")); 436 | $this->setDataLimiteDesconto($leitor->getValorNo("dtLimiDesc")); 437 | $this->setJuros($leitor->getValorNo("pcJuro")); 438 | $this->setMulta($leitor->getValorNo("pcMulta")); 439 | $this->setMultarApos($leitor->getValorNo("qtDiasMulta")); 440 | $this->setProtestarApos($leitor->getValorNo("qtDiasProtesto")); 441 | $this->setTipoDesconto($leitor->getValorNo("tpDesc")); 442 | $this->setTipoProtesto($leitor->getValorNo("tpProtesto")); 443 | $this->setValorAbatimento($leitor->getValorNo("vlAbatimento") / 100); 444 | $this->setValorDesconto($leitor->getValorNo("vlDesc") / 100); 445 | $this->setTipoPagamento($leitor->getValorNo("tipoPagto")); 446 | $this->setQtdParciais($leitor->getValorNo("qtdParciais")); 447 | $this->setTipoValor($leitor->getValorNo("tipoValor")); 448 | $this->setPercentualMaximo($leitor->getValorNo("valorMaximo") / 100000); 449 | $this->setPercentualMinimo($leitor->getValorNo("valorMinimo") / 100000); 450 | } 451 | 452 | } 453 | -------------------------------------------------------------------------------- /tests/BoletoSantanderServicoTest.php: -------------------------------------------------------------------------------- 1 | name, self::$faker->streetAddress, self::$faker->city, self::$faker->city, self::$faker->stateAbbr, self::$faker->postcode); 40 | 41 | $instrucoesDeTitulo = new InstrucoesDeTitulo(); 42 | $instrucoesDeTitulo->setTipoPagamento(3); 43 | $instrucoesDeTitulo->setTipoValor(1); 44 | $instrucoesDeTitulo->setPercentualMaximo(mt_rand(0, 9999999) . "." . mt_rand(0, 99999)); 45 | $instrucoesDeTitulo->setPercentualMinimo(mt_rand(0, 9999999) . "." . mt_rand(0, 99999)); 46 | $instrucoesDeTitulo->setQtdParciais(mt_rand(1, 99)); 47 | 48 | $titulo = new Titulo(mt_rand(0, 9999999), mt_rand(100000000, 999999999), mt_rand(100000000, 999999999), "now", NULL, NULL, NULL, $instrucoesDeTitulo); 49 | 50 | self::$boleto = new Boleto($convenio, $pagador, $titulo); 51 | 52 | self::$ticket = new Ticket(); 53 | self::$ticket->setNsu(mt_rand(100000, 999999))->setAutenticacao(self::$faker->regexify("[A-Za-z0-9+/]{48}")); 54 | } 55 | 56 | /** 57 | * @author Denys Xavier 58 | * @test 59 | */ 60 | public function testeParaSolicitarTicketDeInclusao() { 61 | $ticketBase64 = self::$faker->regexify("[A-Za-z0-9+/]{48}"); 62 | 63 | $comunicador = $this->getMockBuilder("TIExpert\WSBoletoSantander\ComunicadorCurlSOAP")->getMock(); 64 | $comunicador->method("chamar")->willReturn(' 65 | 66 | 67 | 68 | 69 | 0 70 | ' . $ticketBase64 . ' 71 | 72 | 73 | 74 | '); 75 | 76 | $svc = new BoletoSantanderServico($comunicador); 77 | $ticket = $svc->solicitarTicketInclusao(self::$boleto); 78 | 79 | $this->assertEquals($ticketBase64, $ticket->getAutenticacao()); 80 | } 81 | 82 | /** 83 | * @author Denys Xavier 84 | * @test 85 | */ 86 | public function testeParaSolicitarTicketDeSondagem() { 87 | $ticketBase64 = self::$faker->regexify("[A-Za-z0-9+/]{48}"); 88 | 89 | $comunicador = $this->getMockBuilder("TIExpert\WSBoletoSantander\ComunicadorCurlSOAP")->getMock(); 90 | $comunicador->method("chamar")->willReturn(' 91 | 92 | 93 | 94 | 95 | 0 96 | ' . $ticketBase64 . ' 97 | 98 | 99 | 100 | '); 101 | 102 | $svc = new BoletoSantanderServico($comunicador); 103 | $ticket = $svc->solicitarTicketSondagem(self::$boleto->getConvenio()); 104 | 105 | $this->assertEquals($ticketBase64, $ticket->getAutenticacao()); 106 | } 107 | 108 | /** 109 | * @author Denys Xavier 110 | * @test 111 | */ 112 | public function testeParaIncluirUmTitulo() { 113 | $dataFake = date(Config::getInstance()->getGeral("formato_data")); 114 | 115 | $comunicador = $this->getMockBuilder("TIExpert\WSBoletoSantander\ComunicadorCurlSOAP")->getMock(); 116 | $comunicador->method("chamar")->willReturn(' 117 | 118 | 119 | 120 | 121 | ' . self::$faker->regexify("[0-9]{9}") . ' 122 | 123 | 0033 124 | ' . self::$faker->regexify("[0-9]{9}") . ' 125 | 126 | 00000 - Título registrado em cobrança 127 | ' . $dataFake . ' 128 | ' . self::$faker->regexify("[A-Z0-9]{4}") . ' 129 | TST' . mt_rand(100000, 999999) . ' 130 | 131 | ' . self::$faker->city . ' 132 | ' . self::$faker->postcode . ' 133 | ' . self::$faker->city . ' 134 | ' . self::$faker->streetAddress . ' 135 | ' . self::$faker->name . ' 136 | ' . self::$faker->regexify("[0-9]{15}") . ' 137 | 01 138 | SP 139 | 140 | 0 141 | 142 | 143 | ' . self::$faker->regexify("[0-9]{44}") . ' 144 | ' . $dataFake . ' 145 | ' . $dataFake . ' 146 | ' . $dataFake . ' 147 | ' . $dataFake . ' 148 | 99 149 | ' . self::$faker->regexify("[0-9]{47}") . ' 150 | ' . self::$faker->text . ' 151 | ' . self::$faker->regexify("[0-9]{13}") . ' 152 | 00000 153 | 00000 154 | 00 155 | 00 156 | 00 157 | 123456 158 | 0 159 | 0 160 | 000000000000000 161 | 000000000000000 162 | 000000000000001 163 | 164 | T 165 | 166 | 167 | 168 | 169 | '); 170 | 171 | $svc = new BoletoSantanderServico($comunicador); 172 | $resultado = $svc->incluirTitulo(self::$ticket); 173 | 174 | $this->assertTrue($resultado); 175 | } 176 | 177 | /** 178 | * @author Denys Xavier 179 | * @test 180 | */ 181 | public function testeParaSondarUmTitulo() { 182 | $formato_data = Config::getInstance()->getGeral("formato_data"); 183 | 184 | $comunicador = $this->getMockBuilder("TIExpert\WSBoletoSantander\ComunicadorCurlSOAP")->getMock(); 185 | $comunicador->method("chamar")->willReturn(' 186 | 187 | 188 | 189 | 190 | ' . self::$faker->regexify("[0-9]{9}") . ' 191 | 192 | ' . self::$boleto->getConvenio()->getCodigoBanco() . ' 193 | ' . self::$boleto->getConvenio()->getCodigoConvenio() . ' 194 | 195 | 196 | 10022017 197 | ' . self::$faker->regexify("[A-Z0-9]{4}") . ' 198 | TST' . self::$faker->regexify("[0-9]{4}") . ' 199 | 200 | ' . self::$boleto->getPagador()->getBairro() . ' 201 | ' . self::$boleto->getPagador()->getCEP() . ' 202 | ' . self::$boleto->getPagador()->getCidade() . ' 203 | ' . self::$boleto->getPagador()->getEndereco() . ' 204 | ' . self::$boleto->getPagador()->getNome() . ' 205 | ' . self::$boleto->getPagador()->getNumeroDoc() . ' 206 | ' . self::$boleto->getPagador()->getTipoDoc() . ' 207 | ' . self::$boleto->getPagador()->getUF() . ' 208 | 209 | 0 210 | 211 | 212 | ' . self::$faker->regexify("[0-9]{44}") . ' 213 | ' . self::$boleto->getTitulo()->getDataEmissao()->format($formato_data) . ' 214 | ' . date($formato_data) . ' 215 | ' . self::$boleto->getTitulo()->getInstrucoes()->getDataLimiteDesconto()->format($formato_data) . ' 216 | ' . self::$boleto->getTitulo()->getDataVencimento()->format($formato_data) . ' 217 | ' . self::$boleto->getTitulo()->getEspecie() . ' 218 | ' . self::$faker->regexify("[0-9]{47}") . ' 219 | 220 | ' . self::$boleto->getTitulo()->getNossoNumeroComDigito() . ' 221 | ' . self::$boleto->getTitulo()->getInstrucoes()->getJuros() . ' 222 | ' . self::$boleto->getTitulo()->getInstrucoes()->getMulta() . ' 223 | ' . self::$boleto->getTitulo()->getInstrucoes()->getBaixarApos() . ' 224 | ' . self::$boleto->getTitulo()->getInstrucoes()->getMultarApos() . ' 225 | ' . self::$boleto->getTitulo()->getInstrucoes()->getProtestarApos() . ' 226 | ' . self::$boleto->getTitulo()->getInstrucoes()->getQtdParciais() . ' 227 | ' . self::$boleto->getTitulo()->getSeuNumero() . ' 228 | ' . self::$boleto->getTitulo()->getInstrucoes()->getTipoPagamento() . ' 229 | ' . self::$boleto->getTitulo()->getInstrucoes()->getTipoValor() . ' 230 | ' . self::$boleto->getTitulo()->getInstrucoes()->getTipoDesconto() . ' 231 | ' . self::$boleto->getTitulo()->getInstrucoes()->getTipoProtesto() . ' 232 | ' . \TIExpert\WSBoletoSantander\Util::formatarNumero(self::$boleto->getTitulo()->getInstrucoes()->getPercentualMaximo(), 5, '') . ' 233 | ' . \TIExpert\WSBoletoSantander\Util::formatarNumero(self::$boleto->getTitulo()->getInstrucoes()->getPercentualMinimo(), 5, '') . ' 234 | ' . self::$boleto->getTitulo()->getInstrucoes()->getValorAbatimento() * 100 . ' 235 | ' . self::$boleto->getTitulo()->getInstrucoes()->getValorDesconto() * 100 . ' 236 | ' . self::$boleto->getTitulo()->getValor() * 100 . ' 237 | 238 | T 239 | 240 | 241 | 242 | '); 243 | 244 | $svc = new BoletoSantanderServico($comunicador); 245 | $resultado = $svc->sondarTitulo(self::$ticket); 246 | 247 | $this->assertEquals(self::$boleto, $resultado, '', 60); 248 | } 249 | 250 | /** 251 | * @author Denys Xavier 252 | * @test 253 | * @expectedException \Exception 254 | */ 255 | public function seOBoletoSondadoNaoExistirEntaoUmaExcecaoDeveSerLancada() { 256 | $comunicador = $this->getMockBuilder("TIExpert\WSBoletoSantander\ComunicadorCurlSOAP")->getMock(); 257 | $comunicador->method("chamar")->willReturn(' 258 | 259 | 260 | 261 | 262 | 263 | 264 | 0033 265 | ' . self::$faker->regexify('[0-9]{9}') . ' 266 | 267 | @ERYKE0411 - TITULO NAO ENCONTRADO 268 | ' . date(Config::getInstance()->getGeral("formato_data")) . ' 269 | ' . self::$faker->regexify("[A-Z0-9]{4}") . ' 270 | TST' . self::$faker->regexify("[0-9]{4}") . ' 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 20 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | T 306 | 307 | 308 | 309 | 310 | '); 311 | 312 | $svc = new BoletoSantanderServico($comunicador); 313 | $svc->sondarTitulo(self::$ticket); 314 | } 315 | 316 | /** 317 | * @author Denys Xavier 318 | * @test 319 | * @expectedException \Exception 320 | */ 321 | public function incluirUmTituloBaseadoEmUmTicketDeBoletoIncorretoDeveLancarUmaExcecao() { 322 | $hoje = date(Config::getInstance()->getGeral("formato_data")); 323 | 324 | $comunicador = $this->getMockBuilder("TIExpert\WSBoletoSantander\ComunicadorCurlSOAP")->getMock(); 325 | $comunicador->method("chamar")->willReturn(' 326 | 327 | 328 | 329 | 330 | 331 | 332 | 0033 333 | ' . self::$faker->regexify('[0-9]{9}') . ' 334 | 335 | 00058-CPF / CNPJ INCORRETO 336 | ' . $hoje . ' 337 | ' . self::$faker->regexify("[A-Z0-9]{4}") . ' 338 | TST' . self::$faker->regexify("[0-9]{4}") . ' 339 | 340 | ' . self::$faker->city . ' 341 | ' . self::$faker->postcode . ' 342 | ' . self::$faker->city . ' 343 | ' . self::$faker->streetAddress . ' 344 | ' . self::$faker->name . ' 345 | 00000000000 346 | 01 347 | SP 348 | 349 | 20 350 | 351 | 352 | 353 | ' . $hoje . ' 354 | 355 | ' . $hoje . ' 356 | ' . $hoje . ' 357 | 99 358 | 359 | ' . self::$faker->text . ' 360 | ' . self::$faker->regexify("[0-9]{13}") . ' 361 | 00000 362 | 00000 363 | 00 364 | 00 365 | 00 366 | ' . self::$faker->regexify("[0-9]{15}") . ' 367 | 0 368 | 0 369 | 000000000000000 370 | 000000000000000 371 | 000000000000110 372 | 373 | T 374 | 375 | 376 | 377 | 378 | '); 379 | 380 | $svc = new BoletoSantanderServico($comunicador); 381 | $svc->incluirTitulo(self::$ticket); 382 | } 383 | 384 | /** 385 | * @author Denys Xavier 386 | * @test 387 | * @expectedException \Exception 388 | */ 389 | public function xmlDeTicketRetornadoComNodeRetcodeDiferenteDeZeroDeveLancarUmaExcecao() { 390 | $comunicador = $this->getMockBuilder("TIExpert\WSBoletoSantander\ComunicadorCurlSOAP")->getMock(); 391 | $comunicador->method("chamar")->willReturn(' 392 | 393 | 394 | 395 | 396 | 4 397 | ' . self::$faker->regexify("[A-Za-z0-9+/]{48}") . ' 398 | 399 | 400 | 401 | '); 402 | 403 | $svc = new BoletoSantanderServico($comunicador); 404 | $svc->solicitarTicketInclusao(self::$boleto); 405 | } 406 | 407 | } 408 | --------------------------------------------------------------------------------