From f31a863a8a98e469c7735c51e9d1abc037cadfda Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Fri, 29 Sep 2023 14:18:07 +0000 Subject: [PATCH] refactor(api)!: remove `post /webhooks/account_holders` endpoint --- .stats.yml | 2 +- accountholder.go | 417 ++++++++++++++++++++++++++++++-------- accountholder_test.go | 20 -- aliases.go | 3 + api.md | 6 +- event.go | 19 ++ event_test.go | 2 +- eventsubscription.go | 9 + eventsubscription_test.go | 6 +- internal/shared/shared.go | 35 ++++ transaction.go | 112 ++++++++-- 11 files changed, 507 insertions(+), 124 deletions(-) diff --git a/.stats.yml b/.stats.yml index 52b606c..e337832 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 90 +configured_endpoints: 89 diff --git a/accountholder.go b/accountholder.go index 18c8abe..40e6f24 100644 --- a/accountholder.go +++ b/accountholder.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "net/http" + "time" "github.com/lithic-com/lithic-go/internal/apijson" "github.com/lithic-com/lithic-go/internal/param" @@ -65,27 +66,6 @@ func (r *AccountHolderService) Update(ctx context.Context, accountHolderToken st return } -// Create a webhook to receive KYC or KYB evaluation events. -// -// There are two types of account holder webhooks: -// -// - `verification`: Webhook sent when the status of a KYC or KYB evaluation -// changes from `PENDING_DOCUMENT` (KYC) or `PENDING` (KYB) to `ACCEPTED` or -// `REJECTED`. -// - `document_upload_front`/`document_upload_back`: Webhook sent when a document -// upload fails. -// -// After a webhook has been created, this endpoint can be used to rotate a webhooks -// HMAC token or modify the registered URL. Only a single webhook is allowed per -// program. Since HMAC verification is available, the IP addresses from which -// KYC/KYB webhooks are sent are subject to change. -func (r *AccountHolderService) NewWebhook(ctx context.Context, body AccountHolderNewWebhookParams, opts ...option.RequestOption) (res *AccountHolderNewWebhookResponse, err error) { - opts = append(r.Options[:], opts...) - path := "webhooks/account_holders" - err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) - return -} - // Retrieve the status of account holder document uploads, or retrieve the upload // URLs to process your image uploads. // @@ -174,39 +154,290 @@ type AccountHolder struct { Token string `json:"token" format:"uuid"` // Globally unique identifier for the account. AccountToken string `json:"account_token" format:"uuid"` + // Only present when user_type == "BUSINESS". List of all entities with >25% + // ownership in the company. + BeneficialOwnerEntities []AccountHolderBeneficialOwnerEntity `json:"beneficial_owner_entities"` + // Only present when user_type == "BUSINESS". List of all individuals with >25% + // ownership in the company. + BeneficialOwnerIndividuals []AccountHolderBeneficialOwnerIndividual `json:"beneficial_owner_individuals"` // Only applicable for customers using the KYC-Exempt workflow to enroll authorized // users of businesses. Pass the account_token of the enrolled business associated // with the AUTHORIZED_USER in this field. BusinessAccountToken string `json:"business_account_token" format:"uuid"` - // KYC and KYB evaluation states. + // Only present when user_type == "BUSINESS". Information about the business for + // which the account is being opened and KYB is being run. + BusinessEntity AccountHolderBusinessEntity `json:"business_entity"` + // Information about an individual associated with an account holder. A subset of + // the information provided via KYC. For example, we do not return the government + // id. + ControlPerson AccountHolderControlPerson `json:"control_person"` + // Timestamp of when the account holder was created. + Created time.Time `json:"created" format:"date-time"` + // < Deprecated. Use control_person.email when user_type == "BUSINESS". Use + // individual.phone_number when user_type == "INDIVIDUAL". + // + // > Primary email of Account Holder. + Email string `json:"email"` + // The type of KYC exemption for a KYC-Exempt Account Holder. + ExemptionType AccountHolderExemptionType `json:"exemption_type"` + // Customer-provided token that indicates a relationship with an object outside of + // the Lithic ecosystem. + ExternalID string `json:"external_id" format:"string"` + // Only present when user_type == "INDIVIDUAL". Information about the individual + // for which the account is being opened and KYC is being run. + Individual AccountHolderIndividual `json:"individual"` + // Only present when user_type == "BUSINESS". User-submitted description of the + // business. + NatureOfBusiness string `json:"nature_of_business" format:"string"` + // < Deprecated. Use control_person.phone_number when user_type == "BUSINESS". Use + // individual.phone_number when user_type == "INDIVIDUAL". // - // Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for the - // `ADVANCED` workflow. + // > Primary phone of Account Holder, entered in E.164 format. + PhoneNumber string `json:"phone_number"` + // KYC and KYB evaluation + // states. Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for + // the `ADVANCED` workflow. Status AccountHolderStatus `json:"status"` - // Reason for the evaluation status. + // Reason for the + // evaluation status. StatusReasons []AccountHolderStatusReason `json:"status_reasons"` - JSON accountHolderJSON + // The type of Account Holder. If the type is "INDIVIDUAL", the "individual" + // attribute will be present. If the type is "BUSINESS" then the "business_entity", + // "control_person", "beneficial_owner_individuals", "beneficial_owner_entities", + // "nature_of_business", and "website_url" attributes will be present. + UserType AccountHolderUserType `json:"user_type"` + // Information about the most recent identity verification attempt + VerificationApplication AccountHolderVerificationApplication `json:"verification_application"` + // Only present when user_type == "BUSINESS". Business's primary website. + WebsiteURL string `json:"website_url" format:"string"` + JSON accountHolderJSON } // accountHolderJSON contains the JSON metadata for the struct [AccountHolder] type accountHolderJSON struct { - Token apijson.Field - AccountToken apijson.Field - BusinessAccountToken apijson.Field - Status apijson.Field - StatusReasons apijson.Field - raw string - ExtraFields map[string]apijson.Field + Token apijson.Field + AccountToken apijson.Field + BeneficialOwnerEntities apijson.Field + BeneficialOwnerIndividuals apijson.Field + BusinessAccountToken apijson.Field + BusinessEntity apijson.Field + ControlPerson apijson.Field + Created apijson.Field + Email apijson.Field + ExemptionType apijson.Field + ExternalID apijson.Field + Individual apijson.Field + NatureOfBusiness apijson.Field + PhoneNumber apijson.Field + Status apijson.Field + StatusReasons apijson.Field + UserType apijson.Field + VerificationApplication apijson.Field + WebsiteURL apijson.Field + raw string + ExtraFields map[string]apijson.Field } func (r *AccountHolder) UnmarshalJSON(data []byte) (err error) { return apijson.UnmarshalRoot(data, r) } -// KYC and KYB evaluation states. -// -// Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for the -// `ADVANCED` workflow. +type AccountHolderBeneficialOwnerEntity struct { + // Business's physical address - PO boxes, UPS drops, and FedEx drops are not + // acceptable; APO/FPO are acceptable. + Address shared.Address `json:"address,required"` + // Government-issued identification number. US Federal Employer Identification + // Numbers (EIN) are currently supported, entered as full nine-digits, with or + // without hyphens. + GovernmentID string `json:"government_id,required"` + // Legal (formal) business name. + LegalBusinessName string `json:"legal_business_name,required"` + // One or more of the business's phone number(s), entered as a list in E.164 + // format. + PhoneNumbers []string `json:"phone_numbers,required"` + // Any name that the business operates under that is not its legal business name + // (if applicable). + DbaBusinessName string `json:"dba_business_name"` + // Parent company name (if applicable). + ParentCompany string `json:"parent_company"` + JSON accountHolderBeneficialOwnerEntityJSON +} + +// accountHolderBeneficialOwnerEntityJSON contains the JSON metadata for the struct +// [AccountHolderBeneficialOwnerEntity] +type accountHolderBeneficialOwnerEntityJSON struct { + Address apijson.Field + GovernmentID apijson.Field + LegalBusinessName apijson.Field + PhoneNumbers apijson.Field + DbaBusinessName apijson.Field + ParentCompany apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *AccountHolderBeneficialOwnerEntity) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +// Information about an individual associated with an account holder. A subset of +// the information provided via KYC. For example, we do not return the government +// id. +type AccountHolderBeneficialOwnerIndividual struct { + // Individual's current address + Address shared.Address `json:"address"` + // Individual's date of birth, as an RFC 3339 date. + Dob string `json:"dob"` + // Individual's email address. + Email string `json:"email"` + // Individual's first name, as it appears on government-issued identity documents. + FirstName string `json:"first_name"` + // Individual's last name, as it appears on government-issued identity documents. + LastName string `json:"last_name"` + // Individual's phone number, entered in E.164 format. + PhoneNumber string `json:"phone_number"` + JSON accountHolderBeneficialOwnerIndividualJSON +} + +// accountHolderBeneficialOwnerIndividualJSON contains the JSON metadata for the +// struct [AccountHolderBeneficialOwnerIndividual] +type accountHolderBeneficialOwnerIndividualJSON struct { + Address apijson.Field + Dob apijson.Field + Email apijson.Field + FirstName apijson.Field + LastName apijson.Field + PhoneNumber apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *AccountHolderBeneficialOwnerIndividual) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +// Only present when user_type == "BUSINESS". Information about the business for +// which the account is being opened and KYB is being run. +type AccountHolderBusinessEntity struct { + // Business's physical address - PO boxes, UPS drops, and FedEx drops are not + // acceptable; APO/FPO are acceptable. + Address shared.Address `json:"address,required"` + // Government-issued identification number. US Federal Employer Identification + // Numbers (EIN) are currently supported, entered as full nine-digits, with or + // without hyphens. + GovernmentID string `json:"government_id,required"` + // Legal (formal) business name. + LegalBusinessName string `json:"legal_business_name,required"` + // One or more of the business's phone number(s), entered as a list in E.164 + // format. + PhoneNumbers []string `json:"phone_numbers,required"` + // Any name that the business operates under that is not its legal business name + // (if applicable). + DbaBusinessName string `json:"dba_business_name"` + // Parent company name (if applicable). + ParentCompany string `json:"parent_company"` + JSON accountHolderBusinessEntityJSON +} + +// accountHolderBusinessEntityJSON contains the JSON metadata for the struct +// [AccountHolderBusinessEntity] +type accountHolderBusinessEntityJSON struct { + Address apijson.Field + GovernmentID apijson.Field + LegalBusinessName apijson.Field + PhoneNumbers apijson.Field + DbaBusinessName apijson.Field + ParentCompany apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *AccountHolderBusinessEntity) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +// Information about an individual associated with an account holder. A subset of +// the information provided via KYC. For example, we do not return the government +// id. +type AccountHolderControlPerson struct { + // Individual's current address + Address shared.Address `json:"address"` + // Individual's date of birth, as an RFC 3339 date. + Dob string `json:"dob"` + // Individual's email address. + Email string `json:"email"` + // Individual's first name, as it appears on government-issued identity documents. + FirstName string `json:"first_name"` + // Individual's last name, as it appears on government-issued identity documents. + LastName string `json:"last_name"` + // Individual's phone number, entered in E.164 format. + PhoneNumber string `json:"phone_number"` + JSON accountHolderControlPersonJSON +} + +// accountHolderControlPersonJSON contains the JSON metadata for the struct +// [AccountHolderControlPerson] +type accountHolderControlPersonJSON struct { + Address apijson.Field + Dob apijson.Field + Email apijson.Field + FirstName apijson.Field + LastName apijson.Field + PhoneNumber apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *AccountHolderControlPerson) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +// The type of KYC exemption for a KYC-Exempt Account Holder. +type AccountHolderExemptionType string + +const ( + AccountHolderExemptionTypeAuthorizedUser AccountHolderExemptionType = "AUTHORIZED_USER" + AccountHolderExemptionTypePrepaidCardUser AccountHolderExemptionType = "PREPAID_CARD_USER" +) + +// Only present when user_type == "INDIVIDUAL". Information about the individual +// for which the account is being opened and KYC is being run. +type AccountHolderIndividual struct { + // Individual's current address + Address shared.Address `json:"address"` + // Individual's date of birth, as an RFC 3339 date. + Dob string `json:"dob"` + // Individual's email address. + Email string `json:"email"` + // Individual's first name, as it appears on government-issued identity documents. + FirstName string `json:"first_name"` + // Individual's last name, as it appears on government-issued identity documents. + LastName string `json:"last_name"` + // Individual's phone number, entered in E.164 format. + PhoneNumber string `json:"phone_number"` + JSON accountHolderIndividualJSON +} + +// accountHolderIndividualJSON contains the JSON metadata for the struct +// [AccountHolderIndividual] +type accountHolderIndividualJSON struct { + Address apijson.Field + Dob apijson.Field + Email apijson.Field + FirstName apijson.Field + LastName apijson.Field + PhoneNumber apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *AccountHolderIndividual) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +// KYC and KYB evaluation +// states. Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for +// the `ADVANCED` workflow. type AccountHolderStatus string const ( @@ -232,6 +463,73 @@ const ( AccountHolderStatusReasonWatchlistAlertFailure AccountHolderStatusReason = "WATCHLIST_ALERT_FAILURE" ) +// The type of Account Holder. If the type is "INDIVIDUAL", the "individual" +// attribute will be present. If the type is "BUSINESS" then the "business_entity", +// "control_person", "beneficial_owner_individuals", "beneficial_owner_entities", +// "nature_of_business", and "website_url" attributes will be present. +type AccountHolderUserType string + +const ( + AccountHolderUserTypeBusiness AccountHolderUserType = "BUSINESS" + AccountHolderUserTypeIndividual AccountHolderUserType = "INDIVIDUAL" +) + +// Information about the most recent identity verification attempt +type AccountHolderVerificationApplication struct { + // Timestamp of when the application was created. + Created time.Time `json:"created" format:"date-time"` + // KYC and KYB evaluation states. Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` + // are only applicable for the `ADVANCED` workflow. + Status AccountHolderVerificationApplicationStatus `json:"status"` + // Reason for the evaluation status. + StatusReasons []AccountHolderVerificationApplicationStatusReason `json:"status_reasons"` + // Timestamp of when the application was last updated. + Updated time.Time `json:"updated" format:"date-time"` + JSON accountHolderVerificationApplicationJSON +} + +// accountHolderVerificationApplicationJSON contains the JSON metadata for the +// struct [AccountHolderVerificationApplication] +type accountHolderVerificationApplicationJSON struct { + Created apijson.Field + Status apijson.Field + StatusReasons apijson.Field + Updated apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *AccountHolderVerificationApplication) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +// KYC and KYB evaluation states. Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` +// are only applicable for the `ADVANCED` workflow. +type AccountHolderVerificationApplicationStatus string + +const ( + AccountHolderVerificationApplicationStatusAccepted AccountHolderVerificationApplicationStatus = "ACCEPTED" + AccountHolderVerificationApplicationStatusRejected AccountHolderVerificationApplicationStatus = "REJECTED" + AccountHolderVerificationApplicationStatusPendingResubmit AccountHolderVerificationApplicationStatus = "PENDING_RESUBMIT" + AccountHolderVerificationApplicationStatusPendingDocument AccountHolderVerificationApplicationStatus = "PENDING_DOCUMENT" +) + +type AccountHolderVerificationApplicationStatusReason string + +const ( + AccountHolderVerificationApplicationStatusReasonAddressVerificationFailure AccountHolderVerificationApplicationStatusReason = "ADDRESS_VERIFICATION_FAILURE" + AccountHolderVerificationApplicationStatusReasonAgeThresholdFailure AccountHolderVerificationApplicationStatusReason = "AGE_THRESHOLD_FAILURE" + AccountHolderVerificationApplicationStatusReasonCompleteVerificationFailure AccountHolderVerificationApplicationStatusReason = "COMPLETE_VERIFICATION_FAILURE" + AccountHolderVerificationApplicationStatusReasonDobVerificationFailure AccountHolderVerificationApplicationStatusReason = "DOB_VERIFICATION_FAILURE" + AccountHolderVerificationApplicationStatusReasonIDVerificationFailure AccountHolderVerificationApplicationStatusReason = "ID_VERIFICATION_FAILURE" + AccountHolderVerificationApplicationStatusReasonMaxDocumentAttempts AccountHolderVerificationApplicationStatusReason = "MAX_DOCUMENT_ATTEMPTS" + AccountHolderVerificationApplicationStatusReasonMaxResubmissionAttempts AccountHolderVerificationApplicationStatusReason = "MAX_RESUBMISSION_ATTEMPTS" + AccountHolderVerificationApplicationStatusReasonNameVerificationFailure AccountHolderVerificationApplicationStatusReason = "NAME_VERIFICATION_FAILURE" + AccountHolderVerificationApplicationStatusReasonOtherVerificationFailure AccountHolderVerificationApplicationStatusReason = "OTHER_VERIFICATION_FAILURE" + AccountHolderVerificationApplicationStatusReasonRiskThresholdFailure AccountHolderVerificationApplicationStatusReason = "RISK_THRESHOLD_FAILURE" + AccountHolderVerificationApplicationStatusReasonWatchlistAlertFailure AccountHolderVerificationApplicationStatusReason = "WATCHLIST_ALERT_FAILURE" +) + // Describes the document and the required document image uploads required to // re-run KYC. type AccountHolderDocument struct { @@ -361,42 +659,6 @@ func (r *AccountHolderUpdateResponse) UnmarshalJSON(data []byte) (err error) { return apijson.UnmarshalRoot(data, r) } -type AccountHolderNewWebhookResponse struct { - Data AccountHolderNewWebhookResponseData `json:"data"` - JSON accountHolderNewWebhookResponseJSON -} - -// accountHolderNewWebhookResponseJSON contains the JSON metadata for the struct -// [AccountHolderNewWebhookResponse] -type accountHolderNewWebhookResponseJSON struct { - Data apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *AccountHolderNewWebhookResponse) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - -type AccountHolderNewWebhookResponseData struct { - // Shared secret which can optionally be used to validate the authenticity of - // incoming identity webhooks. - HmacToken string `json:"hmac_token" format:"uuid"` - JSON accountHolderNewWebhookResponseDataJSON -} - -// accountHolderNewWebhookResponseDataJSON contains the JSON metadata for the -// struct [AccountHolderNewWebhookResponseData] -type accountHolderNewWebhookResponseDataJSON struct { - HmacToken apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *AccountHolderNewWebhookResponseData) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - type AccountHolderListDocumentsResponse struct { Data []AccountHolderDocument `json:"data"` JSON accountHolderListDocumentsResponseJSON @@ -720,15 +982,6 @@ func (r AccountHolderUpdateParams) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } -type AccountHolderNewWebhookParams struct { - // URL to receive webhook requests. Must be a valid HTTPS address. - URL param.Field[string] `json:"url,required"` -} - -func (r AccountHolderNewWebhookParams) MarshalJSON() (data []byte, err error) { - return apijson.MarshalRoot(r) -} - type AccountHolderResubmitParams struct { // Information on individual for whom the account is being opened and KYC is being // re-run. diff --git a/accountholder_test.go b/accountholder_test.go index 7ac6124..e531923 100644 --- a/accountholder_test.go +++ b/accountholder_test.go @@ -201,26 +201,6 @@ func TestAccountHolderUpdateWithOptionalParams(t *testing.T) { } } -func TestAccountHolderNewWebhook(t *testing.T) { - if !testutil.CheckTestServer(t) { - return - } - client := lithic.NewClient( - option.WithBaseURL("http://127.0.0.1:4010"), - option.WithAPIKey("APIKey"), - ) - _, err := client.AccountHolders.NewWebhook(context.TODO(), lithic.AccountHolderNewWebhookParams{ - URL: lithic.F("string"), - }) - if err != nil { - var apierr *lithic.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} - func TestAccountHolderListDocuments(t *testing.T) { if !testutil.CheckTestServer(t) { return diff --git a/aliases.go b/aliases.go index 9d4206b..f240689 100644 --- a/aliases.go +++ b/aliases.go @@ -9,6 +9,9 @@ import ( type Error = apierror.Error +// This is an alias to an internal type. +type Address = shared.Address + // This is an alias to an internal type. type AddressParam = shared.AddressParam diff --git a/api.md b/api.md index 10ad691..8377fc5 100644 --- a/api.md +++ b/api.md @@ -4,6 +4,10 @@ - shared.CarrierParam - shared.ShippingAddressParam +# Shared Response Types + +- shared.Address + # lithic Response Types: @@ -33,7 +37,6 @@ Response Types: - lithic.AccountHolder - lithic.AccountHolderDocument - lithic.AccountHolderUpdateResponse -- lithic.AccountHolderNewWebhookResponse - lithic.AccountHolderListDocumentsResponse Methods: @@ -41,7 +44,6 @@ Methods: - client.AccountHolders.New(ctx context.Context, body lithic.AccountHolderNewParams) (lithic.AccountHolder, error) - client.AccountHolders.Get(ctx context.Context, accountHolderToken string) (lithic.AccountHolder, error) - client.AccountHolders.Update(ctx context.Context, accountHolderToken string, body lithic.AccountHolderUpdateParams) (lithic.AccountHolderUpdateResponse, error) -- client.AccountHolders.NewWebhook(ctx context.Context, body lithic.AccountHolderNewWebhookParams) (lithic.AccountHolderNewWebhookResponse, error) - client.AccountHolders.ListDocuments(ctx context.Context, accountHolderToken string) (lithic.AccountHolderListDocumentsResponse, error) - client.AccountHolders.Resubmit(ctx context.Context, accountHolderToken string, body lithic.AccountHolderResubmitParams) (lithic.AccountHolder, error) - client.AccountHolders.GetDocument(ctx context.Context, accountHolderToken string, documentToken string) (lithic.AccountHolderDocument, error) diff --git a/event.go b/event.go index 52f7c6a..74fa0f5 100644 --- a/event.go +++ b/event.go @@ -100,6 +100,11 @@ type Event struct { Created time.Time `json:"created,required" format:"date-time"` // Event types: // + // - `account_holder.created` - Notification that a new account holder has been + // created and was not rejected. + // - `account_holder.updated` - Notification that an account holder was updated. + // - `account_holder.verification` - Notification than an account holder's identity + // verification is complete. // - `card.created` - Notification that a card has been created. // - `card.shipped` - Physical card shipment notification. See // https://docs.lithic.com/docs/cards#physical-card-shipped-webhook. @@ -134,6 +139,11 @@ func (r *Event) UnmarshalJSON(data []byte) (err error) { // Event types: // +// - `account_holder.created` - Notification that a new account holder has been +// created and was not rejected. +// - `account_holder.updated` - Notification that an account holder was updated. +// - `account_holder.verification` - Notification than an account holder's identity +// verification is complete. // - `card.created` - Notification that a card has been created. // - `card.shipped` - Physical card shipment notification. See // https://docs.lithic.com/docs/cards#physical-card-shipped-webhook. @@ -150,6 +160,9 @@ func (r *Event) UnmarshalJSON(data []byte) (err error) { type EventEventType string const ( + EventEventTypeAccountHolderCreated EventEventType = "account_holder.created" + EventEventTypeAccountHolderUpdated EventEventType = "account_holder.updated" + EventEventTypeAccountHolderVerification EventEventType = "account_holder.verification" EventEventTypeCardCreated EventEventType = "card.created" EventEventTypeCardShipped EventEventType = "card.shipped" EventEventTypeCardTransactionUpdated EventEventType = "card_transaction.updated" @@ -196,6 +209,9 @@ func (r *EventSubscription) UnmarshalJSON(data []byte) (err error) { type EventSubscriptionEventType string const ( + EventSubscriptionEventTypeAccountHolderCreated EventSubscriptionEventType = "account_holder.created" + EventSubscriptionEventTypeAccountHolderUpdated EventSubscriptionEventType = "account_holder.updated" + EventSubscriptionEventTypeAccountHolderVerification EventSubscriptionEventType = "account_holder.verification" EventSubscriptionEventTypeCardCreated EventSubscriptionEventType = "card.created" EventSubscriptionEventTypeCardShipped EventSubscriptionEventType = "card.shipped" EventSubscriptionEventTypeCardTransactionUpdated EventSubscriptionEventType = "card_transaction.updated" @@ -292,6 +308,9 @@ func (r EventListParams) URLQuery() (v url.Values) { type EventListParamsEventType string const ( + EventListParamsEventTypeAccountHolderCreated EventListParamsEventType = "account_holder.created" + EventListParamsEventTypeAccountHolderUpdated EventListParamsEventType = "account_holder.updated" + EventListParamsEventTypeAccountHolderVerification EventListParamsEventType = "account_holder.verification" EventListParamsEventTypeCardCreated EventListParamsEventType = "card.created" EventListParamsEventTypeCardShipped EventListParamsEventType = "card.shipped" EventListParamsEventTypeCardTransactionUpdated EventListParamsEventType = "card_transaction.updated" diff --git a/event_test.go b/event_test.go index d668af6..2359811 100644 --- a/event_test.go +++ b/event_test.go @@ -43,7 +43,7 @@ func TestEventListWithOptionalParams(t *testing.T) { Begin: lithic.F(time.Now()), End: lithic.F(time.Now()), EndingBefore: lithic.F("string"), - EventTypes: lithic.F([]lithic.EventListParamsEventType{lithic.EventListParamsEventTypeCardCreated, lithic.EventListParamsEventTypeCardShipped, lithic.EventListParamsEventTypeCardTransactionUpdated}), + EventTypes: lithic.F([]lithic.EventListParamsEventType{lithic.EventListParamsEventTypeAccountHolderCreated, lithic.EventListParamsEventTypeAccountHolderUpdated, lithic.EventListParamsEventTypeAccountHolderVerification}), PageSize: lithic.F(int64(1)), StartingAfter: lithic.F("string"), WithContent: lithic.F(true), diff --git a/eventsubscription.go b/eventsubscription.go index 16ab302..15541ff 100644 --- a/eventsubscription.go +++ b/eventsubscription.go @@ -197,6 +197,9 @@ func (r EventSubscriptionNewParams) MarshalJSON() (data []byte, err error) { type EventSubscriptionNewParamsEventType string const ( + EventSubscriptionNewParamsEventTypeAccountHolderCreated EventSubscriptionNewParamsEventType = "account_holder.created" + EventSubscriptionNewParamsEventTypeAccountHolderUpdated EventSubscriptionNewParamsEventType = "account_holder.updated" + EventSubscriptionNewParamsEventTypeAccountHolderVerification EventSubscriptionNewParamsEventType = "account_holder.verification" EventSubscriptionNewParamsEventTypeCardCreated EventSubscriptionNewParamsEventType = "card.created" EventSubscriptionNewParamsEventTypeCardShipped EventSubscriptionNewParamsEventType = "card.shipped" EventSubscriptionNewParamsEventTypeCardTransactionUpdated EventSubscriptionNewParamsEventType = "card_transaction.updated" @@ -230,6 +233,9 @@ func (r EventSubscriptionUpdateParams) MarshalJSON() (data []byte, err error) { type EventSubscriptionUpdateParamsEventType string const ( + EventSubscriptionUpdateParamsEventTypeAccountHolderCreated EventSubscriptionUpdateParamsEventType = "account_holder.created" + EventSubscriptionUpdateParamsEventTypeAccountHolderUpdated EventSubscriptionUpdateParamsEventType = "account_holder.updated" + EventSubscriptionUpdateParamsEventTypeAccountHolderVerification EventSubscriptionUpdateParamsEventType = "account_holder.verification" EventSubscriptionUpdateParamsEventTypeCardCreated EventSubscriptionUpdateParamsEventType = "card.created" EventSubscriptionUpdateParamsEventTypeCardShipped EventSubscriptionUpdateParamsEventType = "card.shipped" EventSubscriptionUpdateParamsEventTypeCardTransactionUpdated EventSubscriptionUpdateParamsEventType = "card_transaction.updated" @@ -349,6 +355,9 @@ func (r EventSubscriptionSendSimulatedExampleParams) MarshalJSON() (data []byte, type EventSubscriptionSendSimulatedExampleParamsEventType string const ( + EventSubscriptionSendSimulatedExampleParamsEventTypeAccountHolderCreated EventSubscriptionSendSimulatedExampleParamsEventType = "account_holder.created" + EventSubscriptionSendSimulatedExampleParamsEventTypeAccountHolderUpdated EventSubscriptionSendSimulatedExampleParamsEventType = "account_holder.updated" + EventSubscriptionSendSimulatedExampleParamsEventTypeAccountHolderVerification EventSubscriptionSendSimulatedExampleParamsEventType = "account_holder.verification" EventSubscriptionSendSimulatedExampleParamsEventTypeCardCreated EventSubscriptionSendSimulatedExampleParamsEventType = "card.created" EventSubscriptionSendSimulatedExampleParamsEventTypeCardShipped EventSubscriptionSendSimulatedExampleParamsEventType = "card.shipped" EventSubscriptionSendSimulatedExampleParamsEventTypeCardTransactionUpdated EventSubscriptionSendSimulatedExampleParamsEventType = "card_transaction.updated" diff --git a/eventsubscription_test.go b/eventsubscription_test.go index a6929b1..45befc1 100644 --- a/eventsubscription_test.go +++ b/eventsubscription_test.go @@ -25,7 +25,7 @@ func TestEventSubscriptionNewWithOptionalParams(t *testing.T) { URL: lithic.F("https://example.com"), Description: lithic.F("string"), Disabled: lithic.F(true), - EventTypes: lithic.F([]lithic.EventSubscriptionNewParamsEventType{lithic.EventSubscriptionNewParamsEventTypeCardCreated, lithic.EventSubscriptionNewParamsEventTypeCardShipped, lithic.EventSubscriptionNewParamsEventTypeCardTransactionUpdated}), + EventTypes: lithic.F([]lithic.EventSubscriptionNewParamsEventType{lithic.EventSubscriptionNewParamsEventTypeAccountHolderCreated, lithic.EventSubscriptionNewParamsEventTypeAccountHolderUpdated, lithic.EventSubscriptionNewParamsEventTypeAccountHolderVerification}), }) if err != nil { var apierr *lithic.Error @@ -69,7 +69,7 @@ func TestEventSubscriptionUpdateWithOptionalParams(t *testing.T) { URL: lithic.F("https://example.com"), Description: lithic.F("string"), Disabled: lithic.F(true), - EventTypes: lithic.F([]lithic.EventSubscriptionUpdateParamsEventType{lithic.EventSubscriptionUpdateParamsEventTypeCardCreated, lithic.EventSubscriptionUpdateParamsEventTypeCardShipped, lithic.EventSubscriptionUpdateParamsEventTypeCardTransactionUpdated}), + EventTypes: lithic.F([]lithic.EventSubscriptionUpdateParamsEventType{lithic.EventSubscriptionUpdateParamsEventTypeAccountHolderCreated, lithic.EventSubscriptionUpdateParamsEventTypeAccountHolderUpdated, lithic.EventSubscriptionUpdateParamsEventTypeAccountHolderVerification}), }, ) if err != nil { @@ -252,7 +252,7 @@ func TestEventSubscriptionSendSimulatedExampleWithOptionalParams(t *testing.T) { context.TODO(), "string", lithic.EventSubscriptionSendSimulatedExampleParams{ - EventType: lithic.F(lithic.EventSubscriptionSendSimulatedExampleParamsEventTypeCardCreated), + EventType: lithic.F(lithic.EventSubscriptionSendSimulatedExampleParamsEventTypeAccountHolderCreated), }, ) if err != nil { diff --git a/internal/shared/shared.go b/internal/shared/shared.go index 550f238..4745f55 100644 --- a/internal/shared/shared.go +++ b/internal/shared/shared.go @@ -7,6 +7,41 @@ import ( "github.com/lithic-com/lithic-go/internal/param" ) +type Address struct { + // Valid deliverable address (no PO boxes). + Address1 string `json:"address1,required"` + // Name of city. + City string `json:"city,required"` + // Valid country code. Only USA is currently supported, entered in uppercase ISO + // 3166-1 alpha-3 three-character format. + Country string `json:"country,required"` + // Valid postal code. Only USA ZIP codes are currently supported, entered as a + // five-digit ZIP or nine-digit ZIP+4. + PostalCode string `json:"postal_code,required"` + // Valid state code. Only USA state codes are currently supported, entered in + // uppercase ISO 3166-2 two-character format. + State string `json:"state,required"` + // Unit or apartment number (if applicable). + Address2 string `json:"address2"` + JSON addressJSON +} + +// addressJSON contains the JSON metadata for the struct [Address] +type addressJSON struct { + Address1 apijson.Field + City apijson.Field + Country apijson.Field + PostalCode apijson.Field + State apijson.Field + Address2 apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *Address) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + type AddressParam struct { // Valid deliverable address (no PO boxes). Address1 param.Field[string] `json:"address1,required"` diff --git a/transaction.go b/transaction.go index a8fc9e5..395c5c2 100644 --- a/transaction.go +++ b/transaction.go @@ -147,9 +147,12 @@ func (r *TransactionService) SimulateVoid(ctx context.Context, body TransactionS type Transaction struct { // Globally unique identifier. Token string `json:"token,required" format:"uuid"` - // A fixed-width 23-digit numeric identifier for the Transaction that may be set if - // the transaction originated from the Mastercard network. This number may be used - // for dispute tracking. + // Fee assessed by the merchant and paid for by the cardholder in the smallest unit + // of the currency. Will be zero if no fee is assessed. Rebates may be transmitted + // as a negative value to indicate credited fees. + AcquirerFee int64 `json:"acquirer_fee,required"` + // Unique identifier assigned to a transaction by the acquirer that can be used in + // dispute and chargeback filing. AcquirerReferenceNumber string `json:"acquirer_reference_number,required,nullable"` // Authorization amount of the transaction (in cents), including any acquirer fees. // This may change over time, and will represent the settled amount once the @@ -203,6 +206,7 @@ type Transaction struct { // transactionJSON contains the JSON metadata for the struct [Transaction] type transactionJSON struct { Token apijson.Field + AcquirerFee apijson.Field AcquirerReferenceNumber apijson.Field Amount apijson.Field AuthorizationAmount apijson.Field @@ -499,14 +503,14 @@ const ( ) type TransactionCardholderAuthentication struct { - // 3-D Secure Protocol version. Possible values: + // 3-D Secure Protocol version. Possible enum values: // // - `1`: 3-D Secure Protocol version 1.x applied to the transaction. // - `2`: 3-D Secure Protocol version 2.x applied to the transaction. // - `null`: 3-D Secure was not used for the transaction ThreeDSVersion string `json:"3ds_version,required,nullable"` // Exemption applied by the ACS to authenticate the transaction without requesting - // a challenge. Possible values: + // a challenge. Possible enum values: // // - `AUTHENTICATION_OUTAGE_EXCEPTION`: Authentication Outage Exception exemption. // - `LOW_VALUE`: Low Value Payment exemption. @@ -521,8 +525,35 @@ type TransactionCardholderAuthentication struct { // // Maps to the 3-D Secure `transChallengeExemption` field. AcquirerExemption TransactionCardholderAuthenticationAcquirerExemption `json:"acquirer_exemption,required"` + // Outcome of the 3DS authentication process. Possible enum values: + // + // - `SUCCESS`: 3DS authentication was successful and the transaction is considered + // authenticated. + // - `DECLINE`: 3DS authentication was attempted but was unsuccessful — i.e., the + // issuer declined to authenticate the cardholder; note that Lithic populates + // this value on a best-effort basis based on common data across the 3DS + // authentication and ASA data elements. + // - `ATTEMPTS`: 3DS authentication was attempted but full authentication did not + // occur. A proof of attempted authenticated is provided by the merchant. + // - `NONE`: 3DS authentication was not performed on the transaction. + AuthenticationResult TransactionCardholderAuthenticationAuthenticationResult `json:"authentication_result,required"` + // Indicator for which party made the 3DS authentication decision. Possible enum + // values: + // + // - `NETWORK`: A networks tand-in service decided on the outcome; for token + // authentications (as indicated in the `liability_shift` attribute), this is the + // default value + // - `LITHIC_DEFAULT`: A default decision was made by Lithic, without running a + // rules-based authentication; this value will be set on card programs that do + // not participate in one of our two 3DS product tiers + // - `LITHIC_RULES`: A rules-based authentication was conducted by Lithic and + // Lithic decided on the outcome + // - `CUSTOMER_ENDPOINT`: Lithic customer decided on the outcome based on a + // real-time request sent to a configured endpoint + // - `UNKNOWN`: Data on which party decided is unavailable + DecisionMadeBy TransactionCardholderAuthenticationDecisionMadeBy `json:"decision_made_by,required"` // Indicates whether chargeback liability shift applies to the transaction. - // Possible values: + // Possible enum values: // // - `3DS_AUTHENTICATED`: The transaction was fully authenticated through a 3-D // Secure flow, chargeback liability shift applies. @@ -536,6 +567,10 @@ type TransactionCardholderAuthentication struct { // cryptography, possibly recurring. Chargeback liability shift to the issuer // applies. LiabilityShift TransactionCardholderAuthenticationLiabilityShift `json:"liability_shift,required"` + // Unique identifier you can use to match a given 3DS authentication and the + // transaction. Note that in cases where liability shift does not occur, this token + // is matched to the transaction on a best-effort basis. + ThreeDSAuthenticationToken string `json:"three_ds_authentication_token,required" format:"uuid"` // Verification attempted values: // // - `APP_LOGIN`: Out-of-band login verification was attempted by the ACS. @@ -585,13 +620,16 @@ type TransactionCardholderAuthentication struct { // transactionCardholderAuthenticationJSON contains the JSON metadata for the // struct [TransactionCardholderAuthentication] type transactionCardholderAuthenticationJSON struct { - ThreeDSVersion apijson.Field - AcquirerExemption apijson.Field - LiabilityShift apijson.Field - VerificationAttempted apijson.Field - VerificationResult apijson.Field - raw string - ExtraFields map[string]apijson.Field + ThreeDSVersion apijson.Field + AcquirerExemption apijson.Field + AuthenticationResult apijson.Field + DecisionMadeBy apijson.Field + LiabilityShift apijson.Field + ThreeDSAuthenticationToken apijson.Field + VerificationAttempted apijson.Field + VerificationResult apijson.Field + raw string + ExtraFields map[string]apijson.Field } func (r *TransactionCardholderAuthentication) UnmarshalJSON(data []byte) (err error) { @@ -599,7 +637,7 @@ func (r *TransactionCardholderAuthentication) UnmarshalJSON(data []byte) (err er } // Exemption applied by the ACS to authenticate the transaction without requesting -// a challenge. Possible values: +// a challenge. Possible enum values: // // - `AUTHENTICATION_OUTAGE_EXCEPTION`: Authentication Outage Exception exemption. // - `LOW_VALUE`: Low Value Payment exemption. @@ -626,8 +664,52 @@ const ( TransactionCardholderAuthenticationAcquirerExemptionTransactionRiskAnalysis TransactionCardholderAuthenticationAcquirerExemption = "TRANSACTION_RISK_ANALYSIS" ) +// Outcome of the 3DS authentication process. Possible enum values: +// +// - `SUCCESS`: 3DS authentication was successful and the transaction is considered +// authenticated. +// - `DECLINE`: 3DS authentication was attempted but was unsuccessful — i.e., the +// issuer declined to authenticate the cardholder; note that Lithic populates +// this value on a best-effort basis based on common data across the 3DS +// authentication and ASA data elements. +// - `ATTEMPTS`: 3DS authentication was attempted but full authentication did not +// occur. A proof of attempted authenticated is provided by the merchant. +// - `NONE`: 3DS authentication was not performed on the transaction. +type TransactionCardholderAuthenticationAuthenticationResult string + +const ( + TransactionCardholderAuthenticationAuthenticationResultSuccess TransactionCardholderAuthenticationAuthenticationResult = "SUCCESS" + TransactionCardholderAuthenticationAuthenticationResultDecline TransactionCardholderAuthenticationAuthenticationResult = "DECLINE" + TransactionCardholderAuthenticationAuthenticationResultAttempts TransactionCardholderAuthenticationAuthenticationResult = "ATTEMPTS" + TransactionCardholderAuthenticationAuthenticationResultNone TransactionCardholderAuthenticationAuthenticationResult = "NONE" +) + +// Indicator for which party made the 3DS authentication decision. Possible enum +// values: +// +// - `NETWORK`: A networks tand-in service decided on the outcome; for token +// authentications (as indicated in the `liability_shift` attribute), this is the +// default value +// - `LITHIC_DEFAULT`: A default decision was made by Lithic, without running a +// rules-based authentication; this value will be set on card programs that do +// not participate in one of our two 3DS product tiers +// - `LITHIC_RULES`: A rules-based authentication was conducted by Lithic and +// Lithic decided on the outcome +// - `CUSTOMER_ENDPOINT`: Lithic customer decided on the outcome based on a +// real-time request sent to a configured endpoint +// - `UNKNOWN`: Data on which party decided is unavailable +type TransactionCardholderAuthenticationDecisionMadeBy string + +const ( + TransactionCardholderAuthenticationDecisionMadeByNetwork TransactionCardholderAuthenticationDecisionMadeBy = "NETWORK" + TransactionCardholderAuthenticationDecisionMadeByLithicDefault TransactionCardholderAuthenticationDecisionMadeBy = "LITHIC_DEFAULT" + TransactionCardholderAuthenticationDecisionMadeByLithicRules TransactionCardholderAuthenticationDecisionMadeBy = "LITHIC_RULES" + TransactionCardholderAuthenticationDecisionMadeByCustomerEndpoint TransactionCardholderAuthenticationDecisionMadeBy = "CUSTOMER_ENDPOINT" + TransactionCardholderAuthenticationDecisionMadeByUnknown TransactionCardholderAuthenticationDecisionMadeBy = "UNKNOWN" +) + // Indicates whether chargeback liability shift applies to the transaction. -// Possible values: +// Possible enum values: // // - `3DS_AUTHENTICATED`: The transaction was fully authenticated through a 3-D // Secure flow, chargeback liability shift applies.