package client import ( "bytes" "context" "encoding/json" "fmt" "io" "ips-lacpass-backend/pkg/errors" "ips-lacpass-backend/pkg/utils" "net/http" "net/url" ) type VhlClient struct { Client *http.Client BaseURL string ICVPValidatorUrl string } func NewClient(baseURL string, icvpValidatorUrl string) VhlClient { return VhlClient{ Client: &http.Client{}, BaseURL: baseURL, ICVPValidatorUrl: icvpValidatorUrl, } } func (c *VhlClient) CreateQr(ctx context.Context, body CreateQrRequest) (*QrData, error) { // TODO: To be implemented by the participant resp, err := c.Client.Post(fmt.Sprintf("%s/v2/vshcIssuance", c.BaseURL), "application/json", bytes.NewBuffer(body)) if resp.StatusCode != http.StatusOK { return nil, &errors.HttpError{ StatusCode: 500, Body: []map[string]interface{}{{"error": "Not implemented error", "message": "this method is not implemented yet"}}, Err: fmt.Errorf("failed to get document reference"), } } return resp, nil } func (c *VhlClient) Validate(ctx context.Context, qrData string) (*QRValidationResponse, error) { body, err := json.Marshal(QrValidationRequest{QRCodeContent: qrData}) if err != nil { return nil, fmt.Errorf("failed to marshal validation payload: %w", err) } resp, err := c.Client.Post(fmt.Sprintf("%s/v2/vshcValidation", c.BaseURL), "application/json", bytes.NewBuffer(body)) if err != nil { return nil, &errors.HttpError{ StatusCode: http.StatusBadGateway, Body: []map[string]interface{}{{"error": "service_unavailable", "message": "Failed to connect to VHL service"}}, Err: err, } } defer utils.CloseBody(resp.Body) if resp.StatusCode != http.StatusOK { bodyBytes, readErr := io.ReadAll(resp.Body) if readErr != nil { return nil, &errors.HttpError{ StatusCode: resp.StatusCode, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}}, Err: readErr, } } return nil, &errors.HttpError{ StatusCode: resp.StatusCode, Body: []map[string]interface{}{{"error": "service_error", "message": string(bodyBytes)}}, Err: fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes)), } } var valResp QRValidationResponse if err := json.NewDecoder(resp.Body).Decode(&valResp); err != nil { return nil, &errors.HttpError{ StatusCode: http.StatusInternalServerError, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to decode response"}}, Err: err, } } return &valResp, nil } func (c *VhlClient) GetIpsUrl(ctx context.Context, shLink string, passCode string) (*VhlManifestResponse, error) { u, err := url.Parse(fmt.Sprintf("%s/ips-url", c.BaseURL)) if err != nil { return nil, &errors.HttpError{ StatusCode: http.StatusInternalServerError, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to create request"}}, Err: err, } } q := u.Query() q.Set("shLink", shLink) q.Set("passCode", passCode) u.RawQuery = q.Encode() req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) if err != nil { return nil, &errors.HttpError{ StatusCode: http.StatusInternalServerError, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to create request"}}, Err: err, } } req.Header.Set("Accept", "application/json") resp, err := c.Client.Do(req) if err != nil { return nil, &errors.HttpError{ StatusCode: http.StatusBadGateway, Body: []map[string]interface{}{{"error": "service_unavailable", "message": "Failed to connect to VHL service"}}, Err: err, } } defer utils.CloseBody(resp.Body) if resp.StatusCode != http.StatusOK { bodyBytes, readErr := io.ReadAll(resp.Body) if readErr != nil { return nil, &errors.HttpError{ StatusCode: resp.StatusCode, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}}, Err: readErr, } } return nil, &errors.HttpError{ StatusCode: resp.StatusCode, Body: []map[string]interface{}{{"error": "service_error", "message": string(bodyBytes)}}, Err: fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes)), } } var manifest VhlManifestResponse if err := json.NewDecoder(resp.Body).Decode(&manifest); err != nil { return nil, &errors.HttpError{ StatusCode: http.StatusInternalServerError, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to decode manifest response"}}, Err: err, } } return &manifest, nil } func (c *VhlClient) ICVPValidate(ctx context.Context, qrData string) (*ICVPQRValidationResponse, error) { r := ICVPQrValidationRequest{ IncludeRaw: true, QRData: qrData, } body, err := json.Marshal(r) if err != nil { return nil, fmt.Errorf("failed to marshal user payload: %w", err) } vu := fmt.Sprintf("%s/decode/hcert", c.ICVPValidatorUrl) req, err := http.NewRequest("POST", vu, bytes.NewBuffer(body)) if err != nil { return nil, &errors.HttpError{ StatusCode: 500, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to create request"}}, Err: err, } } req.Header.Set("Content-Type", "application/json") resp, err := c.Client.Do(req) if err != nil { return nil, &errors.HttpError{ StatusCode: 502, Body: []map[string]interface{}{{"error": "service_unavailable", "message": "Failed to connect to VHL service"}}, Err: err, } } defer utils.CloseBody(resp.Body) if resp.StatusCode != http.StatusOK { bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return nil, &errors.HttpError{ StatusCode: resp.StatusCode, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}}, Err: err, } } return nil, &errors.HttpError{ StatusCode: resp.StatusCode, Body: []map[string]interface{}{{"error": "service_error", "message": string(bodyBytes)}}, Err: fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes)), } } bb, err := io.ReadAll(resp.Body) if err != nil { return nil, &errors.HttpError{ StatusCode: 500, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}}, Err: err, } } var valResp ICVPQRValidationResponse err = json.Unmarshal(bb, &valResp) if err != nil { return nil, &errors.HttpError{ StatusCode: 500, Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to read parse response body"}}, Err: err, } } return &valResp, nil }