├── .gitignore ├── .rspec ├── .ruby-gemset ├── .ruby-version ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── lib ├── data │ └── ssl-bundle.crt ├── nfe.rb └── nfe │ ├── api_operations │ ├── cancel.rb │ ├── create.rb │ ├── delete.rb │ ├── download.rb │ ├── list.rb │ ├── retrieve.rb │ └── update.rb │ ├── api_resource.rb │ ├── company.rb │ ├── configuration.rb │ ├── errors │ └── nfe_error.rb │ ├── legal_people.rb │ ├── natural_people.rb │ ├── nfe_object.rb │ ├── service_invoice.rb │ ├── util.rb │ └── version.rb ├── nfe.gemspec └── spec ├── nfe ├── api_resource_spec.rb ├── company_spec.rb ├── legal_people_spec.rb ├── natural_people_spec.rb ├── nfe_spec.rb ├── service_invoices_spec.rb └── webhooks_spec.rb └── rspec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /.bundle/ 3 | /.yardoc 4 | /Gemfile.lock 5 | /_yardoc/ 6 | /coverage/ 7 | /doc/ 8 | /pkg/ 9 | /spec/reports/ 10 | /tmp/ 11 | /nfe-io-*.gem 12 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | nfe_io 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.4.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.1.5 4 | before_install: gem install bundler -v 1.10.6 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in nfe.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ricardo Caldeira 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 Ruby 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 | Para executar a instalação do nosso pacote, você deverá incluir essa linha no Gemfile da sua aplicação: 10 | 11 | ```ruby 12 | gem 'nfe-io' 13 | ``` 14 | 15 | E depois executar: 16 | 17 | $ bundle 18 | 19 | Ou se preferir, instale diretamente via comando: 20 | 21 | $ gem install nfe-io 22 | 23 | ## Exemplos de uso 24 | 25 | > Em construção! 26 | 27 | ### Como emitir uma Nota Fiscal de Serviço? 28 | Abaixo, temos um código-exemplo para realizar uma Emissão de Nota Fiscal de Serviço: 29 | 30 | ```ruby 31 | # Define a API Key, conforme está no painel 32 | Nfe.api_key('c73d49f9649046eeba36dcf69f6334fd') 33 | 34 | # ID da empresa, você encontra no painel 35 | Nfe::ServiceInvoice.company_id("55df4dc6b6cd9007e4f13ee8") 36 | 37 | # Dados do Tomador dos Serviços 38 | customer_params = { 39 | borrower: { 40 | federalTaxNumber: '191', # CNPJ ou CPF (opcional para tomadores no exterior) 41 | name: 'BANCO DO BRASIL SA', # Nome da pessoa física ou Razão Social da Empresa 42 | email: 'nfe-io@mailinator.com', # Email para onde deverá ser enviado a nota fiscal 43 | # Endereço do tomador 44 | address: { 45 | country: 'BRA', # Código do pais com três letras 46 | postalCode: '70073901', # CEP do endereço (opcional para tomadores no exterior) 47 | street: 'Rua Do Cliente', # Logradouro 48 | number: 'S/N', # Número (opcional) 49 | additionalInformation: 'QUADRA 01 BLOCO G', # Complemento (opcional) 50 | district: 'Asa Sul', # Bairro 51 | city: { # Cidade é opcional para tomadores no exterior 52 | code: 4204202, # Código do IBGE para a Cidade 53 | name: 'Brasilia' # Nome da Cidade 54 | }, 55 | state: 'DF' 56 | } 57 | } 58 | } 59 | 60 | # Dados da nota fiscal de serviço 61 | service_params = { 62 | cityServiceCode: '2690', # Código do serviço de acordo com o a cidade 63 | description: 'Teste, para manutenção e suporte técnico.', # Descrição dos serviços prestados 64 | servicesAmount: 0.1 # Valor total do serviços 65 | } 66 | 67 | # Emite a nota fiscal 68 | invoice_create_result = Nfe::ServiceInvoice.create(customer_params.merge(service_params)) 69 | ``` 70 | 71 | ### Como cancelar uma nota? 72 | Abaixo, temos um código-exemplo para efetuar o cancelamento de uma nota: 73 | 74 | ```ruby 75 | # Define a API Key, conforme está no painel 76 | Nfe.api_key('c73d49f9649046eeba36dcf69f6334fd') 77 | # ID da empresa, você encontra no painel 78 | Nfe::ServiceInvoice.company_id("55df4dc6b6cd9007e4f13ee8") 79 | # O parâmetro é o ID da nota 80 | invoice = Nfe::ServiceInvoice.cancel("59443a0e2a8b6806986d7a2d") 81 | # A resposta são os dados da nota com a mudança de estado para "WaitingSendCancel" 82 | ``` 83 | 84 | ### Criar uma Empresa para Emissão de Notas 85 | >Em construção! 86 | 87 | ### Como efetuar o download de uma nota em PDF? 88 | Abaixo, temos um código exemplo para baixar uma nota em PDF: 89 | 90 | ```ruby 91 | # Define a API Key, conforme está no painel 92 | Nfe.api_key('c73d49f9649046eeba36dcf69f6334fd') 93 | # ID da empresa, você encontra no painel 94 | Nfe::ServiceInvoice.company_id("55df4dc6b6cd9007e4f13ee8") 95 | # Os formatos suportados são :pdf e :xml, e o primeiro parâmetro é o ID da nota 96 | invoice = Nfe::ServiceInvoice.download("59443a0e2a8b6806986d7a2d", :pdf) 97 | # O conteúdo do PDF/XML pode ser acessado da seguinte forma 98 | invoice.body 99 | # Caso você esteja utilizando Rails, pode usar o método send_data para retornar 100 | # o conteúdo da Nota Fiscal diretamente para o usuário 101 | # Note que neste caso o arquivo é o PDF, mas poderia ser o XML, mude se necessário 102 | send_data(invoice.body, filename: 'invoice.pdf', type: 'application/pdf') 103 | ``` 104 | 105 | ### Como validar o Webhook? 106 | ```ruby 107 | def request_is_authentic? 108 | body = request.body.read 109 | signature = request.headers['X-NFEIO-Signature'] 110 | 111 | hash = 'sha1=' + Base64.strict_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), ENV.fetch("NFEIO_WEBHOOK_SECRET"), body)) 112 | 113 | ActiveSupport::SecurityUtils.secure_compare(hash, signature) 114 | end 115 | ``` 116 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "nfe" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /lib/nfe.rb: -------------------------------------------------------------------------------- 1 | require "rest-client" 2 | require "json" 3 | require "nfe/version" 4 | require "nfe/configuration" 5 | 6 | require "nfe/api_resource" 7 | require "nfe/api_operations/create" 8 | require "nfe/api_operations/delete" 9 | require "nfe/api_operations/cancel" 10 | require "nfe/api_operations/list" 11 | require "nfe/api_operations/retrieve" 12 | require "nfe/api_operations/update" 13 | require "nfe/api_operations/download" 14 | 15 | require "nfe/nfe_object" 16 | require "nfe/service_invoice" 17 | require "nfe/company" 18 | require "nfe/natural_people" 19 | require "nfe/legal_people" 20 | 21 | require "nfe/util" 22 | require "nfe/errors/nfe_error" 23 | 24 | 25 | module Nfe 26 | @@api_key = '' 27 | 28 | def self.api_key(api_key) 29 | @@api_key = api_key 30 | end 31 | 32 | def self.access_keys 33 | "#{@@api_key}" 34 | end 35 | 36 | def self.configuration 37 | @configuration ||= Configuration.new 38 | end 39 | 40 | def self.configure 41 | yield(configuration) if block_given? 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/nfe/api_operations/cancel.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | module ApiOperations 3 | module Cancel 4 | def cancel(nfe_id) 5 | method = :delete 6 | response = api_request("#{url}/#{nfe_id}", method) 7 | create_from(response) 8 | end 9 | 10 | def self.included(base) 11 | base.extend(Cancel) 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/nfe/api_operations/create.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | module ApiOperations 3 | module Create 4 | def create(params) 5 | method = :post 6 | response = api_request(url, method, params) 7 | create_from(response) 8 | end 9 | 10 | def self.included(base) 11 | base.extend(Create) 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/nfe/api_operations/delete.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | module ApiOperations 3 | module Delete 4 | def delete(params) 5 | method = :delete 6 | response = api_request(url, method, params) 7 | # reflesh_object(response) 8 | response 9 | end 10 | 11 | def self.included(base) 12 | base.extend(Delete) 13 | end 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /lib/nfe/api_operations/download.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | module ApiOperations 3 | module Download 4 | def download(nfe_id, file_format) 5 | if file_format != :pdf && file_format != :xml 6 | rcode = '422' 7 | message = 'Invalid file format. Only :pdf or :xml are supported' 8 | formatted = { error: message } 9 | raise NfeError.new(rcode, formatted, formatted, message) 10 | else 11 | url = "#{self.url}/#{nfe_id}/#{file_format}" 12 | method = :get 13 | api_request_file(url, method) 14 | end 15 | end 16 | 17 | def self.included(base) 18 | base.extend(Download) 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/nfe/api_operations/list.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | module ApiOperations 3 | module List 4 | def list_all(params=nil) 5 | method = :get 6 | response = api_request(url, method, params) 7 | create_from(response) 8 | end 9 | 10 | def self.included(base) 11 | base.extend(List) 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/nfe/api_operations/retrieve.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | module ApiOperations 3 | module Retrieve 4 | def retrieve(obj_id) 5 | obj_id = "?id=#{obj_id}" if obj_id.include? '@' 6 | method = :get 7 | response = api_request("#{url}/#{obj_id}",method) 8 | create_from(response) 9 | end 10 | 11 | def self.included(base) 12 | base.extend(Retrieve) 13 | end 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /lib/nfe/api_operations/update.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | module ApiOperations 3 | module Update 4 | def save 5 | params_to_update = {} 6 | self.serialize_params.each do |key| 7 | return if (@values[key] || @values[key.to_s]).is_a? Nfe::NfeObject 8 | params_to_update[key] = (@values[key] || @values[key.to_s]) if @values 9 | end 10 | puts params_to_update 11 | 12 | update(params_to_update) 13 | end 14 | 15 | def update(params=nil, url_sufix=nil) 16 | method = :post 17 | response = api_request("#{url}/#{url_sufix}",method,params) 18 | reflesh_object(response) 19 | end 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/nfe/api_resource.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | module ApiResource 3 | SSL_BUNDLE_PATH = File.dirname(__FILE__) + '/../data/ssl-bundle.crt' 4 | 5 | def url_encode(key) 6 | URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")) 7 | end 8 | 9 | def encode(params) 10 | params.map { |k,v| "#{k}=#{url_encode(v)}" }.join('&') 11 | end 12 | 13 | def api_request(url, method, params=nil) 14 | url = "#{Nfe.configuration.url}#{url}" 15 | api_key = Nfe.access_keys 16 | 17 | if method == :get && params 18 | params_encoded = encode(params) 19 | url = "#{url}?#{params_encoded}" 20 | params = nil 21 | end 22 | 23 | payload = (params != nil) ? params.to_json : '' 24 | request = RestClient::Request.new( 25 | method: method, 26 | url: url, 27 | payload: payload, 28 | headers: { 29 | content_type: 'application/json', 30 | accept: 'application/json', 31 | authorization: api_key, 32 | user_agent: Nfe.configuration.user_agent 33 | }) 34 | 35 | begin 36 | response = request.execute 37 | rescue RestClient::ExceptionWithResponse => e 38 | if rcode = e.http_code and rbody = e.http_body 39 | rbody = JSON.parse(rbody) 40 | rbody = Util.symbolize_names(rbody) 41 | 42 | raise NfeError.new(rcode, rbody, rbody, rbody[:message]) 43 | else 44 | raise e 45 | end 46 | rescue RestClient::Exception => e 47 | raise e 48 | end 49 | JSON.parse(response.to_s) 50 | end 51 | 52 | def self.included(base) 53 | base.extend(ApiResource) 54 | end 55 | 56 | def api_request_file(url, method, params=nil) 57 | api_key = Nfe.access_keys 58 | url = "#{Nfe.configuration.url}#{url}?api_key=#{api_key}" 59 | 60 | request = RestClient::Request.new( 61 | method: method, 62 | url: url, 63 | headers: { user_agent: Nfe.configuration.user_agent } 64 | ) 65 | 66 | begin 67 | response = request.execute 68 | rescue RestClient::ExceptionWithResponse => e 69 | if rcode = e.http_code and rbody = e.http_body 70 | rbody = JSON.parse(rbody) 71 | rbody = Util.symbolize_names(rbody) 72 | 73 | raise NfeError.new(rcode, rbody, rbody, rbody[:message]) 74 | else 75 | raise e 76 | end 77 | rescue RestClient::Exception => e 78 | raise e 79 | end 80 | 81 | response 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/nfe/company.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | class Company < NfeObject 3 | include ApiResource 4 | include ApiOperations::Create 5 | include ApiOperations::List 6 | include ApiOperations::Retrieve 7 | include ApiOperations::Delete 8 | include ApiOperations::Update 9 | 10 | def self.url 11 | "/v1/companies" 12 | end 13 | 14 | def url 15 | "#{self.class.url}/#{self.id}" 16 | end 17 | 18 | def self.create_from(params) 19 | obj = self.new 20 | obj.reflesh_object(params["companies"]) 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /lib/nfe/configuration.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | class Configuration 3 | attr_accessor :url, :user_agent 4 | 5 | def initialize 6 | @url = "https://api.nfe.io" 7 | @user_agent = "NFe.io Ruby Client v#{Nfe::VERSION}" 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/nfe/errors/nfe_error.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | class NfeError < StandardError 3 | attr_reader :http_status, :json_message, :http_message, :message 4 | 5 | def initialize(http_status=nil, json_message=nil, http_message=nil, message=nil) 6 | @http_status = http_status 7 | @json_message = json_message 8 | @http_message = http_message 9 | @message = message 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /lib/nfe/legal_people.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | class LegalPeople < NfeObject 3 | include ApiResource 4 | include ApiOperations::List 5 | include ApiOperations::Retrieve 6 | 7 | def self.company_id(company_id) 8 | @company_id = company_id 9 | end 10 | 11 | def self.url 12 | "/v1/companies/#{@company_id}/legalpeople" 13 | end 14 | 15 | def url 16 | "#{self.class.url}/#{self.id}" 17 | end 18 | 19 | def self.create_from(params) 20 | obj = self.new 21 | obj.reflesh_object(params["legalPeople"]) 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /lib/nfe/natural_people.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | class NaturalPeople < NfeObject 3 | include ApiResource 4 | include ApiOperations::List 5 | include ApiOperations::Retrieve 6 | 7 | def self.company_id(company_id) 8 | @company_id = company_id 9 | end 10 | 11 | def self.url 12 | "/v1/companies/#{@company_id}/naturalpeople" 13 | end 14 | 15 | def url 16 | "#{self.class.url}/#{self.id}" 17 | end 18 | 19 | def self.create_from(params) 20 | obj = self.new 21 | obj.reflesh_object(params["naturalPeople"]) 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /lib/nfe/nfe_object.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | class NfeObject 3 | @values 4 | def metaclass 5 | class << self; self; end 6 | end 7 | 8 | def self.create_from(params) 9 | obj = self.new 10 | obj.reflesh_object(params) 11 | end 12 | 13 | def reflesh_object(params) 14 | if params.class == Array 15 | params = create_list(params) 16 | end 17 | create_fields(params) 18 | set_properties(params) 19 | self 20 | end 21 | 22 | def create_fields(params) 23 | instance_eval do 24 | metaclass.instance_eval do 25 | params.keys.each do |key| 26 | define_method(key) { @values[key] } 27 | 28 | define_method("#{key}=") do |value| 29 | @values[key] = value 30 | end 31 | end 32 | end 33 | end 34 | end 35 | 36 | def set_properties(params) 37 | instance_eval do 38 | params.each do |key, value| 39 | if value.class == Hash 40 | @values[key] = Util.get_object_type(value['object']).create_from(value) 41 | elsif value.class == Array 42 | @values[key] = value.map{ |v| Util.get_object_type(v['object']).create_from(v) } 43 | else 44 | @values[key] = value 45 | end 46 | end 47 | end 48 | end 49 | 50 | def self.url 51 | "/v1/#{CGI.escape(class_name.downcase)}s" 52 | end 53 | 54 | def url 55 | "#{self.class.url}/#{self.id}" 56 | end 57 | 58 | def self.class_name 59 | self.name.split('::')[-1] 60 | end 61 | 62 | def initialize() 63 | @values = {} 64 | end 65 | 66 | def method_missing(name, *args) 67 | if name.to_s.end_with? '=' 68 | name = name.to_s[0...-1].to_sym 69 | end 70 | 71 | if serialize_params.include? name 72 | @values[name] = args[0] 73 | else 74 | raise NoMemoryError.new ("Cannot set #{name} on this object") 75 | end 76 | end 77 | 78 | def to_hash 79 | @values.inject({}) do |result, (key,value)| 80 | if value.is_a? Array 81 | list = [] 82 | value.each do |obj| 83 | list.push(obj.respond_to?(:to_hash) ? obj.to_hash : value) 84 | end 85 | result[key] = list 86 | else if value.respond_to?(:to_hash) 87 | result[key] = value.to_hash 88 | else 89 | result[key] = value 90 | end 91 | end 92 | result 93 | end 94 | end 95 | 96 | def to_json(*a) 97 | JSON.generate(@values) 98 | end 99 | 100 | def to_s 101 | JSON.generate(@values) 102 | end 103 | 104 | def create_list(array) 105 | hash = {} 106 | hash[:data] = array 107 | hash[:object] = 'list' 108 | hash 109 | end 110 | end 111 | end -------------------------------------------------------------------------------- /lib/nfe/service_invoice.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | class ServiceInvoice < NfeObject 3 | include ApiResource 4 | include ApiOperations::Create 5 | include ApiOperations::List 6 | include ApiOperations::Retrieve 7 | include ApiOperations::Cancel 8 | include ApiOperations::Update 9 | include ApiOperations::Download 10 | 11 | def self.company_id(company_id) 12 | @company_id = company_id 13 | end 14 | 15 | def self.url 16 | "/v1/companies/#{@company_id}/serviceinvoices" 17 | end 18 | 19 | def url 20 | self.class.url 21 | end 22 | 23 | def self.create_from(params) 24 | params 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/nfe/util.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | class Util 3 | OBJECT_TYPES = { 4 | 'company' => Nfe::Company, 5 | 'serviceInvoice' => Nfe::ServiceInvoice, 6 | 'naturalPeople' => Nfe::NaturalPeople 7 | } 8 | 9 | def self.get_object_type(type) 10 | object_type = Nfe::NfeObject 11 | object_type = OBJECT_TYPES[type] if OBJECT_TYPES[type] 12 | object_type 13 | end 14 | 15 | def self.symbolize_names(object) 16 | case object 17 | when Hash 18 | new = {} 19 | object.each do |key, value| 20 | key = (key.to_sym rescue key) || key 21 | new[key] = symbolize_names(value) 22 | end 23 | new 24 | when Array 25 | object.map { |value| symbolize_names(value) } 26 | else 27 | object 28 | end 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /lib/nfe/version.rb: -------------------------------------------------------------------------------- 1 | module Nfe 2 | VERSION = "0.3.2" 3 | end 4 | -------------------------------------------------------------------------------- /nfe.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'nfe/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "nfe-io" 8 | spec.version = Nfe::VERSION 9 | spec.authors = ["Ricardo Caldeira"] 10 | spec.email = ["ricardo.nezz@gmail.com"] 11 | 12 | spec.summary = %q{Nfe.io's ruby gem} 13 | spec.description = %q{Nfe.io's ruby gem} 14 | spec.homepage = "https://pluga.co/" 15 | spec.license = "MIT" 16 | 17 | # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or 18 | # delete this section to allow pushing this gem to any host. 19 | # if spec.respond_to?(:metadata) 20 | # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'" 21 | # else 22 | # raise "RubyGems 2.0 or newer is required to protect against public gem pushes." 23 | # end 24 | 25 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 26 | spec.bindir = "exe" 27 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 28 | spec.require_paths = ["lib"] 29 | 30 | spec.add_dependency "rest-client", "~> 2.0.2" 31 | 32 | spec.add_development_dependency "bundler", "~> 1.10" 33 | spec.add_development_dependency "rake", "~> 10.0" 34 | spec.add_development_dependency "rspec" 35 | spec.add_development_dependency "byebug" 36 | end 37 | -------------------------------------------------------------------------------- /spec/nfe/api_resource_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../rspec_helper' 2 | 3 | describe Nfe::ApiResource do 4 | before(:each) do 5 | Nfe.api_key('e12cmDevG5iLhSd9Y7BOpxynL86Detjd2R1D5jsP5UGXA8gwxug0Vojl3H9TIzBpbhI') 6 | end 7 | 8 | it 'call Nfe API' do 9 | url = "/v1/companies" 10 | method = :get 11 | 12 | response = Nfe::Company.api_request(url, method) 13 | expect(response['companies'].class).to eq(Array) 14 | end 15 | 16 | it 'should throw error' do 17 | url = "/v1/companies" 18 | method = :post 19 | 20 | params = {exp_month: 5, 21 | exp_year: 2017, 22 | cvc: '021', 23 | name: 'Doctor Who' 24 | } 25 | 26 | expect{ Nfe::Company.api_request(url, method, params) }.to raise_error Nfe::NfeError 27 | 28 | end 29 | 30 | it 'should create NfeError with error message and http code' do 31 | http_code = 400 32 | json_message = {error: {type: '', message: 'Error Message'}} 33 | http_message = '' 34 | message = 'Error Message' 35 | nfe_error = Nfe::NfeError.new(http_code, json_message, http_message, message) 36 | expect(nfe_error.http_status).to eq(http_code) 37 | expect(nfe_error.json_message).to eq(json_message) 38 | expect(nfe_error.http_message).to eq(http_message) 39 | expect(nfe_error.message).to eq(message) 40 | end 41 | end -------------------------------------------------------------------------------- /spec/nfe/company_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../rspec_helper' 2 | 3 | describe Nfe::Company do 4 | before(:each) do 5 | Nfe.api_key('e12cmDevG5iLhSd9Y7BOpxynL86Detjd2R1D5jsP5UGXA8gwxug0Vojl3H9TIzBpbhI') 6 | end 7 | 8 | let(:company_params) { 9 | company_params = { id: "55df4dc6b6cd9007e4f13ee8", federaltaxnumber: "25953366000106", name: "Empresa de Teste"} 10 | } 11 | 12 | it 'should create a Company' do 13 | company_params = {name: 'MyCompany', 14 | federaltaxnumber: 54458287000127, 15 | email: 'empresa@teste.com.br', 16 | openningdate: '21/02/2005', 17 | taxregime: 'LucroReal', 18 | legalnature: 'EmpresaPublica', 19 | municipaltaxnumber: 2} 20 | response = {"companies"=>{"id"=>"55e0a00c41c23f0a584b398a", "name"=>"MyCompany", "federalTaxNumber"=>82388034000160, 21 | "email"=>"empresa@teste.com.br", "taxRegime"=>"LucroReal", "legalNature"=>"EmpresaPublica", 22 | "economicActivities"=>[], "municipalTaxNumber"=>2, "rpsSerialNumber"=>"IO", "rpsNumber"=>0, 23 | "environment"=>"Development", "fiscalStatus"=>"Pending", "certificate"=>{"status"=>"Pending"}, 24 | "createdOn"=>"2015-09-03T19:43:05.3772349+00:00", "modifiedOn"=>"2015-09-03T19:43:05.3772349+00:00"}} 25 | allow(Nfe::Company).to receive(:api_request).and_return(response) 26 | company = Nfe::Company.create(company_params) 27 | 28 | expect(company.name).to eq('MyCompany') 29 | expect(company.email).to eq('empresa@teste.com.br') 30 | end 31 | 32 | it 'should list all active companies' do 33 | company_list = Nfe::Company.list_all 34 | expect(company_list.object).to eq('list') 35 | expect(company_list.data.size).to be >= 1 36 | end 37 | 38 | it 'should retrieve a company by id' do 39 | company = Nfe::Company.retrieve(company_params[:id]) 40 | expect(company.id).to eq(company_params[:id]) 41 | expect(company.name).to eq(company_params[:name]) 42 | end 43 | 44 | it 'should retrieve a company by federalTaxNumber' do 45 | company = Nfe::Company.retrieve(company_params[:federaltaxnumber]) 46 | expect(company.name).to eq("Company Name") 47 | end 48 | 49 | it 'should delete a company' do 50 | skip "Recurso não está sendo deletado na API" 51 | company_params = {name: 'Company Teste', 52 | federaltaxnumber: 32657522000157, 53 | email: 'empresa@teste.com.br', 54 | openningdate: '21/02/2005', 55 | taxregime: 'LucroReal', 56 | legalnature: 'EmpresaPublica', 57 | municipaltaxnumber: 2} 58 | response = {"companies"=>{"id"=>"55e9a4cf440c3b0b84ceace6", "name"=>"Company Teste", "federalTaxNumber"=>32657522000157, 59 | "email"=>"empresa@teste.com.br", "taxRegime"=>"LucroReal", "legalNature"=>"EmpresaPublica", 60 | "economicActivities"=>[], "municipalTaxNumber"=>2, "rpsSerialNumber"=>"IO", "rpsNumber"=>0, 61 | "environment"=>"Development", "fiscalStatus"=>"Pending", "certificate"=>{"status"=>"Pending"}, 62 | "createdOn"=>"2015-09-03T19:43:05.3772349+00:00", "modifiedOn"=>"2015-09-03T19:43:05.3772349+00:00"}} 63 | allow(Nfe::Company).to receive(:api_request).and_return(response) 64 | company = Nfe::Company.create(company_params) 65 | deleted_company = company.delete('55e9a4cf440c3b0b84ceace6') 66 | expect(deleted_company).to eq('teste') 67 | end 68 | 69 | it 'should update a company' do 70 | skip "Recurso retorna 0 para atributos (taxRegime e legalNature)" 71 | company = Nfe::Company.retrieve(company_params[:id]) 72 | company.name = "New Company Name" 73 | company.save 74 | company_updated = Nfe::Company.retrieve(company_params[:id]) 75 | expect(company.name).to eq(company_updated.name) 76 | expect(company.name).to eq("New Company Name") 77 | expect(company_updated.name).to eq("New Company Name") 78 | end 79 | 80 | it 'should set digital certificate to a company' do 81 | skip "To be implemented" 82 | end 83 | 84 | end 85 | -------------------------------------------------------------------------------- /spec/nfe/legal_people_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../rspec_helper' 2 | 3 | describe Nfe::LegalPeople do 4 | before(:each) do 5 | Nfe.api_key('e12cmDevG5iLhSd9Y7BOpxynL86Detjd2R1D5jsP5UGXA8gwxug0Vojl3H9TIzBpbhI') 6 | Nfe::LegalPeople.company_id("55df4dc6b6cd9007e4f13ee8") 7 | end 8 | 9 | it 'should list all LegalPeople' do 10 | natural_peoples_list = Nfe::LegalPeople.list_all 11 | expect(natural_peoples_list.object).to eq('list') 12 | expect(natural_peoples_list.data.size).to be >= 0 13 | end 14 | 15 | it 'should retrieve a LegalPeople' do 16 | customer_params = {borrower: { federalTaxNumber: '69919334000160', name: 'Empresa Teste', email: 'ricardo.nezz@mailinator.com', 17 | postalCode: '21231110', street: 'Rua Do Cliente', number: '1310', additionalInformation: 'AP 202', 18 | district: 'Centro', city_code: 4204202, city_name: 'Chapecó', city_state: 'SC' }} 19 | 20 | service_params = { cityServiceCode: '0107', description: 'Manutenção e suporte técnico.', servicesAmount: 0.15 } 21 | 22 | Nfe::ServiceInvoice.company_id("55df4dc6b6cd9007e4f13ee8") 23 | nfe = Nfe::ServiceInvoice.create(customer_params.merge(service_params)) 24 | 25 | natural_people = Nfe::LegalPeople.retrieve("55ef27f8440c3b0b84cebc2d") 26 | expect(natural_people.id).to eq("55ef27f8440c3b0b84cebc2d") 27 | expect(natural_people.name).to eq("Nome da Empresa") 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/nfe/natural_people_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../rspec_helper' 2 | 3 | describe Nfe::NaturalPeople do 4 | before(:each) do 5 | Nfe.api_key('e12cmDevG5iLhSd9Y7BOpxynL86Detjd2R1D5jsP5UGXA8gwxug0Vojl3H9TIzBpbhI') 6 | Nfe::NaturalPeople.company_id("55e8a2c9440c3b0b84ceaa12") 7 | end 8 | 9 | it 'should list all NaturalPeople' do 10 | natural_peoples_list = Nfe::NaturalPeople.list_all 11 | expect(natural_peoples_list.object).to eq('list') 12 | expect(natural_peoples_list.data.size).to be >= 1 13 | end 14 | 15 | it 'should retrieve a NaturalPeople' do 16 | natural_people = Nfe::NaturalPeople.retrieve("55eeebf8440c3b0b84ceb1a8") 17 | expect(natural_people.id).to eq("55eeebf8440c3b0b84ceb1a8") 18 | expect(natural_people.name).to eq("Ricardo Caldeira") 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/nfe/nfe_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../rspec_helper' 2 | 3 | describe Nfe do 4 | it 'has a version number' do 5 | expect(Nfe::VERSION).not_to be nil 6 | end 7 | 8 | it 'should set api key' do 9 | Nfe.api_key('e12cmDevG5iLhSd9Y7BOpxynL86Detjd2R1D5jsP5UGXA8gwxug0Vojl3H9TIzBpbhI') 10 | expect(Nfe.access_keys).to eq('e12cmDevG5iLhSd9Y7BOpxynL86Detjd2R1D5jsP5UGXA8gwxug0Vojl3H9TIzBpbhI') 11 | end 12 | 13 | it 'should get empty access keys' do 14 | Nfe.api_key('') 15 | expect(Nfe.access_keys).to eq("") 16 | end 17 | 18 | describe Nfe::NfeObject do 19 | it 'should convert to hash' do 20 | params_2 = {a: 1, b: 'b'} 21 | params = {a: 'a', b: 'b', c: 1, d: Nfe::NfeObject.create_from(params_2), e:[a: 'a', b: 1, c: Nfe::NfeObject.create_from(params_2)]} 22 | obj = Nfe::NfeObject.create_from(params) 23 | expect(obj.to_hash).to eq(params.merge({d: params_2, e: [a: 'a', b: 1, c: params_2]})) 24 | end 25 | 26 | it 'should convert to json' do 27 | params_2 = {a: 1, b: 'b'} 28 | params = {a: 'a', b: 'b', c: 1, d: Nfe::NfeObject.create_from(params_2), e:[a: 'a', b: 1, c: Nfe::NfeObject.create_from(params_2)]} 29 | obj = Nfe::NfeObject.create_from(params) 30 | expect(obj.to_json).to eq(params.to_json) 31 | end 32 | 33 | it 'should convert to string' do 34 | params_2 = {a: 1, b: 'b'} 35 | params = {a: 'a', b: 'b', c: 1, d: Nfe::NfeObject.create_from(params_2), e:[a: 'a', b: 1, c: Nfe::NfeObject.create_from(params_2)]} 36 | obj = Nfe::NfeObject.create_from(params) 37 | expect(obj.to_s).to eq(params.to_json.to_s) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/nfe/service_invoices_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../rspec_helper' 2 | 3 | describe Nfe::ServiceInvoice do 4 | before(:each) do 5 | Nfe.api_key('e12cmDevG5iLhSd9Y7BOpxynL86Detjd2R1D5jsP5UGXA8gwxug0Vojl3H9TIzBpbhI') 6 | Nfe::ServiceInvoice.company_id("55df4dc6b6cd9007e4f13ee8") 7 | end 8 | 9 | it 'should create a ServiceInvoice' do 10 | customer_params = {borrower: { federalTaxNumber: '01946377198', 11 | name: 'Ricardo Caldeira', 12 | email: 'ricardo.nezz@mailinator.com', 13 | address: { 14 | country: 'BRA', 15 | postalCode: '22231110', 16 | street: 'Rua Do Cliente', 17 | number: '1310', 18 | additionalInformation: 'AP 202', 19 | district: 'Centro', 20 | city: { 21 | code: 4204202, 22 | name: 'Chapecó' 23 | }, 24 | state: 'SC' 25 | }}} 26 | service_params = { cityServiceCode: '2690', description: 'Manutenção e suporte técnico.', servicesAmount: 0.15 } 27 | nfe = Nfe::ServiceInvoice.create(customer_params.merge(service_params)) 28 | 29 | expect(nfe["environment"]).to eq('Development') 30 | expect(nfe["borrower"]["name"]).to eq('Ricardo Caldeira') 31 | end 32 | 33 | it 'should list all service invoices' do 34 | service_invoices_list = Nfe::ServiceInvoice.list_all 35 | expect(service_invoices_list["totalResults"]).to be >= 1 36 | expect(service_invoices_list["serviceInvoices"].size).to be >= 1 37 | end 38 | 39 | it 'should list service invoices by page' do 40 | service_invoices_list = Nfe::ServiceInvoice.list_all(pageCount: 5, pageIndex: 2) 41 | expect(service_invoices_list["totalResults"]).to be >= 1 42 | expect(service_invoices_list["totalPages"]).to be >= 1 43 | expect(service_invoices_list["page"]).to eq(2) 44 | expect(service_invoices_list["serviceInvoices"].size).to be >= 1 45 | end 46 | 47 | it 'should retrieve a ServiceInvoice' do 48 | service_invoices_list = Nfe::ServiceInvoice.list_all 49 | service_invoice_params = service_invoices_list["serviceInvoices"].first 50 | service_invoice = Nfe::ServiceInvoice.retrieve(service_invoice_params["id"]) 51 | expect(service_invoice["id"]).to eq(service_invoice_params["id"]) 52 | expect(service_invoice["rpsStatus"]).to eq("Normal") 53 | end 54 | 55 | it 'should cancel a ServiceInvoice' do 56 | service_invoices_list = Nfe::ServiceInvoice.list_all 57 | service_invoice = service_invoices_list["serviceInvoices"].first 58 | response = Nfe::ServiceInvoice.cancel(service_invoice["id"]) 59 | expect(response["id"]).to eq(service_invoice["id"]) 60 | expect(response["flowStatus"]).to eq("WaitingSendCancel") 61 | end 62 | 63 | it 'should send a email to Tomador' do 64 | skip "To be implemented" 65 | end 66 | 67 | it 'should retrieve a ServiceInvoice XML file' do 68 | service_invoices_list = Nfe::ServiceInvoice.list_all 69 | service_invoice_params = service_invoices_list["serviceInvoices"].first 70 | 71 | response = Nfe::ServiceInvoice.download(service_invoice_params["id"], :xml) 72 | 73 | expect(response.headers[:content_type]).to eq("application/xml") 74 | expect(response.headers[:content_length].size).to be >= 1 75 | expect(response.size).to be >= 1 76 | end 77 | 78 | it 'should retrieve a ServiceInvoice PDF file' do 79 | service_invoices_list = Nfe::ServiceInvoice.list_all 80 | service_invoice_params = service_invoices_list["serviceInvoices"].first 81 | 82 | response = Nfe::ServiceInvoice.download(service_invoice_params["id"], :pdf) 83 | 84 | expect(response.headers[:content_type]).to eq("application/pdf") 85 | expect(response.headers[:content_length].size).to be >= 1 86 | expect(response.size).to be >= 1 87 | end 88 | 89 | it 'should not retrieve a ServiceInvoice PDF file when API Key is not valid' do 90 | service_invoices_list = Nfe::ServiceInvoice.list_all 91 | service_invoice_params = service_invoices_list["serviceInvoices"].first 92 | 93 | Nfe.api_key('not_valid_api_keycont') 94 | expect { 95 | Nfe::ServiceInvoice.download(service_invoice_params["id"], :pdf) 96 | }.to raise_error(Nfe::NfeError) 97 | end 98 | 99 | it 'should not retrieve a ServiceInvoice XML file when API Key is not valid' do 100 | service_invoices_list = Nfe::ServiceInvoice.list_all 101 | service_invoice_params = service_invoices_list["serviceInvoices"].first 102 | 103 | Nfe.api_key('not_valid_api_keycont') 104 | expect { 105 | Nfe::ServiceInvoice.download(service_invoice_params["id"], :xml) 106 | }.to raise_error(Nfe::NfeError) 107 | end 108 | 109 | it 'should retrieve Service Invoices from Prefeitura' do 110 | skip "To be implemented" 111 | end 112 | 113 | end 114 | -------------------------------------------------------------------------------- /spec/nfe/webhooks_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nfe/client-ruby/4a8ad8fd3250dec1cbed038f65401c349972f9dd/spec/nfe/webhooks_spec.rb -------------------------------------------------------------------------------- /spec/rspec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'byebug' 3 | require_relative '../lib/nfe' 4 | --------------------------------------------------------------------------------