github.com/companieshouse/lfp-pay-api@v0.0.0-20230203133422-0ca455cd79f9/handlers/patch_payable_resource.go (about) 1 package handlers 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "sync" 8 9 "github.com/companieshouse/chs.go/log" 10 "github.com/companieshouse/lfp-pay-api-core/models" 11 "github.com/companieshouse/lfp-pay-api-core/validators" 12 "github.com/companieshouse/lfp-pay-api/config" 13 "github.com/companieshouse/lfp-pay-api/e5" 14 "github.com/companieshouse/lfp-pay-api/service" 15 "github.com/companieshouse/lfp-pay-api/utils" 16 "gopkg.in/go-playground/validator.v9" 17 ) 18 19 // handleEmailKafkaMessage allows us to mock the call to sendEmailKafkaMessage for unit tests 20 var handleEmailKafkaMessage = service.SendEmailKafkaMessage 21 22 var wg sync.WaitGroup 23 24 // PayResourceHandler will update the resource to mark it as paid and also tell the finance system that the 25 // transaction(s) associated with it are paid. 26 func PayResourceHandler(svc *service.PayableResourceService, e5Client *e5.Client) http.Handler { 27 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 28 // 1. get the payable resource our of the context. authorisation is already handled in the interceptor 29 i := r.Context().Value(config.PayableResource) 30 if i == nil { 31 err := fmt.Errorf("no payable resource in context. check PayableAuthenticationInterceptor is installed") 32 log.ErrorR(r, err) 33 m := models.NewMessageResponse("no payable request present in request context") 34 utils.WriteJSONWithStatus(w, r, m, http.StatusBadRequest) 35 return 36 } 37 38 resource := i.(*models.PayableResource) 39 40 log.Info("processing LFP payment", log.Data{ 41 "lfp_reference": resource.Reference, 42 "company_number": resource.CompanyNumber, 43 }) 44 45 // 2. validate the request and check the reference number against the payment api to validate that is has 46 // actually been paid 47 var request models.PatchResourceRequest 48 err := json.NewDecoder(r.Body).Decode(&request) 49 if err != nil { 50 log.ErrorR(r, err, log.Data{"lfp_reference": resource.Reference}) 51 m := models.NewMessageResponse("there was a problem reading the request body") 52 utils.WriteJSONWithStatus(w, r, m, http.StatusBadRequest) 53 return 54 } 55 v := validator.New() 56 err = v.Struct(request) 57 58 if err != nil { 59 log.ErrorR(r, err, log.Data{"lfp_reference": resource.Reference, "payment_id": request.Reference}) 60 m := models.NewMessageResponse("the request contained insufficient data and/or failed validation") 61 utils.WriteJSONWithStatus(w, r, m, http.StatusBadRequest) 62 return 63 } 64 65 payment, err := service.GetPaymentInformation(request.Reference, r) 66 if err != nil { 67 log.ErrorR(r, err, log.Data{"lfp_reference": resource.Reference, "payment_id": request.Reference}) 68 m := models.NewMessageResponse("the payable resource does not exist") 69 utils.WriteJSONWithStatus(w, r, m, http.StatusBadRequest) 70 return 71 } 72 73 err = validators.New().ValidateForPayment(*resource, *payment) 74 if err != nil { 75 m := models.NewMessageResponse("there was a problem validating this payment") 76 utils.WriteJSONWithStatus(w, r, m, http.StatusBadRequest) 77 return 78 } 79 80 wg.Add(3) 81 82 go sendConfirmationEmail(resource, payment, r, w) 83 go updateDatabase(resource, payment, svc, r, w) 84 go updateE5(e5Client, resource, payment, svc, r, w) 85 86 wg.Wait() 87 88 w.Header().Set("Content-Type", "application/json") 89 w.WriteHeader(http.StatusNoContent) // This will not be set if status has already been set 90 }) 91 } 92 func sendConfirmationEmail(resource *models.PayableResource, payment *validators.PaymentInformation, r *http.Request, w http.ResponseWriter) { 93 // Send confirmation email 94 defer wg.Done() 95 err := handleEmailKafkaMessage(*resource, r) 96 if err != nil { 97 log.ErrorR(r, err, log.Data{"lfp_reference": resource.Reference, "payment_id": payment.Reference}) 98 w.WriteHeader(http.StatusInternalServerError) 99 return 100 } 101 102 log.Info("confirmation email sent to customer", log.Data{ 103 "lfp_reference": resource.Reference, 104 "company_number": resource.CompanyNumber, 105 "email_address": resource.CreatedBy.Email, 106 }) 107 } 108 109 func updateDatabase(resource *models.PayableResource, payment *validators.PaymentInformation, svc *service.PayableResourceService, r *http.Request, w http.ResponseWriter) { 110 // Update the payable resource in the db 111 defer wg.Done() 112 err := svc.UpdateAsPaid(*resource, *payment) 113 if err != nil { 114 log.ErrorR(r, err, log.Data{"lfp_reference": resource.Reference, "payment_id": payment.Reference}) 115 w.WriteHeader(http.StatusInternalServerError) 116 return 117 } 118 119 log.Info("payment resource is now marked as paid in db", log.Data{ 120 "lfp_reference": resource.Reference, 121 "company_number": resource.CompanyNumber, 122 }) 123 } 124 125 func updateE5(e5Client *e5.Client, resource *models.PayableResource, payment *validators.PaymentInformation, svc *service.PayableResourceService, r *http.Request, w http.ResponseWriter) { 126 // Mark the resource as paid in e5 127 defer wg.Done() 128 err := service.MarkTransactionsAsPaid(svc, e5Client, *resource, *payment) 129 if err != nil { 130 log.ErrorR(r, err, log.Data{ 131 "lfp_reference": resource.Reference, 132 "company_number": resource.CompanyNumber, 133 }) 134 w.WriteHeader(http.StatusInternalServerError) 135 return 136 } 137 }