From d0e83fb5ae0dc1c1106454f6ee5da677a0fa7608 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 11:47:35 +0000 Subject: [PATCH 1/4] feat(api): add card_authorization.challenge_response webhook event --- .stats.yml | 4 +- ...horizationChallengeResponseWebhookEvent.kt | 874 ++++++++++++++++++ .../kotlin/com/lithic/api/models/Event.kt | 13 + .../com/lithic/api/models/EventListParams.kt | 9 + .../lithic/api/models/EventSubscription.kt | 9 + .../models/EventSubscriptionCreateParams.kt | 9 + ...tSubscriptionSendSimulatedExampleParams.kt | 7 + .../models/EventSubscriptionUpdateParams.kt | 9 + .../lithic/api/models/ParsedWebhookEvent.kt | 54 ++ ...zationChallengeResponseWebhookEventTest.kt | 78 ++ .../api/models/ParsedWebhookEventTest.kt | 173 ++++ 11 files changed, 1237 insertions(+), 2 deletions(-) create mode 100644 lithic-java-core/src/main/kotlin/com/lithic/api/models/CardAuthorizationChallengeResponseWebhookEvent.kt create mode 100644 lithic-java-core/src/test/kotlin/com/lithic/api/models/CardAuthorizationChallengeResponseWebhookEventTest.kt diff --git a/.stats.yml b/.stats.yml index d1255ff6..b222baac 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 190 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-fd53f07a8b1f148011972d7eba834d5995778d707ff8ef1ead92580b44142a15.yml -openapi_spec_hash: 45892e6361542824364d82181d4eb80d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-28c9b43d3182bf0e1c9635f6100e4995a744724db1edd635cfd3fc7702ced68c.yml +openapi_spec_hash: aba00a65f877c5095499d9d1a66b5e5f config_hash: 5eca052bb23d273fa970eac3127dd919 diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/CardAuthorizationChallengeResponseWebhookEvent.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/CardAuthorizationChallengeResponseWebhookEvent.kt new file mode 100644 index 00000000..ba0b8c2f --- /dev/null +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/CardAuthorizationChallengeResponseWebhookEvent.kt @@ -0,0 +1,874 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.lithic.api.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.lithic.api.core.Enum +import com.lithic.api.core.ExcludeMissing +import com.lithic.api.core.JsonField +import com.lithic.api.core.JsonMissing +import com.lithic.api.core.JsonValue +import com.lithic.api.core.checkRequired +import com.lithic.api.errors.LithicInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class CardAuthorizationChallengeResponseWebhookEvent +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val cardToken: JsonField, + private val challengeMethod: JsonField, + private val completed: JsonField, + private val created: JsonField, + private val eventToken: JsonField, + private val eventType: JsonField, + private val response: JsonField, + private val transactionToken: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("card_token") @ExcludeMissing cardToken: JsonField = JsonMissing.of(), + @JsonProperty("challenge_method") + @ExcludeMissing + challengeMethod: JsonField = JsonMissing.of(), + @JsonProperty("completed") + @ExcludeMissing + completed: JsonField = JsonMissing.of(), + @JsonProperty("created") + @ExcludeMissing + created: JsonField = JsonMissing.of(), + @JsonProperty("event_token") + @ExcludeMissing + eventToken: JsonField = JsonMissing.of(), + @JsonProperty("event_type") + @ExcludeMissing + eventType: JsonField = JsonMissing.of(), + @JsonProperty("response") @ExcludeMissing response: JsonField = JsonMissing.of(), + @JsonProperty("transaction_token") + @ExcludeMissing + transactionToken: JsonField = JsonMissing.of(), + ) : this( + cardToken, + challengeMethod, + completed, + created, + eventToken, + eventType, + response, + transactionToken, + mutableMapOf(), + ) + + /** + * The token of the card associated with the challenge + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun cardToken(): Optional = cardToken.getOptional("card_token") + + /** + * The method used to deliver the challenge to the cardholder + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun challengeMethod(): ChallengeMethod = challengeMethod.getRequired("challenge_method") + + /** + * The timestamp of when the challenge was completed + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun completed(): Optional = completed.getOptional("completed") + + /** + * The timestamp of when the challenge was created + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun created(): OffsetDateTime = created.getRequired("created") + + /** + * Globally unique identifier for the event + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun eventToken(): String = eventToken.getRequired("event_token") + + /** + * Event type + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun eventType(): EventType = eventType.getRequired("event_type") + + /** + * The cardholder's response to the challenge + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun response(): Response = response.getRequired("response") + + /** + * The token of the transaction associated with the authorization event being challenged + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun transactionToken(): Optional = transactionToken.getOptional("transaction_token") + + /** + * Returns the raw JSON value of [cardToken]. + * + * Unlike [cardToken], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("card_token") @ExcludeMissing fun _cardToken(): JsonField = cardToken + + /** + * Returns the raw JSON value of [challengeMethod]. + * + * Unlike [challengeMethod], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("challenge_method") + @ExcludeMissing + fun _challengeMethod(): JsonField = challengeMethod + + /** + * Returns the raw JSON value of [completed]. + * + * Unlike [completed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("completed") + @ExcludeMissing + fun _completed(): JsonField = completed + + /** + * Returns the raw JSON value of [created]. + * + * Unlike [created], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("created") @ExcludeMissing fun _created(): JsonField = created + + /** + * Returns the raw JSON value of [eventToken]. + * + * Unlike [eventToken], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("event_token") @ExcludeMissing fun _eventToken(): JsonField = eventToken + + /** + * Returns the raw JSON value of [eventType]. + * + * Unlike [eventType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("event_type") @ExcludeMissing fun _eventType(): JsonField = eventType + + /** + * Returns the raw JSON value of [response]. + * + * Unlike [response], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("response") @ExcludeMissing fun _response(): JsonField = response + + /** + * Returns the raw JSON value of [transactionToken]. + * + * Unlike [transactionToken], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("transaction_token") + @ExcludeMissing + fun _transactionToken(): JsonField = transactionToken + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [CardAuthorizationChallengeResponseWebhookEvent]. + * + * The following fields are required: + * ```java + * .cardToken() + * .challengeMethod() + * .completed() + * .created() + * .eventToken() + * .eventType() + * .response() + * .transactionToken() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CardAuthorizationChallengeResponseWebhookEvent]. */ + class Builder internal constructor() { + + private var cardToken: JsonField? = null + private var challengeMethod: JsonField? = null + private var completed: JsonField? = null + private var created: JsonField? = null + private var eventToken: JsonField? = null + private var eventType: JsonField? = null + private var response: JsonField? = null + private var transactionToken: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from( + cardAuthorizationChallengeResponseWebhookEvent: + CardAuthorizationChallengeResponseWebhookEvent + ) = apply { + cardToken = cardAuthorizationChallengeResponseWebhookEvent.cardToken + challengeMethod = cardAuthorizationChallengeResponseWebhookEvent.challengeMethod + completed = cardAuthorizationChallengeResponseWebhookEvent.completed + created = cardAuthorizationChallengeResponseWebhookEvent.created + eventToken = cardAuthorizationChallengeResponseWebhookEvent.eventToken + eventType = cardAuthorizationChallengeResponseWebhookEvent.eventType + response = cardAuthorizationChallengeResponseWebhookEvent.response + transactionToken = cardAuthorizationChallengeResponseWebhookEvent.transactionToken + additionalProperties = + cardAuthorizationChallengeResponseWebhookEvent.additionalProperties.toMutableMap() + } + + /** The token of the card associated with the challenge */ + fun cardToken(cardToken: String?) = cardToken(JsonField.ofNullable(cardToken)) + + /** Alias for calling [Builder.cardToken] with `cardToken.orElse(null)`. */ + fun cardToken(cardToken: Optional) = cardToken(cardToken.getOrNull()) + + /** + * Sets [Builder.cardToken] to an arbitrary JSON value. + * + * You should usually call [Builder.cardToken] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun cardToken(cardToken: JsonField) = apply { this.cardToken = cardToken } + + /** The method used to deliver the challenge to the cardholder */ + fun challengeMethod(challengeMethod: ChallengeMethod) = + challengeMethod(JsonField.of(challengeMethod)) + + /** + * Sets [Builder.challengeMethod] to an arbitrary JSON value. + * + * You should usually call [Builder.challengeMethod] with a well-typed [ChallengeMethod] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun challengeMethod(challengeMethod: JsonField) = apply { + this.challengeMethod = challengeMethod + } + + /** The timestamp of when the challenge was completed */ + fun completed(completed: OffsetDateTime?) = completed(JsonField.ofNullable(completed)) + + /** Alias for calling [Builder.completed] with `completed.orElse(null)`. */ + fun completed(completed: Optional) = completed(completed.getOrNull()) + + /** + * Sets [Builder.completed] to an arbitrary JSON value. + * + * You should usually call [Builder.completed] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun completed(completed: JsonField) = apply { this.completed = completed } + + /** The timestamp of when the challenge was created */ + fun created(created: OffsetDateTime) = created(JsonField.of(created)) + + /** + * Sets [Builder.created] to an arbitrary JSON value. + * + * You should usually call [Builder.created] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun created(created: JsonField) = apply { this.created = created } + + /** Globally unique identifier for the event */ + fun eventToken(eventToken: String) = eventToken(JsonField.of(eventToken)) + + /** + * Sets [Builder.eventToken] to an arbitrary JSON value. + * + * You should usually call [Builder.eventToken] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun eventToken(eventToken: JsonField) = apply { this.eventToken = eventToken } + + /** Event type */ + fun eventType(eventType: EventType) = eventType(JsonField.of(eventType)) + + /** + * Sets [Builder.eventType] to an arbitrary JSON value. + * + * You should usually call [Builder.eventType] with a well-typed [EventType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun eventType(eventType: JsonField) = apply { this.eventType = eventType } + + /** The cardholder's response to the challenge */ + fun response(response: Response) = response(JsonField.of(response)) + + /** + * Sets [Builder.response] to an arbitrary JSON value. + * + * You should usually call [Builder.response] with a well-typed [Response] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun response(response: JsonField) = apply { this.response = response } + + /** The token of the transaction associated with the authorization event being challenged */ + fun transactionToken(transactionToken: String?) = + transactionToken(JsonField.ofNullable(transactionToken)) + + /** Alias for calling [Builder.transactionToken] with `transactionToken.orElse(null)`. */ + fun transactionToken(transactionToken: Optional) = + transactionToken(transactionToken.getOrNull()) + + /** + * Sets [Builder.transactionToken] to an arbitrary JSON value. + * + * You should usually call [Builder.transactionToken] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun transactionToken(transactionToken: JsonField) = apply { + this.transactionToken = transactionToken + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CardAuthorizationChallengeResponseWebhookEvent]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .cardToken() + * .challengeMethod() + * .completed() + * .created() + * .eventToken() + * .eventType() + * .response() + * .transactionToken() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CardAuthorizationChallengeResponseWebhookEvent = + CardAuthorizationChallengeResponseWebhookEvent( + checkRequired("cardToken", cardToken), + checkRequired("challengeMethod", challengeMethod), + checkRequired("completed", completed), + checkRequired("created", created), + checkRequired("eventToken", eventToken), + checkRequired("eventType", eventType), + checkRequired("response", response), + checkRequired("transactionToken", transactionToken), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): CardAuthorizationChallengeResponseWebhookEvent = apply { + if (validated) { + return@apply + } + + cardToken() + challengeMethod().validate() + completed() + created() + eventToken() + eventType().validate() + response().validate() + transactionToken() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (cardToken.asKnown().isPresent) 1 else 0) + + (challengeMethod.asKnown().getOrNull()?.validity() ?: 0) + + (if (completed.asKnown().isPresent) 1 else 0) + + (if (created.asKnown().isPresent) 1 else 0) + + (if (eventToken.asKnown().isPresent) 1 else 0) + + (eventType.asKnown().getOrNull()?.validity() ?: 0) + + (response.asKnown().getOrNull()?.validity() ?: 0) + + (if (transactionToken.asKnown().isPresent) 1 else 0) + + /** The method used to deliver the challenge to the cardholder */ + class ChallengeMethod @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val SMS = of("SMS") + + @JvmStatic fun of(value: String) = ChallengeMethod(JsonField.of(value)) + } + + /** An enum containing [ChallengeMethod]'s known values. */ + enum class Known { + SMS + } + + /** + * An enum containing [ChallengeMethod]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [ChallengeMethod] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + SMS, + /** + * An enum member indicating that [ChallengeMethod] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + SMS -> Value.SMS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws LithicInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + SMS -> Known.SMS + else -> throw LithicInvalidDataException("Unknown ChallengeMethod: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws LithicInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { LithicInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): ChallengeMethod = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ChallengeMethod && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Event type */ + class EventType @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField + val CARD_AUTHORIZATION_CHALLENGE_RESPONSE = of("card_authorization.challenge_response") + + @JvmStatic fun of(value: String) = EventType(JsonField.of(value)) + } + + /** An enum containing [EventType]'s known values. */ + enum class Known { + CARD_AUTHORIZATION_CHALLENGE_RESPONSE + } + + /** + * An enum containing [EventType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [EventType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, + /** + * An enum member indicating that [EventType] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Value.CARD_AUTHORIZATION_CHALLENGE_RESPONSE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws LithicInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Known.CARD_AUTHORIZATION_CHALLENGE_RESPONSE + else -> throw LithicInvalidDataException("Unknown EventType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws LithicInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { LithicInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): EventType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EventType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The cardholder's response to the challenge */ + class Response @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val APPROVE = of("APPROVE") + + @JvmField val DECLINE = of("DECLINE") + + @JvmStatic fun of(value: String) = Response(JsonField.of(value)) + } + + /** An enum containing [Response]'s known values. */ + enum class Known { + APPROVE, + DECLINE, + } + + /** + * An enum containing [Response]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Response] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + APPROVE, + DECLINE, + /** An enum member indicating that [Response] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + APPROVE -> Value.APPROVE + DECLINE -> Value.DECLINE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws LithicInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + APPROVE -> Known.APPROVE + DECLINE -> Known.DECLINE + else -> throw LithicInvalidDataException("Unknown Response: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws LithicInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { LithicInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): Response = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Response && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CardAuthorizationChallengeResponseWebhookEvent && + cardToken == other.cardToken && + challengeMethod == other.challengeMethod && + completed == other.completed && + created == other.created && + eventToken == other.eventToken && + eventType == other.eventType && + response == other.response && + transactionToken == other.transactionToken && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + cardToken, + challengeMethod, + completed, + created, + eventToken, + eventType, + response, + transactionToken, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CardAuthorizationChallengeResponseWebhookEvent{cardToken=$cardToken, challengeMethod=$challengeMethod, completed=$completed, created=$created, eventToken=$eventToken, eventType=$eventType, response=$response, transactionToken=$transactionToken, additionalProperties=$additionalProperties}" +} diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/Event.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/Event.kt index 0ab47880..caf888f2 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/Event.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/Event.kt @@ -72,6 +72,8 @@ private constructor( * - balance.updated: Financial Account Balance Update * - book_transfer_transaction.created: Occurs when a book transfer transaction is created. * - book_transfer_transaction.updated: Occurs when a book transfer transaction is updated. + * - card_authorization.challenge_response: Occurs when a cardholder responds to an SMS + * challenge during card authorization. * - card_transaction.enhanced_data.created: Occurs when L2/L3 enhanced commercial data is * processed for a transaction event. * - card_transaction.enhanced_data.updated: Occurs when L2/L3 enhanced commercial data is @@ -268,6 +270,8 @@ private constructor( * - balance.updated: Financial Account Balance Update * - book_transfer_transaction.created: Occurs when a book transfer transaction is created. * - book_transfer_transaction.updated: Occurs when a book transfer transaction is updated. + * - card_authorization.challenge_response: Occurs when a cardholder responds to an SMS + * challenge during card authorization. * - card_transaction.enhanced_data.created: Occurs when L2/L3 enhanced commercial data is * processed for a transaction event. * - card_transaction.enhanced_data.updated: Occurs when L2/L3 enhanced commercial data is @@ -453,6 +457,8 @@ private constructor( * - balance.updated: Financial Account Balance Update * - book_transfer_transaction.created: Occurs when a book transfer transaction is created. * - book_transfer_transaction.updated: Occurs when a book transfer transaction is updated. + * - card_authorization.challenge_response: Occurs when a cardholder responds to an SMS + * challenge during card authorization. * - card_transaction.enhanced_data.created: Occurs when L2/L3 enhanced commercial data is * processed for a transaction event. * - card_transaction.enhanced_data.updated: Occurs when L2/L3 enhanced commercial data is @@ -558,6 +564,9 @@ private constructor( @JvmField val BOOK_TRANSFER_TRANSACTION_UPDATED = of("book_transfer_transaction.updated") + @JvmField + val CARD_AUTHORIZATION_CHALLENGE_RESPONSE = of("card_authorization.challenge_response") + @JvmField val CARD_TRANSACTION_ENHANCED_DATA_CREATED = of("card_transaction.enhanced_data.created") @@ -674,6 +683,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -738,6 +748,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -805,6 +816,7 @@ private constructor( BALANCE_UPDATED -> Value.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Value.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Value.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Value.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Value.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> @@ -877,6 +889,7 @@ private constructor( BALANCE_UPDATED -> Known.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Known.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Known.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Known.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Known.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventListParams.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventListParams.kt index d96b858f..f8a8d29b 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventListParams.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventListParams.kt @@ -330,6 +330,8 @@ private constructor( * - balance.updated: Financial Account Balance Update * - book_transfer_transaction.created: Occurs when a book transfer transaction is created. * - book_transfer_transaction.updated: Occurs when a book transfer transaction is updated. + * - card_authorization.challenge_response: Occurs when a cardholder responds to an SMS + * challenge during card authorization. * - card_transaction.enhanced_data.created: Occurs when L2/L3 enhanced commercial data is * processed for a transaction event. * - card_transaction.enhanced_data.updated: Occurs when L2/L3 enhanced commercial data is @@ -435,6 +437,9 @@ private constructor( @JvmField val BOOK_TRANSFER_TRANSACTION_UPDATED = of("book_transfer_transaction.updated") + @JvmField + val CARD_AUTHORIZATION_CHALLENGE_RESPONSE = of("card_authorization.challenge_response") + @JvmField val CARD_TRANSACTION_ENHANCED_DATA_CREATED = of("card_transaction.enhanced_data.created") @@ -551,6 +556,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -615,6 +621,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -682,6 +689,7 @@ private constructor( BALANCE_UPDATED -> Value.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Value.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Value.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Value.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Value.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> @@ -754,6 +762,7 @@ private constructor( BALANCE_UPDATED -> Known.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Known.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Known.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Known.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Known.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscription.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscription.kt index 64aae9d6..9a039466 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscription.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscription.kt @@ -332,6 +332,8 @@ private constructor( * - balance.updated: Financial Account Balance Update * - book_transfer_transaction.created: Occurs when a book transfer transaction is created. * - book_transfer_transaction.updated: Occurs when a book transfer transaction is updated. + * - card_authorization.challenge_response: Occurs when a cardholder responds to an SMS + * challenge during card authorization. * - card_transaction.enhanced_data.created: Occurs when L2/L3 enhanced commercial data is * processed for a transaction event. * - card_transaction.enhanced_data.updated: Occurs when L2/L3 enhanced commercial data is @@ -437,6 +439,9 @@ private constructor( @JvmField val BOOK_TRANSFER_TRANSACTION_UPDATED = of("book_transfer_transaction.updated") + @JvmField + val CARD_AUTHORIZATION_CHALLENGE_RESPONSE = of("card_authorization.challenge_response") + @JvmField val CARD_TRANSACTION_ENHANCED_DATA_CREATED = of("card_transaction.enhanced_data.created") @@ -553,6 +558,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -617,6 +623,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -684,6 +691,7 @@ private constructor( BALANCE_UPDATED -> Value.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Value.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Value.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Value.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Value.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> @@ -756,6 +764,7 @@ private constructor( BALANCE_UPDATED -> Known.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Known.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Known.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Known.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Known.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionCreateParams.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionCreateParams.kt index 185e725e..5e3e8d12 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionCreateParams.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionCreateParams.kt @@ -654,6 +654,8 @@ private constructor( * - balance.updated: Financial Account Balance Update * - book_transfer_transaction.created: Occurs when a book transfer transaction is created. * - book_transfer_transaction.updated: Occurs when a book transfer transaction is updated. + * - card_authorization.challenge_response: Occurs when a cardholder responds to an SMS + * challenge during card authorization. * - card_transaction.enhanced_data.created: Occurs when L2/L3 enhanced commercial data is * processed for a transaction event. * - card_transaction.enhanced_data.updated: Occurs when L2/L3 enhanced commercial data is @@ -759,6 +761,9 @@ private constructor( @JvmField val BOOK_TRANSFER_TRANSACTION_UPDATED = of("book_transfer_transaction.updated") + @JvmField + val CARD_AUTHORIZATION_CHALLENGE_RESPONSE = of("card_authorization.challenge_response") + @JvmField val CARD_TRANSACTION_ENHANCED_DATA_CREATED = of("card_transaction.enhanced_data.created") @@ -875,6 +880,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -939,6 +945,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -1006,6 +1013,7 @@ private constructor( BALANCE_UPDATED -> Value.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Value.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Value.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Value.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Value.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> @@ -1078,6 +1086,7 @@ private constructor( BALANCE_UPDATED -> Known.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Known.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Known.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Known.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Known.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionSendSimulatedExampleParams.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionSendSimulatedExampleParams.kt index 65077bdf..85a54c16 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionSendSimulatedExampleParams.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionSendSimulatedExampleParams.kt @@ -443,6 +443,9 @@ private constructor( @JvmField val BOOK_TRANSFER_TRANSACTION_UPDATED = of("book_transfer_transaction.updated") + @JvmField + val CARD_AUTHORIZATION_CHALLENGE_RESPONSE = of("card_authorization.challenge_response") + @JvmField val CARD_TRANSACTION_ENHANCED_DATA_CREATED = of("card_transaction.enhanced_data.created") @@ -559,6 +562,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -623,6 +627,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -690,6 +695,7 @@ private constructor( BALANCE_UPDATED -> Value.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Value.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Value.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Value.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Value.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> @@ -762,6 +768,7 @@ private constructor( BALANCE_UPDATED -> Known.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Known.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Known.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Known.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Known.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionUpdateParams.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionUpdateParams.kt index 26ee65e3..c57c87f8 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionUpdateParams.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/EventSubscriptionUpdateParams.kt @@ -677,6 +677,8 @@ private constructor( * - balance.updated: Financial Account Balance Update * - book_transfer_transaction.created: Occurs when a book transfer transaction is created. * - book_transfer_transaction.updated: Occurs when a book transfer transaction is updated. + * - card_authorization.challenge_response: Occurs when a cardholder responds to an SMS + * challenge during card authorization. * - card_transaction.enhanced_data.created: Occurs when L2/L3 enhanced commercial data is * processed for a transaction event. * - card_transaction.enhanced_data.updated: Occurs when L2/L3 enhanced commercial data is @@ -782,6 +784,9 @@ private constructor( @JvmField val BOOK_TRANSFER_TRANSACTION_UPDATED = of("book_transfer_transaction.updated") + @JvmField + val CARD_AUTHORIZATION_CHALLENGE_RESPONSE = of("card_authorization.challenge_response") + @JvmField val CARD_TRANSACTION_ENHANCED_DATA_CREATED = of("card_transaction.enhanced_data.created") @@ -898,6 +903,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -962,6 +968,7 @@ private constructor( BALANCE_UPDATED, BOOK_TRANSFER_TRANSACTION_CREATED, BOOK_TRANSFER_TRANSACTION_UPDATED, + CARD_AUTHORIZATION_CHALLENGE_RESPONSE, CARD_TRANSACTION_ENHANCED_DATA_CREATED, CARD_TRANSACTION_ENHANCED_DATA_UPDATED, CARD_TRANSACTION_UPDATED, @@ -1029,6 +1036,7 @@ private constructor( BALANCE_UPDATED -> Value.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Value.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Value.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Value.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Value.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> @@ -1101,6 +1109,7 @@ private constructor( BALANCE_UPDATED -> Known.BALANCE_UPDATED BOOK_TRANSFER_TRANSACTION_CREATED -> Known.BOOK_TRANSFER_TRANSACTION_CREATED BOOK_TRANSFER_TRANSACTION_UPDATED -> Known.BOOK_TRANSFER_TRANSACTION_UPDATED + CARD_AUTHORIZATION_CHALLENGE_RESPONSE -> Known.CARD_AUTHORIZATION_CHALLENGE_RESPONSE CARD_TRANSACTION_ENHANCED_DATA_CREATED -> Known.CARD_TRANSACTION_ENHANCED_DATA_CREATED CARD_TRANSACTION_ENHANCED_DATA_UPDATED -> diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/ParsedWebhookEvent.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/ParsedWebhookEvent.kt index 567bdb4a..fc484a57 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/ParsedWebhookEvent.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/ParsedWebhookEvent.kt @@ -45,6 +45,9 @@ private constructor( private val accountHolderDocumentUpdated: AccountHolderDocumentUpdatedWebhookEvent? = null, private val cardAuthorizationApprovalRequest: CardAuthorizationApprovalRequestWebhookEvent? = null, + private val cardAuthorizationChallengeResponse: + CardAuthorizationChallengeResponseWebhookEvent? = + null, private val authRulesBacktestReportCreated: AuthRulesBacktestReportCreatedWebhookEvent? = null, private val balanceUpdated: BalanceUpdatedWebhookEvent? = null, private val bookTransferTransactionCreated: BookTransferTransactionCreatedWebhookEvent? = null, @@ -137,6 +140,10 @@ private constructor( fun cardAuthorizationApprovalRequest(): Optional = Optional.ofNullable(cardAuthorizationApprovalRequest) + fun cardAuthorizationChallengeResponse(): + Optional = + Optional.ofNullable(cardAuthorizationChallengeResponse) + fun authRulesBacktestReportCreated(): Optional = Optional.ofNullable(authRulesBacktestReportCreated) @@ -319,6 +326,8 @@ private constructor( fun isCardAuthorizationApprovalRequest(): Boolean = cardAuthorizationApprovalRequest != null + fun isCardAuthorizationChallengeResponse(): Boolean = cardAuthorizationChallengeResponse != null + fun isAuthRulesBacktestReportCreated(): Boolean = authRulesBacktestReportCreated != null fun isBalanceUpdated(): Boolean = balanceUpdated != null @@ -446,6 +455,9 @@ private constructor( fun asCardAuthorizationApprovalRequest(): CardAuthorizationApprovalRequestWebhookEvent = cardAuthorizationApprovalRequest.getOrThrow("cardAuthorizationApprovalRequest") + fun asCardAuthorizationChallengeResponse(): CardAuthorizationChallengeResponseWebhookEvent = + cardAuthorizationChallengeResponse.getOrThrow("cardAuthorizationChallengeResponse") + fun asAuthRulesBacktestReportCreated(): AuthRulesBacktestReportCreatedWebhookEvent = authRulesBacktestReportCreated.getOrThrow("authRulesBacktestReportCreated") @@ -635,6 +647,8 @@ private constructor( visitor.visitAccountHolderDocumentUpdated(accountHolderDocumentUpdated) cardAuthorizationApprovalRequest != null -> visitor.visitCardAuthorizationApprovalRequest(cardAuthorizationApprovalRequest) + cardAuthorizationChallengeResponse != null -> + visitor.visitCardAuthorizationChallengeResponse(cardAuthorizationChallengeResponse) authRulesBacktestReportCreated != null -> visitor.visitAuthRulesBacktestReportCreated(authRulesBacktestReportCreated) balanceUpdated != null -> visitor.visitBalanceUpdated(balanceUpdated) @@ -779,6 +793,13 @@ private constructor( cardAuthorizationApprovalRequest.validate() } + override fun visitCardAuthorizationChallengeResponse( + cardAuthorizationChallengeResponse: + CardAuthorizationChallengeResponseWebhookEvent + ) { + cardAuthorizationChallengeResponse.validate() + } + override fun visitAuthRulesBacktestReportCreated( authRulesBacktestReportCreated: AuthRulesBacktestReportCreatedWebhookEvent ) { @@ -1111,6 +1132,11 @@ private constructor( cardAuthorizationApprovalRequest: CardAuthorizationApprovalRequestWebhookEvent ) = cardAuthorizationApprovalRequest.validity() + override fun visitCardAuthorizationChallengeResponse( + cardAuthorizationChallengeResponse: + CardAuthorizationChallengeResponseWebhookEvent + ) = cardAuthorizationChallengeResponse.validity() + override fun visitAuthRulesBacktestReportCreated( authRulesBacktestReportCreated: AuthRulesBacktestReportCreatedWebhookEvent ) = authRulesBacktestReportCreated.validity() @@ -1326,6 +1352,7 @@ private constructor( accountHolderVerification == other.accountHolderVerification && accountHolderDocumentUpdated == other.accountHolderDocumentUpdated && cardAuthorizationApprovalRequest == other.cardAuthorizationApprovalRequest && + cardAuthorizationChallengeResponse == other.cardAuthorizationChallengeResponse && authRulesBacktestReportCreated == other.authRulesBacktestReportCreated && balanceUpdated == other.balanceUpdated && bookTransferTransactionCreated == other.bookTransferTransactionCreated && @@ -1392,6 +1419,7 @@ private constructor( accountHolderVerification, accountHolderDocumentUpdated, cardAuthorizationApprovalRequest, + cardAuthorizationChallengeResponse, authRulesBacktestReportCreated, balanceUpdated, bookTransferTransactionCreated, @@ -1457,6 +1485,8 @@ private constructor( "ParsedWebhookEvent{accountHolderDocumentUpdated=$accountHolderDocumentUpdated}" cardAuthorizationApprovalRequest != null -> "ParsedWebhookEvent{cardAuthorizationApprovalRequest=$cardAuthorizationApprovalRequest}" + cardAuthorizationChallengeResponse != null -> + "ParsedWebhookEvent{cardAuthorizationChallengeResponse=$cardAuthorizationChallengeResponse}" authRulesBacktestReportCreated != null -> "ParsedWebhookEvent{authRulesBacktestReportCreated=$authRulesBacktestReportCreated}" balanceUpdated != null -> "ParsedWebhookEvent{balanceUpdated=$balanceUpdated}" @@ -1584,6 +1614,14 @@ private constructor( cardAuthorizationApprovalRequest: CardAuthorizationApprovalRequestWebhookEvent ) = ParsedWebhookEvent(cardAuthorizationApprovalRequest = cardAuthorizationApprovalRequest) + @JvmStatic + fun ofCardAuthorizationChallengeResponse( + cardAuthorizationChallengeResponse: CardAuthorizationChallengeResponseWebhookEvent + ) = + ParsedWebhookEvent( + cardAuthorizationChallengeResponse = cardAuthorizationChallengeResponse + ) + @JvmStatic fun ofAuthRulesBacktestReportCreated( authRulesBacktestReportCreated: AuthRulesBacktestReportCreatedWebhookEvent @@ -1896,6 +1934,10 @@ private constructor( cardAuthorizationApprovalRequest: CardAuthorizationApprovalRequestWebhookEvent ): T + fun visitCardAuthorizationChallengeResponse( + cardAuthorizationChallengeResponse: CardAuthorizationChallengeResponseWebhookEvent + ): T + fun visitAuthRulesBacktestReportCreated( authRulesBacktestReportCreated: AuthRulesBacktestReportCreatedWebhookEvent ): T @@ -2145,6 +2187,16 @@ private constructor( _json = json, ) }, + tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { + ParsedWebhookEvent( + cardAuthorizationChallengeResponse = it, + _json = json, + ) + }, tryDeserialize( node, jacksonTypeRef(), @@ -2489,6 +2541,8 @@ private constructor( generator.writeObject(value.accountHolderDocumentUpdated) value.cardAuthorizationApprovalRequest != null -> generator.writeObject(value.cardAuthorizationApprovalRequest) + value.cardAuthorizationChallengeResponse != null -> + generator.writeObject(value.cardAuthorizationChallengeResponse) value.authRulesBacktestReportCreated != null -> generator.writeObject(value.authRulesBacktestReportCreated) value.balanceUpdated != null -> generator.writeObject(value.balanceUpdated) diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardAuthorizationChallengeResponseWebhookEventTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardAuthorizationChallengeResponseWebhookEventTest.kt new file mode 100644 index 00000000..f9a926b8 --- /dev/null +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardAuthorizationChallengeResponseWebhookEventTest.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.lithic.api.models + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.lithic.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CardAuthorizationChallengeResponseWebhookEventTest { + + @Test + fun create() { + val cardAuthorizationChallengeResponseWebhookEvent = + CardAuthorizationChallengeResponseWebhookEvent.builder() + .cardToken("00000000-0000-0000-0000-000000000003") + .challengeMethod(CardAuthorizationChallengeResponseWebhookEvent.ChallengeMethod.SMS) + .completed(OffsetDateTime.parse("2025-07-17T07:08:15Z")) + .created(OffsetDateTime.parse("2025-07-17T07:07:29Z")) + .eventToken("00000000-0000-0000-0000-000000000001") + .eventType( + CardAuthorizationChallengeResponseWebhookEvent.EventType + .CARD_AUTHORIZATION_CHALLENGE_RESPONSE + ) + .response(CardAuthorizationChallengeResponseWebhookEvent.Response.APPROVE) + .transactionToken("00000000-0000-0000-0000-000000000002") + .build() + + assertThat(cardAuthorizationChallengeResponseWebhookEvent.cardToken()) + .contains("00000000-0000-0000-0000-000000000003") + assertThat(cardAuthorizationChallengeResponseWebhookEvent.challengeMethod()) + .isEqualTo(CardAuthorizationChallengeResponseWebhookEvent.ChallengeMethod.SMS) + assertThat(cardAuthorizationChallengeResponseWebhookEvent.completed()) + .contains(OffsetDateTime.parse("2025-07-17T07:08:15Z")) + assertThat(cardAuthorizationChallengeResponseWebhookEvent.created()) + .isEqualTo(OffsetDateTime.parse("2025-07-17T07:07:29Z")) + assertThat(cardAuthorizationChallengeResponseWebhookEvent.eventToken()) + .isEqualTo("00000000-0000-0000-0000-000000000001") + assertThat(cardAuthorizationChallengeResponseWebhookEvent.eventType()) + .isEqualTo( + CardAuthorizationChallengeResponseWebhookEvent.EventType + .CARD_AUTHORIZATION_CHALLENGE_RESPONSE + ) + assertThat(cardAuthorizationChallengeResponseWebhookEvent.response()) + .isEqualTo(CardAuthorizationChallengeResponseWebhookEvent.Response.APPROVE) + assertThat(cardAuthorizationChallengeResponseWebhookEvent.transactionToken()) + .contains("00000000-0000-0000-0000-000000000002") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val cardAuthorizationChallengeResponseWebhookEvent = + CardAuthorizationChallengeResponseWebhookEvent.builder() + .cardToken("00000000-0000-0000-0000-000000000003") + .challengeMethod(CardAuthorizationChallengeResponseWebhookEvent.ChallengeMethod.SMS) + .completed(OffsetDateTime.parse("2025-07-17T07:08:15Z")) + .created(OffsetDateTime.parse("2025-07-17T07:07:29Z")) + .eventToken("00000000-0000-0000-0000-000000000001") + .eventType( + CardAuthorizationChallengeResponseWebhookEvent.EventType + .CARD_AUTHORIZATION_CHALLENGE_RESPONSE + ) + .response(CardAuthorizationChallengeResponseWebhookEvent.Response.APPROVE) + .transactionToken("00000000-0000-0000-0000-000000000002") + .build() + + val roundtrippedCardAuthorizationChallengeResponseWebhookEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(cardAuthorizationChallengeResponseWebhookEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCardAuthorizationChallengeResponseWebhookEvent) + .isEqualTo(cardAuthorizationChallengeResponseWebhookEvent) + } +} diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/models/ParsedWebhookEventTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/models/ParsedWebhookEventTest.kt index 2a12d757..aabc1c02 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/models/ParsedWebhookEventTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/models/ParsedWebhookEventTest.kt @@ -44,6 +44,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -215,6 +216,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -405,6 +407,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -531,6 +534,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -638,6 +642,7 @@ internal class ParsedWebhookEventTest { .contains(accountHolderVerification) assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -764,6 +769,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()) .contains(accountHolderDocumentUpdated) assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -1113,6 +1119,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()) .contains(cardAuthorizationApprovalRequest) + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -1431,6 +1438,122 @@ internal class ParsedWebhookEventTest { assertThat(roundtrippedParsedWebhookEvent).isEqualTo(parsedWebhookEvent) } + @Test + fun ofCardAuthorizationChallengeResponse() { + val cardAuthorizationChallengeResponse = + CardAuthorizationChallengeResponseWebhookEvent.builder() + .cardToken("00000000-0000-0000-0000-000000000003") + .challengeMethod(CardAuthorizationChallengeResponseWebhookEvent.ChallengeMethod.SMS) + .completed(OffsetDateTime.parse("2025-07-17T07:08:15Z")) + .created(OffsetDateTime.parse("2025-07-17T07:07:29Z")) + .eventToken("00000000-0000-0000-0000-000000000001") + .eventType( + CardAuthorizationChallengeResponseWebhookEvent.EventType + .CARD_AUTHORIZATION_CHALLENGE_RESPONSE + ) + .response(CardAuthorizationChallengeResponseWebhookEvent.Response.APPROVE) + .transactionToken("00000000-0000-0000-0000-000000000002") + .build() + + val parsedWebhookEvent = + ParsedWebhookEvent.ofCardAuthorizationChallengeResponse( + cardAuthorizationChallengeResponse + ) + + assertThat(parsedWebhookEvent.accountHolderCreated()).isEmpty + assertThat(parsedWebhookEvent.kybPayload()).isEmpty + assertThat(parsedWebhookEvent.kycPayload()).isEmpty + assertThat(parsedWebhookEvent.legacyPayload()).isEmpty + assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty + assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()) + .contains(cardAuthorizationChallengeResponse) + assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty + assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty + assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty + assertThat(parsedWebhookEvent.bookTransferTransactionUpdated()).isEmpty + assertThat(parsedWebhookEvent.cardCreated()).isEmpty + assertThat(parsedWebhookEvent.cardConverted()).isEmpty + assertThat(parsedWebhookEvent.cardRenewed()).isEmpty + assertThat(parsedWebhookEvent.cardReissued()).isEmpty + assertThat(parsedWebhookEvent.cardShipped()).isEmpty + assertThat(parsedWebhookEvent.cardUpdated()).isEmpty + assertThat(parsedWebhookEvent.cardTransactionUpdated()).isEmpty + assertThat(parsedWebhookEvent.cardTransactionEnhancedDataCreated()).isEmpty + assertThat(parsedWebhookEvent.cardTransactionEnhancedDataUpdated()).isEmpty + assertThat(parsedWebhookEvent.digitalWalletTokenizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.digitalWalletTokenizationResult()).isEmpty + assertThat(parsedWebhookEvent.digitalWalletTokenizationTwoFactorAuthenticationCode()) + .isEmpty + assertThat(parsedWebhookEvent.digitalWalletTokenizationTwoFactorAuthenticationCodeSent()) + .isEmpty + assertThat(parsedWebhookEvent.digitalWalletTokenizationUpdated()).isEmpty + assertThat(parsedWebhookEvent.disputeUpdated()).isEmpty + assertThat(parsedWebhookEvent.disputeEvidenceUploadFailed()).isEmpty + assertThat(parsedWebhookEvent.externalBankAccountCreated()).isEmpty + assertThat(parsedWebhookEvent.externalBankAccountUpdated()).isEmpty + assertThat(parsedWebhookEvent.externalPaymentCreated()).isEmpty + assertThat(parsedWebhookEvent.externalPaymentUpdated()).isEmpty + assertThat(parsedWebhookEvent.financialAccountCreated()).isEmpty + assertThat(parsedWebhookEvent.financialAccountUpdated()).isEmpty + assertThat(parsedWebhookEvent.fundingEventCreated()).isEmpty + assertThat(parsedWebhookEvent.loanTapeCreated()).isEmpty + assertThat(parsedWebhookEvent.loanTapeUpdated()).isEmpty + assertThat(parsedWebhookEvent.managementOperationCreated()).isEmpty + assertThat(parsedWebhookEvent.managementOperationUpdated()).isEmpty + assertThat(parsedWebhookEvent.internalTransactionCreated()).isEmpty + assertThat(parsedWebhookEvent.internalTransactionUpdated()).isEmpty + assertThat(parsedWebhookEvent.networkTotalCreated()).isEmpty + assertThat(parsedWebhookEvent.networkTotalUpdated()).isEmpty + assertThat(parsedWebhookEvent.paymentTransactionCreated()).isEmpty + assertThat(parsedWebhookEvent.paymentTransactionUpdated()).isEmpty + assertThat(parsedWebhookEvent.settlementReportUpdated()).isEmpty + assertThat(parsedWebhookEvent.statementsCreated()).isEmpty + assertThat(parsedWebhookEvent.threeDSAuthenticationCreated()).isEmpty + assertThat(parsedWebhookEvent.threeDSAuthenticationUpdated()).isEmpty + assertThat(parsedWebhookEvent.threeDSAuthenticationChallenge()).isEmpty + assertThat(parsedWebhookEvent.tokenizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.tokenizationResult()).isEmpty + assertThat(parsedWebhookEvent.tokenizationTwoFactorAuthenticationCode()).isEmpty + assertThat(parsedWebhookEvent.tokenizationTwoFactorAuthenticationCodeSent()).isEmpty + assertThat(parsedWebhookEvent.tokenizationUpdated()).isEmpty + assertThat(parsedWebhookEvent.threeDSAuthenticationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.disputeTransactionCreated()).isEmpty + assertThat(parsedWebhookEvent.disputeTransactionUpdated()).isEmpty + } + + @Test + fun ofCardAuthorizationChallengeResponseRoundtrip() { + val jsonMapper = jsonMapper() + val parsedWebhookEvent = + ParsedWebhookEvent.ofCardAuthorizationChallengeResponse( + CardAuthorizationChallengeResponseWebhookEvent.builder() + .cardToken("00000000-0000-0000-0000-000000000003") + .challengeMethod( + CardAuthorizationChallengeResponseWebhookEvent.ChallengeMethod.SMS + ) + .completed(OffsetDateTime.parse("2025-07-17T07:08:15Z")) + .created(OffsetDateTime.parse("2025-07-17T07:07:29Z")) + .eventToken("00000000-0000-0000-0000-000000000001") + .eventType( + CardAuthorizationChallengeResponseWebhookEvent.EventType + .CARD_AUTHORIZATION_CHALLENGE_RESPONSE + ) + .response(CardAuthorizationChallengeResponseWebhookEvent.Response.APPROVE) + .transactionToken("00000000-0000-0000-0000-000000000002") + .build() + ) + + val roundtrippedParsedWebhookEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(parsedWebhookEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedParsedWebhookEvent).isEqualTo(parsedWebhookEvent) + } + @Test fun ofAuthRulesBacktestReportCreated() { val authRulesBacktestReportCreated = @@ -1494,6 +1617,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()) .contains(authRulesBacktestReportCreated) assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty @@ -1652,6 +1776,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).contains(balanceUpdated) assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -1804,6 +1929,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()) @@ -1991,6 +2117,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -2131,6 +2258,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -2223,6 +2351,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -2318,6 +2447,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -2413,6 +2543,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -2509,6 +2640,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -2607,6 +2739,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -2927,6 +3060,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -3315,6 +3449,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -3519,6 +3654,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -3748,6 +3884,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -3955,6 +4092,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -4106,6 +4244,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -4239,6 +4378,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -4397,6 +4537,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -4553,6 +4694,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -4670,6 +4812,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -4804,6 +4947,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -4962,6 +5106,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -5116,6 +5261,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -5267,6 +5413,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -5416,6 +5563,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -5562,6 +5710,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -5693,6 +5842,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -5957,6 +6107,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -6388,6 +6539,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -6711,6 +6863,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -6912,6 +7065,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -7083,6 +7237,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -7224,6 +7379,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -7360,6 +7516,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -7487,6 +7644,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -7658,6 +7816,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -7873,6 +8032,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -8048,6 +8208,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -8288,6 +8449,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -8642,6 +8804,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -9015,6 +9178,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -9417,6 +9581,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -9749,6 +9914,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -9942,6 +10108,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -10083,6 +10250,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -10213,6 +10381,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -10366,6 +10535,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -10640,6 +10810,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -10932,6 +11103,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty @@ -11137,6 +11309,7 @@ internal class ParsedWebhookEventTest { assertThat(parsedWebhookEvent.accountHolderVerification()).isEmpty assertThat(parsedWebhookEvent.accountHolderDocumentUpdated()).isEmpty assertThat(parsedWebhookEvent.cardAuthorizationApprovalRequest()).isEmpty + assertThat(parsedWebhookEvent.cardAuthorizationChallengeResponse()).isEmpty assertThat(parsedWebhookEvent.authRulesBacktestReportCreated()).isEmpty assertThat(parsedWebhookEvent.balanceUpdated()).isEmpty assertThat(parsedWebhookEvent.bookTransferTransactionCreated()).isEmpty From 7e086eeea0efed53c022e1f471cb2a5e83dd1427 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 14:25:14 +0000 Subject: [PATCH 2/4] feat(api): add setVerificationMethod method to external_bank_accounts --- .stats.yml | 4 +- ...lBankAccountSetVerificationMethodParams.kt | 714 ++++++++++++++++++ .../async/ExternalBankAccountServiceAsync.kt | 67 ++ .../ExternalBankAccountServiceAsyncImpl.kt | 47 ++ .../blocking/ExternalBankAccountService.kt | 71 ++ .../ExternalBankAccountServiceImpl.kt | 44 ++ ...kAccountSetVerificationMethodParamsTest.kt | 85 +++ .../ExternalBankAccountServiceAsyncTest.kt | 27 + .../ExternalBankAccountServiceTest.kt | 26 + 9 files changed, 1083 insertions(+), 2 deletions(-) create mode 100644 lithic-java-core/src/main/kotlin/com/lithic/api/models/ExternalBankAccountSetVerificationMethodParams.kt create mode 100644 lithic-java-core/src/test/kotlin/com/lithic/api/models/ExternalBankAccountSetVerificationMethodParamsTest.kt diff --git a/.stats.yml b/.stats.yml index b222baac..f542910e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 190 +configured_endpoints: 191 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-28c9b43d3182bf0e1c9635f6100e4995a744724db1edd635cfd3fc7702ced68c.yml openapi_spec_hash: aba00a65f877c5095499d9d1a66b5e5f -config_hash: 5eca052bb23d273fa970eac3127dd919 +config_hash: ac8326134e692f3f3bdec82396bbec80 diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/ExternalBankAccountSetVerificationMethodParams.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/ExternalBankAccountSetVerificationMethodParams.kt new file mode 100644 index 00000000..135ab2e3 --- /dev/null +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/ExternalBankAccountSetVerificationMethodParams.kt @@ -0,0 +1,714 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.lithic.api.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.lithic.api.core.Enum +import com.lithic.api.core.ExcludeMissing +import com.lithic.api.core.JsonField +import com.lithic.api.core.JsonMissing +import com.lithic.api.core.JsonValue +import com.lithic.api.core.Params +import com.lithic.api.core.checkRequired +import com.lithic.api.core.http.Headers +import com.lithic.api.core.http.QueryParams +import com.lithic.api.errors.LithicInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Update the verification method for an external bank account. Verification method can only be + * updated if the `verification_state` is `PENDING`. + */ +class ExternalBankAccountSetVerificationMethodParams +private constructor( + private val externalBankAccountToken: String?, + private val body: SetVerificationMethodRequest, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun externalBankAccountToken(): Optional = Optional.ofNullable(externalBankAccountToken) + + /** + * The verification method to set for the external bank account + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun verificationMethod(): SetVerificationMethodAllowedVerificationMethods = + body.verificationMethod() + + /** + * The financial account token of the operating account to fund the micro deposits. Required + * when verification_method is MICRO_DEPOSIT or PRENOTE. + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun financialAccountToken(): Optional = body.financialAccountToken() + + /** + * Returns the raw JSON value of [verificationMethod]. + * + * Unlike [verificationMethod], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _verificationMethod(): JsonField = + body._verificationMethod() + + /** + * Returns the raw JSON value of [financialAccountToken]. + * + * Unlike [financialAccountToken], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _financialAccountToken(): JsonField = body._financialAccountToken() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [ExternalBankAccountSetVerificationMethodParams]. + * + * The following fields are required: + * ```java + * .verificationMethod() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ExternalBankAccountSetVerificationMethodParams]. */ + class Builder internal constructor() { + + private var externalBankAccountToken: String? = null + private var body: SetVerificationMethodRequest.Builder = + SetVerificationMethodRequest.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from( + externalBankAccountSetVerificationMethodParams: + ExternalBankAccountSetVerificationMethodParams + ) = apply { + externalBankAccountToken = + externalBankAccountSetVerificationMethodParams.externalBankAccountToken + body = externalBankAccountSetVerificationMethodParams.body.toBuilder() + additionalHeaders = + externalBankAccountSetVerificationMethodParams.additionalHeaders.toBuilder() + additionalQueryParams = + externalBankAccountSetVerificationMethodParams.additionalQueryParams.toBuilder() + } + + fun externalBankAccountToken(externalBankAccountToken: String?) = apply { + this.externalBankAccountToken = externalBankAccountToken + } + + /** + * Alias for calling [Builder.externalBankAccountToken] with + * `externalBankAccountToken.orElse(null)`. + */ + fun externalBankAccountToken(externalBankAccountToken: Optional) = + externalBankAccountToken(externalBankAccountToken.getOrNull()) + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [verificationMethod] + * - [financialAccountToken] + */ + fun body(body: SetVerificationMethodRequest) = apply { this.body = body.toBuilder() } + + /** The verification method to set for the external bank account */ + fun verificationMethod( + verificationMethod: SetVerificationMethodAllowedVerificationMethods + ) = apply { body.verificationMethod(verificationMethod) } + + /** + * Sets [Builder.verificationMethod] to an arbitrary JSON value. + * + * You should usually call [Builder.verificationMethod] with a well-typed + * [SetVerificationMethodAllowedVerificationMethods] value instead. This method is primarily + * for setting the field to an undocumented or not yet supported value. + */ + fun verificationMethod( + verificationMethod: JsonField + ) = apply { body.verificationMethod(verificationMethod) } + + /** + * The financial account token of the operating account to fund the micro deposits. Required + * when verification_method is MICRO_DEPOSIT or PRENOTE. + */ + fun financialAccountToken(financialAccountToken: String) = apply { + body.financialAccountToken(financialAccountToken) + } + + /** + * Sets [Builder.financialAccountToken] to an arbitrary JSON value. + * + * You should usually call [Builder.financialAccountToken] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun financialAccountToken(financialAccountToken: JsonField) = apply { + body.financialAccountToken(financialAccountToken) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ExternalBankAccountSetVerificationMethodParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .verificationMethod() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalBankAccountSetVerificationMethodParams = + ExternalBankAccountSetVerificationMethodParams( + externalBankAccountToken, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): SetVerificationMethodRequest = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> externalBankAccountToken ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class SetVerificationMethodRequest + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val verificationMethod: JsonField, + private val financialAccountToken: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("verification_method") + @ExcludeMissing + verificationMethod: JsonField = + JsonMissing.of(), + @JsonProperty("financial_account_token") + @ExcludeMissing + financialAccountToken: JsonField = JsonMissing.of(), + ) : this(verificationMethod, financialAccountToken, mutableMapOf()) + + /** + * The verification method to set for the external bank account + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun verificationMethod(): SetVerificationMethodAllowedVerificationMethods = + verificationMethod.getRequired("verification_method") + + /** + * The financial account token of the operating account to fund the micro deposits. Required + * when verification_method is MICRO_DEPOSIT or PRENOTE. + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun financialAccountToken(): Optional = + financialAccountToken.getOptional("financial_account_token") + + /** + * Returns the raw JSON value of [verificationMethod]. + * + * Unlike [verificationMethod], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("verification_method") + @ExcludeMissing + fun _verificationMethod(): JsonField = + verificationMethod + + /** + * Returns the raw JSON value of [financialAccountToken]. + * + * Unlike [financialAccountToken], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("financial_account_token") + @ExcludeMissing + fun _financialAccountToken(): JsonField = financialAccountToken + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [SetVerificationMethodRequest]. + * + * The following fields are required: + * ```java + * .verificationMethod() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SetVerificationMethodRequest]. */ + class Builder internal constructor() { + + private var verificationMethod: + JsonField? = + null + private var financialAccountToken: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(setVerificationMethodRequest: SetVerificationMethodRequest) = apply { + verificationMethod = setVerificationMethodRequest.verificationMethod + financialAccountToken = setVerificationMethodRequest.financialAccountToken + additionalProperties = + setVerificationMethodRequest.additionalProperties.toMutableMap() + } + + /** The verification method to set for the external bank account */ + fun verificationMethod( + verificationMethod: SetVerificationMethodAllowedVerificationMethods + ) = verificationMethod(JsonField.of(verificationMethod)) + + /** + * Sets [Builder.verificationMethod] to an arbitrary JSON value. + * + * You should usually call [Builder.verificationMethod] with a well-typed + * [SetVerificationMethodAllowedVerificationMethods] value instead. This method is + * primarily for setting the field to an undocumented or not yet supported value. + */ + fun verificationMethod( + verificationMethod: JsonField + ) = apply { this.verificationMethod = verificationMethod } + + /** + * The financial account token of the operating account to fund the micro deposits. + * Required when verification_method is MICRO_DEPOSIT or PRENOTE. + */ + fun financialAccountToken(financialAccountToken: String) = + financialAccountToken(JsonField.of(financialAccountToken)) + + /** + * Sets [Builder.financialAccountToken] to an arbitrary JSON value. + * + * You should usually call [Builder.financialAccountToken] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun financialAccountToken(financialAccountToken: JsonField) = apply { + this.financialAccountToken = financialAccountToken + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SetVerificationMethodRequest]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .verificationMethod() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SetVerificationMethodRequest = + SetVerificationMethodRequest( + checkRequired("verificationMethod", verificationMethod), + financialAccountToken, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SetVerificationMethodRequest = apply { + if (validated) { + return@apply + } + + verificationMethod().validate() + financialAccountToken() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (verificationMethod.asKnown().getOrNull()?.validity() ?: 0) + + (if (financialAccountToken.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SetVerificationMethodRequest && + verificationMethod == other.verificationMethod && + financialAccountToken == other.financialAccountToken && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(verificationMethod, financialAccountToken, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SetVerificationMethodRequest{verificationMethod=$verificationMethod, financialAccountToken=$financialAccountToken, additionalProperties=$additionalProperties}" + } + + /** The verification method to set for the external bank account */ + class SetVerificationMethodAllowedVerificationMethods + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val MICRO_DEPOSIT = of("MICRO_DEPOSIT") + + @JvmField val PRENOTE = of("PRENOTE") + + @JvmField val EXTERNALLY_VERIFIED = of("EXTERNALLY_VERIFIED") + + @JvmStatic + fun of(value: String) = + SetVerificationMethodAllowedVerificationMethods(JsonField.of(value)) + } + + /** An enum containing [SetVerificationMethodAllowedVerificationMethods]'s known values. */ + enum class Known { + MICRO_DEPOSIT, + PRENOTE, + EXTERNALLY_VERIFIED, + } + + /** + * An enum containing [SetVerificationMethodAllowedVerificationMethods]'s known values, as + * well as an [_UNKNOWN] member. + * + * An instance of [SetVerificationMethodAllowedVerificationMethods] can contain an unknown + * value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + MICRO_DEPOSIT, + PRENOTE, + EXTERNALLY_VERIFIED, + /** + * An enum member indicating that [SetVerificationMethodAllowedVerificationMethods] was + * instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + MICRO_DEPOSIT -> Value.MICRO_DEPOSIT + PRENOTE -> Value.PRENOTE + EXTERNALLY_VERIFIED -> Value.EXTERNALLY_VERIFIED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws LithicInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + MICRO_DEPOSIT -> Known.MICRO_DEPOSIT + PRENOTE -> Known.PRENOTE + EXTERNALLY_VERIFIED -> Known.EXTERNALLY_VERIFIED + else -> + throw LithicInvalidDataException( + "Unknown SetVerificationMethodAllowedVerificationMethods: $value" + ) + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws LithicInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { LithicInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): SetVerificationMethodAllowedVerificationMethods = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SetVerificationMethodAllowedVerificationMethods && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalBankAccountSetVerificationMethodParams && + externalBankAccountToken == other.externalBankAccountToken && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(externalBankAccountToken, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ExternalBankAccountSetVerificationMethodParams{externalBankAccountToken=$externalBankAccountToken, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsync.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsync.kt index 8dd832a5..222a8aeb 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsync.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsync.kt @@ -15,6 +15,7 @@ import com.lithic.api.models.ExternalBankAccountRetrieveResponse import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsParams import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsResponse import com.lithic.api.models.ExternalBankAccountRetryPrenoteParams +import com.lithic.api.models.ExternalBankAccountSetVerificationMethodParams import com.lithic.api.models.ExternalBankAccountUnpauseParams import com.lithic.api.models.ExternalBankAccountUpdateParams import com.lithic.api.models.ExternalBankAccountUpdateResponse @@ -313,6 +314,38 @@ interface ExternalBankAccountServiceAsync { requestOptions, ) + /** + * Update the verification method for an external bank account. Verification method can only be + * updated if the `verification_state` is `PENDING`. + */ + fun setVerificationMethod( + externalBankAccountToken: String, + params: ExternalBankAccountSetVerificationMethodParams, + ): CompletableFuture = + setVerificationMethod(externalBankAccountToken, params, RequestOptions.none()) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + externalBankAccountToken: String, + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + setVerificationMethod( + params.toBuilder().externalBankAccountToken(externalBankAccountToken).build(), + requestOptions, + ) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams + ): CompletableFuture = setVerificationMethod(params, RequestOptions.none()) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + /** Unpause an external bank account */ fun unpause(externalBankAccountToken: String): CompletableFuture = unpause(externalBankAccountToken, ExternalBankAccountUnpauseParams.none()) @@ -680,6 +713,40 @@ interface ExternalBankAccountServiceAsync { requestOptions, ) + /** + * Returns a raw HTTP response for `post + * /v1/external_bank_accounts/{external_bank_account_token}/set_verification_method`, but is + * otherwise the same as [ExternalBankAccountServiceAsync.setVerificationMethod]. + */ + fun setVerificationMethod( + externalBankAccountToken: String, + params: ExternalBankAccountSetVerificationMethodParams, + ): CompletableFuture> = + setVerificationMethod(externalBankAccountToken, params, RequestOptions.none()) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + externalBankAccountToken: String, + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + setVerificationMethod( + params.toBuilder().externalBankAccountToken(externalBankAccountToken).build(), + requestOptions, + ) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams + ): CompletableFuture> = + setVerificationMethod(params, RequestOptions.none()) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + /** * Returns a raw HTTP response for `post * /v1/external_bank_accounts/{external_bank_account_token}/unpause`, but is otherwise the diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsyncImpl.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsyncImpl.kt index 9514f7e6..5fd2a238 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsyncImpl.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsyncImpl.kt @@ -27,6 +27,7 @@ import com.lithic.api.models.ExternalBankAccountRetrieveResponse import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsParams import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsResponse import com.lithic.api.models.ExternalBankAccountRetryPrenoteParams +import com.lithic.api.models.ExternalBankAccountSetVerificationMethodParams import com.lithic.api.models.ExternalBankAccountUnpauseParams import com.lithic.api.models.ExternalBankAccountUpdateParams import com.lithic.api.models.ExternalBankAccountUpdateResponse @@ -101,6 +102,13 @@ internal constructor(private val clientOptions: ClientOptions) : ExternalBankAcc // post /v1/external_bank_accounts/{external_bank_account_token}/retry_prenote withRawResponse().retryPrenote(params, requestOptions).thenApply { it.parse() } + override fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v1/external_bank_accounts/{external_bank_account_token}/set_verification_method + withRawResponse().setVerificationMethod(params, requestOptions).thenApply { it.parse() } + override fun unpause( params: ExternalBankAccountUnpauseParams, requestOptions: RequestOptions, @@ -342,6 +350,45 @@ internal constructor(private val clientOptions: ClientOptions) : ExternalBankAcc } } + private val setVerificationMethodHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("externalBankAccountToken", params.externalBankAccountToken().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "v1", + "external_bank_accounts", + params._pathParam(0), + "set_verification_method", + ) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { setVerificationMethodHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + private val unpauseHandler: Handler = jsonHandler(clientOptions.jsonMapper) diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/services/blocking/ExternalBankAccountService.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/services/blocking/ExternalBankAccountService.kt index 968e9303..ad396dd1 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/services/blocking/ExternalBankAccountService.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/services/blocking/ExternalBankAccountService.kt @@ -16,6 +16,7 @@ import com.lithic.api.models.ExternalBankAccountRetrieveResponse import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsParams import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsResponse import com.lithic.api.models.ExternalBankAccountRetryPrenoteParams +import com.lithic.api.models.ExternalBankAccountSetVerificationMethodParams import com.lithic.api.models.ExternalBankAccountUnpauseParams import com.lithic.api.models.ExternalBankAccountUpdateParams import com.lithic.api.models.ExternalBankAccountUpdateResponse @@ -299,6 +300,38 @@ interface ExternalBankAccountService { requestOptions, ) + /** + * Update the verification method for an external bank account. Verification method can only be + * updated if the `verification_state` is `PENDING`. + */ + fun setVerificationMethod( + externalBankAccountToken: String, + params: ExternalBankAccountSetVerificationMethodParams, + ): ExternalBankAccount = + setVerificationMethod(externalBankAccountToken, params, RequestOptions.none()) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + externalBankAccountToken: String, + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalBankAccount = + setVerificationMethod( + params.toBuilder().externalBankAccountToken(externalBankAccountToken).build(), + requestOptions, + ) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams + ): ExternalBankAccount = setVerificationMethod(params, RequestOptions.none()) + + /** @see setVerificationMethod */ + fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalBankAccount + /** Unpause an external bank account */ fun unpause(externalBankAccountToken: String): ExternalBankAccount = unpause(externalBankAccountToken, ExternalBankAccountUnpauseParams.none()) @@ -696,6 +729,44 @@ interface ExternalBankAccountService { requestOptions, ) + /** + * Returns a raw HTTP response for `post + * /v1/external_bank_accounts/{external_bank_account_token}/set_verification_method`, but is + * otherwise the same as [ExternalBankAccountService.setVerificationMethod]. + */ + @MustBeClosed + fun setVerificationMethod( + externalBankAccountToken: String, + params: ExternalBankAccountSetVerificationMethodParams, + ): HttpResponseFor = + setVerificationMethod(externalBankAccountToken, params, RequestOptions.none()) + + /** @see setVerificationMethod */ + @MustBeClosed + fun setVerificationMethod( + externalBankAccountToken: String, + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + setVerificationMethod( + params.toBuilder().externalBankAccountToken(externalBankAccountToken).build(), + requestOptions, + ) + + /** @see setVerificationMethod */ + @MustBeClosed + fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams + ): HttpResponseFor = + setVerificationMethod(params, RequestOptions.none()) + + /** @see setVerificationMethod */ + @MustBeClosed + fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + /** * Returns a raw HTTP response for `post * /v1/external_bank_accounts/{external_bank_account_token}/unpause`, but is otherwise the diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/services/blocking/ExternalBankAccountServiceImpl.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/services/blocking/ExternalBankAccountServiceImpl.kt index 01f0678b..9852c883 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/services/blocking/ExternalBankAccountServiceImpl.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/services/blocking/ExternalBankAccountServiceImpl.kt @@ -27,6 +27,7 @@ import com.lithic.api.models.ExternalBankAccountRetrieveResponse import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsParams import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsResponse import com.lithic.api.models.ExternalBankAccountRetryPrenoteParams +import com.lithic.api.models.ExternalBankAccountSetVerificationMethodParams import com.lithic.api.models.ExternalBankAccountUnpauseParams import com.lithic.api.models.ExternalBankAccountUpdateParams import com.lithic.api.models.ExternalBankAccountUpdateResponse @@ -97,6 +98,13 @@ internal constructor(private val clientOptions: ClientOptions) : ExternalBankAcc // post /v1/external_bank_accounts/{external_bank_account_token}/retry_prenote withRawResponse().retryPrenote(params, requestOptions).parse() + override fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions, + ): ExternalBankAccount = + // post /v1/external_bank_accounts/{external_bank_account_token}/set_verification_method + withRawResponse().setVerificationMethod(params, requestOptions).parse() + override fun unpause( params: ExternalBankAccountUnpauseParams, requestOptions: RequestOptions, @@ -319,6 +327,42 @@ internal constructor(private val clientOptions: ClientOptions) : ExternalBankAcc } } + private val setVerificationMethodHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun setVerificationMethod( + params: ExternalBankAccountSetVerificationMethodParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("externalBankAccountToken", params.externalBankAccountToken().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "v1", + "external_bank_accounts", + params._pathParam(0), + "set_verification_method", + ) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { setVerificationMethodHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + private val unpauseHandler: Handler = jsonHandler(clientOptions.jsonMapper) diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/models/ExternalBankAccountSetVerificationMethodParamsTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/models/ExternalBankAccountSetVerificationMethodParamsTest.kt new file mode 100644 index 00000000..30aae7f8 --- /dev/null +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/models/ExternalBankAccountSetVerificationMethodParamsTest.kt @@ -0,0 +1,85 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.lithic.api.models + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalBankAccountSetVerificationMethodParamsTest { + + @Test + fun create() { + ExternalBankAccountSetVerificationMethodParams.builder() + .externalBankAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .verificationMethod( + ExternalBankAccountSetVerificationMethodParams + .SetVerificationMethodAllowedVerificationMethods + .MICRO_DEPOSIT + ) + .financialAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + } + + @Test + fun pathParams() { + val params = + ExternalBankAccountSetVerificationMethodParams.builder() + .externalBankAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .verificationMethod( + ExternalBankAccountSetVerificationMethodParams + .SetVerificationMethodAllowedVerificationMethods + .MICRO_DEPOSIT + ) + .build() + + assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun body() { + val params = + ExternalBankAccountSetVerificationMethodParams.builder() + .externalBankAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .verificationMethod( + ExternalBankAccountSetVerificationMethodParams + .SetVerificationMethodAllowedVerificationMethods + .MICRO_DEPOSIT + ) + .financialAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + + val body = params._body() + + assertThat(body.verificationMethod()) + .isEqualTo( + ExternalBankAccountSetVerificationMethodParams + .SetVerificationMethodAllowedVerificationMethods + .MICRO_DEPOSIT + ) + assertThat(body.financialAccountToken()).contains("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + ExternalBankAccountSetVerificationMethodParams.builder() + .externalBankAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .verificationMethod( + ExternalBankAccountSetVerificationMethodParams + .SetVerificationMethodAllowedVerificationMethods + .MICRO_DEPOSIT + ) + .build() + + val body = params._body() + + assertThat(body.verificationMethod()) + .isEqualTo( + ExternalBankAccountSetVerificationMethodParams + .SetVerificationMethodAllowedVerificationMethods + .MICRO_DEPOSIT + ) + } +} diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsyncTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsyncTest.kt index 857fe26c..8335a41a 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsyncTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/services/async/ExternalBankAccountServiceAsyncTest.kt @@ -8,6 +8,7 @@ import com.lithic.api.models.ExternalBankAccountAddress import com.lithic.api.models.ExternalBankAccountCreateParams import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsParams import com.lithic.api.models.ExternalBankAccountRetryPrenoteParams +import com.lithic.api.models.ExternalBankAccountSetVerificationMethodParams import com.lithic.api.models.ExternalBankAccountUpdateParams import com.lithic.api.models.OwnerType import com.lithic.api.models.VerificationMethod @@ -179,6 +180,32 @@ internal class ExternalBankAccountServiceAsyncTest { externalBankAccount.validate() } + @Test + fun setVerificationMethod() { + val client = + LithicOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My Lithic API Key") + .build() + val externalBankAccountServiceAsync = client.externalBankAccounts() + + val externalBankAccountFuture = + externalBankAccountServiceAsync.setVerificationMethod( + ExternalBankAccountSetVerificationMethodParams.builder() + .externalBankAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .verificationMethod( + ExternalBankAccountSetVerificationMethodParams + .SetVerificationMethodAllowedVerificationMethods + .MICRO_DEPOSIT + ) + .financialAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + ) + + val externalBankAccount = externalBankAccountFuture.get() + externalBankAccount.validate() + } + @Test fun unpause() { val client = diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/services/blocking/ExternalBankAccountServiceTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/services/blocking/ExternalBankAccountServiceTest.kt index a659b745..7e603d81 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/services/blocking/ExternalBankAccountServiceTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/services/blocking/ExternalBankAccountServiceTest.kt @@ -8,6 +8,7 @@ import com.lithic.api.models.ExternalBankAccountAddress import com.lithic.api.models.ExternalBankAccountCreateParams import com.lithic.api.models.ExternalBankAccountRetryMicroDepositsParams import com.lithic.api.models.ExternalBankAccountRetryPrenoteParams +import com.lithic.api.models.ExternalBankAccountSetVerificationMethodParams import com.lithic.api.models.ExternalBankAccountUpdateParams import com.lithic.api.models.OwnerType import com.lithic.api.models.VerificationMethod @@ -173,6 +174,31 @@ internal class ExternalBankAccountServiceTest { externalBankAccount.validate() } + @Test + fun setVerificationMethod() { + val client = + LithicOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My Lithic API Key") + .build() + val externalBankAccountService = client.externalBankAccounts() + + val externalBankAccount = + externalBankAccountService.setVerificationMethod( + ExternalBankAccountSetVerificationMethodParams.builder() + .externalBankAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .verificationMethod( + ExternalBankAccountSetVerificationMethodParams + .SetVerificationMethodAllowedVerificationMethods + .MICRO_DEPOSIT + ) + .financialAccountToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + ) + + externalBankAccount.validate() + } + @Test fun unpause() { val client = From 380b3c24bcdd5c98cb1f85c22fcc4d45e19534ab Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 16:35:21 +0000 Subject: [PATCH 3/4] feat(api): Add card/account/business account signals endpoints and behavioral rule attributes --- .stats.yml | 4 +- ...onditionalAuthorizationActionParameters.kt | 683 +++++++++++++++++- .../com/lithic/api/models/RuleFeature.kt | 533 +++++++++++++- .../api/models/TypescriptCodeParameters.kt | 7 + ...tionalAuthorizationActionParametersTest.kt | 41 ++ .../com/lithic/api/models/RuleFeatureTest.kt | 54 ++ 6 files changed, 1315 insertions(+), 7 deletions(-) diff --git a/.stats.yml b/.stats.yml index f542910e..1a1957f0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 191 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-28c9b43d3182bf0e1c9635f6100e4995a744724db1edd635cfd3fc7702ced68c.yml -openapi_spec_hash: aba00a65f877c5095499d9d1a66b5e5f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-d29b68bb85936070878d8badaa8a7c5991313285e70a990bc812c838eba85373.yml +openapi_spec_hash: 54b44da68df22eb0ea99f2bc564667a2 config_hash: ac8326134e692f3f3bdec82396bbec80 diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/ConditionalAuthorizationActionParameters.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/ConditionalAuthorizationActionParameters.kt index b571582e..643ffb57 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/ConditionalAuthorizationActionParameters.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/ConditionalAuthorizationActionParameters.kt @@ -18,6 +18,7 @@ import com.lithic.api.errors.LithicInvalidDataException import java.time.OffsetDateTime import java.util.Collections import java.util.Objects +import java.util.Optional import kotlin.jvm.optionals.getOrNull class ConditionalAuthorizationActionParameters @@ -357,6 +358,7 @@ private constructor( private val attribute: JsonField, private val operation: JsonField, private val value: JsonField, + private val parameters: JsonField, private val additionalProperties: MutableMap, ) { @@ -371,7 +373,10 @@ private constructor( @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), - ) : this(attribute, operation, value, mutableMapOf()) + @JsonProperty("parameters") + @ExcludeMissing + parameters: JsonField = JsonMissing.of(), + ) : this(attribute, operation, value, parameters, mutableMapOf()) /** * The attribute to target. @@ -440,6 +445,31 @@ private constructor( * * `CARD_AGE`: The age of the card in seconds at the time of the authorization. * * `ACCOUNT_AGE`: The age of the account holder's account in seconds at the time of the * authorization. + * * `AMOUNT_Z_SCORE`: The z-score of the transaction amount relative to the entity's + * transaction history. Null if fewer than 30 approved transactions in the specified + * window. Requires `parameters.scope` and `parameters.interval`. + * * `AVG_TRANSACTION_AMOUNT`: The average approved transaction amount for the entity over + * the specified window, in cents. Requires `parameters.scope` and `parameters.interval`. + * * `STDEV_TRANSACTION_AMOUNT`: The standard deviation of approved transaction amounts for + * the entity over the specified window, in cents. Null if fewer than 30 approved + * transactions in the specified window. Requires `parameters.scope` and + * `parameters.interval`. + * * `IS_NEW_COUNTRY`: Whether the transaction's merchant country has not been seen in the + * entity's transaction history. Valid values are `TRUE`, `FALSE`. Requires + * `parameters.scope`. + * * `IS_NEW_MCC`: Whether the transaction's MCC has not been seen in the entity's + * transaction history. Valid values are `TRUE`, `FALSE`. Requires `parameters.scope`. + * * `IS_FIRST_TRANSACTION`: Whether this is the first transaction for the entity. Valid + * values are `TRUE`, `FALSE`. Requires `parameters.scope`. + * * `CONSECUTIVE_DECLINES`: The number of consecutive declined transactions for the entity + * over the last 30 days (rolling). Requires `parameters.scope`. Not supported for + * `BUSINESS_ACCOUNT` scope. + * * `TIME_SINCE_LAST_TRANSACTION`: The number of days since the last approved transaction + * for the entity. Requires `parameters.scope`. + * * `DISTINCT_COUNTRY_COUNT`: The number of distinct merchant countries seen in the + * entity's transaction history. Requires `parameters.scope`. + * * `THREE_DS_SUCCESS_RATE`: The 3DS authentication success rate for the card, as a + * percentage from 0.0 to 100.0. Card-scoped only; no `parameters` required. * * @throws LithicInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -462,6 +492,18 @@ private constructor( */ fun value(): ConditionalValue = value.getRequired("value") + /** + * Additional parameters required for transaction history signal attributes. Required when + * `attribute` is one of `AMOUNT_Z_SCORE`, `AVG_TRANSACTION_AMOUNT`, + * `STDEV_TRANSACTION_AMOUNT`, `IS_NEW_COUNTRY`, `IS_NEW_MCC`, `IS_FIRST_TRANSACTION`, + * `CONSECUTIVE_DECLINES`, `TIME_SINCE_LAST_TRANSACTION`, or `DISTINCT_COUNTRY_COUNT`. Not + * used for other attributes. + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun parameters(): Optional = parameters.getOptional("parameters") + /** * Returns the raw JSON value of [attribute]. * @@ -487,6 +529,15 @@ private constructor( */ @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value + /** + * Returns the raw JSON value of [parameters]. + * + * Unlike [parameters], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("parameters") + @ExcludeMissing + fun _parameters(): JsonField = parameters + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -520,6 +571,7 @@ private constructor( private var attribute: JsonField? = null private var operation: JsonField? = null private var value: JsonField? = null + private var parameters: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -527,6 +579,7 @@ private constructor( attribute = condition.attribute operation = condition.operation value = condition.value + parameters = condition.parameters additionalProperties = condition.additionalProperties.toMutableMap() } @@ -600,6 +653,32 @@ private constructor( * * `CARD_AGE`: The age of the card in seconds at the time of the authorization. * * `ACCOUNT_AGE`: The age of the account holder's account in seconds at the time of * the authorization. + * * `AMOUNT_Z_SCORE`: The z-score of the transaction amount relative to the entity's + * transaction history. Null if fewer than 30 approved transactions in the specified + * window. Requires `parameters.scope` and `parameters.interval`. + * * `AVG_TRANSACTION_AMOUNT`: The average approved transaction amount for the entity + * over the specified window, in cents. Requires `parameters.scope` and + * `parameters.interval`. + * * `STDEV_TRANSACTION_AMOUNT`: The standard deviation of approved transaction amounts + * for the entity over the specified window, in cents. Null if fewer than 30 approved + * transactions in the specified window. Requires `parameters.scope` and + * `parameters.interval`. + * * `IS_NEW_COUNTRY`: Whether the transaction's merchant country has not been seen in + * the entity's transaction history. Valid values are `TRUE`, `FALSE`. Requires + * `parameters.scope`. + * * `IS_NEW_MCC`: Whether the transaction's MCC has not been seen in the entity's + * transaction history. Valid values are `TRUE`, `FALSE`. Requires `parameters.scope`. + * * `IS_FIRST_TRANSACTION`: Whether this is the first transaction for the entity. Valid + * values are `TRUE`, `FALSE`. Requires `parameters.scope`. + * * `CONSECUTIVE_DECLINES`: The number of consecutive declined transactions for the + * entity over the last 30 days (rolling). Requires `parameters.scope`. Not supported + * for `BUSINESS_ACCOUNT` scope. + * * `TIME_SINCE_LAST_TRANSACTION`: The number of days since the last approved + * transaction for the entity. Requires `parameters.scope`. + * * `DISTINCT_COUNTRY_COUNT`: The number of distinct merchant countries seen in the + * entity's transaction history. Requires `parameters.scope`. + * * `THREE_DS_SUCCESS_RATE`: The 3DS authentication success rate for the card, as a + * percentage from 0.0 to 100.0. Card-scoped only; no `parameters` required. */ fun attribute(attribute: Attribute) = attribute(JsonField.of(attribute)) @@ -651,6 +730,26 @@ private constructor( /** Alias for calling [value] with `ConditionalValue.ofTimestamp(timestamp)`. */ fun value(timestamp: OffsetDateTime) = value(ConditionalValue.ofTimestamp(timestamp)) + /** + * Additional parameters required for transaction history signal attributes. Required + * when `attribute` is one of `AMOUNT_Z_SCORE`, `AVG_TRANSACTION_AMOUNT`, + * `STDEV_TRANSACTION_AMOUNT`, `IS_NEW_COUNTRY`, `IS_NEW_MCC`, `IS_FIRST_TRANSACTION`, + * `CONSECUTIVE_DECLINES`, `TIME_SINCE_LAST_TRANSACTION`, or `DISTINCT_COUNTRY_COUNT`. + * Not used for other attributes. + */ + fun parameters(parameters: Parameters) = parameters(JsonField.of(parameters)) + + /** + * Sets [Builder.parameters] to an arbitrary JSON value. + * + * You should usually call [Builder.parameters] with a well-typed [Parameters] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun parameters(parameters: JsonField) = apply { + this.parameters = parameters + } + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -689,6 +788,7 @@ private constructor( checkRequired("attribute", attribute), checkRequired("operation", operation), checkRequired("value", value), + parameters, additionalProperties.toMutableMap(), ) } @@ -703,6 +803,7 @@ private constructor( attribute().validate() operation().validate() value().validate() + parameters().ifPresent { it.validate() } validated = true } @@ -724,7 +825,8 @@ private constructor( internal fun validity(): Int = (attribute.asKnown().getOrNull()?.validity() ?: 0) + (operation.asKnown().getOrNull()?.validity() ?: 0) + - (value.asKnown().getOrNull()?.validity() ?: 0) + (value.asKnown().getOrNull()?.validity() ?: 0) + + (parameters.asKnown().getOrNull()?.validity() ?: 0) /** * The attribute to target. @@ -793,6 +895,31 @@ private constructor( * * `CARD_AGE`: The age of the card in seconds at the time of the authorization. * * `ACCOUNT_AGE`: The age of the account holder's account in seconds at the time of the * authorization. + * * `AMOUNT_Z_SCORE`: The z-score of the transaction amount relative to the entity's + * transaction history. Null if fewer than 30 approved transactions in the specified + * window. Requires `parameters.scope` and `parameters.interval`. + * * `AVG_TRANSACTION_AMOUNT`: The average approved transaction amount for the entity over + * the specified window, in cents. Requires `parameters.scope` and `parameters.interval`. + * * `STDEV_TRANSACTION_AMOUNT`: The standard deviation of approved transaction amounts for + * the entity over the specified window, in cents. Null if fewer than 30 approved + * transactions in the specified window. Requires `parameters.scope` and + * `parameters.interval`. + * * `IS_NEW_COUNTRY`: Whether the transaction's merchant country has not been seen in the + * entity's transaction history. Valid values are `TRUE`, `FALSE`. Requires + * `parameters.scope`. + * * `IS_NEW_MCC`: Whether the transaction's MCC has not been seen in the entity's + * transaction history. Valid values are `TRUE`, `FALSE`. Requires `parameters.scope`. + * * `IS_FIRST_TRANSACTION`: Whether this is the first transaction for the entity. Valid + * values are `TRUE`, `FALSE`. Requires `parameters.scope`. + * * `CONSECUTIVE_DECLINES`: The number of consecutive declined transactions for the entity + * over the last 30 days (rolling). Requires `parameters.scope`. Not supported for + * `BUSINESS_ACCOUNT` scope. + * * `TIME_SINCE_LAST_TRANSACTION`: The number of days since the last approved transaction + * for the entity. Requires `parameters.scope`. + * * `DISTINCT_COUNTRY_COUNT`: The number of distinct merchant countries seen in the + * entity's transaction history. Requires `parameters.scope`. + * * `THREE_DS_SUCCESS_RATE`: The 3DS authentication success rate for the card, as a + * percentage from 0.0 to 100.0. Card-scoped only; no `parameters` required. */ class Attribute @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -861,6 +988,26 @@ private constructor( @JvmField val ACCOUNT_AGE = of("ACCOUNT_AGE") + @JvmField val AMOUNT_Z_SCORE = of("AMOUNT_Z_SCORE") + + @JvmField val AVG_TRANSACTION_AMOUNT = of("AVG_TRANSACTION_AMOUNT") + + @JvmField val STDEV_TRANSACTION_AMOUNT = of("STDEV_TRANSACTION_AMOUNT") + + @JvmField val IS_NEW_COUNTRY = of("IS_NEW_COUNTRY") + + @JvmField val IS_NEW_MCC = of("IS_NEW_MCC") + + @JvmField val IS_FIRST_TRANSACTION = of("IS_FIRST_TRANSACTION") + + @JvmField val CONSECUTIVE_DECLINES = of("CONSECUTIVE_DECLINES") + + @JvmField val TIME_SINCE_LAST_TRANSACTION = of("TIME_SINCE_LAST_TRANSACTION") + + @JvmField val DISTINCT_COUNTRY_COUNT = of("DISTINCT_COUNTRY_COUNT") + + @JvmField val THREE_DS_SUCCESS_RATE = of("THREE_DS_SUCCESS_RATE") + @JvmStatic fun of(value: String) = Attribute(JsonField.of(value)) } @@ -892,6 +1039,16 @@ private constructor( SERVICE_LOCATION_POSTAL_CODE, CARD_AGE, ACCOUNT_AGE, + AMOUNT_Z_SCORE, + AVG_TRANSACTION_AMOUNT, + STDEV_TRANSACTION_AMOUNT, + IS_NEW_COUNTRY, + IS_NEW_MCC, + IS_FIRST_TRANSACTION, + CONSECUTIVE_DECLINES, + TIME_SINCE_LAST_TRANSACTION, + DISTINCT_COUNTRY_COUNT, + THREE_DS_SUCCESS_RATE, } /** @@ -930,6 +1087,16 @@ private constructor( SERVICE_LOCATION_POSTAL_CODE, CARD_AGE, ACCOUNT_AGE, + AMOUNT_Z_SCORE, + AVG_TRANSACTION_AMOUNT, + STDEV_TRANSACTION_AMOUNT, + IS_NEW_COUNTRY, + IS_NEW_MCC, + IS_FIRST_TRANSACTION, + CONSECUTIVE_DECLINES, + TIME_SINCE_LAST_TRANSACTION, + DISTINCT_COUNTRY_COUNT, + THREE_DS_SUCCESS_RATE, /** * An enum member indicating that [Attribute] was instantiated with an unknown * value. @@ -972,6 +1139,16 @@ private constructor( SERVICE_LOCATION_POSTAL_CODE -> Value.SERVICE_LOCATION_POSTAL_CODE CARD_AGE -> Value.CARD_AGE ACCOUNT_AGE -> Value.ACCOUNT_AGE + AMOUNT_Z_SCORE -> Value.AMOUNT_Z_SCORE + AVG_TRANSACTION_AMOUNT -> Value.AVG_TRANSACTION_AMOUNT + STDEV_TRANSACTION_AMOUNT -> Value.STDEV_TRANSACTION_AMOUNT + IS_NEW_COUNTRY -> Value.IS_NEW_COUNTRY + IS_NEW_MCC -> Value.IS_NEW_MCC + IS_FIRST_TRANSACTION -> Value.IS_FIRST_TRANSACTION + CONSECUTIVE_DECLINES -> Value.CONSECUTIVE_DECLINES + TIME_SINCE_LAST_TRANSACTION -> Value.TIME_SINCE_LAST_TRANSACTION + DISTINCT_COUNTRY_COUNT -> Value.DISTINCT_COUNTRY_COUNT + THREE_DS_SUCCESS_RATE -> Value.THREE_DS_SUCCESS_RATE else -> Value._UNKNOWN } @@ -1012,6 +1189,16 @@ private constructor( SERVICE_LOCATION_POSTAL_CODE -> Known.SERVICE_LOCATION_POSTAL_CODE CARD_AGE -> Known.CARD_AGE ACCOUNT_AGE -> Known.ACCOUNT_AGE + AMOUNT_Z_SCORE -> Known.AMOUNT_Z_SCORE + AVG_TRANSACTION_AMOUNT -> Known.AVG_TRANSACTION_AMOUNT + STDEV_TRANSACTION_AMOUNT -> Known.STDEV_TRANSACTION_AMOUNT + IS_NEW_COUNTRY -> Known.IS_NEW_COUNTRY + IS_NEW_MCC -> Known.IS_NEW_MCC + IS_FIRST_TRANSACTION -> Known.IS_FIRST_TRANSACTION + CONSECUTIVE_DECLINES -> Known.CONSECUTIVE_DECLINES + TIME_SINCE_LAST_TRANSACTION -> Known.TIME_SINCE_LAST_TRANSACTION + DISTINCT_COUNTRY_COUNT -> Known.DISTINCT_COUNTRY_COUNT + THREE_DS_SUCCESS_RATE -> Known.THREE_DS_SUCCESS_RATE else -> throw LithicInvalidDataException("Unknown Attribute: $value") } @@ -1069,6 +1256,493 @@ private constructor( override fun toString() = value.toString() } + /** + * Additional parameters required for transaction history signal attributes. Required when + * `attribute` is one of `AMOUNT_Z_SCORE`, `AVG_TRANSACTION_AMOUNT`, + * `STDEV_TRANSACTION_AMOUNT`, `IS_NEW_COUNTRY`, `IS_NEW_MCC`, `IS_FIRST_TRANSACTION`, + * `CONSECUTIVE_DECLINES`, `TIME_SINCE_LAST_TRANSACTION`, or `DISTINCT_COUNTRY_COUNT`. Not + * used for other attributes. + */ + class Parameters + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val interval: JsonField, + private val scope: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("interval") + @ExcludeMissing + interval: JsonField = JsonMissing.of(), + @JsonProperty("scope") @ExcludeMissing scope: JsonField = JsonMissing.of(), + ) : this(interval, scope, mutableMapOf()) + + /** + * The time window for statistical attributes (`AMOUNT_Z_SCORE`, + * `AVG_TRANSACTION_AMOUNT`, `STDEV_TRANSACTION_AMOUNT`). Use `LIFETIME` for all-time + * history or a specific window (`7D`, `30D`, `90D`). + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun interval(): Optional = interval.getOptional("interval") + + /** + * The entity scope to evaluate the attribute against. + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun scope(): Optional = scope.getOptional("scope") + + /** + * Returns the raw JSON value of [interval]. + * + * Unlike [interval], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("interval") + @ExcludeMissing + fun _interval(): JsonField = interval + + /** + * Returns the raw JSON value of [scope]. + * + * Unlike [scope], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("scope") @ExcludeMissing fun _scope(): JsonField = scope + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Parameters]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Parameters]. */ + class Builder internal constructor() { + + private var interval: JsonField = JsonMissing.of() + private var scope: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(parameters: Parameters) = apply { + interval = parameters.interval + scope = parameters.scope + additionalProperties = parameters.additionalProperties.toMutableMap() + } + + /** + * The time window for statistical attributes (`AMOUNT_Z_SCORE`, + * `AVG_TRANSACTION_AMOUNT`, `STDEV_TRANSACTION_AMOUNT`). Use `LIFETIME` for + * all-time history or a specific window (`7D`, `30D`, `90D`). + */ + fun interval(interval: Interval) = interval(JsonField.of(interval)) + + /** + * Sets [Builder.interval] to an arbitrary JSON value. + * + * You should usually call [Builder.interval] with a well-typed [Interval] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun interval(interval: JsonField) = apply { this.interval = interval } + + /** The entity scope to evaluate the attribute against. */ + fun scope(scope: Scope) = scope(JsonField.of(scope)) + + /** + * Sets [Builder.scope] to an arbitrary JSON value. + * + * You should usually call [Builder.scope] with a well-typed [Scope] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun scope(scope: JsonField) = apply { this.scope = scope } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Parameters]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Parameters = + Parameters(interval, scope, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Parameters = apply { + if (validated) { + return@apply + } + + interval().ifPresent { it.validate() } + scope().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (interval.asKnown().getOrNull()?.validity() ?: 0) + + (scope.asKnown().getOrNull()?.validity() ?: 0) + + /** + * The time window for statistical attributes (`AMOUNT_Z_SCORE`, + * `AVG_TRANSACTION_AMOUNT`, `STDEV_TRANSACTION_AMOUNT`). Use `LIFETIME` for all-time + * history or a specific window (`7D`, `30D`, `90D`). + */ + class Interval @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val LIFETIME = of("LIFETIME") + + @JvmField val _7_D = of("7D") + + @JvmField val _30_D = of("30D") + + @JvmField val _90_D = of("90D") + + @JvmStatic fun of(value: String) = Interval(JsonField.of(value)) + } + + /** An enum containing [Interval]'s known values. */ + enum class Known { + LIFETIME, + _7_D, + _30_D, + _90_D, + } + + /** + * An enum containing [Interval]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Interval] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + LIFETIME, + _7_D, + _30_D, + _90_D, + /** + * An enum member indicating that [Interval] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + LIFETIME -> Value.LIFETIME + _7_D -> Value._7_D + _30_D -> Value._30_D + _90_D -> Value._90_D + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws LithicInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + LIFETIME -> Known.LIFETIME + _7_D -> Known._7_D + _30_D -> Known._30_D + _90_D -> Known._90_D + else -> throw LithicInvalidDataException("Unknown Interval: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws LithicInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + LithicInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + fun validate(): Interval = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Interval && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The entity scope to evaluate the attribute against. */ + class Scope @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val CARD = of("CARD") + + @JvmField val ACCOUNT = of("ACCOUNT") + + @JvmField val BUSINESS_ACCOUNT = of("BUSINESS_ACCOUNT") + + @JvmStatic fun of(value: String) = Scope(JsonField.of(value)) + } + + /** An enum containing [Scope]'s known values. */ + enum class Known { + CARD, + ACCOUNT, + BUSINESS_ACCOUNT, + } + + /** + * An enum containing [Scope]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Scope] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CARD, + ACCOUNT, + BUSINESS_ACCOUNT, + /** + * An enum member indicating that [Scope] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CARD -> Value.CARD + ACCOUNT -> Value.ACCOUNT + BUSINESS_ACCOUNT -> Value.BUSINESS_ACCOUNT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws LithicInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + CARD -> Known.CARD + ACCOUNT -> Known.ACCOUNT + BUSINESS_ACCOUNT -> Known.BUSINESS_ACCOUNT + else -> throw LithicInvalidDataException("Unknown Scope: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws LithicInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + LithicInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + fun validate(): Scope = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scope && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Parameters && + interval == other.interval && + scope == other.scope && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(interval, scope, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Parameters{interval=$interval, scope=$scope, additionalProperties=$additionalProperties}" + } + override fun equals(other: Any?): Boolean { if (this === other) { return true @@ -1078,17 +1752,18 @@ private constructor( attribute == other.attribute && operation == other.operation && value == other.value && + parameters == other.parameters && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { - Objects.hash(attribute, operation, value, additionalProperties) + Objects.hash(attribute, operation, value, parameters, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "Condition{attribute=$attribute, operation=$operation, value=$value, additionalProperties=$additionalProperties}" + "Condition{attribute=$attribute, operation=$operation, value=$value, parameters=$parameters, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/RuleFeature.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/RuleFeature.kt index ca83209c..4202f328 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/RuleFeature.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/RuleFeature.kt @@ -50,6 +50,9 @@ import kotlin.jvm.optionals.getOrNull * - `SPEND_VELOCITY`: Spend velocity data for the card or account. Requires `scope`, `period`, and * optionally `filters` to configure the velocity calculation. Available for AUTHORIZATION event * stream rules. + * - `TRANSACTION_HISTORY_SIGNALS`: Behavioral feature state derived from the entity's transaction + * history. Requires `scope` to specify whether to load card, account, or business account + * history. Available for AUTHORIZATION event stream rules. */ @JsonDeserialize(using = RuleFeature.Deserializer::class) @JsonSerialize(using = RuleFeature.Serializer::class) @@ -63,6 +66,7 @@ private constructor( private val accountHolder: AccountHolderFeature? = null, private val ipMetadata: IpMetadataFeature? = null, private val spendVelocity: SpendVelocityFeature? = null, + private val transactionHistorySignals: TransactionHistorySignalsFeature? = null, private val _json: JsonValue? = null, ) { @@ -82,6 +86,9 @@ private constructor( fun spendVelocity(): Optional = Optional.ofNullable(spendVelocity) + fun transactionHistorySignals(): Optional = + Optional.ofNullable(transactionHistorySignals) + fun isAuthorization(): Boolean = authorization != null fun isAuthentication(): Boolean = authentication != null @@ -98,6 +105,8 @@ private constructor( fun isSpendVelocity(): Boolean = spendVelocity != null + fun isTransactionHistorySignals(): Boolean = transactionHistorySignals != null + fun asAuthorization(): AuthorizationFeature = authorization.getOrThrow("authorization") fun asAuthentication(): AuthenticationFeature = authentication.getOrThrow("authentication") @@ -114,6 +123,9 @@ private constructor( fun asSpendVelocity(): SpendVelocityFeature = spendVelocity.getOrThrow("spendVelocity") + fun asTransactionHistorySignals(): TransactionHistorySignalsFeature = + transactionHistorySignals.getOrThrow("transactionHistorySignals") + fun _json(): Optional = Optional.ofNullable(_json) fun accept(visitor: Visitor): T = @@ -126,6 +138,8 @@ private constructor( accountHolder != null -> visitor.visitAccountHolder(accountHolder) ipMetadata != null -> visitor.visitIpMetadata(ipMetadata) spendVelocity != null -> visitor.visitSpendVelocity(spendVelocity) + transactionHistorySignals != null -> + visitor.visitTransactionHistorySignals(transactionHistorySignals) else -> visitor.unknown(_json) } @@ -169,6 +183,12 @@ private constructor( override fun visitSpendVelocity(spendVelocity: SpendVelocityFeature) { spendVelocity.validate() } + + override fun visitTransactionHistorySignals( + transactionHistorySignals: TransactionHistorySignalsFeature + ) { + transactionHistorySignals.validate() + } } ) validated = true @@ -212,6 +232,10 @@ private constructor( override fun visitSpendVelocity(spendVelocity: SpendVelocityFeature) = spendVelocity.validity() + override fun visitTransactionHistorySignals( + transactionHistorySignals: TransactionHistorySignalsFeature + ) = transactionHistorySignals.validity() + override fun unknown(json: JsonValue?) = 0 } ) @@ -229,7 +253,8 @@ private constructor( card == other.card && accountHolder == other.accountHolder && ipMetadata == other.ipMetadata && - spendVelocity == other.spendVelocity + spendVelocity == other.spendVelocity && + transactionHistorySignals == other.transactionHistorySignals } override fun hashCode(): Int = @@ -242,6 +267,7 @@ private constructor( accountHolder, ipMetadata, spendVelocity, + transactionHistorySignals, ) override fun toString(): String = @@ -254,6 +280,8 @@ private constructor( accountHolder != null -> "RuleFeature{accountHolder=$accountHolder}" ipMetadata != null -> "RuleFeature{ipMetadata=$ipMetadata}" spendVelocity != null -> "RuleFeature{spendVelocity=$spendVelocity}" + transactionHistorySignals != null -> + "RuleFeature{transactionHistorySignals=$transactionHistorySignals}" _json != null -> "RuleFeature{_unknown=$_json}" else -> throw IllegalStateException("Invalid RuleFeature") } @@ -287,6 +315,11 @@ private constructor( @JvmStatic fun ofSpendVelocity(spendVelocity: SpendVelocityFeature) = RuleFeature(spendVelocity = spendVelocity) + + @JvmStatic + fun ofTransactionHistorySignals( + transactionHistorySignals: TransactionHistorySignalsFeature + ) = RuleFeature(transactionHistorySignals = transactionHistorySignals) } /** @@ -310,6 +343,10 @@ private constructor( fun visitSpendVelocity(spendVelocity: SpendVelocityFeature): T + fun visitTransactionHistorySignals( + transactionHistorySignals: TransactionHistorySignalsFeature + ): T + /** * Maps an unknown variant of [RuleFeature] to a value of type [T]. * @@ -355,6 +392,8 @@ private constructor( tryDeserialize(node, jacksonTypeRef())?.let { RuleFeature(spendVelocity = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { RuleFeature(transactionHistorySignals = it, _json = json) }, ) .filterNotNull() .allMaxBy { it.validity() } @@ -387,6 +426,8 @@ private constructor( value.accountHolder != null -> generator.writeObject(value.accountHolder) value.ipMetadata != null -> generator.writeObject(value.ipMetadata) value.spendVelocity != null -> generator.writeObject(value.spendVelocity) + value.transactionHistorySignals != null -> + generator.writeObject(value.transactionHistorySignals) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid RuleFeature") } @@ -3178,4 +3219,494 @@ private constructor( override fun toString() = "SpendVelocityFeature{period=$period, scope=$scope, type=$type, filters=$filters, name=$name, additionalProperties=$additionalProperties}" } + + class TransactionHistorySignalsFeature + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val scope: JsonField, + private val type: JsonField, + private val name: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("scope") @ExcludeMissing scope: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + ) : this(scope, type, name, mutableMapOf()) + + /** + * The entity scope to load transaction history signals for. + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun scope(): Scope = scope.getRequired("scope") + + /** + * @throws LithicInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun type(): Type = type.getRequired("type") + + /** + * The variable name for this feature in the rule function signature + * + * @throws LithicInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun name(): Optional = name.getOptional("name") + + /** + * Returns the raw JSON value of [scope]. + * + * Unlike [scope], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("scope") @ExcludeMissing fun _scope(): JsonField = scope + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [TransactionHistorySignalsFeature]. + * + * The following fields are required: + * ```java + * .scope() + * .type() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [TransactionHistorySignalsFeature]. */ + class Builder internal constructor() { + + private var scope: JsonField? = null + private var type: JsonField? = null + private var name: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(transactionHistorySignalsFeature: TransactionHistorySignalsFeature) = + apply { + scope = transactionHistorySignalsFeature.scope + type = transactionHistorySignalsFeature.type + name = transactionHistorySignalsFeature.name + additionalProperties = + transactionHistorySignalsFeature.additionalProperties.toMutableMap() + } + + /** The entity scope to load transaction history signals for. */ + fun scope(scope: Scope) = scope(JsonField.of(scope)) + + /** + * Sets [Builder.scope] to an arbitrary JSON value. + * + * You should usually call [Builder.scope] with a well-typed [Scope] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun scope(scope: JsonField) = apply { this.scope = scope } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + /** The variable name for this feature in the rule function signature */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun name(name: JsonField) = apply { this.name = name } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [TransactionHistorySignalsFeature]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .scope() + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TransactionHistorySignalsFeature = + TransactionHistorySignalsFeature( + checkRequired("scope", scope), + checkRequired("type", type), + name, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): TransactionHistorySignalsFeature = apply { + if (validated) { + return@apply + } + + scope().validate() + type().validate() + name() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (scope.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (name.asKnown().isPresent) 1 else 0) + + /** The entity scope to load transaction history signals for. */ + class Scope @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val CARD = of("CARD") + + @JvmField val ACCOUNT = of("ACCOUNT") + + @JvmField val BUSINESS_ACCOUNT = of("BUSINESS_ACCOUNT") + + @JvmStatic fun of(value: String) = Scope(JsonField.of(value)) + } + + /** An enum containing [Scope]'s known values. */ + enum class Known { + CARD, + ACCOUNT, + BUSINESS_ACCOUNT, + } + + /** + * An enum containing [Scope]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Scope] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CARD, + ACCOUNT, + BUSINESS_ACCOUNT, + /** + * An enum member indicating that [Scope] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CARD -> Value.CARD + ACCOUNT -> Value.ACCOUNT + BUSINESS_ACCOUNT -> Value.BUSINESS_ACCOUNT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws LithicInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + CARD -> Known.CARD + ACCOUNT -> Known.ACCOUNT + BUSINESS_ACCOUNT -> Known.BUSINESS_ACCOUNT + else -> throw LithicInvalidDataException("Unknown Scope: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws LithicInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + LithicInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + fun validate(): Scope = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scope && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val TRANSACTION_HISTORY_SIGNALS = of("TRANSACTION_HISTORY_SIGNALS") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + TRANSACTION_HISTORY_SIGNALS + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + TRANSACTION_HISTORY_SIGNALS, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + TRANSACTION_HISTORY_SIGNALS -> Value.TRANSACTION_HISTORY_SIGNALS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws LithicInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + TRANSACTION_HISTORY_SIGNALS -> Known.TRANSACTION_HISTORY_SIGNALS + else -> throw LithicInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws LithicInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + LithicInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LithicInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionHistorySignalsFeature && + scope == other.scope && + type == other.type && + name == other.name && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(scope, type, name, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TransactionHistorySignalsFeature{scope=$scope, type=$type, name=$name, additionalProperties=$additionalProperties}" + } } diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/TypescriptCodeParameters.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/TypescriptCodeParameters.kt index e0b86cc8..348a2d24 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/TypescriptCodeParameters.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/TypescriptCodeParameters.kt @@ -181,6 +181,13 @@ private constructor( fun addFeature(spendVelocity: RuleFeature.SpendVelocityFeature) = addFeature(RuleFeature.ofSpendVelocity(spendVelocity)) + /** + * Alias for calling [addFeature] with + * `RuleFeature.ofTransactionHistorySignals(transactionHistorySignals)`. + */ + fun addFeature(transactionHistorySignals: RuleFeature.TransactionHistorySignalsFeature) = + addFeature(RuleFeature.ofTransactionHistorySignals(transactionHistorySignals)) + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/models/ConditionalAuthorizationActionParametersTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/models/ConditionalAuthorizationActionParametersTest.kt index d30700c4..7eb7694e 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/models/ConditionalAuthorizationActionParametersTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/models/ConditionalAuthorizationActionParametersTest.kt @@ -19,6 +19,20 @@ internal class ConditionalAuthorizationActionParametersTest { .attribute(ConditionalAuthorizationActionParameters.Condition.Attribute.MCC) .operation(ConditionalOperation.IS_ONE_OF) .value("string") + .parameters( + ConditionalAuthorizationActionParameters.Condition.Parameters.builder() + .interval( + ConditionalAuthorizationActionParameters.Condition.Parameters + .Interval + .LIFETIME + ) + .scope( + ConditionalAuthorizationActionParameters.Condition.Parameters + .Scope + .CARD + ) + .build() + ) .build() ) .build() @@ -31,6 +45,19 @@ internal class ConditionalAuthorizationActionParametersTest { .attribute(ConditionalAuthorizationActionParameters.Condition.Attribute.MCC) .operation(ConditionalOperation.IS_ONE_OF) .value("string") + .parameters( + ConditionalAuthorizationActionParameters.Condition.Parameters.builder() + .interval( + ConditionalAuthorizationActionParameters.Condition.Parameters + .Interval + .LIFETIME + ) + .scope( + ConditionalAuthorizationActionParameters.Condition.Parameters.Scope + .CARD + ) + .build() + ) .build() ) } @@ -46,6 +73,20 @@ internal class ConditionalAuthorizationActionParametersTest { .attribute(ConditionalAuthorizationActionParameters.Condition.Attribute.MCC) .operation(ConditionalOperation.IS_ONE_OF) .value("string") + .parameters( + ConditionalAuthorizationActionParameters.Condition.Parameters.builder() + .interval( + ConditionalAuthorizationActionParameters.Condition.Parameters + .Interval + .LIFETIME + ) + .scope( + ConditionalAuthorizationActionParameters.Condition.Parameters + .Scope + .CARD + ) + .build() + ) .build() ) .build() diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/models/RuleFeatureTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/models/RuleFeatureTest.kt index 08105ce4..140f76ea 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/models/RuleFeatureTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/models/RuleFeatureTest.kt @@ -32,6 +32,7 @@ internal class RuleFeatureTest { assertThat(ruleFeature.accountHolder()).isEmpty assertThat(ruleFeature.ipMetadata()).isEmpty assertThat(ruleFeature.spendVelocity()).isEmpty + assertThat(ruleFeature.transactionHistorySignals()).isEmpty } @Test @@ -72,6 +73,7 @@ internal class RuleFeatureTest { assertThat(ruleFeature.accountHolder()).isEmpty assertThat(ruleFeature.ipMetadata()).isEmpty assertThat(ruleFeature.spendVelocity()).isEmpty + assertThat(ruleFeature.transactionHistorySignals()).isEmpty } @Test @@ -112,6 +114,7 @@ internal class RuleFeatureTest { assertThat(ruleFeature.accountHolder()).isEmpty assertThat(ruleFeature.ipMetadata()).isEmpty assertThat(ruleFeature.spendVelocity()).isEmpty + assertThat(ruleFeature.transactionHistorySignals()).isEmpty } @Test @@ -152,6 +155,7 @@ internal class RuleFeatureTest { assertThat(ruleFeature.accountHolder()).isEmpty assertThat(ruleFeature.ipMetadata()).isEmpty assertThat(ruleFeature.spendVelocity()).isEmpty + assertThat(ruleFeature.transactionHistorySignals()).isEmpty } @Test @@ -192,6 +196,7 @@ internal class RuleFeatureTest { assertThat(ruleFeature.accountHolder()).isEmpty assertThat(ruleFeature.ipMetadata()).isEmpty assertThat(ruleFeature.spendVelocity()).isEmpty + assertThat(ruleFeature.transactionHistorySignals()).isEmpty } @Test @@ -232,6 +237,7 @@ internal class RuleFeatureTest { assertThat(ruleFeature.accountHolder()).contains(accountHolder) assertThat(ruleFeature.ipMetadata()).isEmpty assertThat(ruleFeature.spendVelocity()).isEmpty + assertThat(ruleFeature.transactionHistorySignals()).isEmpty } @Test @@ -272,6 +278,7 @@ internal class RuleFeatureTest { assertThat(ruleFeature.accountHolder()).isEmpty assertThat(ruleFeature.ipMetadata()).contains(ipMetadata) assertThat(ruleFeature.spendVelocity()).isEmpty + assertThat(ruleFeature.transactionHistorySignals()).isEmpty } @Test @@ -328,6 +335,7 @@ internal class RuleFeatureTest { assertThat(ruleFeature.accountHolder()).isEmpty assertThat(ruleFeature.ipMetadata()).isEmpty assertThat(ruleFeature.spendVelocity()).contains(spendVelocity) + assertThat(ruleFeature.transactionHistorySignals()).isEmpty } @Test @@ -368,6 +376,52 @@ internal class RuleFeatureTest { assertThat(roundtrippedRuleFeature).isEqualTo(ruleFeature) } + @Test + fun ofTransactionHistorySignals() { + val transactionHistorySignals = + RuleFeature.TransactionHistorySignalsFeature.builder() + .scope(RuleFeature.TransactionHistorySignalsFeature.Scope.CARD) + .type(RuleFeature.TransactionHistorySignalsFeature.Type.TRANSACTION_HISTORY_SIGNALS) + .name("name") + .build() + + val ruleFeature = RuleFeature.ofTransactionHistorySignals(transactionHistorySignals) + + assertThat(ruleFeature.authorization()).isEmpty + assertThat(ruleFeature.authentication()).isEmpty + assertThat(ruleFeature.tokenization()).isEmpty + assertThat(ruleFeature.achReceipt()).isEmpty + assertThat(ruleFeature.card()).isEmpty + assertThat(ruleFeature.accountHolder()).isEmpty + assertThat(ruleFeature.ipMetadata()).isEmpty + assertThat(ruleFeature.spendVelocity()).isEmpty + assertThat(ruleFeature.transactionHistorySignals()).contains(transactionHistorySignals) + } + + @Test + fun ofTransactionHistorySignalsRoundtrip() { + val jsonMapper = jsonMapper() + val ruleFeature = + RuleFeature.ofTransactionHistorySignals( + RuleFeature.TransactionHistorySignalsFeature.builder() + .scope(RuleFeature.TransactionHistorySignalsFeature.Scope.CARD) + .type( + RuleFeature.TransactionHistorySignalsFeature.Type + .TRANSACTION_HISTORY_SIGNALS + ) + .name("name") + .build() + ) + + val roundtrippedRuleFeature = + jsonMapper.readValue( + jsonMapper.writeValueAsString(ruleFeature), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRuleFeature).isEqualTo(ruleFeature) + } + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { BOOLEAN(JsonValue.from(false)), STRING(JsonValue.from("invalid")), From 3a1dc4c0691c9d0e7926b83b959ec2412fd76b66 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 16:35:56 +0000 Subject: [PATCH 4/4] release: 0.124.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 10 ++++++++++ README.md | 10 +++++----- build.gradle.kts | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 58ebf7f2..cadbcc6f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.123.0" + ".": "0.124.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 62be7384..4dbb06e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.124.0 (2026-04-20) + +Full Changelog: [v0.123.0...v0.124.0](https://github.com/lithic-com/lithic-java/compare/v0.123.0...v0.124.0) + +### Features + +* **api:** add card_authorization.challenge_response webhook event ([d0e83fb](https://github.com/lithic-com/lithic-java/commit/d0e83fb5ae0dc1c1106454f6ee5da677a0fa7608)) +* **api:** Add card/account/business account signals endpoints and behavioral rule attributes ([380b3c2](https://github.com/lithic-com/lithic-java/commit/380b3c24bcdd5c98cb1f85c22fcc4d45e19534ab)) +* **api:** add setVerificationMethod method to external_bank_accounts ([7e086ee](https://github.com/lithic-com/lithic-java/commit/7e086eeea0efed53c022e1f471cb2a5e83dd1427)) + ## 0.123.0 (2026-04-13) Full Changelog: [v0.122.0...v0.123.0](https://github.com/lithic-com/lithic-java/compare/v0.122.0...v0.123.0) diff --git a/README.md b/README.md index b7fe002a..0af7be25 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.lithic.api/lithic-java)](https://central.sonatype.com/artifact/com.lithic.api/lithic-java/0.123.0) -[![javadoc](https://javadoc.io/badge2/com.lithic.api/lithic-java/0.123.0/javadoc.svg)](https://javadoc.io/doc/com.lithic.api/lithic-java/0.123.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.lithic.api/lithic-java)](https://central.sonatype.com/artifact/com.lithic.api/lithic-java/0.124.0) +[![javadoc](https://javadoc.io/badge2/com.lithic.api/lithic-java/0.124.0/javadoc.svg)](https://javadoc.io/doc/com.lithic.api/lithic-java/0.124.0) @@ -22,7 +22,7 @@ Use the Lithic MCP Server to enable AI assistants to interact with this API, all -The REST API documentation can be found on [docs.lithic.com](https://docs.lithic.com). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.lithic.api/lithic-java/0.123.0). +The REST API documentation can be found on [docs.lithic.com](https://docs.lithic.com). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.lithic.api/lithic-java/0.124.0). @@ -33,7 +33,7 @@ The REST API documentation can be found on [docs.lithic.com](https://docs.lithic ### Gradle ```kotlin -implementation("com.lithic.api:lithic-java:0.123.0") +implementation("com.lithic.api:lithic-java:0.124.0") ``` ### Maven @@ -42,7 +42,7 @@ implementation("com.lithic.api:lithic-java:0.123.0") com.lithic.api lithic-java - 0.123.0 + 0.124.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 97b99941..666c0157 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.lithic.api" - version = "0.123.0" // x-release-please-version + version = "0.124.0" // x-release-please-version } subprojects {