github.com/mundipagg/boleto-api@v0.0.0-20230620145841-3f9ec742599f/santander/santander.go (about)

     1  package santander
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  
    10  	"net/http"
    11  
    12  	. "github.com/PMoneda/flow"
    13  	"github.com/mundipagg/boleto-api/certificate"
    14  	"github.com/mundipagg/boleto-api/config"
    15  	"github.com/mundipagg/boleto-api/log"
    16  	"github.com/mundipagg/boleto-api/metrics"
    17  	"github.com/mundipagg/boleto-api/models"
    18  	"github.com/mundipagg/boleto-api/tmpl"
    19  	"github.com/mundipagg/boleto-api/util"
    20  	"github.com/mundipagg/boleto-api/validations"
    21  )
    22  
    23  var (
    24  	onceMap       = &sync.Once{}
    25  	onceTransport = &sync.Once{}
    26  	transportTLS  *http.Transport
    27  	m             map[string]string
    28  )
    29  
    30  type bankSantander struct {
    31  	validate  *models.Validator
    32  	log       *log.Log
    33  	transport *http.Transport
    34  }
    35  
    36  //New Create a new Santander Integration Instance
    37  func New() (bankSantander, error) {
    38  	var err error
    39  	b := bankSantander{
    40  		validate: models.NewValidator(),
    41  		log:      log.CreateLog(),
    42  	}
    43  
    44  	certificates := certificate.TLSCertificate{
    45  		Crt: config.Get().SantanderCertificateSSLName,
    46  		Key: config.Get().SantanderCertificateSSLName,
    47  	}
    48  
    49  	onceTransport.Do(func() {
    50  		transportTLS, err = util.BuildTLSTransport(certificates)
    51  	})
    52  	b.transport = transportTLS
    53  
    54  	if err != nil || (b.transport == nil && !config.Get().MockMode) {
    55  		return bankSantander{}, fmt.Errorf("fail on load TLSTransport: %v", err)
    56  	}
    57  
    58  	b.validate.Push(validations.ValidateAmount)
    59  	b.validate.Push(validations.ValidateExpireDate)
    60  	b.validate.Push(validations.ValidateBuyerDocumentNumber)
    61  	b.validate.Push(validations.ValidateRecipientDocumentNumber)
    62  	b.validate.Push(santanderValidateAgreementNumber)
    63  	b.validate.Push(satanderBoletoTypeValidate)
    64  
    65  	return b, nil
    66  }
    67  
    68  //Log retorna a referencia do log
    69  func (b bankSantander) Log() *log.Log {
    70  	return b.log
    71  }
    72  
    73  func (b bankSantander) GetTicket(boleto *models.BoletoRequest) (string, error) {
    74  	boleto.Title.OurNumber = calculateOurNumber(boleto)
    75  	boleto.Title.BoletoType, boleto.Title.BoletoTypeCode = getBoletoType(boleto)
    76  	pipe := NewFlow()
    77  	url := config.Get().URLTicketSantander
    78  	tlsURL := strings.Replace(config.Get().URLTicketSantander, "https", "tls", 1)
    79  	pipe.From("message://?source=inline", boleto, getRequestTicket(), tmpl.GetFuncMaps())
    80  	pipe.To("log://?type=request&format=xml&url="+url, b.log)
    81  	duration := util.Duration(func() {
    82  		pipe.To(tlsURL, b.transport, map[string]string{"timeout": config.Get().TimeoutToken})
    83  	})
    84  	metrics.PushTimingMetric("santander-get-ticket-boleto-time", duration.Seconds())
    85  	pipe.To("log://?type=response&format=xml&url="+url, b.log)
    86  	ch := pipe.Choice()
    87  	ch.When(Header("status").IsEqualTo("200"))
    88  	ch.To("transform://?format=xml", getTicketResponse(), `{{.returnCode}}:::{{.ticket}}`, tmpl.GetFuncMaps())
    89  	ch.When(Header("status").IsEqualTo("403"))
    90  	ch.To("set://?prop=body", errors.New("403 Forbidden"))
    91  	ch.Otherwise()
    92  	ch.To("log://?type=request&url="+url, b.log).To("set://?prop=body", errors.New("integration error"))
    93  	switch t := pipe.GetBody().(type) {
    94  	case string:
    95  		items := pipe.GetBody().(string)
    96  		parts := strings.Split(items, ":::")
    97  		returnCode, ticket := parts[0], parts[1]
    98  		return ticket, checkError(returnCode)
    99  	case error:
   100  		return "", t
   101  	}
   102  	return "", nil
   103  }
   104  
   105  func (b bankSantander) RegisterBoleto(input *models.BoletoRequest) (models.BoletoResponse, error) {
   106  	serviceURL := config.Get().URLRegisterBoletoSantander
   107  	fromResponse := getResponseSantander()
   108  	toAPI := getAPIResponseSantander()
   109  	inputTemplate := getRequestSantander()
   110  	santanderURL := strings.Replace(serviceURL, "https", "tls", 1)
   111  
   112  	exec := NewFlow().From("message://?source=inline", input, inputTemplate, tmpl.GetFuncMaps())
   113  	exec.To("log://?type=request&format=xml&url="+serviceURL, b.log)
   114  	duration := util.Duration(func() {
   115  		exec.To(santanderURL, b.transport, map[string]string{"method": "POST", "insecureSkipVerify": "true", "timeout": config.Get().TimeoutRegister})
   116  	})
   117  	metrics.PushTimingMetric("santander-register-boleto-time", duration.Seconds())
   118  	exec.To("log://?type=response&format=xml&url="+serviceURL, b.log)
   119  	ch := exec.Choice()
   120  	ch.When(Header("status").IsEqualTo("200"))
   121  	ch.To("transform://?format=xml", fromResponse, toAPI, tmpl.GetFuncMaps())
   122  	ch.To("unmarshall://?format=json", new(models.BoletoResponse))
   123  	ch.Otherwise()
   124  	ch.To("log://?type=response&url="+serviceURL, b.log).To("apierro://")
   125  	switch t := exec.GetBody().(type) {
   126  	case *models.BoletoResponse:
   127  		return *t, nil
   128  	case error:
   129  		return models.BoletoResponse{}, t
   130  	}
   131  	return models.BoletoResponse{}, models.NewInternalServerError("MP500", "Internal error")
   132  }
   133  func (b bankSantander) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
   134  	errs := b.ValidateBoleto(boleto)
   135  	if len(errs) > 0 {
   136  		return models.BoletoResponse{Errors: errs}, nil
   137  	}
   138  	if ticket, err := b.GetTicket(boleto); err != nil {
   139  		return models.BoletoResponse{Errors: errs}, err
   140  	} else {
   141  		boleto.Authentication.AuthorizationToken = ticket
   142  	}
   143  	return b.RegisterBoleto(boleto)
   144  }
   145  
   146  func (b bankSantander) ValidateBoleto(boleto *models.BoletoRequest) models.Errors {
   147  	return models.Errors(b.validate.Assert(boleto))
   148  }
   149  
   150  //GetBankNumber retorna o codigo do banco
   151  func (b bankSantander) GetBankNumber() models.BankNumber {
   152  	return models.Santander
   153  }
   154  
   155  func (b bankSantander) GetErrorsMap() map[string]int {
   156  	return nil
   157  }
   158  
   159  func calculateOurNumber(boleto *models.BoletoRequest) uint {
   160  	ourNumberWithDigit := strconv.Itoa(int(boleto.Title.OurNumber)) + util.OurNumberDv(strconv.Itoa(int(boleto.Title.OurNumber)), util.MOD11)
   161  	value, _ := strconv.Atoi(ourNumberWithDigit)
   162  	return uint(value)
   163  }
   164  
   165  func (b bankSantander) GetBankNameIntegration() string {
   166  	return "Santander"
   167  }
   168  
   169  func santanderBoletoTypes() map[string]string {
   170  	onceMap.Do(func() {
   171  		m = make(map[string]string)
   172  
   173  		m["DM"] = "02"  //Duplicata Mercantil
   174  		m["DS"] = "04"  //Duplicata de serviço
   175  		m["NP"] = "12"  //Nota promissória
   176  		m["RC"] = "17"  //Recibo
   177  		m["BDP"] = "32" //Boleto de proposta
   178  		m["CH"] = "97"  //Cheque
   179  		m["OUT"] = "99" //Outros
   180  	})
   181  
   182  	return m
   183  }
   184  
   185  func getBoletoType(boleto *models.BoletoRequest) (bt string, btc string) {
   186  	if len(boleto.Title.BoletoType) < 1 {
   187  		return "DM", "02"
   188  	}
   189  
   190  	btm := santanderBoletoTypes()
   191  
   192  	if btm[strings.ToUpper(boleto.Title.BoletoType)] == "" {
   193  		return "DM", "02"
   194  	}
   195  
   196  	return boleto.Title.BoletoType, btm[strings.ToUpper(boleto.Title.BoletoType)]
   197  }