diff --git a/.env.example b/.env.example index f6331757..d3a418c7 100644 --- a/.env.example +++ b/.env.example @@ -50,3 +50,4 @@ RECEIPTS_EMAIL_ADDRESS=receipts@stringxyz.com CARD_FAIL_PROBABILITY= [0.0 - 1.0] WEBHOOK_SECRET_KEY=secret SLACK_WEBHOOK_URL=https://hooks.slack.com/services/<>/<> +PERSONA_API_KEY= diff --git a/config/config.go b/config/config.go index b41010b8..92c12ed2 100644 --- a/config/config.go +++ b/config/config.go @@ -50,6 +50,7 @@ type vars struct { RECEIPTS_EMAIL_ADDRESS string `required:"true"` WEBHOOK_SECRET_KEY string `required:"true"` SLACK_WEBHOOK_URL string `required:"true"` + PERSONA_API_KEY string `required:"true"` } var Var vars diff --git a/pkg/internal/persona/account.go b/pkg/internal/persona/account.go new file mode 100644 index 00000000..daf4b313 --- /dev/null +++ b/pkg/internal/persona/account.go @@ -0,0 +1,62 @@ +package persona + +import ( + "net/http" + + "github.com/String-xyz/go-lib/v2/common" +) + +/* +The account represents a verified individual and contains one or more inquiries. +The primary use of the account endpoints is to fetch previously submitted information for an individual. +*/ + +type Account struct { + Id string `json:"id"` + Type string `json:"type"` + Attributes AccountAttributes `json:"attributes"` +} + +type AccountCreate struct { + Attributes CommonFields `json:"attributes"` +} + +type AccountCreateRequest struct { + Data AccountCreate `json:"data"` +} + +type AccountResponse struct { + Data Account `json:"data"` +} + +type ListAccountResponse struct { + Data []Account `json:"data"` + Links Link `json:"links"` +} + +func (c *PersonaClient) CreateAccount(request AccountCreateRequest) (*AccountResponse, error) { + account := &AccountResponse{} + err := c.doRequest(http.MethodPost, "/v1/accounts", request, account) + if err != nil { + return nil, common.StringError(err, "failed to create account") + } + return account, nil +} + +func (c *PersonaClient) GetAccountById(id string) (*AccountResponse, error) { + account := &AccountResponse{} + err := c.doRequest(http.MethodGet, "/v1/accounts/"+id, nil, account) + if err != nil { + return nil, common.StringError(err, "failed to get account") + } + return account, nil +} + +func (c *PersonaClient) ListAccounts() (*ListAccountResponse, error) { + accounts := &ListAccountResponse{} + err := c.doRequest(http.MethodGet, "/v1/accounts", nil, accounts) + if err != nil { + return nil, common.StringError(err, "failed to list accounts") + } + return accounts, nil +} diff --git a/pkg/internal/persona/client.go b/pkg/internal/persona/client.go new file mode 100644 index 00000000..c4b60320 --- /dev/null +++ b/pkg/internal/persona/client.go @@ -0,0 +1,68 @@ +package persona + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" +) + +type PersonaClient struct { + BaseURL string + APIKey string + Client *http.Client +} + +func New(apiKey string) *PersonaClient { + return &PersonaClient{ + APIKey: apiKey, + BaseURL: "https://withpersona.com/api/", + Client: &http.Client{}, + } +} + +func NewPersonaClient(baseURL, apiKey string) *PersonaClient { + return &PersonaClient{ + BaseURL: baseURL, + APIKey: apiKey, + Client: &http.Client{}, + } +} + +func (c *PersonaClient) doRequest(method, url string, payload, result interface{}) error { + var body io.Reader + if payload != nil { + jsonPayload, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("failed to marshal payload: %w", err) + } + body = bytes.NewBuffer(jsonPayload) + } + + req, err := http.NewRequest(method, c.BaseURL+url, body) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.APIKey)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Key-Inflection", "camel") + + resp, err := c.Client.Do(req) + if err != nil { + return fmt.Errorf("request failed: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return fmt.Errorf("request failed with status code: %d", resp.StatusCode) + } + + if result != nil { + if err := json.NewDecoder(resp.Body).Decode(result); err != nil { + return fmt.Errorf("failed to decode response: %w", err) + } + } + return nil +} diff --git a/pkg/internal/persona/inquiries.go b/pkg/internal/persona/inquiries.go new file mode 100644 index 00000000..71795e77 --- /dev/null +++ b/pkg/internal/persona/inquiries.go @@ -0,0 +1,76 @@ +package persona + +import ( + "net/http" + + "github.com/String-xyz/go-lib/v2/common" +) + +/* +The inquiry represents a single instance of an individual attempting to verify their identity. +The primary use of the inquiry endpoints is to fetch submitted information from the flow. + +Inquiries are created when the individual begins to verify their identity. +Check for the following statuses to determine whether the individual has finished the flow. + +* Created - The individual started the inquiry. +* Pending - The individual submitted a verification within the inquiry. +* Completed - The individual passed all required verifications within the inquiry. + +Approved/Declined (Optional) +These are optional statuses applied by you to execute custom decisioning logic. +* Expired - The individual did not complete the inquiry within 24 hours. +* Failed - The individual exceeded the allowed number of verification attempts on the inquiry and cannot continue. +*/ + +type InquiryCreate struct { + Attributes InquiryCreationAttributes `json:"attributes"` +} + +type InquiryCreateRequest struct { + Data InquiryCreate `json:"data"` +} + +type Inquiry struct { + Id string `json:"id"` + Type string `json:"type"` + Attributes InquiryAttributes `json:"attributes"` + Relationships Relationships `json:"relationships"` +} + +type InquiryResponse struct { + Data Inquiry `json:"data"` + Included []Included `json:"included"` +} + +type ListInquiryResponse struct { + Data []Inquiry `json:"data"` + Links Link `json:"links"` +} + +func (c *PersonaClient) CreateInquiry(request InquiryCreateRequest) (*InquiryResponse, error) { + inquiry := &InquiryResponse{} + err := c.doRequest(http.MethodPost, "/v1/inquiries", request, inquiry) + if err != nil { + return nil, common.StringError(err, "failed to create inquiry") + } + return inquiry, nil +} + +func (c *PersonaClient) GetInquiryById(id string) (*InquiryResponse, error) { + inquiry := &InquiryResponse{} + err := c.doRequest(http.MethodGet, "/v1/inquiries/"+id, nil, inquiry) + if err != nil { + return nil, common.StringError(err, "failed to get inquiry") + } + return inquiry, nil +} + +func (c *PersonaClient) ListInquiriesByAccount(accountId string) (*ListInquiryResponse, error) { + inquiries := &ListInquiryResponse{} + err := c.doRequest(http.MethodGet, "/v1/inquiries?filter[account-id]="+accountId, nil, inquiries) + if err != nil { + return nil, common.StringError(err, "failed to list inquiries") + } + return inquiries, nil +} diff --git a/pkg/internal/persona/persona_integration_test.go b/pkg/internal/persona/persona_integration_test.go new file mode 100644 index 00000000..049cc69d --- /dev/null +++ b/pkg/internal/persona/persona_integration_test.go @@ -0,0 +1,71 @@ +//go:build integration +// +build integration + +package persona + +import ( + "fmt" + "testing" + + env "github.com/String-xyz/go-lib/v2/config" + "github.com/stretchr/testify/assert" + + "github.com/String-xyz/string-api/config" +) + +func init() { + err := env.LoadEnv(&config.Var, "../../../.env") + if err != nil { + fmt.Printf("error loading env: %v", err) + } +} + +func client() *PersonaClient { + return New(config.Var.PERSONA_API_KEY) +} + +func TestIntegrationCreateAccount(t *testing.T) { + request := AccountCreateRequest{AccountCreate{Attributes: CommonFields{NameFirst: "Mister", NameLast: "Tester"}}} + account, err := client().CreateAccount(request) + assert.NoError(t, err) + assert.NotNil(t, account) +} + +func TestIntegrationGetAccount(t *testing.T) { + account, err := client().GetAccountById("act_Q1zEPYBZ6Qx8qJKcMrwDXxVA") + assert.NoError(t, err) + assert.NotNil(t, account) +} + +func TestIntegrationListAccounts(t *testing.T) { + accounts, err := client().ListAccounts() + assert.NoError(t, err) + assert.NotNil(t, accounts) + assert.NotEmpty(t, accounts.Data) +} + +func TestIntegrationCreateInquiry(t *testing.T) { + request := InquiryCreateRequest{InquiryCreate{Attributes: InquiryCreationAttributes{AccountId: "act_ndJNqdhWNi44S4Twf4bqzod1", InquityTemplateId: "itmpl_z2so7W2bCFHELp2dhxqqQjGy"}}} + inquiry, err := client().CreateInquiry(request) + assert.NoError(t, err) + assert.NotNil(t, inquiry) +} + +func TestIntegrationGetInquiry(t *testing.T) { + inquiry, err := client().GetInquiryById("inq_kmXCg5pLzWTwg2LuAjiaBsoC") + assert.NoError(t, err) + assert.NotNil(t, inquiry) +} + +func TestIntegrationListInquiriesByAccount(t *testing.T) { + inquiries, err := client().ListInquiriesByAccount("act_ndJNqdhWNi44S4Twf4bqzod1") + assert.NoError(t, err) + assert.NotNil(t, inquiries) + assert.NotEmpty(t, inquiries.Data) +} + +func TestIntegrationGetVerification(t *testing.T) { + verification, err := client().GetVerificationById("ver_ww2rkwtA6c9FiuCG8Jsk1DJt") + assert.NoError(t, err) + assert.NotNil(t, verification) +} diff --git a/pkg/internal/persona/persona_test.go b/pkg/internal/persona/persona_test.go new file mode 100644 index 00000000..ae7da1f2 --- /dev/null +++ b/pkg/internal/persona/persona_test.go @@ -0,0 +1,133 @@ +package persona + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func testServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(handler)) +} + +func TestNew(t *testing.T) { + c := New("test-key") + if c == nil { + t.Error("Expected persona client to be created") + } +} + +func TestDoRequest(t *testing.T) { + testServer := testServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.Write([]byte(`OK`)) + })) + defer func() { testServer.Close() }() + + c := New("test-key") + c.BaseURL = testServer.URL + + err := c.doRequest("GET", "/", nil, nil) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } +} + +func TestCreateAccount(t *testing.T) { + testServer := testServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/v1/accounts", r.URL.String()) + assert.Equal(t, http.MethodPost, r.Method) + + var payload AccountCreateRequest + err := json.NewDecoder(r.Body).Decode(&payload) + assert.NoError(t, err) + assert.Equal(t, "Testable", payload.Data.Attributes.NameFirst) + assert.Equal(t, "Testerson", payload.Data.Attributes.NameLast) + + account := &AccountResponse{Data: Account{ + Id: "test-id", + Type: "account", + }} + + json.NewEncoder(w).Encode(account) + })) + defer func() { testServer.Close() }() + c := New("test-key") + c.BaseURL = testServer.URL + v, err := c.CreateAccount(AccountCreateRequest{Data: AccountCreate{Attributes: CommonFields{NameFirst: "Testable", NameLast: "Testerson"}}}) + assert.NoError(t, err) + assert.NotNil(t, v) + assert.Equal(t, "test-id", v.Data.Id) +} + +func TestGetVerificationById(t *testing.T) { + testServer := testServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + verification := &VerificationResponse{Data: Verification{ + Id: "test-id", + Type: "verification", + }} + + json.NewEncoder(w).Encode(verification) + })) + defer func() { testServer.Close() }() + + c := New("test-key") + c.BaseURL = testServer.URL + + v, err := c.GetVerificationById("test-verification") + + assert.NoError(t, err) + assert.NotNil(t, v) +} + +func TestCreateInquiry(t *testing.T) { + server := testServer(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/v1/inquiries", r.URL.String()) + assert.Equal(t, http.MethodPost, r.Method) + + var payload InquiryCreateRequest + err := json.NewDecoder(r.Body).Decode(&payload) + assert.NoError(t, err) + assert.Equal(t, "test-account", payload.Data.Attributes.AccountId) + assert.Equal(t, "test-template", payload.Data.Attributes.TemplateId) + + inquiry := &InquiryResponse{Data: Inquiry{ + Id: "test-id", + Type: "inquiry", + }} + + json.NewEncoder(w).Encode(inquiry) + }) + defer server.Close() + + client := NewPersonaClient(server.URL, "test-key") + inquiry, err := client.CreateInquiry(InquiryCreateRequest{ + InquiryCreate{InquiryCreationAttributes{AccountId: "test-account", TemplateId: "test-template"}}}) + assert.NoError(t, err) + assert.Equal(t, "test-id", inquiry.Data.Id) + assert.Equal(t, "inquiry", inquiry.Data.Type) +} + +func TestGetInquiry(t *testing.T) { + server := testServer(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/v1/inquiries/test-id", r.URL.String()) + assert.Equal(t, http.MethodGet, r.Method) + + inquiry := &InquiryResponse{Data: Inquiry{ + Id: "test-id", + Type: "inquiry", + }, + } + + json.NewEncoder(w).Encode(inquiry) + }) + defer server.Close() + + client := NewPersonaClient(server.URL, "test-key") + inquiry, err := client.GetInquiryById("test-id") + assert.NoError(t, err) + assert.Equal(t, "test-id", inquiry.Data.Id) + assert.Equal(t, "inquiry", inquiry.Data.Type) +} diff --git a/pkg/internal/persona/templates.go b/pkg/internal/persona/templates.go new file mode 100644 index 00000000..a73f4a39 --- /dev/null +++ b/pkg/internal/persona/templates.go @@ -0,0 +1,31 @@ +package persona + +import ( + "fmt" + "net/http" +) + +type Template struct { + Id string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + CreatedAt string `json:"created_at"` +} + +func (c *PersonaClient) GetTemplates() ([]Template, error) { + var templates []Template + err := c.doRequest(http.MethodGet, "/v1/templates", nil, &templates) + if err != nil { + return nil, fmt.Errorf("failed to get templates: %w", err) + } + return templates, nil +} + +func (c *PersonaClient) GetTemplate(id string) (*Template, error) { + template := &Template{} + err := c.doRequest(http.MethodGet, fmt.Sprintf("/v1/templates/%s", id), nil, template) + if err != nil { + return nil, fmt.Errorf("failed to get template: %w", err) + } + return template, nil +} diff --git a/pkg/internal/persona/types.go b/pkg/internal/persona/types.go new file mode 100644 index 00000000..84b804a1 --- /dev/null +++ b/pkg/internal/persona/types.go @@ -0,0 +1,242 @@ +package persona + +import "time" + +type IdValue struct { + Type string `json:"type"` + Id string `json:"id"` +} + +type HashValue struct { + Type string `json:"type"` + Value map[string]Value `json:"value"` +} + +type StringValue struct { + Type string `json:"type"` + Value *string `json:"value"` +} + +type ArrayValue struct { + Type string `json:"type"` + Value []HashValue `json:"value"` +} + +type Value struct { + Type string `json:"type"` + Value interface{} `json:"value"` +} + +type Link struct { + Prev *string `json:"prev"` + Next *string `json:"next"` +} + +type PhotoURL struct { + Page *string `json:"page"` + Url *string `json:"url"` + fileName *string `json:"fileName"` + NormalizedUrl *string `json:"normalizedUrl"` + OriginalUrls []string `json:"originalUrls"` + ByteSize int `json:"byteSize"` +} + +type Check struct { + Name string `json:"name"` + Status string `json:"status"` + Reason []interface{} `json:"reason"` + Requirement string `json:"requirement"` + Metadata interface{} `json:"metadata"` +} + +type RelationshipId struct { + Data *IdValue `json:"data"` +} + +type RelationshipIds struct { + Data []IdValue `json:"data"` +} + +type Relationships struct { + Account *RelationshipId `json:"account"` + Inquity *RelationshipId `json:"inquiry"` + Template *RelationshipId `json:"template"` + InquityTemplate *RelationshipId `json:"inquiryTemplate"` + InquityTemplateVersion *RelationshipId `json:"inquiryTemplateVersion"` + VerificationTemplate *RelationshipId `json:"verificationTemplate"` + VerificationTemplateVersion *RelationshipId `json:"verificationTemplateVersion"` + Verifications *RelationshipIds `json:"verifications"` + Sessions *RelationshipIds `json:"sessions"` + Documents *RelationshipIds `json:"documents"` + DocumentFiles *RelationshipIds `json:"documentFiles"` + Selfies *RelationshipIds `json:"selfies"` +} + +type Behavior struct { + RequestSpoofAttempts int `json:"requestSpoofAttempts"` + UserAgentSpoofAttempts int `json:"userAgentSpoofAttempts"` + DistractionEvents int `json:"distractionEvents"` + HesitationBaseline int `json:"hesitationBaseline"` + HesitationCount int `json:"hesitationCount"` + HesitationTime int `json:"hesitationTime"` + ShortcutCopies int `json:"shortcutCopies"` + ShortcutPastes int `json:"shortcutPastes"` + AutofillCancels int `json:"autofillCancels"` + AutofillStarts int `json:"autofillStarts"` + DevtoolsOpen bool `json:"devtoolsOpen"` + CompletionTime float64 `json:"completionTime"` + HesitationPercentage float64 `json:"hesitationPercentage"` + BehaviorThreatLevel string `json:"behaviorThreatLevel"` +} + +type AccountFields struct { + Name HashValue `json:"name"` + Address HashValue `json:"address"` + IdentificationNumbers ArrayValue `json:"identificationNumbers"` + Birthdate Value `json:"birthdate"` + PhoneNumber StringValue `json:"phoneNumber"` + EmailAddress StringValue `json:"emailAddress"` + SelfiePhoto Value `json:"selfiePhoto"` +} + +type InquiryFields struct { + AddressStreet1 StringValue `json:"addressStreet1"` + AddressStreet2 StringValue `json:"addressStreet2"` +} + +type Attribute struct { + Status string `json:"status"` + CreatedAt time.Time `json:"createdAt"` + StartedAt *time.Time `json:"startedAt"` + FailedAt *time.Time `json:"failedAt"` + DecesionedAt *time.Time `json:"decesionedAt"` + MarkForReviewAt *time.Time `json:"markForReviewAt"` + UpdatedAt time.Time `json:"updatedAt"` + RedactedAt *time.Time `json:"redactedAt"` + SubmittedAt *time.Time `json:"submittedAt"` + CompletedAt *time.Time `json:"completedAt"` + ExpiredAt *time.Time `json:"expiredAt"` +} + +type AccountAttributes struct { + Attribute + Fields AccountFields `json:"fields"` +} + +type CommonFields struct { + Attribute + // City of residence address. Not all international addresses use this attribute. + AddresCity string `json:"addressCity,omitempty"` + // Street name of residence address. + AddressStreet1 string `json:"addressStreet1,omitempty"` + // Extension of residence address, usually apartment or suite number. + AddressStreet2 string `json:"addressStreet2,omitempty"` + // State or subdivision of residence address. In the US, + // this should be the unabbreviated name. Not all international addresses use this attribute. + AddressSubdivision string `json:"addressSubdivision,omitempty"` + // Postal code of residence address. Not all international addresses use this attribute. + AddressPostalCode string `json:"addressPostalCode,omitempty"` + // Birthdate, must be in the format "YYYY-MM-DD". + Birthdate string `json:"birthdate,omitempty"` + // ISO 3166-1 alpha 2 country code of the government ID to be verified. This is generally their country of residence as well. + CountryCode string `json:"countryCode,omitempty"` + + EmailAddress string `json:"emailAddress,omitempty"` + // Given or first name. + NameFirst string `json:"nameFirst,omitempty"` + // Family or last name. + NameLast string `json:"nameLast,omitempty"` + + NameMiddle string `json:"nameMiddle,omitempty"` + + PhoneNumber string `json:"phoneNumber,omitempty"` + + SocialSecurityNumber string `json:"socialSecurityNumber,omitempty"` +} + +type CommonAttributes struct { + SelfiePhoto *string `json:"selfiePhoto"` + SelfiePhotoUrl *string `json:"selfiePhotoUrl"` + FrontPhotoUrl *string `json:"frontPhotoUrl"` + BackPhotoUrl *string `json:"backPhotoUrl"` + VideoUrl *string `json:"videoUrl"` + IdClass string `json:"idClass"` + CaptureMethod string `json:"captureMethod"` + EntityConfidenceScore float64 `json:"entityConfidenceScore"` + EntityConfidenceReasons []string `json:"entityConfidenceReasons"` + NameFirst string `json:"nameFirst"` + NameMiddle *string `json:"nameMiddle"` + NameLast string `json:"nameLast"` + NameSuffix *string `json:"nameSuffix"` + Birthdate string `json:"birthdate"` + AddressStreet1 string `json:"addressStreet1"` + AddressStreet2 *string `json:"addressStreet2"` + AddressCity string `json:"addressCity"` + AddressSubdivision string `json:"addressSubdivision"` + AddressPostalCode string `json:"addressPostalCode"` + IssuingAuthority string `json:"issuingAuthority"` + IssuingSubdivision string `json:"issuingSubdivision"` + Nationality *string `json:"nationality"` + DocumentNumber *string `json:"documentNumber"` + VisaStatus *string `json:"visaStatus"` + IssueDate string `json:"issueDate"` + ExpirationDate string `json:"expirationDate"` + Designations *string `json:"designations"` + Birthplace *string `json:"birthplace"` + Endorsements *string `json:"endorsements"` + Height *string `json:"height"` + Sex string `json:"sex"` + Restrictions *string `json:"restrictions"` + VehicleClass *string `json:"vehicleClass"` + IdentificationNumber string `json:"identificationNumber"` +} + +type InquiryCreationAttributes struct { + AccountId string `json:"accountId"` + CountryCode string `json:"countryCode"` + InquityTemplateId string `json:"inquiryTemplateId"` + InquityTemplateVersionId string `json:"inquiryTemplateVersionId"` + // Template ID for flow requirements (use this field if your template ID starts with tmpl_). + // You must pass in either template-id OR inquiry-template-id OR inquiry-template-version-id + TemplateId string `json:"templateId"` + TemplateVersionId string `json:"templateVersionId"` + // for styling + ThemeId string `json:"themeId"` + + Fields *CommonFields `json:"fields"` +} + +type InquiryAttributes struct { + Attribute + ReferenceId *string `json:"referenceId"` + Behaviors Behavior `json:"behaviors"` + Notes *string `json:"notes"` + Tags []interface{} `json:"tags"` + PreviousStepName string `json:"previousStepName"` + NextStepName string `json:"nextStepName"` + Fields InquiryFields `json:"fields"` +} + +type CompletedSteps struct { + Type string `json:"type"` + Status string `json:"status"` +} + +type VerificationAttributes struct { + Attribute + CommonAttributes + CountryCode *string `json:"countryCode"` + LeftPhotoUrl *string `json:"leftPhotoUrl"` + RightPhotoUrl *string `json:"rightPhotoUrl"` + CenterPhotoUrl *string `json:"centerPhotoUrl"` + PhotoUrls []PhotoURL `json:"photoUrls"` + Checks []Check `json:"checks"` + CaptureMethod string `json:"captureMethod"` +} + +type Included struct { + Id string `json:"id"` + Type string `json:"type"` + Atrributes VerificationAttributes `json:"attributes"` + Relationships Relationships `json:"relationships"` +} diff --git a/pkg/internal/persona/verification.go b/pkg/internal/persona/verification.go new file mode 100644 index 00000000..6b8efa93 --- /dev/null +++ b/pkg/internal/persona/verification.go @@ -0,0 +1,54 @@ +package persona + +import ( + "net/http" + + "github.com/String-xyz/go-lib/v2/common" +) + +/* + +To verify a set of inputs from an individual, a Verification object is created. +A Verification enables an Organization to answer “Is this person who they claim to be?” with a focus on verifying digital transactions. +The verification process is accomplished through the generation and processing of Checks against the information provided by the individual. + +The collective process of mixing and matching verifications to achieve sufficient assurance that +an individual is indeed who they claim to be is often called identity proofing. +The goal of identity proofing is often tying digital identities to physical identities. + +An Inquiry contains one or more verifications. The attributes available for any given verification depends on its type. +Each inquiry’s relationships field lists the IDs of all associated verifications. +To authenticate when fetching photo URLs, pass the same Authorization header. + +Verifications change statuses as the individual progresses through the flow. +Check for the following statuses to monitor progress and find completed results. + +* Initiated - Verification has started, claimed information can now sent and saved to the server for verification +* Confirmed - Verification has been confirmed. This is a status specific to PhoneNumber verifications where they have verified a confirmation code that was entered. +* Submitted - Verification has been submitted, the claimed information is frozen and the server will process the verification +* Passed - Verification has passed. The required checks have passed and the information is verified +* Requires Retry - Verification requires a resubmission. The checks could not be fully processed due to issues with the submitted information +* Failed - Verification has failed. Some or all of the required checks have failed and verification has failed + +*/ + +type Verification struct { + Id string `json:"id"` + Type string `json:"type"` + Attributes VerificationAttributes `json:"attributes"` + Relationships Relationships `json:"relationships"` +} + +type VerificationResponse struct { + Data Verification `json:"data"` +} + +func (c *PersonaClient) GetVerificationById(id string) (*VerificationResponse, error) { + verification := &VerificationResponse{} + err := c.doRequest(http.MethodGet, "/v1/verifications/"+id, nil, verification) + if err != nil { + return nil, common.StringError(err, "failed to get verification by id") + } + + return verification, nil +}