├── .gitignore ├── .travis.yml ├── CHANGELOG.MD ├── LICENSE ├── README.md ├── VERSION ├── composer.json ├── composer.phar ├── lib ├── NFe │ ├── APIChildResource.php │ ├── APIRequest.php │ ├── APIResource.php │ ├── Backward_Compatibility.php │ ├── Company.php │ ├── Error.php │ ├── LegalPerson.php │ ├── NFe.php │ ├── NaturalPerson.php │ ├── Object.php │ ├── SearchResult.php │ ├── ServiceInvoice.php │ ├── Utilities.php │ └── Webhook.php ├── data │ └── ca-bundle.crt └── init.php └── test ├── NFe.php ├── NFe ├── CompanyTest.php ├── LegalPersonTest.php ├── NaturalPersonTest.php ├── ServiceInvoiceTest.php ├── TestCase.php └── WebhookTest.php ├── Nfe.php └── console.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X dumps these all over the place. 2 | .DS_Store 3 | 4 | # Ignore the SimpleTest library if it is installed to /test/. 5 | /test/simpletest/ 6 | 7 | # Ignore the /vendor/ directory for people using composer 8 | /vendor/ 9 | 10 | # If the vendor directory isn't being commited the composer.lock file should also be ignored 11 | composer.lock 12 | 13 | # Ignore PHPUnit coverage file 14 | clover.xml 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '5.4' 4 | - '5.5' 5 | - '5.6' 6 | - '7.0' 7 | - hhvm 8 | before_script: 9 | - composer update --dev 10 | script: 11 | - php test/NFe.php 12 | -------------------------------------------------------------------------------- /CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | ### 2.5 2017-01-22 2 | * Fix a conflict in the name of the main class 3 | 4 | ### 2.4 2016-08-31 5 | * Fix strtolower on api resource 6 | 7 | ### 2.3 2016-08-31 8 | * Removed dependency of mbstring php module 9 | * Removed unused files 10 | 11 | ### 2.2 2016-07-27 12 | * Moved files to new structure 13 | 14 | ### 2.1 2016-07-18 15 | 16 | * Updated readme.me with banners 17 | * Bumped PHP version into 5.4 18 | * Added more unit testing 19 | * Updated unit testing setup to easier testing 20 | * Added more examples, for documentation 21 | 22 | ### 2.0.0 2016-07-18 23 | 24 | * Updated readme.me with banners 25 | * Bumped PHP version into 5.4 26 | * Added more unit testing 27 | * Updated unit testing setup to easier testing 28 | * Added more examples, for documentation 29 | 30 | ### 2.0.0 2016-07-15 31 | 32 | * Added .travis.yml to bring Travis-cli support 33 | * Updated tests 34 | 35 | ### 2.0.0 2016-07-14 36 | 37 | * Several improvements in the library organization 38 | * Readme.me updated to reflect new folder names and also new class names 39 | * composer.json updated to reflect new folder names 40 | 41 | ### 1.0.2 2016-07-13 42 | 43 | * Class name changes to fix #5 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2012-2016 NFe.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cliente PHP para emissão de notas fiscais - NFe.io 2 | 3 | ## Onde acessar a documentação da API? 4 | 5 | > Acesse a [nossa documentação](https://nfe.io/doc/rest-api/nfe-v1) para mais detalhes e referências. 6 | 7 | ## Como realizar a instalação do pacote? 8 | 9 | Você pode instalar via [Composer](http://getcomposer.org/), executando o comando a seguir: 10 | 11 | ```bash 12 | composer require nfe/nfe 13 | ``` 14 | 15 | Para usar a biblioteca, use o [Composer autoload](https://getcomposer.org/doc/00-intro.md#autoloading): 16 | 17 | ```php 18 | require_once('vendor/autoload.php'); 19 | ``` 20 | > **Observação**: A versão do PHP deverá ser 5.4 ou superior. 21 | 22 | ## Dependencias 23 | 24 | Esta biblioteca requer as seguintes extensões para funcionamento correto: 25 | 26 | **-** [`curl`](https://secure.php.net/manual/en/book.curl.php) 27 | 28 | **-** [`json`](https://secure.php.net/manual/en/book.json.php) 29 | 30 | Se você usa o Composer, essas dependencias são gerenciadas automaticamente. Caso tenha feito a instalação manual, você precisa ter certeza que estas extensões estão instaladas e disponíveis. 31 | 32 | > Se você não quiser utilizar o Composer, você pode fazer o download de uma das últimas versões, utilizando o endereço 33 | [https://github.com/nfe/client-php/releases](https://github.com/nfe/client-php/releases) 34 | 35 | ## Exemplos de uso 36 | 37 | Depois de baixar o pacote, inclua a biblioteca em seu arquivo PHP, utilizando o código abaixo: 38 | 39 | ```php 40 | require_once("caminho-para/client-php/lib/init.php"); 41 | ``` 42 | > **Observação**: Caso você utilizar mais de um arquivo .php para fazer a integração, o código acima deverá ser replicado nos outros arquivos. 43 | 44 | ### Como emitir uma Nota Fiscal de Serviço? 45 | Abaixo, temos um código-exemplo para realizar uma Emissão de Nota Fiscal de Serviço: 46 | 47 | ```php 48 | require_once("caminho-para/client-php/lib/init.php"); 49 | 50 | NFe_io::setApiKey('c73d49f9649046eeba36dcf69f6334fd'); // Ache sua chave API no painel (https://app.nfe.io/account/apikeys) 51 | 52 | $invoiceCreated = NFe_ServiceInvoice::create( 53 | // ID da empresa, você deve copiar exatamente como está no painel 54 | '64555e0ee340420fdc94ad09', 55 | // Dados da nota fiscal de serviço 56 | array( 57 | // Código do serviço de acordo com o a cidade 58 | 'cityServiceCode' => '2690', 59 | // Descrição dos serviços prestados 60 | 'description' => 'TESTE EMISSAO', 61 | // Valor total do serviços 62 | 'servicesAmount' => 0.01, 63 | // Dados do Tomador dos Serviços 64 | 'borrower' => array( 65 | // CNPJ ou CPF (opcional para tomadores no exterior) 66 | 'federalTaxNumber' => 191, 67 | // Nome da pessoa física ou Razão Social da Empresa 68 | 'name' => 'BANCO DO BRASIL SA', 69 | // Email para onde deverá ser enviado a nota fiscal 70 | 'email' => 'nfe@mailinator.com', // Para visualizar os e-mails https://www.mailinator.com/ 71 | // Endereço do tomador 72 | 'address' => array( 73 | // Código do pais com três letras 74 | 'country' => 'BRA', 75 | // CEP do endereço (opcional para tomadores no exterior) 76 | 'postalCode' => '70073901', 77 | // Logradouro 78 | 'street' => 'Outros Quadra 1 Bloco G Lote 32', 79 | // Número (opcional) 80 | 'number' => 'S/N', 81 | // Complemento (opcional) 82 | 'additionalInformation' => 'QUADRA 01 BLOCO G', 83 | // Bairro 84 | 'district' => 'Asa Sul', 85 | // Cidade é opcional para tomadores no exterior 86 | 'city' => array( 87 | // Código do IBGE para a Cidade 88 | 'code' => '5300108', 89 | // Nome da Cidade 90 | 'name' => 'Brasilia' 91 | ), 92 | // Sigla do estado (opcional para tomadores no exterior) 93 | 'state' => 'DF' 94 | ) 95 | ) 96 | ) 97 | ); 98 | 99 | echo($invoiceCreated->id); 100 | ``` 101 | 102 | ### Como cancelar uma nota? 103 | Abaixo, temos um código-exemplo para efetuar o cancelamento de uma nota: 104 | 105 | ```php 106 | require_once("caminho-para/client-php/lib/init.php"); 107 | 108 | NFe_io::setApiKey("c73d49f9649046eeba36dcf69f6334fd"); // Ache sua chave API no painel (https://app.nfe.io/account/apikeys) 109 | 110 | $invoice = NFe_ServiceInvoice::fetch( 111 | '64555e0ee340420fdc94ad09', // ID da empresa, você deve copiar exatamente como está no painel 112 | 'wPi7i954QAcr6kmy17BtEKtN' // ID da nota fiscal 113 | ); 114 | 115 | if ( $invoice->status == 'Issued' ) { 116 | $invoice->cancel(); 117 | } 118 | ``` 119 | ### Como criar uma empresa para realizar a emissão de notas fiscais? 120 | Abaixo, temos um código-exemplo de criação de uma empresa, para realizar a emissão de nota fiscal: 121 | 122 | ```php 123 | require_once("caminho-para/client-php/lib/init.php"); 124 | 125 | NFe_io::setApiKey("c73d49f9649046eeba36dcf69f6334fd"); // Ache sua chave API no painel (https://app.nfe.io/account/apikeys) 126 | 127 | $companyCreated = NFe_Company::create( 128 | array( 129 | 'federalTaxNumber' => 87502637000164, // Use esse gerador para testar: http://www.geradordecnpj.org/ 130 | 'name' => 'BANCO DO BRASIL SA', 131 | 'tradeName' => 'BANCO DO BRASIL', 132 | 'email' => 'nfe@mailinator.com', // Para visualizar os e-mails https://www.mailinator.com/inbox2.jsp?public_to=nfe 133 | // Endereço da empresa 134 | 'address' => array( 135 | // Código do pais com três letras 136 | 'country' => 'BRA', 137 | // CEP do endereço (opcional para tomadores no exterior) 138 | 'postalCode' => '70073901', 139 | // Logradouro 140 | 'street' => 'Outros Quadra 1 Bloco G Lote 32', 141 | // Número (opcional) 142 | 'number' => 'S/N', 143 | // Complemento (opcional) 144 | 'additionalInformation' => 'QUADRA 01 BLOCO G', 145 | // Bairro 146 | 'district' => 'Asa Sul', 147 | // Cidade é opcional para tomadores no exterior 148 | 'city' => array( 149 | // Código do IBGE para a Cidade 150 | 'code' => '5300108', 151 | // Nome da Cidade 152 | 'name' => 'Brasilia' 153 | ), 154 | // Sigla do estado (opcional para tomadores no exterior) 155 | 'state' => 'DF' 156 | ) 157 | ) 158 | ); 159 | 160 | echo($companyCreated->id); 161 | ``` 162 | 163 | ### Como efetuar o download de uma nota em PDF? 164 | Abaixo, temos um código exemplo para baixar uma nota em PDF: 165 | 166 | ```php 167 | require_once("caminho-para/client-php/lib/init.php"); 168 | 169 | NFe_io::setApiKey('c73d49f9649046eeba36dcf69f6334fd'); // Ache sua chave API no painel (https://app.nfe.io/account/apikeys) 170 | 171 | $url = NFe_ServiceInvoice::pdf( 172 | '64555e0ee340420fdc94ad09', // ID da empresa, você deve copiar exatamente como está no painel 173 | 'wPi7i954QAcr6kmy17BtEKtN' // ID da nota fiscal 174 | ); 175 | 176 | file_put_contents( './invoice_file.pdf', file_get_contents($url) ); 177 | ``` 178 | 179 | ### Como validar o Webhook? 180 | 181 | PHP > 5.3 182 | ``` 183 | define('WEBHOOK_SECRET_KEY', 'COLOQUE SUA CHAVE DEFINIDA NO SITE AQUI'); 184 | function verify_webhook($data, $hmac_header) 185 | { 186 | $calculated_hmac = base64_encode(hash_hmac('sha1', $data, WEBHOOK_SECRET_KEY,true)); 187 | return ($hmac_header == $calculated_hmac); 188 | } 189 | 190 | $hmac_header = str_replace("sha1=", '', $_SERVER['HTTP_X_NFEIO_SIGNATURE']); 191 | $data = file_get_contents('php://input'); 192 | $verified = verify_webhook($data, $hmac_header); 193 | if(!$verified) { 194 | //Código para requisição que não foi validada 195 | } else { 196 | //Código para requisição validada 197 | } 198 | ``` 199 | ## Testes 200 | 201 | Instale as dependências necessárias para executar os testes. O client-php do NFe utiliza [SimpleTest](http://simpletest.org/). 202 | ``` bash 203 | composer update --dev 204 | ``` 205 | 206 | Execute a comitiva de testes: 207 | ``` bash 208 | php ./test/NFe.php 209 | ``` 210 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 2.5 2 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nfe/nfe", 3 | "description": "NFe.io PHP Library", 4 | "homepage": "https://nfe.io", 5 | "version": "2.5", 6 | "keywords": [ 7 | "nfe", 8 | "nfse", 9 | "nfe.io", 10 | "api", 11 | "nota", 12 | "nota fiscal" 13 | ], 14 | "license": "MIT", 15 | "authors": [ 16 | { 17 | "name": "NFe.io team and contributors", 18 | "homepage": "https://github.com/nfe/client-php/contributors" 19 | }, 20 | { 21 | "name": "Renato Alves", 22 | "homepage": "http://ralv.es/" 23 | } 24 | ], 25 | "require": { 26 | "php": ">=5.4", 27 | "ext-curl": "*", 28 | "ext-json": "*", 29 | "ext-mbstring": "*" 30 | }, 31 | "require-dev": { 32 | "simpletest/simpletest": "^1.1" 33 | }, 34 | "autoload": { 35 | "classmap": ["lib/NFe/"] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /composer.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nfe/client-php/HEAD/composer.phar -------------------------------------------------------------------------------- /lib/NFe/APIChildResource.php: -------------------------------------------------------------------------------- 1 | _fabricator = $className; 13 | $this->_parentKeys = $parentKeys; 14 | } 15 | 16 | function mergeParams( $attributes ) { 17 | return array_merge( $attributes, $this->_parentKeys ); 18 | } 19 | 20 | private function configureParentKeys($object) { 21 | foreach ($this->_parentKeys as $key => $value) { 22 | $object[$key] = $value; 23 | } 24 | return $object; 25 | } 26 | 27 | public function create( $attributes = array() ) { 28 | $result = call_user_func_array( $this->_fabricator . '::create', array( $this->mergeparams($attributes), $this->_parentKeys ) ); 29 | 30 | if ($result) { 31 | $this->configureParentKeys( $result ); 32 | } 33 | 34 | return $result; 35 | } 36 | 37 | public function search( $options = array() ) { 38 | $results = call_user_func_array($this->_fabricator . '::search', array( $this->mergeParams($options), $this->_parentKeys )); 39 | 40 | if ( $results && $results->total() ) { 41 | $modifiedResults = $results->results(); 42 | 43 | for ( $i = 0; $i < count($modifiedResults); $i++ ) { 44 | $modifiedResults[$i] = $this->configureParentKeys( $modifiedResults[$i] ); 45 | } 46 | $results->set($modifiedResults, $results->total()); 47 | } 48 | return $results; 49 | } 50 | 51 | public function fetch( $key = array() ) { 52 | if ( is_string($key) ) { 53 | $key = array( "id" => $key ); 54 | } 55 | 56 | $result = call_user_func_array($this->_fabricator . '::fetch', array( $this->mergeParams($key), $this->_parentKeys )); 57 | 58 | if ( $result ) { 59 | $this->configureParentKeys( $result ); 60 | } 61 | 62 | return $result; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/NFe/APIRequest.php: -------------------------------------------------------------------------------- 1 | _defaultHeaders(); 29 | 30 | list( $response_body, $response_code ) = $this->requestWithCURL( $method, $url, $headers, $data ); 31 | if ( $response_code == 302 ) { 32 | $response = $response_body; 33 | } 34 | else { 35 | $response = json_decode($response_body); 36 | } 37 | 38 | if ( json_last_error() != JSON_ERROR_NONE ) { 39 | throw new NFeObjectNotFound($response_body); 40 | } 41 | 42 | if ( $response_code == 404 ) { 43 | throw new NFeObjectNotFound($response_body); 44 | } 45 | 46 | if ( isset($response->errors) ) { 47 | if ( ( gettype($response->errors) != "string") && count( get_object_vars($response->errors) ) == 0 ) { 48 | unset($response->errors); 49 | } 50 | elseif ( ( gettype($response->errors) != "string") && count( get_object_vars($response->errors) ) > 0 ) { 51 | $response->errors = (array) $response->errors; 52 | } 53 | 54 | if ( isset($response->errors) && ( gettype($response->errors) == "string") ) { 55 | $response->errors = $response->errors; 56 | } 57 | } 58 | 59 | $last_api_response_code = $response_code; 60 | 61 | return $response; 62 | } 63 | 64 | private function requestWithCURL( $method, $url, $headers, $data = array() ) { 65 | $curl = curl_init(); 66 | $data = NFe_Utilities::arrayToParams($data); 67 | $method = strtolower($method); 68 | $opts = array(); 69 | 70 | if ($method == 'post') { 71 | $opts[CURLOPT_POST] = 1; 72 | $opts[CURLOPT_POSTFIELDS] = $data; 73 | } 74 | elseif ($method == 'delete') { 75 | $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE'; 76 | } 77 | elseif ($method == 'put') { 78 | $opts[CURLOPT_CUSTOMREQUEST] = 'PUT'; 79 | $opts[CURLOPT_POSTFIELDS] = $data; 80 | } 81 | 82 | $opts[CURLOPT_URL] = $url; 83 | $opts[CURLOPT_RETURNTRANSFER] = TRUE; 84 | $opts[CURLOPT_FOLLOWLOCATION] = FALSE; 85 | $opts[CURLOPT_HEADER] = TRUE; 86 | $opts[CURLOPT_TIMEOUT] = 80; 87 | $opts[CURLOPT_CONNECTTIMEOUT] = 30; 88 | $opts[CURLOPT_HTTPHEADER] = $headers; 89 | if ( NFe_io::$verifySslCerts == false ) { 90 | $opts[CURLOPT_SSL_VERIFYPEER] = false; 91 | } 92 | $opts[CURLOPT_SSL_VERIFYHOST] = 2; 93 | 94 | // @codingStandardsIgnoreStart 95 | // PSR2 requires all constants be upper case. Sadly, the CURL_SSLVERSION 96 | // constants to not abide by those rules. 97 | // 98 | // Opt into TLS 1.x support on older versions of curl. This causes some 99 | // curl versions, notably on RedHat, to upgrade the connection to TLS 100 | // 1.2, from the default TLS 1.0. 101 | if ( ! defined('CURL_SSLVERSION_TLSv1') ) { 102 | define('CURL_SSLVERSION_TLSv1', 1); // constant not defined in PHP < 5.5 103 | } 104 | $opts[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1; 105 | // @codingStandardsIgnoreEnd 106 | 107 | curl_setopt_array($curl, $opts); 108 | 109 | // For debugging 110 | if ( NFe_io::$debug == true ) { 111 | curl_setopt( $curl, CURLOPT_PROXY, "127.0.0.1:8888" ); 112 | curl_setopt( $curl, CURLOPT_VERBOSE, 1 ); 113 | } 114 | 115 | $response = curl_exec($curl); 116 | 117 | if ( ! defined('CURLE_SSL_CACERT_BADFILE') ) { 118 | define('CURLE_SSL_CACERT_BADFILE', 77); // constant not defined in PHP 119 | } 120 | 121 | $errno = curl_errno($curl); 122 | if ( $errno == CURLE_SSL_CACERT || 123 | $errno == CURLE_SSL_PEER_CERTIFICATE || 124 | $errno == CURLE_SSL_CACERT_BADFILE 125 | ) { 126 | array_push( 127 | $headers, 128 | 'X-NFe-Client-Info: {"ca":"using NFe-supplied CA bundle"}' 129 | ); 130 | $cert = self::caBundle(); 131 | curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 132 | curl_setopt($curl, CURLOPT_CAINFO, $cert); 133 | $response = curl_exec($curl); 134 | } 135 | 136 | if ( $response === false ) { 137 | $errno = curl_errno($curl); 138 | $message = curl_error($curl); 139 | curl_close($curl); 140 | $this->handleCurlError($url, $errno, $message); 141 | } 142 | 143 | $response_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); 144 | $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE); 145 | $response_header = substr($response, 0, $header_size); 146 | $response_body = substr($response, $header_size); 147 | 148 | // if we have a redirect, we need to get the location header 149 | if ( $response_code == 302 ) { 150 | preg_match_all('/^Location:\s?(.*)$/mi', $response, $matches); 151 | return array(trim($matches[1][0]), $response_code); 152 | } 153 | 154 | curl_close($curl); 155 | return array($response_body, $response_code); 156 | } 157 | 158 | /** 159 | * @param string $url 160 | * @param number $errno 161 | * @param string $message 162 | * @throws NFeAuthenticationException 163 | */ 164 | private function handleCurlError($url, $errno, $message) { 165 | switch ($errno) { 166 | case CURLE_COULDNT_CONNECT: 167 | case CURLE_COULDNT_RESOLVE_HOST: 168 | case CURLE_OPERATION_TIMEOUTED: 169 | $msg = "Não foi possível conectar ao ($url). Por favor, cheque sua " 170 | . "conexão com a internet e tente novamente. Se o problema persistir,"; 171 | break; 172 | case CURLE_SSL_CACERT: 173 | case CURLE_SSL_PEER_CERTIFICATE: 174 | $msg = "Não foi possível verificar o certificado SSL do NFe. Se certifique " 175 | . "que sua rede não está interceptando certificados. " 176 | . "(Tente ir $url em seu navegador.) " 177 | . "Se o problema persistir,"; 178 | break; 179 | default: 180 | $msg = "Error inesperado ao comunicar com a API do NFe.io. " . "Se o problema persistir,"; 181 | } 182 | $msg .= " entre em contato com NFe.io (https://nfe.io)."; 183 | $msg .= "\n\n(Erro na rede [errno $errno]: $message)"; 184 | 185 | return new NFeAuthenticationException( $msg ); 186 | } 187 | 188 | /** 189 | * NFe.io Certification Bundle 190 | */ 191 | private static function caBundle() { 192 | return dirname(__FILE__) . '/../../data/ca-bundle.crt'; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /lib/NFe/APIResource.php: -------------------------------------------------------------------------------- 1 | provider) && isset($object->provider->id) ) { 50 | $uri_path = '/companies/' . $object->provider->id; 51 | } 52 | 53 | if (isset($object['id'])) { 54 | $path = '/' . $object['id']; 55 | } 56 | 57 | return strtolower( NFe_io::getBaseURI() . $uri_path . '/' . self::objectBaseURI() . $path ); 58 | } 59 | 60 | public static function url( $object = null ) { 61 | return self::endpointAPI( $object ); 62 | } 63 | 64 | protected static function createFromResponse($response) { 65 | return NFe_Utilities::createFromResponse( self::convertClassToObjectType(), $response ); 66 | } 67 | 68 | protected static function createAPI( $attributes = array() ) { 69 | return self::createFromResponse( 70 | self::API()->request( 'POST', self::endpointAPI( $attributes ), $attributes ) ); 71 | } 72 | 73 | protected function deleteAPI() { 74 | // if ( $this['id'] == null ) { // $this['id'] 75 | // return false; 76 | // } 77 | 78 | try { 79 | $response = self::API()->request( 'DELETE', static::url($this) ); 80 | 81 | if ( isset($response->errors) ) { 82 | throw NFeException(); 83 | } 84 | } 85 | catch (Exception $e) { 86 | return false; 87 | } 88 | 89 | return true; 90 | } 91 | 92 | protected static function searchAPI( $options = array() ) { 93 | try { 94 | $response = self::API()->request( 'GET', static::url($options), $options ); 95 | 96 | return self::createFromResponse($response); 97 | } 98 | catch (Exception $e) {} 99 | 100 | return array(); 101 | } 102 | 103 | protected static function fetchAPI($key) { 104 | try { 105 | $response = self::API()->request( 'GET', static::url($key) ); 106 | return self::createFromResponse($response); 107 | } 108 | catch ( NFeObjectNotFound $e ) { 109 | throw new NFeObjectNotFound( self::convertClassToObjectType( get_called_class() ) . ':' . ' não encontrado.'); 110 | } 111 | } 112 | 113 | protected function refreshAPI() { 114 | if ( $this->is_new() ) { 115 | return false; 116 | } 117 | 118 | try { 119 | $response = self::API()->request( 'GET', static::url($this) ); 120 | 121 | if ( isset($response->errors) ) { 122 | throw NFeObjectNotFound(); 123 | } 124 | 125 | $type = self::objectBaseURI(); 126 | $new_object = self::createFromResponse($response->$type); 127 | $this->copy( $new_object ); 128 | $this->resetStates(); 129 | } 130 | catch (Exception $e) { 131 | return false; 132 | } 133 | 134 | return true; 135 | } 136 | 137 | protected function saveAPI() { 138 | try { 139 | $response = self::API()->request( $this->is_new() ? 'POST' : 'PUT', static::url($this), $this->getAttributes() ); 140 | $type = self::objectBaseURI(); 141 | $new_object = self::createFromResponse($response->$type); 142 | 143 | $this->copy( $new_object ); 144 | $this->resetStates(); 145 | 146 | if ( isset($response->errors) ) { 147 | throw new NFeException(); 148 | } 149 | } 150 | catch (Exception $e) { 151 | return false; 152 | } 153 | 154 | return true; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /lib/NFe/Backward_Compatibility.php: -------------------------------------------------------------------------------- 1 | saveAPI(); 14 | } 15 | 16 | public function delete() { 17 | return $this->deleteAPI(); 18 | } 19 | 20 | public function refresh() { 21 | return $this->refreshAPI(); 22 | } 23 | 24 | public static function search( $options = array() ) { 25 | return self::searchAPI($options); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/NFe/Error.php: -------------------------------------------------------------------------------- 1 | $companyId, 'id' => $id ) ); 6 | } 7 | 8 | public static function search( $companyId ) { 9 | return self::searchAPI( array( 'company_id' => $companyId ) ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/NFe/NFe.php: -------------------------------------------------------------------------------- 1 | $companyId, 'id' => $id ) ); 6 | } 7 | 8 | public static function search( $companyId ) { 9 | return self::searchAPI( array( 'company_id' => $companyId ) ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/NFe/Object.php: -------------------------------------------------------------------------------- 1 | _attributes = array(); 14 | $this->_unsavedAttributes = array(); 15 | 16 | foreach ($attributes as $key => $value) { 17 | $this->_attributes[$key] = $value; 18 | } 19 | } 20 | 21 | public function __set($key, $value) { 22 | $this->offsetSet($key, $value); 23 | } 24 | 25 | public function __isset( $key ) { 26 | return $this->offsetExists( $key ); 27 | } 28 | 29 | public function __unset( $key ) { 30 | $this->offsetUnset( $key ); 31 | } 32 | 33 | public function __get( $key ) { 34 | return $this->offsetGet( $key ); 35 | } 36 | 37 | public function offsetSet($key, $value) { 38 | $this->_attributes[$key] = $value; 39 | $this->_unsavedAttributes[$key] = 1; 40 | } 41 | 42 | public function offsetExists($k) { 43 | return array_key_exists($k, $this->_attributes); 44 | } 45 | 46 | public function offsetUnset( $key ) { 47 | unset($this->_attributes[$key]); 48 | unset($this->_unsavedAttributes[$key]); 49 | } 50 | 51 | public function offsetGet( $key ) { 52 | return array_key_exists($key, $this->_attributes) ? $this->_attributes[$key] : null; 53 | } 54 | 55 | public function keys() { 56 | return array_keys($this->_attributes); 57 | } 58 | 59 | public function modifiedAttributes() { 60 | return array_intersect_key( $this->_attributes, $this->_unsavedAttributes ); 61 | } 62 | 63 | public function getAttributes() { 64 | return $this->_attributes; 65 | } 66 | 67 | public function resetStates() { 68 | $this->_unsavedAttributes = array(); 69 | } 70 | 71 | public function is_new() { 72 | return ! isset($this->_attributes['id']); 73 | } 74 | 75 | public function copy($object) { 76 | foreach ($object->keys() as $key) { 77 | $this->_attributes[$key] = $object[$key]; 78 | } 79 | } 80 | 81 | public function __toString() { 82 | if ( isset($this->_attributes['id']) ) { 83 | return $this->_attributes['id']; 84 | } 85 | 86 | return get_called_class(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/NFe/SearchResult.php: -------------------------------------------------------------------------------- 1 | _totalResults = $totalResults; 9 | $this->_results = $results; 10 | } 11 | 12 | public function total() { 13 | return $this->_totalResults; 14 | } 15 | 16 | public function results() { 17 | return $this->_results; 18 | } 19 | 20 | public function set( $results, $totalResults ) { 21 | $this->_totalResults = $totalResults; 22 | $this->_results = $results; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/NFe/ServiceInvoice.php: -------------------------------------------------------------------------------- 1 | $companyId, 'id' => $id ) ); 13 | } 14 | 15 | public static function pdf( $companyId, $id ) { 16 | try { 17 | $url = static::url( array( 'company_id' => $companyId, 'id' => $id ) ) . '/pdf'; 18 | $response = self::API()->request( 'GET', $url ); 19 | 20 | return $response; 21 | } 22 | catch (Exception $e) { 23 | return false; 24 | } 25 | } 26 | 27 | public static function xml( $companyId, $id ) { 28 | try { 29 | $url = static::url( array( 'company_id' => $companyId, 'id' => $id ) ) . '/xml'; 30 | $response = self::API()->request( 'GET', $url ); 31 | 32 | return $response; 33 | } 34 | catch (Exception $e) { 35 | return false; 36 | } 37 | } 38 | 39 | public function cancel() { 40 | return $this->deleteAPI(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/NFe/Utilities.php: -------------------------------------------------------------------------------- 1 | $v) { 39 | if ( is_null($v) ) { 40 | continue; 41 | } 42 | 43 | if ($prefix && $k && !is_int($k)) 44 | $k = $prefix."[".$k."]"; 45 | else if ($prefix) 46 | $k = $prefix."[]"; 47 | 48 | if (is_array($v)) { 49 | $params[] = self::arrayToParams($v, $k); 50 | } else { 51 | $params[] = $k."=".urlencode($v); 52 | } 53 | } 54 | 55 | $v = implode("&", $params); 56 | 57 | // Encode the array into JSON. 58 | $jsonDataEncoded = json_encode($jsonData); 59 | 60 | return $v; 61 | } 62 | 63 | public static function createFromResponse( $object_type, $response ) { 64 | // Should i send fetch to here? 65 | $object_type = str_replace(" ", "", ucwords( str_replace("_", " ", $object_type) ) ); 66 | $class_name = 'NFe_' . $object_type; 67 | 68 | // Bail if class doesn't exist 69 | if ( ! class_exists($class_name) ) { 70 | return null; 71 | } 72 | 73 | if ( is_object($response) && ( isset($response->items) ) && ( isset($response->totalItems) ) ) { 74 | $results = array(); 75 | 76 | foreach ($response->items as $item) { 77 | array_push( $results, self::createFromResponse($object_type, $item) ); 78 | } 79 | 80 | return new NFe_SearchResult( $results, $response->totalItems ); 81 | } 82 | elseif ( is_array($response) ) { 83 | $results = array(); 84 | 85 | foreach ($response as $item) { 86 | array_push( $results, self::createFromResponse($object_type, $item) ); 87 | } 88 | 89 | return new NFe_SearchResult( $results, count($results) ); 90 | } 91 | elseif ( is_object($response) ) { 92 | return new $class_name( (array) $response ); 93 | } 94 | 95 | return null; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/NFe/Webhook.php: -------------------------------------------------------------------------------- 1 | saveAPI(); 14 | } 15 | 16 | public function delete() { 17 | return $this->deleteAPI(); 18 | } 19 | 20 | public function refresh() { 21 | return $this->refreshAPI(); 22 | } 23 | 24 | public static function search( $options = array() ) { 25 | return self::searchAPI($options); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/init.php: -------------------------------------------------------------------------------- 1 | 48527553000123, // Generate CNPJ here: http://www.geradordecnpj.org/ 9 | 'name' => 'TEST Company Name', 10 | 'tradeName' => 'Company Name', 11 | 'email' => 'nfe@mailinator.com', 12 | 'address' => array( 13 | 'country' => 'BRA', 14 | 'postalCode' => '70073901', 15 | 'street' => 'Outros Quadra 1 Bloco G Lote 32', 16 | 'number' => 'S/N', 17 | 'additionalInformation' => 'QUADRA 01 BLOCO G', 18 | 'district' => 'Asa Sul', 19 | 'city' => array( 20 | 'code' => '5300108', 21 | 'name' => 'Brasilia' 22 | ), 23 | 'state' => 'DF' 24 | ), 25 | 'environment' => 'Development' 26 | ); 27 | 28 | $object = NFe_Company::create( $attributes ); 29 | 30 | $this->assertNotNull($object); 31 | $this->assertNotNull( $object->companies->name ); 32 | $this->assertEqual( $object->companies->name, 'TEST Company Name' ); 33 | 34 | self::$id = $object->companies->id; 35 | } 36 | 37 | public function testGet() { 38 | $object = NFe_Company::fetch( self::$id ); 39 | 40 | $this->assertNotNull($object); 41 | $this->assertNotNull($object->companies->name); 42 | $this->assertEqual($object->companies->name, 'TEST Company Name'); 43 | } 44 | 45 | public function testUpdate() { 46 | $object = NFe_Company::fetch( self::$id ); 47 | 48 | $object->companies->name = 'BB SA'; 49 | 50 | // @todo Check it out why this is giving error 51 | // $this->assertTrue( $object->save() ); 52 | 53 | $this->assertNotNull($object); 54 | $this->assertNotNull($object->companies->name); 55 | $this->assertEqual($object->companies->name, 'BB SA'); 56 | } 57 | 58 | public function testDelete() { 59 | $object = NFe_Company::fetch( self::$id ); 60 | 61 | $this->assertNotNull($object); 62 | $this->assertNotNull($object->companies->name); 63 | $this->assertTrue( $object->delete() ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/NFe/LegalPersonTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($result); 10 | } 11 | 12 | public function testFetchFail() { 13 | $this->expectException('NFeObjectNotFound'); 14 | 15 | $result = NFe_LegalPerson::fetch( self::$company_id, self::$company_id ); 16 | 17 | $this->assertNull($result); 18 | } 19 | 20 | public function testSearch() { 21 | $persons = NFe_LegalPerson::search( self::$company_id ); 22 | 23 | $this->assertTrue( count( $persons ) >= 1 ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/NFe/NaturalPersonTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($result); 10 | } 11 | 12 | public function testFetchFail() { 13 | $this->expectException('NFeObjectNotFound'); 14 | 15 | $result = NFe_NaturalPerson::fetch( self::$company_id, self::$company_id ); 16 | 17 | $this->assertNull($result); 18 | } 19 | 20 | public function testSearch() { 21 | $persons = NFe_NaturalPerson::search( self::$company_id ); 22 | 23 | $this->assertTrue( count( $persons ) >= 1 ); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /test/NFe/ServiceInvoiceTest.php: -------------------------------------------------------------------------------- 1 | invoice = NFe_ServiceInvoice::create( 7 | // ID da empresa, você deve copiar exatamente como está no painel 8 | "54244e0ee340420fdc94ad09", 9 | 10 | // Dados da nota fiscal de serviço 11 | array( 12 | // Código do serviço de acordo com o a cidade 13 | 'cityServiceCode' => '2690', 14 | // Descrição dos serviços prestados 15 | 'description' => 'TESTE EMISSAO', 16 | // Valor total do serviços 17 | 'servicesAmount' => 0.01, 18 | 19 | // Dados do Tomador dos Serviços 20 | 'borrower' => array( 21 | // CNPJ ou CPF (opcional para tomadores no exterior) 22 | 'federalTaxNumber' => 191, 23 | // Nome da pessoa física ou Razão Social da Empresa 24 | 'name' => 'TEST BANCO DO BRASIL SA', 25 | // Email para onde deverá ser enviado a nota fiscal 26 | 'email' => 'exemplo@company.com.br', 27 | // Endereço do tomador 28 | 'address' => array( 29 | // Código do pais com três letras 30 | 'country' => 'BRA', 31 | // CEP do endereço (opcional para tomadores no exterior) 32 | 'postalCode' => '70073901', 33 | // Logradouro 34 | 'street' => 'Outros Quadra 1 Bloco G Lote 32', 35 | // Número (opcional) 36 | 'number' => 'S/N', 37 | // Complemento (opcional) 38 | 'additionalInformation' => 'QUADRA 01 BLOCO G', 39 | // Bairro 40 | 'district' => 'Asa Sul', 41 | // Cidade é opcional para tomadores no exterior 42 | 'city' => array( 43 | // Código do IBGE para a Cidade 44 | 'code' => '5300108', 45 | // Nome da Cidade 46 | 'name' => 'Brasilia' 47 | ), 48 | // Sigla do estado (opcional para tomadores no exterior) 49 | 'state' => 'DF' 50 | ) 51 | ) 52 | ) 53 | ); 54 | 55 | $this->assertNotNull($this->invoice); 56 | $this->assertNotNull($this->invoice->id); 57 | $this->assertEqual($this->invoice->servicesAmount, 0.01); 58 | $this->assertEqual($this->invoice->cityServiceCode, '2690'); 59 | } 60 | 61 | public function testFetchInvoice() { 62 | $fetched_invoice = NFe_ServiceInvoice::fetch( 63 | "54244e0ee340420fdc94ad09", 64 | $this->invoice->id 65 | ); 66 | 67 | $this->assertNotNull( $fetched_invoice ); 68 | $this->assertNotNull( $fetched_invoice->id ); 69 | $this->assertNotNull( $fetched_invoice->borrower ); 70 | $this->assertEqual( $fetched_invoice->borrower->name, "TEST BANCO DO BRASIL SA" ); 71 | } 72 | 73 | public function testDownloadPdfInvoice() { 74 | $url = NFe_ServiceInvoice::pdf( 75 | "54244e0ee340420fdc94ad09", 76 | $this->invoice->id 77 | ); 78 | 79 | $this->assertTrue( strpos($url, "pdf") ); 80 | } 81 | 82 | public function testDownloadXmlInvoice() { 83 | $url = NFe_ServiceInvoice::xml( 84 | "54244e0ee340420fdc94ad09", 85 | $this->invoice->id 86 | ); 87 | 88 | $this->assertTrue( strpos($url, "xml") ); 89 | } 90 | 91 | public function testCancelInvoice() { 92 | $fetched_invoice = NFe_ServiceInvoice::fetch( 93 | "54244e0ee340420fdc94ad09", 94 | $this->invoice->id 95 | ); 96 | 97 | $this->assertNotNull($fetched_invoice); 98 | $this->assertNotNull($fetched_invoice->id); 99 | $this->assertEqual($fetched_invoice->id, $this->invoice->id); 100 | 101 | // cancel invoice 102 | $this->assertTrue($fetched_invoice->cancel()); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/NFe/TestCase.php: -------------------------------------------------------------------------------- 1 | 'http://google.com/test', 9 | 'events' => array( 10 | 'issue', 11 | 'cancel' 12 | ), 13 | 'status' => 'active' 14 | ); 15 | 16 | $object = NFe_Webhook::create( $attributes ); 17 | 18 | $this->assertNotNull($object); 19 | $this->assertNotNull($object->hooks->url); 20 | $this->assertEqual($object->hooks->url, $attributes['url']); 21 | 22 | self::$id = $object->hooks->id; 23 | } 24 | 25 | public function testGet() { 26 | $object = NFe_Webhook::fetch( self::$id ); 27 | 28 | $this->assertNotNull( $object ); 29 | $this->assertNotNull( $object['hooks'][0]->url ); 30 | $this->assertEqual( $object['hooks'][0]->url, 'http://google.com/test' ); 31 | } 32 | 33 | public function testUpdate() { 34 | $object = NFe_Webhook::fetch( self::$id ); 35 | 36 | $new_url = 'http://google.com/test2'; 37 | $object->hooks->url = $new_url; 38 | 39 | $this->assertTrue($object->save()); 40 | $this->assertNotNull($object); 41 | $this->assertNotNull($object->hooks->url); 42 | $this->assertEqual($object->hooks->url, $new_url); 43 | } 44 | 45 | public function testDelete() { 46 | $object = NFe_Webhook::fetch( self::$id ); 47 | 48 | $this->assertNotNull($object); 49 | $this->assertNotNull( $object->hooks->url ); 50 | $this->assertTrue($object->delete()); 51 | } 52 | 53 | public function testSearch() { 54 | $hooks = NFe_Webhook::search(); 55 | 56 | $this->assertNotNull( $hooks ); 57 | $this->assertNotNull( $hooks->hooks ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/Nfe.php: -------------------------------------------------------------------------------- 1 | ... ) 26 | * 27 | * @var array 28 | * @see registerCommand 29 | */ 30 | protected $commands = array(); 31 | 32 | /** 33 | * register your own command for the shell 34 | * 35 | * @param string $regex a regex to match against the input line 36 | * @param string $obj a Object 37 | * @param string $method a method in the object to call of the regex matches 38 | * @param string $cmd the command string for the help 39 | * @param string $help the full help description for this command 40 | */ 41 | public function registerCommand($regex, $obj, $method, $cmd, $help) { 42 | $this->commands[] = array( 43 | 'regex' => $regex, 44 | 'obj' => $obj, 45 | 'method' => $method, 46 | 'command' => $cmd, 47 | 'description' => $help 48 | ); 49 | } 50 | 51 | /** 52 | * return a copy of the commands array 53 | * 54 | * @return all commands 55 | */ 56 | public function getCommands() { 57 | return $this->commands; 58 | } 59 | 60 | static function getInstance() { 61 | if (is_null(self::$instance)) { 62 | $class = __CLASS__; 63 | self::$instance = new $class(); 64 | } 65 | return self::$instance; 66 | } 67 | } 68 | 69 | //Shell/Extensions.php 70 | 71 | interface PHP_Shell_Extension { 72 | public function register(); 73 | } 74 | 75 | /** 76 | * storage class for Shell Extensions 77 | * 78 | * 79 | */ 80 | class PHP_Shell_Extensions { 81 | /** 82 | * @var PHP_Shell_Extensions 83 | */ 84 | static protected $instance; 85 | 86 | /** 87 | * storage for the extension 88 | * 89 | * @var array 90 | */ 91 | protected $exts = array(); 92 | 93 | /** 94 | * the extension object gives access to the register objects 95 | * through the a simple $exts->name->... 96 | * 97 | * @param string registered name of the extension 98 | * @return PHP_Shell_Extension object handle 99 | */ 100 | public function __get($key) { 101 | if (!isset($this->exts[$key])) { 102 | throw new Exception("Extension $s is not known."); 103 | } 104 | return $this->exts[$key]; 105 | } 106 | 107 | /** 108 | * register set of extensions 109 | * 110 | * @param array set of (name, class-name) pairs 111 | */ 112 | public function registerExtensions($exts) { 113 | foreach ($exts as $k => $v) { 114 | $this->registerExtension($k, $v); 115 | } 116 | } 117 | 118 | /** 119 | * register a single extension 120 | * 121 | * @param string name of the registered extension 122 | * @param PHP_Shell_Extension the extension object 123 | */ 124 | public function registerExtension($k, PHP_Shell_Extension $obj) { 125 | $obj->register(); 126 | 127 | $this->exts[$k] = $obj; 128 | } 129 | 130 | /** 131 | * @return object a singleton of the class 132 | */ 133 | static function getInstance() { 134 | if (is_null(self::$instance)) { 135 | $class = __CLASS__; 136 | self::$instance = new $class(); 137 | } 138 | return self::$instance; 139 | } 140 | } 141 | 142 | //Shell/Options.php 143 | class PHP_Shell_Options implements PHP_Shell_Extension { 144 | /* 145 | * instance of the current class 146 | * 147 | * @var PHP_Shell_Options 148 | */ 149 | static protected $instance; 150 | 151 | /** 152 | * known options and their setors 153 | * 154 | * @var array 155 | * @see registerOption 156 | */ 157 | protected $options = array(); 158 | 159 | /** 160 | * known options and their setors 161 | * 162 | * @var array 163 | * @see registerOptionAlias 164 | */ 165 | protected $option_aliases = array(); 166 | 167 | public function register() { 168 | $cmd = PHP_Shell_Commands::getInstance(); 169 | $cmd->registerCommand('#^:set #', $this, 'cmdSet', ':set ', 'set a shell variable'); 170 | } 171 | 172 | /** 173 | * register a option 174 | * 175 | * @param string name of the option 176 | * @param object a object handle 177 | * @param string method-name of the setor in the object 178 | * @param string (unused) 179 | */ 180 | public function registerOption($option, $obj, $setor, $getor = null) { 181 | if (!method_exists($obj, $setor)) { 182 | throw new Exception(sprintf("setor %s doesn't exist on class %s", $setor, get_class($obj))); 183 | } 184 | 185 | $this->options[trim($option)] = array("obj" => $obj, "setor" => $setor); 186 | } 187 | 188 | /** 189 | * set a shell-var 190 | * 191 | * :set al to enable autoload 192 | * :set bg=dark to enable highlighting with a dark backgroud 193 | */ 194 | public function cmdSet($l) { 195 | if (!preg_match('#:set\s+([a-z]+)\s*(?:=\s*([a-z0-9]+)\s*)?$#i', $l, $a)) { 196 | print(':set failed: either :set