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 |
3 |
4 |
5 |
6 |
7 |
8 |
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 | 
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 | 
168 |
169 | 2) Deve conter número de série.
170 |
171 | 
172 |
173 | 3) Possuir uma impressão digital.
174 |
175 | 
176 |
177 | 4) E, um Common Name.
178 |
179 | 
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 |
--------------------------------------------------------------------------------