Skip to content

Instantly share code, notes, and snippets.

@antonyalkmim
Created January 30, 2018 10:57
Show Gist options
  • Save antonyalkmim/dd2369b358b7ea8cbf423b026233111a to your computer and use it in GitHub Desktop.
Save antonyalkmim/dd2369b358b7ea8cbf423b026233111a to your computer and use it in GitHub Desktop.
Classe para identificar informacoes do boleto a partir do codigo de barras ou linha digitavel
[
{
"id":"246",
"name":"Banco ABC Brasil S.A."
},
{
"id":"075",
"name":"Banco ABN AMRO S.A."
},
{
"id":"025",
"name":"Banco Alfa S.A."
},
{
"id":"641",
"name":"Banco Alvorada S.A."
},
{
"id":"065",
"name":"Banco Andbank (Brasil) S.A."
},
{
"id":"024",
"name":"Banco BANDEPE S.A."
},
{
"id":"740",
"name":"Banco Barclays S.A."
},
{
"id":"107",
"name":"Banco BBM S.A."
},
{
"id":"096",
"name":"Banco BM&FBOVESPA de Serviços de Liquidação e Custódia S.A"
},
{
"id":"318",
"name":"Banco BMG S.A."
},
{
"id":"752",
"name":"Banco BNP Paribas Brasil S.A."
},
{
"id":"248",
"name":"Banco Boavista Interatlântico S.A."
},
{
"id":"218",
"name":"Banco Bonsucesso S.A."
},
{
"id":"063",
"name":"Banco Bradescard S.A."
},
{
"id":"036",
"name":"Banco Bradesco BBI S.A."
},
{
"id":"204",
"name":"Banco Bradesco Cartões S.A."
},
{
"id":"394",
"name":"Banco Bradesco Financiamentos S.A."
},
{
"id":"237",
"name":"Banco Bradesco S.A."
},
{
"id":"208",
"name":"Banco BTG Pactual S.A."
},
{
"id":"263",
"name":"Banco Cacique S.A."
},
{
"id":"473",
"name":"Banco Caixa Geral - Brasil S.A."
},
{
"id":"040",
"name":"Banco Cargill S.A."
},
{
"id":"739",
"name":"Banco Cetelem S.A."
},
{
"id":"233",
"name":"Banco Cifra S.A."
},
{
"id":"745",
"name":"Banco Citibank S.A."
},
{
"id":"000",
"name":"Banco CNH Industrial Capital S.A."
},
{
"id":"095",
"name":"Banco Confidence de Câmbio S.A."
},
{
"id":"756",
"name":"Banco Cooperativo do Brasil S.A. - BANCOOB"
},
{
"id":"748",
"name":"Banco Cooperativo Sicredi S.A."
},
{
"id":"222",
"name":"Banco Credit Agricole Brasil S.A."
},
{
"id":"505",
"name":"Banco Credit Suisse (Brasil) S.A."
},
{
"id":"003",
"name":"Banco da Amazônia S.A."
},
{
"id":"083",
"name":"Banco da China Brasil S.A."
},
{
"id":"707",
"name":"Banco Daycoval S.A."
},
{
"id":"456",
"name":"Banco de Tokyo-Mitsubishi UFJ Brasil S.A."
},
{
"id":"001",
"name":"Banco do Brasil S.A."
},
{
"id":"047",
"name":"Banco do Estado de Sergipe S.A."
},
{
"id":"037",
"name":"Banco do Estado do Pará S.A."
},
{
"id":"041",
"name":"Banco do Estado do Rio Grande do Sul S.A."
},
{
"id":"004",
"name":"Banco do Nordeste do Brasil S.A."
},
{
"id":"265",
"name":"Banco Fator S.A."
},
{
"id":"224",
"name":"Banco Fibra S.A."
},
{
"id":"626",
"name":"Banco Ficsa S.A."
},
{
"id":"612",
"name":"Banco Guanabara S.A."
},
{
"id":"012",
"name":"Banco INBURSA de Investimentos S.A."
},
{
"id":"604",
"name":"Banco Industrial do Brasil S.A."
},
{
"id":"653",
"name":"Banco Indusval S.A."
},
{
"id":"249",
"name":"Banco Investcred Unibanco S.A."
},
{
"id":"184",
"name":"Banco Itaú BBA S.A."
},
{
"id":"029",
"name":"Banco Itaú BMG Consignado S.A."
},
{
"id":"479",
"name":"Banco ItaúBank S.A"
},
{
"id":"376",
"name":"Banco J. P. Morgan S.A."
},
{
"id":"074",
"name":"Banco J. Safra S.A."
},
{
"id":"217",
"name":"Banco John Deere S.A."
},
{
"id":"600",
"name":"Banco Luso Brasileiro S.A."
},
{
"id":"389",
"name":"Banco Mercantil do Brasil S.A."
},
{
"id":"370",
"name":"Banco Mizuho do Brasil S.A."
},
{
"id":"746",
"name":"Banco Modal S.A."
},
{
"id":"212",
"name":"Banco Original S.A."
},
{
"id":"623",
"name":"Banco PAN S.A."
},
{
"id":"611",
"name":"Banco Paulista S.A."
},
{
"id":"094",
"name":"Banco Petra S.A."
},
{
"id":"643",
"name":"Banco Pine S.A."
},
{
"id":"747",
"name":"Banco Rabobank International Brasil S.A."
},
{
"id":"633",
"name":"Banco Rendimento S.A."
},
{
"id":"120",
"name":"Banco Rodobens S.A."
},
{
"id":"422",
"name":"Banco Safra S.A."
},
{
"id":"033",
"name":"Banco Santander (Brasil) S.A."
},
{
"id":"366",
"name":"Banco Société Générale Brasil S.A."
},
{
"id":"464",
"name":"Banco Sumitomo Mitsui Brasileiro S.A."
},
{
"id":"082",
"name":"Banco Topázio S.A."
},
{
"id":"634",
"name":"Banco Triângulo S.A."
},
{
"id":"655",
"name":"Banco Votorantim S.A."
},
{
"id":"610",
"name":"Banco VR S.A."
},
{
"id":"119",
"name":"Banco Western Union do Brasil S.A."
},
{
"id":"021",
"name":"BANESTES S.A. Banco do Estado do Espírito Santo"
},
{
"id":"719",
"name":"Banif-Banco Internacional do Funchal (Brasil)S.A."
},
{
"id":"755",
"name":"Bank of America Merrill Lynch Banco Múltiplo S.A."
},
{
"id":"081",
"name":"BBN Banco Brasileiro de Negócios S.A."
},
{
"id":"250",
"name":"BCV - Banco de Crédito e Varejo S.A."
},
{
"id":"017",
"name":"BNY Mellon Banco S.A."
},
{
"id":"069",
"name":"BPN Brasil Banco Múltiplo S.A."
},
{
"id":"125",
"name":"Brasil Plural S.A. - Banco Múltiplo"
},
{
"id":"070",
"name":"BRB - Banco de Brasília S.A."
},
{
"id":"104",
"name":"Caixa Econômica Federal"
},
{
"id":"320",
"name":"China Construction Bank (Brasil) Banco Múltiplo S.A."
},
{
"id":"477",
"name":"Citibank N.A."
},
{
"id":"487",
"name":"Deutsche Bank S.A. - Banco Alemão"
},
{
"id":"064",
"name":"Goldman Sachs do Brasil Banco Múltiplo S.A."
},
{
"id":"078",
"name":"Haitong Banco de Investimento do Brasil S.A."
},
{
"id":"062",
"name":"Hipercard Banco Múltiplo S.A."
},
{
"id":"399",
"name":"HSBC Bank Brasil S.A. - Banco Múltiplo"
},
{
"id":"492",
"name":"ING Bank N.V."
},
{
"id":"652",
"name":"Itaú Unibanco Holding S.A."
},
{
"id":"341",
"name":"Itaú Unibanco S.A."
},
{
"id":"488",
"name":"JPMorgan Chase Bank, National Association"
},
{
"id":"128",
"name":"MSB Bank S.A. Banco de Câmbio"
},
{
"id":"254",
"name":"Paraná Banco S.A."
},
{
"id":"751",
"name":"Scotiabank Brasil S.A. Banco Múltiplo"
},
{
"id":"118",
"name":"Standard Chartered Bank (Brasil) S/A–Bco Invest."
},
{
"id":"129",
"name":"UBS Brasil Banco de Investimento S.A."
},
{
"id" : "077",
"name": "Banco Inter"
}
]
//
// Billit.swift
// antonyalkmim
//
// Implementacao da classe boleto para identificar valor, data de vencimento e linha digitavel a partir de um codigo de barras
// ou identificar valor e data de vencimento a partir de uma linha digitavel.
// Todas as regras implementadas seguem a documentacao da FEBRABRAN (https://cmsportal.febraban.org.br/Arquivos/documentos/PDF/Layout%20-%20C%C3%B3digo%20de%20Barras%20-%20Vers%C3%A3o%205%20-%2001_08_2016.pdf)
//
// Created by Antony Alkmim on 26/01/18.
//
import Foundation
enum BillitError: Swift.Error {
case invalidBarcode
}
class Billit {
/// representacao numerica do codigo de barras
var barcode: String?
/// Linha digitavel
var barcodeLine: String?
/// Data de vencimento do boleto
let dueDate: Date?
/// valor do boleto
let value: Double?
/// Nome do emissor, banco ou convênio
let bankName: String
/// Inicializa um Boleto a partir da linha digitavel do boleto
///
/// - Parameter barcodeLine: Linha digitável do boleto
init(withBarcodeLine barcodeLine: String) {
var _barcode = barcodeLine
.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: "-", with: "")
//Boletos de arrecardação se a primeira posição começa com 8
if _barcode[0] == "8" {
self.barcodeLine = _barcode
// remover digitos verificadores da linha digitavel
_barcode.remove(at: barcodeLine.index(for: 11))
_barcode.remove(at: barcodeLine.index(for: 22))
_barcode.remove(at: barcodeLine.index(for: 33))
_barcode.remove(at: barcodeLine.index(for: 44))
self.barcode = _barcode
// SEGMENT
let segmentId = _barcode[1]
bankName = segmentNameFor(segmentId: segmentId) ?? "Convênio Desconhecido"
// VALUE
value = _barcode[2] == "6" || _barcode[2] == "8" ? Double(_barcode[4...14])! / 100 : nil
/// DUE DATE
let dateformatter = DateFormatter()
dateformatter.locale = Locale(identifier: "pt_BR")
dateformatter.dateFormat = "yyyyMMdd"
dateformatter.timeZone = TimeZone(identifier: "America/Sao_Paulo")
// data parseada para caso campo livre vai de 20...44 ou 24...44
dueDate = dateformatter.date(from: _barcode[19...43][0...7]) ?? dateformatter.date(from: _barcode[23...43][0...7])
} else { //boletos de cobranças
//Codigo do banco 0 a 2
let bankId = _barcode[0...2]
//Fator de vencimento (posições 5 a 8 do código de barras)
let dueFactory = _barcode[33...36]
// Valor nominal do título (posições 10 a 19 do código de barras)
let valueReaded = _barcode[37...46]
self.barcodeLine = _barcode
self.barcode = nil
self.dueDate = dueDateFor(factory: dueFactory)
self.value = Double(valueReaded)! / 100
self.bankName = bankNameFor(bankId: bankId) ?? "Banco Desconhecido"
}
}
/// Inicializa um boleto a partir da representacao numerica do codigo de barras
///
/// - Parameter barcode: codigo de barras
init(withBarcode barcode: String) {
var _barcode = barcode
.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: "-", with: "")
//barcode should contain more than 44 characteres
guard _barcode.count >= 44 else {
fatalError("Billit barcode should contain 44 characteres or more")
}
self.barcode = _barcode
self.barcodeLine = Billit.linhaDigitavelFrom(_barcode)?
.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: "-", with: "")
//Boletos de arrecardação se a primeira posição começa com 8
if _barcode[0] == "8" {
// SEGMENT
let segmentId = _barcode[1]
bankName = segmentNameFor(segmentId: segmentId) ?? "Convênio Desconhecido"
// VALUE
value = _barcode[2] == "6" || _barcode[2] == "8" ? Double(_barcode[4...14])! / 100 : nil
/// DUE DATE
let dateformatter = DateFormatter()
dateformatter.locale = Locale(identifier: "pt_BR")
dateformatter.dateFormat = "yyyyMMdd"
dateformatter.timeZone = TimeZone(identifier: "America/Sao_Paulo")
// data parseada para caso campo livre vai de 20...44 ou 24...44
dueDate = dateformatter.date(from: _barcode[19...43][0...7]) ?? dateformatter.date(from: _barcode[23...43][0...7])
} else { //boletos de cobranças
//Codigo do banco 0 a 2
let bankId = _barcode[0...2]
//Fator de vencimento (posições 5 a 8 do código de barras)
let dueFactory = _barcode[5...9]
// valor real
let valueReaded = _barcode[9...19]
self.dueDate = dueDateFor(factory: dueFactory)
self.value = Double(valueReaded)! / 100
self.bankName = bankNameFor(bankId: bankId) ?? "Banco Desconhecido"
}
}
/// Converte codigo de barras para linha digitavel
private static func linhaDigitavelFrom(_ barcode: String) -> String? {
// Remover caracteres não numéricos.
let _barcode = barcode
.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: "-", with: "")
guard _barcode.count == 44 else {
return nil
}
/// obter campos co codigo de barras
let campo1 = _barcode[0...3] + _barcode[19] + "." + _barcode[20...23]
let campo2 = _barcode[24...28] + "." + _barcode[29...33]
let campo3 = _barcode[34...38] + "." + _barcode[39...43]
let campo4 = _barcode[4] // Digito verificador
let campo5 = _barcode[5...18] // Vencimento + Valor
guard modulo11Banco(_barcode[0...3] + _barcode[5...43]) == Int(campo4)! else {
return nil //'Digito verificador '+campo4+', o correto é '+modulo11_banco( linha.substr(0,4)+linha.substr(5,99) )+'\nO sistema não altera automaticamente o dígito correto na quinta casa!'
}
/// mask 00000.00000 00000.000000 00000.000000 0 00000000000000
return "\(campo1)\(modulo10(campo1)) \(campo2)\(modulo10(campo2)) \(campo3)\(modulo10(campo3)) \(campo4) \(campo5)"
}
private static func modulo10(_ num: String) -> Int {
let numero = num.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: "-", with: "")
var soma = 0
var peso = 2
var contador = numero.count - 1
while contador >= 0 {
var multiplicacao = Int(numero[contador])! * peso
if multiplicacao >= 10 { multiplicacao = 1 + (multiplicacao - 10) }
soma = soma + multiplicacao
peso = peso == 2 ? 1 : 2
contador = contador - 1
}
let digito = 10 - (soma % 10)
return digito == 10 ? 0 : digito
}
private static func modulo11Banco(_ num: String) -> Int {
let numero = num.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: "-", with: "")
var soma = 0
var peso = 2
let base = 9
let contador = numero.count - 1
for i in (0...contador).reversed() {
soma = soma + Int(numero[i])! * peso
peso = peso < base ? peso + 1 : 2
}
var digito = 11 - (soma % 11)
digito = digito > 9 ? 0 : digito
digito = digito == 0 ? 1 : digito
return digito
}
}
extension String {
func index(for i: Int) -> String.Index {
return self.index(self.startIndex, offsetBy: i)
}
subscript (bounds: CountableClosedRange<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return String(self[start...end])
}
subscript (bounds: CountableRange<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return String(self[start..<end])
}
subscript (_ nth: Int) -> String {
return String(self[index(startIndex, offsetBy: nth)])
}
}
private func dueDateFor(factory: String) -> Date? {
guard factory != "0000" else {
return nil
}
let dateformatter = DateFormatter()
dateformatter.locale = Locale(identifier: "pt_BR")
dateformatter.dateFormat = "dd/MM/yyyy"
dateformatter.timeZone = TimeZone(identifier: "America/Sao_Paulo")
// Vencimento e calculado baseado no numero de dias a partir da data base do FEBRABRAN (07/10/1997)
let baseFebrabranDate = dateformatter.date(from: "07/10/1997")!
let daysFromBaseDate = Int(factory)!
return Calendar.current.date(byAdding: .day, value: daysFromBaseDate, to: baseFebrabranDate)
}
private func readJson(withName filename: String, bundle: Bundle = Bundle.main) -> Data! {
let path = bundle.path(forResource: filename, ofType: "json")
return (try? Data(contentsOf: URL(fileURLWithPath: path!)))
}
private func bankNameFor(bankId: String) -> String? {
let jsonData = readJson(withName: "banks")!
let banksJson = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [[String: String]]
let bank = banksJson.first { $0["id"] == bankId }
return bank?["name"]
}
private func segmentNameFor(segmentId: String) -> String? {
let jsonData = readJson(withName: "segments")!
let segmentsJson = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [[String: String]]
let segment = segmentsJson.first { $0["id"] == segmentId }
return segment?["name"]
}
[
{
"id":"1",
"name":"Prefeituras"
},
{
"id":"2",
"name":"Saneamento"
},
{
"id":"3",
"name":"Energia Elétrica e Gás"
},
{
"id":"4",
"name":"Telecomunicações"
},
{
"id":"5",
"name":"Órgãos Governamentais"
},
{
"id":"6",
"name":"Carnes e Assemelhados ou Outros"
},
{
"id":"7",
"name":"Multas de trânsito"
},
{
"id":"9",
"name":"Uso exclusivo do banco"
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment