diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/Card.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/Card.kt index 5fac9c25..d91d688a 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/Card.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/Card.kt @@ -37,6 +37,8 @@ private constructor( private val lastFour: JsonField, private val memo: JsonField, private val pan: JsonField, + private val pendingCommands: JsonField>, + private val pinStatus: JsonField, private val productId: JsonField, private val spendLimit: JsonField, private val spendLimitDuration: JsonField, @@ -112,6 +114,19 @@ private constructor( */ fun pan(): Optional = Optional.ofNullable(pan.getNullable("pan")) + /** + * Indicates if there are offline PIN changes pending card interaction with an offline PIN + * terminal. Possible commands are: CHANGE_PIN, UNBLOCK_PIN. Applicable only to cards issued in + * markets supporting offline PINs. + */ + fun pendingCommands(): Optional> = + Optional.ofNullable(pendingCommands.getNullable("pending_commands")) + + /** + * Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect attempts). + */ + fun pinStatus(): PinStatus = pinStatus.getRequired("pin_status") + /** * Only applicable to cards of type `PHYSICAL`. This must be configured with Lithic before use. * Specifies the configuration (i.e., physical card art) that the card should be manufactured @@ -237,6 +252,18 @@ private constructor( */ @JsonProperty("pan") @ExcludeMissing fun _pan() = pan + /** + * Indicates if there are offline PIN changes pending card interaction with an offline PIN + * terminal. Possible commands are: CHANGE_PIN, UNBLOCK_PIN. Applicable only to cards issued in + * markets supporting offline PINs. + */ + @JsonProperty("pending_commands") @ExcludeMissing fun _pendingCommands() = pendingCommands + + /** + * Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect attempts). + */ + @JsonProperty("pin_status") @ExcludeMissing fun _pinStatus() = pinStatus + /** * Only applicable to cards of type `PHYSICAL`. This must be configured with Lithic before use. * Specifies the configuration (i.e., physical card art) that the card should be manufactured @@ -320,6 +347,8 @@ private constructor( lastFour() memo() pan() + pendingCommands() + pinStatus() productId() spendLimit() spendLimitDuration() @@ -352,6 +381,8 @@ private constructor( this.lastFour == other.lastFour && this.memo == other.memo && this.pan == other.pan && + this.pendingCommands == other.pendingCommands && + this.pinStatus == other.pinStatus && this.productId == other.productId && this.spendLimit == other.spendLimit && this.spendLimitDuration == other.spendLimitDuration && @@ -379,6 +410,8 @@ private constructor( lastFour, memo, pan, + pendingCommands, + pinStatus, productId, spendLimit, spendLimitDuration, @@ -392,7 +425,7 @@ private constructor( } override fun toString() = - "Card{accountToken=$accountToken, authRuleTokens=$authRuleTokens, cardProgramToken=$cardProgramToken, cardholderCurrency=$cardholderCurrency, created=$created, cvv=$cvv, digitalCardArtToken=$digitalCardArtToken, expMonth=$expMonth, expYear=$expYear, funding=$funding, hostname=$hostname, lastFour=$lastFour, memo=$memo, pan=$pan, productId=$productId, spendLimit=$spendLimit, spendLimitDuration=$spendLimitDuration, state=$state, token=$token, type=$type, additionalProperties=$additionalProperties}" + "Card{accountToken=$accountToken, authRuleTokens=$authRuleTokens, cardProgramToken=$cardProgramToken, cardholderCurrency=$cardholderCurrency, created=$created, cvv=$cvv, digitalCardArtToken=$digitalCardArtToken, expMonth=$expMonth, expYear=$expYear, funding=$funding, hostname=$hostname, lastFour=$lastFour, memo=$memo, pan=$pan, pendingCommands=$pendingCommands, pinStatus=$pinStatus, productId=$productId, spendLimit=$spendLimit, spendLimitDuration=$spendLimitDuration, state=$state, token=$token, type=$type, additionalProperties=$additionalProperties}" companion object { @@ -415,6 +448,8 @@ private constructor( private var lastFour: JsonField = JsonMissing.of() private var memo: JsonField = JsonMissing.of() private var pan: JsonField = JsonMissing.of() + private var pendingCommands: JsonField> = JsonMissing.of() + private var pinStatus: JsonField = JsonMissing.of() private var productId: JsonField = JsonMissing.of() private var spendLimit: JsonField = JsonMissing.of() private var spendLimitDuration: JsonField = JsonMissing.of() @@ -439,6 +474,8 @@ private constructor( this.lastFour = card.lastFour this.memo = card.memo this.pan = card.pan + this.pendingCommands = card.pendingCommands + this.pinStatus = card.pinStatus this.productId = card.productId this.spendLimit = card.spendLimit this.spendLimitDuration = card.spendLimitDuration @@ -608,6 +645,39 @@ private constructor( @ExcludeMissing fun pan(pan: JsonField) = apply { this.pan = pan } + /** + * Indicates if there are offline PIN changes pending card interaction with an offline PIN + * terminal. Possible commands are: CHANGE_PIN, UNBLOCK_PIN. Applicable only to cards issued + * in markets supporting offline PINs. + */ + fun pendingCommands(pendingCommands: List) = + pendingCommands(JsonField.of(pendingCommands)) + + /** + * Indicates if there are offline PIN changes pending card interaction with an offline PIN + * terminal. Possible commands are: CHANGE_PIN, UNBLOCK_PIN. Applicable only to cards issued + * in markets supporting offline PINs. + */ + @JsonProperty("pending_commands") + @ExcludeMissing + fun pendingCommands(pendingCommands: JsonField>) = apply { + this.pendingCommands = pendingCommands + } + + /** + * Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect + * attempts). + */ + fun pinStatus(pinStatus: PinStatus) = pinStatus(JsonField.of(pinStatus)) + + /** + * Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect + * attempts). + */ + @JsonProperty("pin_status") + @ExcludeMissing + fun pinStatus(pinStatus: JsonField) = apply { this.pinStatus = pinStatus } + /** * Only applicable to cards of type `PHYSICAL`. This must be configured with Lithic before * use. Specifies the configuration (i.e., physical card art) that the card should be @@ -779,6 +849,8 @@ private constructor( lastFour, memo, pan, + pendingCommands.map { it.toUnmodifiable() }, + pinStatus, productId, spendLimit, spendLimitDuration, @@ -1214,6 +1286,69 @@ private constructor( } } + class PinStatus + @JsonCreator + private constructor( + private val value: JsonField, + ) : Enum { + + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PinStatus && this.value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + companion object { + + @JvmField val OK = PinStatus(JsonField.of("OK")) + + @JvmField val BLOCKED = PinStatus(JsonField.of("BLOCKED")) + + @JvmField val NOT_SET = PinStatus(JsonField.of("NOT_SET")) + + @JvmStatic fun of(value: String) = PinStatus(JsonField.of(value)) + } + + enum class Known { + OK, + BLOCKED, + NOT_SET, + } + + enum class Value { + OK, + BLOCKED, + NOT_SET, + _UNKNOWN, + } + + fun value(): Value = + when (this) { + OK -> Value.OK + BLOCKED -> Value.BLOCKED + NOT_SET -> Value.NOT_SET + else -> Value._UNKNOWN + } + + fun known(): Known = + when (this) { + OK -> Known.OK + BLOCKED -> Known.BLOCKED + NOT_SET -> Known.NOT_SET + else -> throw LithicInvalidDataException("Unknown PinStatus: $value") + } + + fun asString(): String = _value().asStringOrThrow() + } + class State @JsonCreator private constructor( diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/models/CardUpdateParams.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/models/CardUpdateParams.kt index 08f29d03..81264712 100644 --- a/lithic-java-core/src/main/kotlin/com/lithic/api/models/CardUpdateParams.kt +++ b/lithic-java-core/src/main/kotlin/com/lithic/api/models/CardUpdateParams.kt @@ -24,6 +24,7 @@ constructor( private val digitalCardArtToken: String?, private val memo: String?, private val pin: String?, + private val pinStatus: PinStatus?, private val spendLimit: Long?, private val spendLimitDuration: SpendLimitDuration?, private val state: State?, @@ -40,6 +41,8 @@ constructor( fun pin(): Optional = Optional.ofNullable(pin) + fun pinStatus(): Optional = Optional.ofNullable(pinStatus) + fun spendLimit(): Optional = Optional.ofNullable(spendLimit) fun spendLimitDuration(): Optional = Optional.ofNullable(spendLimitDuration) @@ -52,6 +55,7 @@ constructor( digitalCardArtToken, memo, pin, + pinStatus, spendLimit, spendLimitDuration, state, @@ -77,6 +81,7 @@ constructor( private val digitalCardArtToken: String?, private val memo: String?, private val pin: String?, + private val pinStatus: PinStatus?, private val spendLimit: Long?, private val spendLimitDuration: SpendLimitDuration?, private val state: State?, @@ -102,11 +107,17 @@ constructor( /** * Encrypted PIN block (in base64). Only applies to cards of type `PHYSICAL` and `VIRTUAL`. - * See - * [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + * Changing PIN also resets PIN status to `OK`. See + * [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). */ @JsonProperty("pin") fun pin(): String? = pin + /** + * Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect + * attempts). Can only be set to `OK` to unblock a card. + */ + @JsonProperty("pin_status") fun pinStatus(): PinStatus? = pinStatus + /** * Amount (in cents) to limit approved authorizations. Transaction requests above the spend * limit will be declined. Note that a spend limit of 0 is effectively no limit, and should @@ -153,6 +164,7 @@ constructor( this.digitalCardArtToken == other.digitalCardArtToken && this.memo == other.memo && this.pin == other.pin && + this.pinStatus == other.pinStatus && this.spendLimit == other.spendLimit && this.spendLimitDuration == other.spendLimitDuration && this.state == other.state && @@ -166,6 +178,7 @@ constructor( digitalCardArtToken, memo, pin, + pinStatus, spendLimit, spendLimitDuration, state, @@ -176,7 +189,7 @@ constructor( } override fun toString() = - "CardUpdateBody{digitalCardArtToken=$digitalCardArtToken, memo=$memo, pin=$pin, spendLimit=$spendLimit, spendLimitDuration=$spendLimitDuration, state=$state, additionalProperties=$additionalProperties}" + "CardUpdateBody{digitalCardArtToken=$digitalCardArtToken, memo=$memo, pin=$pin, pinStatus=$pinStatus, spendLimit=$spendLimit, spendLimitDuration=$spendLimitDuration, state=$state, additionalProperties=$additionalProperties}" companion object { @@ -188,6 +201,7 @@ constructor( private var digitalCardArtToken: String? = null private var memo: String? = null private var pin: String? = null + private var pinStatus: PinStatus? = null private var spendLimit: Long? = null private var spendLimitDuration: SpendLimitDuration? = null private var state: State? = null @@ -198,6 +212,7 @@ constructor( this.digitalCardArtToken = cardUpdateBody.digitalCardArtToken this.memo = cardUpdateBody.memo this.pin = cardUpdateBody.pin + this.pinStatus = cardUpdateBody.pinStatus this.spendLimit = cardUpdateBody.spendLimit this.spendLimitDuration = cardUpdateBody.spendLimitDuration this.state = cardUpdateBody.state @@ -223,11 +238,18 @@ constructor( /** * Encrypted PIN block (in base64). Only applies to cards of type `PHYSICAL` and - * `VIRTUAL`. See - * [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + * `VIRTUAL`. Changing PIN also resets PIN status to `OK`. See + * [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). */ @JsonProperty("pin") fun pin(pin: String) = apply { this.pin = pin } + /** + * Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect + * attempts). Can only be set to `OK` to unblock a card. + */ + @JsonProperty("pin_status") + fun pinStatus(pinStatus: PinStatus) = apply { this.pinStatus = pinStatus } + /** * Amount (in cents) to limit approved authorizations. Transaction requests above the * spend limit will be declined. Note that a spend limit of 0 is effectively no limit, @@ -284,6 +306,7 @@ constructor( digitalCardArtToken, memo, pin, + pinStatus, spendLimit, spendLimitDuration, state, @@ -308,6 +331,7 @@ constructor( this.digitalCardArtToken == other.digitalCardArtToken && this.memo == other.memo && this.pin == other.pin && + this.pinStatus == other.pinStatus && this.spendLimit == other.spendLimit && this.spendLimitDuration == other.spendLimitDuration && this.state == other.state && @@ -322,6 +346,7 @@ constructor( digitalCardArtToken, memo, pin, + pinStatus, spendLimit, spendLimitDuration, state, @@ -332,7 +357,7 @@ constructor( } override fun toString() = - "CardUpdateParams{cardToken=$cardToken, digitalCardArtToken=$digitalCardArtToken, memo=$memo, pin=$pin, spendLimit=$spendLimit, spendLimitDuration=$spendLimitDuration, state=$state, additionalQueryParams=$additionalQueryParams, additionalHeaders=$additionalHeaders, additionalBodyProperties=$additionalBodyProperties}" + "CardUpdateParams{cardToken=$cardToken, digitalCardArtToken=$digitalCardArtToken, memo=$memo, pin=$pin, pinStatus=$pinStatus, spendLimit=$spendLimit, spendLimitDuration=$spendLimitDuration, state=$state, additionalQueryParams=$additionalQueryParams, additionalHeaders=$additionalHeaders, additionalBodyProperties=$additionalBodyProperties}" fun toBuilder() = Builder().from(this) @@ -348,6 +373,7 @@ constructor( private var digitalCardArtToken: String? = null private var memo: String? = null private var pin: String? = null + private var pinStatus: PinStatus? = null private var spendLimit: Long? = null private var spendLimitDuration: SpendLimitDuration? = null private var state: State? = null @@ -361,6 +387,7 @@ constructor( this.digitalCardArtToken = cardUpdateParams.digitalCardArtToken this.memo = cardUpdateParams.memo this.pin = cardUpdateParams.pin + this.pinStatus = cardUpdateParams.pinStatus this.spendLimit = cardUpdateParams.spendLimit this.spendLimitDuration = cardUpdateParams.spendLimitDuration this.state = cardUpdateParams.state @@ -389,11 +416,17 @@ constructor( /** * Encrypted PIN block (in base64). Only applies to cards of type `PHYSICAL` and `VIRTUAL`. - * See - * [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + * Changing PIN also resets PIN status to `OK`. See + * [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). */ fun pin(pin: String) = apply { this.pin = pin } + /** + * Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect + * attempts). Can only be set to `OK` to unblock a card. + */ + fun pinStatus(pinStatus: PinStatus) = apply { this.pinStatus = pinStatus } + /** * Amount (in cents) to limit approved authorizations. Transaction requests above the spend * limit will be declined. Note that a spend limit of 0 is effectively no limit, and should @@ -486,6 +519,7 @@ constructor( digitalCardArtToken, memo, pin, + pinStatus, spendLimit, spendLimitDuration, state, @@ -495,6 +529,57 @@ constructor( ) } + class PinStatus + @JsonCreator + private constructor( + private val value: JsonField, + ) : Enum { + + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PinStatus && this.value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + companion object { + + @JvmField val OK = PinStatus(JsonField.of("OK")) + + @JvmStatic fun of(value: String) = PinStatus(JsonField.of(value)) + } + + enum class Known { + OK, + } + + enum class Value { + OK, + _UNKNOWN, + } + + fun value(): Value = + when (this) { + OK -> Value.OK + else -> Value._UNKNOWN + } + + fun known(): Known = + when (this) { + OK -> Known.OK + else -> throw LithicInvalidDataException("Unknown PinStatus: $value") + } + + fun asString(): String = _value().asStringOrThrow() + } + class State @JsonCreator private constructor( diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardTest.kt index 5e17404b..3d84826e 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardTest.kt @@ -28,6 +28,7 @@ class CardTest { .build() ) .lastFour("xxxx") + .pinStatus(Card.PinStatus.OK) .spendLimit(123L) .spendLimitDuration(SpendLimitDuration.ANNUALLY) .state(Card.State.CLOSED) @@ -41,6 +42,7 @@ class CardTest { .hostname("hostname") .memo("New Card") .pan("4111111289144142") + .pendingCommands(listOf("string")) .productId("1") .build() assertThat(card).isNotNull @@ -61,6 +63,7 @@ class CardTest { .build() ) assertThat(card.lastFour()).isEqualTo("xxxx") + assertThat(card.pinStatus()).isEqualTo(Card.PinStatus.OK) assertThat(card.spendLimit()).isEqualTo(123L) assertThat(card.spendLimitDuration()).isEqualTo(SpendLimitDuration.ANNUALLY) assertThat(card.state()).isEqualTo(Card.State.CLOSED) @@ -74,6 +77,7 @@ class CardTest { assertThat(card.hostname()).contains("hostname") assertThat(card.memo()).contains("New Card") assertThat(card.pan()).contains("4111111289144142") + assertThat(card.pendingCommands().get()).containsExactly("string") assertThat(card.productId()).contains("1") } } diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardUpdateParamsTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardUpdateParamsTest.kt index cf3967ea..2f9f35a6 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardUpdateParamsTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/models/CardUpdateParamsTest.kt @@ -15,6 +15,7 @@ class CardUpdateParamsTest { .digitalCardArtToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") .memo("New Card") .pin("pin") + .pinStatus(CardUpdateParams.PinStatus.OK) .spendLimit(123L) .spendLimitDuration(SpendLimitDuration.ANNUALLY) .state(CardUpdateParams.State.CLOSED) @@ -29,6 +30,7 @@ class CardUpdateParamsTest { .digitalCardArtToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") .memo("New Card") .pin("pin") + .pinStatus(CardUpdateParams.PinStatus.OK) .spendLimit(123L) .spendLimitDuration(SpendLimitDuration.ANNUALLY) .state(CardUpdateParams.State.CLOSED) @@ -38,6 +40,7 @@ class CardUpdateParamsTest { assertThat(body.digitalCardArtToken()).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") assertThat(body.memo()).isEqualTo("New Card") assertThat(body.pin()).isEqualTo("pin") + assertThat(body.pinStatus()).isEqualTo(CardUpdateParams.PinStatus.OK) assertThat(body.spendLimit()).isEqualTo(123L) assertThat(body.spendLimitDuration()).isEqualTo(SpendLimitDuration.ANNUALLY) assertThat(body.state()).isEqualTo(CardUpdateParams.State.CLOSED) diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/services/ErrorHandlingTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/services/ErrorHandlingTest.kt index 4f063f04..11aacc8b 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/services/ErrorHandlingTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/services/ErrorHandlingTest.kt @@ -110,6 +110,7 @@ class ErrorHandlingTest { .build() ) .lastFour("xxxx") + .pinStatus(Card.PinStatus.OK) .spendLimit(123L) .spendLimitDuration(SpendLimitDuration.ANNUALLY) .state(Card.State.CLOSED) @@ -123,6 +124,7 @@ class ErrorHandlingTest { .hostname("hostname") .memo("New Card") .pan("4111111289144142") + .pendingCommands(listOf("string")) .productId("1") .build() diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/services/ServiceParamsTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/services/ServiceParamsTest.kt index 50ab7d94..4e1e8290 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/services/ServiceParamsTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/services/ServiceParamsTest.kt @@ -111,6 +111,7 @@ class ServiceParamsTest { .build() ) .lastFour("xxxx") + .pinStatus(Card.PinStatus.OK) .spendLimit(123L) .spendLimitDuration(SpendLimitDuration.ANNUALLY) .state(Card.State.CLOSED) @@ -124,6 +125,7 @@ class ServiceParamsTest { .hostname("hostname") .memo("New Card") .pan("4111111289144142") + .pendingCommands(listOf("string")) .productId("1") .build() diff --git a/lithic-java-core/src/test/kotlin/com/lithic/api/services/blocking/CardServiceTest.kt b/lithic-java-core/src/test/kotlin/com/lithic/api/services/blocking/CardServiceTest.kt index b1cce5c8..eb3782ce 100644 --- a/lithic-java-core/src/test/kotlin/com/lithic/api/services/blocking/CardServiceTest.kt +++ b/lithic-java-core/src/test/kotlin/com/lithic/api/services/blocking/CardServiceTest.kt @@ -93,6 +93,7 @@ class CardServiceTest { .digitalCardArtToken("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") .memo("New Card") .pin("pin") + .pinStatus(CardUpdateParams.PinStatus.OK) .spendLimit(123L) .spendLimitDuration(SpendLimitDuration.ANNUALLY) .state(CardUpdateParams.State.CLOSED)