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  }