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

     1  package stone
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"github.com/PMoneda/flow"
     8  	"github.com/mundipagg/boleto-api/config"
     9  	"github.com/mundipagg/boleto-api/log"
    10  	"github.com/mundipagg/boleto-api/metrics"
    11  	"github.com/mundipagg/boleto-api/models"
    12  	"github.com/mundipagg/boleto-api/tmpl"
    13  	"github.com/mundipagg/boleto-api/util"
    14  	"github.com/mundipagg/boleto-api/validations"
    15  )
    16  
    17  type bankStone struct {
    18  	validate *models.Validator
    19  	log      *log.Log
    20  }
    21  
    22  //New Create a new Stone Integration Instance
    23  func New() bankStone {
    24  	b := bankStone{
    25  		validate: models.NewValidator(),
    26  		log:      log.CreateLog(),
    27  	}
    28  
    29  	b.validate.Push(validations.ValidateAmount)
    30  	b.validate.Push(validations.ValidateExpireDate)
    31  	b.validate.Push(validations.ValidateBuyerDocumentNumber)
    32  	b.validate.Push(validations.ValidateRecipientDocumentNumber)
    33  
    34  	b.validate.Push(stoneValidateAccessKeyNotEmpty)
    35  	b.validate.Push(validations.ValidateInterest)
    36  	b.validate.Push(validations.ValidateFine)
    37  
    38  	return b
    39  }
    40  
    41  func (b bankStone) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
    42  	errs := b.ValidateBoleto(boleto)
    43  	if len(errs) > 0 {
    44  		return models.BoletoResponse{Errors: errs}, nil
    45  	}
    46  
    47  	if accToken, err := authenticate(boleto.Authentication.AccessKey, b.log); err != nil {
    48  		return models.GetBoletoResponseError("MP500", err.Error()), nil
    49  	} else {
    50  		boleto.Authentication.AuthorizationToken = accToken
    51  	}
    52  	return b.RegisterBoleto(boleto)
    53  }
    54  
    55  func (b bankStone) RegisterBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
    56  	var response string
    57  	var header string
    58  	var status int
    59  	var err error
    60  
    61  	stoneURL := config.Get().URLStoneRegister
    62  	boleto.Title.BoletoType, boleto.Title.BoletoTypeCode = getBoletoType(boleto)
    63  
    64  	body := flow.NewFlow().From("message://?source=inline", boleto, templateRequest, tmpl.GetFuncMaps()).GetBody().(string)
    65  	head := hearders(boleto.Authentication.AuthorizationToken)
    66  	b.log.Request(body, stoneURL, head)
    67  
    68  	duration := util.Duration(func() {
    69  		response, header, status, err = util.PostReponseWithHeader(stoneURL, util.SanitizeBody(body), config.Get().TimeoutRegister, head)
    70  	})
    71  	metrics.PushTimingMetric("stone-register-boleto-time", duration.Seconds())
    72  
    73  	headers := getLogResponseProperties(header)
    74  
    75  	b.log.Response(response, stoneURL, headers)
    76  
    77  	return mapStoneResponse(boleto, response, status, err), nil
    78  }
    79  
    80  func getLogResponseProperties(header string) map[string]string {
    81  	m := make(map[string]string)
    82  	m["Headers"] = header
    83  	return m
    84  }
    85  
    86  func mapStoneResponse(request *models.BoletoRequest, response string, status int, httpErr error) models.BoletoResponse {
    87  	f := flow.NewFlow().To("set://?prop=body", response)
    88  	switch status {
    89  	case 0, 504:
    90  		var msg string
    91  		if httpErr != nil {
    92  			msg = fmt.Sprintf("%v", httpErr)
    93  		} else {
    94  			msg = "GatewayTimeout"
    95  		}
    96  		return models.GetBoletoResponseError("MPTimeout", msg)
    97  	case 201:
    98  		f.To("transform://?format=json", templateResponse, templateAPI, tmpl.GetFuncMaps())
    99  		f.To("unmarshall://?format=json", new(models.BoletoResponse))
   100  	default:
   101  		f.To("transform://?format=json", templateError, templateAPI, tmpl.GetFuncMaps())
   102  		f.To("unmarshall://?format=json", new(models.BoletoResponse))
   103  	}
   104  
   105  	switch t := f.GetBody().(type) {
   106  	case *models.BoletoResponse:
   107  		if hasOurNumberFail(t) {
   108  			return models.GetBoletoResponseError("MPOurNumberFail", "our number was not returned by the bank")
   109  		} else {
   110  			return *t
   111  		}
   112  	case error:
   113  		return models.GetBoletoResponseError("MP500", t.Error())
   114  	case string:
   115  		return models.GetBoletoResponseError("MP500", t)
   116  	}
   117  
   118  	return models.GetBoletoResponseError("MP500", "Internal Error")
   119  }
   120  
   121  func (b bankStone) ValidateBoleto(request *models.BoletoRequest) models.Errors {
   122  	return models.Errors(b.validate.Assert(request))
   123  }
   124  
   125  func (b bankStone) GetBankNumber() models.BankNumber {
   126  	return models.Stone
   127  }
   128  
   129  func (b bankStone) GetBankNameIntegration() string {
   130  	return "Stone"
   131  }
   132  
   133  func (b bankStone) GetErrorsMap() map[string]int {
   134  	var erros = map[string]int{
   135  		"srn:error:validation":          http.StatusBadRequest,
   136  		"srn:error:unauthenticated":     http.StatusInternalServerError,
   137  		"srn:error:unauthorized":        http.StatusBadGateway,
   138  		"srn:error:not_found":           http.StatusBadGateway,
   139  		"srn:error:conflict":            http.StatusBadGateway,
   140  		"srn:error:product_not_enabled": http.StatusBadRequest,
   141  	}
   142  	return erros
   143  }
   144  
   145  func (b bankStone) Log() *log.Log {
   146  	return b.log
   147  }
   148  
   149  func getBoletoType(boleto *models.BoletoRequest) (bt string, btc string) {
   150  	return "DM", "bill_of_exchange"
   151  }
   152  
   153  func hearders(token string) map[string]string {
   154  	return map[string]string{"Authorization": "Bearer " + token, "Content-Type": "application/json"}
   155  }
   156  
   157  func hasOurNumberFail(response *models.BoletoResponse) bool {
   158  	return !response.HasErrors() && response.OurNumber == ""
   159  }