Skip to content

Commit ec89659

Browse files
feat(api): add endpoints and webhooks for 3DS challenge decisions and challenges (#287)
1 parent 2d496a6 commit ec89659

8 files changed

Lines changed: 488 additions & 1 deletion

File tree

.stats.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
configured_endpoints: 130
1+
configured_endpoints: 131
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
// File generated from our OpenAPI spec by Stainless.
2+
3+
package com.lithic.api.models
4+
5+
import com.fasterxml.jackson.annotation.JsonAnyGetter
6+
import com.fasterxml.jackson.annotation.JsonAnySetter
7+
import com.fasterxml.jackson.annotation.JsonCreator
8+
import com.fasterxml.jackson.annotation.JsonProperty
9+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
10+
import com.lithic.api.core.Enum
11+
import com.lithic.api.core.ExcludeMissing
12+
import com.lithic.api.core.JsonField
13+
import com.lithic.api.core.JsonValue
14+
import com.lithic.api.core.NoAutoDetect
15+
import com.lithic.api.core.toUnmodifiable
16+
import com.lithic.api.errors.LithicInvalidDataException
17+
import com.lithic.api.models.*
18+
import java.util.Objects
19+
20+
class ThreeDSDecisioningChallengeResponseParams
21+
constructor(
22+
private val token: String,
23+
private val challengeResponse: ChallengeResponse,
24+
private val additionalQueryParams: Map<String, List<String>>,
25+
private val additionalHeaders: Map<String, List<String>>,
26+
private val additionalBodyProperties: Map<String, JsonValue>,
27+
) {
28+
29+
fun token(): String = token
30+
31+
fun challengeResponse(): ChallengeResponse = challengeResponse
32+
33+
@JvmSynthetic
34+
internal fun getBody(): ThreeDSDecisioningChallengeResponseBody {
35+
return ThreeDSDecisioningChallengeResponseBody(
36+
token,
37+
challengeResponse,
38+
additionalBodyProperties,
39+
)
40+
}
41+
42+
@JvmSynthetic internal fun getQueryParams(): Map<String, List<String>> = additionalQueryParams
43+
44+
@JvmSynthetic internal fun getHeaders(): Map<String, List<String>> = additionalHeaders
45+
46+
@JsonDeserialize(builder = ThreeDSDecisioningChallengeResponseBody.Builder::class)
47+
@NoAutoDetect
48+
class ThreeDSDecisioningChallengeResponseBody
49+
internal constructor(
50+
private val token: String?,
51+
private val challengeResponse: ChallengeResponse?,
52+
private val additionalProperties: Map<String, JsonValue>,
53+
) {
54+
55+
private var hashCode: Int = 0
56+
57+
/**
58+
* Globally unique identifier for the 3DS authentication. This token is sent as part of the
59+
* initial 3DS Decisioning Request and as part of the 3DS Challenge Event in the
60+
* [ThreeDSAuthentication](#/components/schemas/ThreeDSAuthentication) object
61+
*/
62+
@JsonProperty("token") fun token(): String? = token
63+
64+
/** Whether the Cardholder has Approved or Declined the issued Challenge */
65+
@JsonProperty("challenge_response")
66+
fun challengeResponse(): ChallengeResponse? = challengeResponse
67+
68+
@JsonAnyGetter
69+
@ExcludeMissing
70+
fun _additionalProperties(): Map<String, JsonValue> = additionalProperties
71+
72+
fun toBuilder() = Builder().from(this)
73+
74+
override fun equals(other: Any?): Boolean {
75+
if (this === other) {
76+
return true
77+
}
78+
79+
return other is ThreeDSDecisioningChallengeResponseBody &&
80+
this.token == other.token &&
81+
this.challengeResponse == other.challengeResponse &&
82+
this.additionalProperties == other.additionalProperties
83+
}
84+
85+
override fun hashCode(): Int {
86+
if (hashCode == 0) {
87+
hashCode =
88+
Objects.hash(
89+
token,
90+
challengeResponse,
91+
additionalProperties,
92+
)
93+
}
94+
return hashCode
95+
}
96+
97+
override fun toString() =
98+
"ThreeDSDecisioningChallengeResponseBody{token=$token, challengeResponse=$challengeResponse, additionalProperties=$additionalProperties}"
99+
100+
companion object {
101+
102+
@JvmStatic fun builder() = Builder()
103+
}
104+
105+
class Builder {
106+
107+
private var token: String? = null
108+
private var challengeResponse: ChallengeResponse? = null
109+
private var additionalProperties: MutableMap<String, JsonValue> = mutableMapOf()
110+
111+
@JvmSynthetic
112+
internal fun from(
113+
threeDSDecisioningChallengeResponseBody: ThreeDSDecisioningChallengeResponseBody
114+
) = apply {
115+
this.token = threeDSDecisioningChallengeResponseBody.token
116+
this.challengeResponse = threeDSDecisioningChallengeResponseBody.challengeResponse
117+
additionalProperties(threeDSDecisioningChallengeResponseBody.additionalProperties)
118+
}
119+
120+
/**
121+
* Globally unique identifier for the 3DS authentication. This token is sent as part of
122+
* the initial 3DS Decisioning Request and as part of the 3DS Challenge Event in the
123+
* [ThreeDSAuthentication](#/components/schemas/ThreeDSAuthentication) object
124+
*/
125+
@JsonProperty("token") fun token(token: String) = apply { this.token = token }
126+
127+
/** Whether the Cardholder has Approved or Declined the issued Challenge */
128+
@JsonProperty("challenge_response")
129+
fun challengeResponse(challengeResponse: ChallengeResponse) = apply {
130+
this.challengeResponse = challengeResponse
131+
}
132+
133+
fun additionalProperties(additionalProperties: Map<String, JsonValue>) = apply {
134+
this.additionalProperties.clear()
135+
this.additionalProperties.putAll(additionalProperties)
136+
}
137+
138+
@JsonAnySetter
139+
fun putAdditionalProperty(key: String, value: JsonValue) = apply {
140+
this.additionalProperties.put(key, value)
141+
}
142+
143+
fun putAllAdditionalProperties(additionalProperties: Map<String, JsonValue>) = apply {
144+
this.additionalProperties.putAll(additionalProperties)
145+
}
146+
147+
fun build(): ThreeDSDecisioningChallengeResponseBody =
148+
ThreeDSDecisioningChallengeResponseBody(
149+
checkNotNull(token) { "`token` is required but was not set" },
150+
checkNotNull(challengeResponse) {
151+
"`challengeResponse` is required but was not set"
152+
},
153+
additionalProperties.toUnmodifiable(),
154+
)
155+
}
156+
}
157+
158+
fun _additionalQueryParams(): Map<String, List<String>> = additionalQueryParams
159+
160+
fun _additionalHeaders(): Map<String, List<String>> = additionalHeaders
161+
162+
fun _additionalBodyProperties(): Map<String, JsonValue> = additionalBodyProperties
163+
164+
override fun equals(other: Any?): Boolean {
165+
if (this === other) {
166+
return true
167+
}
168+
169+
return other is ThreeDSDecisioningChallengeResponseParams &&
170+
this.token == other.token &&
171+
this.challengeResponse == other.challengeResponse &&
172+
this.additionalQueryParams == other.additionalQueryParams &&
173+
this.additionalHeaders == other.additionalHeaders &&
174+
this.additionalBodyProperties == other.additionalBodyProperties
175+
}
176+
177+
override fun hashCode(): Int {
178+
return Objects.hash(
179+
token,
180+
challengeResponse,
181+
additionalQueryParams,
182+
additionalHeaders,
183+
additionalBodyProperties,
184+
)
185+
}
186+
187+
override fun toString() =
188+
"ThreeDSDecisioningChallengeResponseParams{token=$token, challengeResponse=$challengeResponse, additionalQueryParams=$additionalQueryParams, additionalHeaders=$additionalHeaders, additionalBodyProperties=$additionalBodyProperties}"
189+
190+
fun toBuilder() = Builder().from(this)
191+
192+
companion object {
193+
194+
@JvmStatic fun builder() = Builder()
195+
}
196+
197+
@NoAutoDetect
198+
class Builder {
199+
200+
private var token: String? = null
201+
private var challengeResponse: ChallengeResponse? = null
202+
private var additionalQueryParams: MutableMap<String, MutableList<String>> = mutableMapOf()
203+
private var additionalHeaders: MutableMap<String, MutableList<String>> = mutableMapOf()
204+
private var additionalBodyProperties: MutableMap<String, JsonValue> = mutableMapOf()
205+
206+
@JvmSynthetic
207+
internal fun from(
208+
threeDSDecisioningChallengeResponseParams: ThreeDSDecisioningChallengeResponseParams
209+
) = apply {
210+
this.token = threeDSDecisioningChallengeResponseParams.token
211+
this.challengeResponse = threeDSDecisioningChallengeResponseParams.challengeResponse
212+
additionalQueryParams(threeDSDecisioningChallengeResponseParams.additionalQueryParams)
213+
additionalHeaders(threeDSDecisioningChallengeResponseParams.additionalHeaders)
214+
additionalBodyProperties(
215+
threeDSDecisioningChallengeResponseParams.additionalBodyProperties
216+
)
217+
}
218+
219+
/**
220+
* Globally unique identifier for the 3DS authentication. This token is sent as part of the
221+
* initial 3DS Decisioning Request and as part of the 3DS Challenge Event in the
222+
* [ThreeDSAuthentication](#/components/schemas/ThreeDSAuthentication) object
223+
*/
224+
fun token(token: String) = apply { this.token = token }
225+
226+
/** Whether the Cardholder has Approved or Declined the issued Challenge */
227+
fun challengeResponse(challengeResponse: ChallengeResponse) = apply {
228+
this.challengeResponse = challengeResponse
229+
}
230+
231+
fun additionalQueryParams(additionalQueryParams: Map<String, List<String>>) = apply {
232+
this.additionalQueryParams.clear()
233+
putAllQueryParams(additionalQueryParams)
234+
}
235+
236+
fun putQueryParam(name: String, value: String) = apply {
237+
this.additionalQueryParams.getOrPut(name) { mutableListOf() }.add(value)
238+
}
239+
240+
fun putQueryParams(name: String, values: Iterable<String>) = apply {
241+
this.additionalQueryParams.getOrPut(name) { mutableListOf() }.addAll(values)
242+
}
243+
244+
fun putAllQueryParams(additionalQueryParams: Map<String, Iterable<String>>) = apply {
245+
additionalQueryParams.forEach(this::putQueryParams)
246+
}
247+
248+
fun removeQueryParam(name: String) = apply {
249+
this.additionalQueryParams.put(name, mutableListOf())
250+
}
251+
252+
fun additionalHeaders(additionalHeaders: Map<String, Iterable<String>>) = apply {
253+
this.additionalHeaders.clear()
254+
putAllHeaders(additionalHeaders)
255+
}
256+
257+
fun putHeader(name: String, value: String) = apply {
258+
this.additionalHeaders.getOrPut(name) { mutableListOf() }.add(value)
259+
}
260+
261+
fun putHeaders(name: String, values: Iterable<String>) = apply {
262+
this.additionalHeaders.getOrPut(name) { mutableListOf() }.addAll(values)
263+
}
264+
265+
fun putAllHeaders(additionalHeaders: Map<String, Iterable<String>>) = apply {
266+
additionalHeaders.forEach(this::putHeaders)
267+
}
268+
269+
fun removeHeader(name: String) = apply { this.additionalHeaders.put(name, mutableListOf()) }
270+
271+
fun additionalBodyProperties(additionalBodyProperties: Map<String, JsonValue>) = apply {
272+
this.additionalBodyProperties.clear()
273+
this.additionalBodyProperties.putAll(additionalBodyProperties)
274+
}
275+
276+
fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply {
277+
this.additionalBodyProperties.put(key, value)
278+
}
279+
280+
fun putAllAdditionalBodyProperties(additionalBodyProperties: Map<String, JsonValue>) =
281+
apply {
282+
this.additionalBodyProperties.putAll(additionalBodyProperties)
283+
}
284+
285+
fun build(): ThreeDSDecisioningChallengeResponseParams =
286+
ThreeDSDecisioningChallengeResponseParams(
287+
checkNotNull(token) { "`token` is required but was not set" },
288+
checkNotNull(challengeResponse) {
289+
"`challengeResponse` is required but was not set"
290+
},
291+
additionalQueryParams.mapValues { it.value.toUnmodifiable() }.toUnmodifiable(),
292+
additionalHeaders.mapValues { it.value.toUnmodifiable() }.toUnmodifiable(),
293+
additionalBodyProperties.toUnmodifiable(),
294+
)
295+
}
296+
297+
class ChallengeResponse
298+
@JsonCreator
299+
private constructor(
300+
private val value: JsonField<String>,
301+
) : Enum {
302+
303+
@com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField<String> = value
304+
305+
override fun equals(other: Any?): Boolean {
306+
if (this === other) {
307+
return true
308+
}
309+
310+
return other is ChallengeResponse && this.value == other.value
311+
}
312+
313+
override fun hashCode() = value.hashCode()
314+
315+
override fun toString() = value.toString()
316+
317+
companion object {
318+
319+
@JvmField val APPROVE = ChallengeResponse(JsonField.of("APPROVE"))
320+
321+
@JvmField
322+
val DECLINE_BY_CUSTOMER = ChallengeResponse(JsonField.of("DECLINE_BY_CUSTOMER"))
323+
324+
@JvmStatic fun of(value: String) = ChallengeResponse(JsonField.of(value))
325+
}
326+
327+
enum class Known {
328+
APPROVE,
329+
DECLINE_BY_CUSTOMER,
330+
}
331+
332+
enum class Value {
333+
APPROVE,
334+
DECLINE_BY_CUSTOMER,
335+
_UNKNOWN,
336+
}
337+
338+
fun value(): Value =
339+
when (this) {
340+
APPROVE -> Value.APPROVE
341+
DECLINE_BY_CUSTOMER -> Value.DECLINE_BY_CUSTOMER
342+
else -> Value._UNKNOWN
343+
}
344+
345+
fun known(): Known =
346+
when (this) {
347+
APPROVE -> Known.APPROVE
348+
DECLINE_BY_CUSTOMER -> Known.DECLINE_BY_CUSTOMER
349+
else -> throw LithicInvalidDataException("Unknown ChallengeResponse: $value")
350+
}
351+
352+
fun asString(): String = _value().asStringOrThrow()
353+
}
354+
}

lithic-java-core/src/main/kotlin/com/lithic/api/services/async/threeDS/DecisioningServiceAsync.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@ package com.lithic.api.services.async.threeDS
66

77
import com.lithic.api.core.RequestOptions
88
import com.lithic.api.models.DecisioningRetrieveSecretResponse
9+
import com.lithic.api.models.ThreeDSDecisioningChallengeResponseParams
910
import com.lithic.api.models.ThreeDSDecisioningRetrieveSecretParams
1011
import com.lithic.api.models.ThreeDSDecisioningRotateSecretParams
1112
import java.util.concurrent.CompletableFuture
1213

1314
interface DecisioningServiceAsync {
1415

16+
/** Card program's response to a 3DS Challenge Request (CReq) */
17+
@JvmOverloads
18+
fun challengeResponse(
19+
params: ThreeDSDecisioningChallengeResponseParams,
20+
requestOptions: RequestOptions = RequestOptions.none()
21+
): CompletableFuture<Void>
22+
1523
/**
1624
* Retrieve the 3DS Decisioning HMAC secret key. If one does not exist for your program yet,
1725
* calling this endpoint will create one for you. The headers (which you can use to verify 3DS

0 commit comments

Comments
 (0)