github.com/mundipagg/boleto-api@v0.0.0-20230620145841-3f9ec742599f/bradescoNetEmpresa/bradescoNetEmpresa.go (about) 1 package bradescoNetEmpresa 2 3 import ( 4 "errors" 5 "fmt" 6 "html" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/mundipagg/boleto-api/metrics" 12 13 "github.com/mundipagg/boleto-api/tmpl" 14 "github.com/mundipagg/boleto-api/util" 15 16 "github.com/PMoneda/flow" 17 "github.com/mundipagg/boleto-api/config" 18 "github.com/mundipagg/boleto-api/log" 19 "github.com/mundipagg/boleto-api/models" 20 "github.com/mundipagg/boleto-api/validations" 21 ) 22 23 var o = &sync.Once{} 24 var m map[string]string 25 26 type bankBradescoNetEmpresa struct { 27 validate *models.Validator 28 log *log.Log 29 } 30 31 type barcode struct { 32 bankCode string 33 currencyCode string 34 dateDueFactor string 35 value string 36 agency string 37 wallet string 38 ourNumber string 39 account string 40 zero string 41 } 42 43 func New() bankBradescoNetEmpresa { 44 b := bankBradescoNetEmpresa{ 45 validate: models.NewValidator(), 46 log: log.CreateLog(), 47 } 48 b.validate.Push(validations.ValidateAmount) 49 b.validate.Push(validations.ValidateExpireDate) 50 b.validate.Push(validations.ValidateMaxExpirationDate) 51 b.validate.Push(validations.ValidateBuyerDocumentNumber) 52 b.validate.Push(validations.ValidateRecipientDocumentNumber) 53 b.validate.Push(validations.ValidateBuyerDocumentNumber) 54 b.validate.Push(validations.ValidateRecipientDocumentNumber) 55 56 b.validate.Push(bradescoNetEmpresaValidateAgency) 57 b.validate.Push(bradescoNetEmpresaValidateAccount) 58 b.validate.Push(bradescoNetEmpresaValidateWallet) 59 b.validate.Push(bradescoNetEmpresaBoletoTypeValidate) 60 61 return b 62 } 63 64 func (b bankBradescoNetEmpresa) Log() *log.Log { 65 return b.log 66 } 67 68 func (b bankBradescoNetEmpresa) RegisterBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) { 69 70 boleto.Title.BoletoType, boleto.Title.BoletoTypeCode = getBoletoType(boleto) 71 r := flow.NewFlow() 72 serviceURL := config.Get().URLBradescoNetEmpresa 73 xmlResponse := getResponseBradescoNetEmpresaXml() 74 jsonReponse := getResponseBradescoNetEmpresaJson() 75 from := getResponseBradescoNetEmpresa() 76 to := getAPIResponseBradescoNetEmpresa() 77 78 bod := r.From("message://?source=inline", boleto, getRequestBradescoNetEmpresa(), tmpl.GetFuncMaps()) 79 bod.To("log://?type=request&url="+serviceURL, b.log) 80 81 err := signRequest(bod) 82 if err != nil { 83 return models.BoletoResponse{}, err 84 } 85 86 duration := util.Duration(func() { 87 bod.To(serviceURL, map[string]string{"method": "POST", "insecureSkipVerify": "true", "timeout": config.Get().TimeoutDefault}) 88 }) 89 90 metrics.PushTimingMetric("bradesco-netempresa-register-boleto-online", duration.Seconds()) 91 bod.To("log://?type=response&url="+serviceURL, b.log) 92 93 bod.To("transform://?format=xml", xmlResponse, jsonReponse) 94 bodyTransform := fmt.Sprintf("%v", bod.GetBody()) 95 bodyJson := html.UnescapeString(bodyTransform) 96 bod.To("set://?prop=body", strings.Replace(bodyJson, "\t", "", -1)) 97 98 ch := bod.Choice() 99 100 if header := bod.GetHeader(); header["status"] == "200" { 101 ch.When(flow.Header("status").IsEqualTo("200")) 102 } else { 103 ch.When(flow.Header("status").IsEqualTo("500")) 104 } 105 106 ch.To("transform://?format=json", from, to, tmpl.GetFuncMaps()) 107 ch.To("unmarshall://?format=json", new(models.BoletoResponse)) 108 ch.Otherwise() 109 ch.To("log://?type=response&url="+serviceURL, b.log).To("apierro://") 110 111 switch t := bod.GetBody().(type) { 112 case *models.BoletoResponse: 113 if !t.HasErrors() { 114 t.BarCodeNumber = getBarcode(*boleto).toString() 115 } 116 return *t, nil 117 case error: 118 return models.BoletoResponse{}, t 119 } 120 return models.BoletoResponse{}, models.NewInternalServerError("MP500", "Internal error") 121 } 122 123 func (b bankBradescoNetEmpresa) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) { 124 errs := b.ValidateBoleto(boleto) 125 if len(errs) > 0 { 126 return models.BoletoResponse{Errors: errs}, nil 127 } 128 return b.RegisterBoleto(boleto) 129 } 130 131 func (b bankBradescoNetEmpresa) ValidateBoleto(boleto *models.BoletoRequest) models.Errors { 132 return models.Errors(b.validate.Assert(boleto)) 133 } 134 135 func (b bankBradescoNetEmpresa) GetBankNumber() models.BankNumber { 136 return models.Bradesco 137 } 138 139 func (b bankBradescoNetEmpresa) GetBankNameIntegration() string { 140 return "BradescoNetEmpresa" 141 } 142 143 func (b bankBradescoNetEmpresa) GetErrorsMap() map[string]int { 144 return nil 145 } 146 147 func signRequest(bod *flow.Flow) error { 148 149 if !config.Get().MockMode { 150 bodyToSign := fmt.Sprintf("%v", bod.GetBody()) 151 signedRequest, err := util.SignRequest(bodyToSign) 152 if err != nil { 153 return err 154 } 155 bod.To("set://?prop=body", signedRequest) 156 } 157 158 return nil 159 } 160 161 func getBarcode(boleto models.BoletoRequest) (bc barcode) { 162 bc.bankCode = fmt.Sprintf("%d", models.Bradesco) 163 bc.currencyCode = fmt.Sprintf("%d", models.Real) 164 bc.account = fmt.Sprintf("%07s", boleto.Agreement.Account) 165 bc.agency = fmt.Sprintf("%04s", boleto.Agreement.Agency) 166 bc.dateDueFactor, _ = dateDueFactor(boleto.Title.ExpireDateTime) 167 bc.ourNumber = fmt.Sprintf("%011d", boleto.Title.OurNumber) 168 bc.value = fmt.Sprintf("%010d", boleto.Title.AmountInCents) 169 bc.wallet = fmt.Sprintf("%02d", boleto.Agreement.Wallet) 170 bc.zero = "0" 171 return 172 } 173 174 func (bc barcode) toString() string { 175 return fmt.Sprintf("%s%s%s%s%s%s%s%s%s%s", bc.bankCode, bc.currencyCode, bc.calcCheckDigit(), bc.dateDueFactor, bc.value, bc.agency, bc.wallet, bc.ourNumber, bc.account, bc.zero) 176 } 177 178 func (bc barcode) calcCheckDigit() string { 179 prevCode := fmt.Sprintf("%s%s%s%s%s%s%s%s%s", bc.bankCode, bc.currencyCode, bc.dateDueFactor, bc.value, bc.agency, bc.wallet, bc.ourNumber, bc.account, bc.zero) 180 return util.BarcodeDv(prevCode) 181 } 182 183 func dateDueFactor(dateDue time.Time) (string, error) { 184 var dateDueFixed = time.Date(1997, 10, 7, 0, 0, 0, 0, time.UTC) 185 dif := dateDue.Sub(dateDueFixed) 186 factor := int(dif.Hours() / 24) 187 if factor <= 0 { 188 return "", errors.New("DateDue must be in the future") 189 } 190 return fmt.Sprintf("%04d", factor), nil 191 } 192 193 func bradescoNetEmpresaBoletoTypes() map[string]string { 194 195 o.Do(func() { 196 m = make(map[string]string) 197 198 m["CH"] = "01" //Cheque 199 m["DM"] = "02" //Duplicata Mercantil 200 m["DS"] = "04" //Duplicata de serviços 201 m["NP"] = "12" //Nota promissória 202 m["RC"] = "17" //Recibo 203 m["OUT"] = "99" //Outros 204 }) 205 206 return m 207 } 208 209 func getBoletoType(boleto *models.BoletoRequest) (bt string, btc string) { 210 if len(boleto.Title.BoletoType) < 1 { 211 return "DM", "02" 212 } 213 btm := bradescoNetEmpresaBoletoTypes() 214 215 if btm[strings.ToUpper(boleto.Title.BoletoType)] == "" { 216 return "DM", "02" 217 } 218 219 return boleto.Title.BoletoType, btm[strings.ToUpper(boleto.Title.BoletoType)] 220 }