From ee870a039a548a5c13dd05d5757c72634b744e69 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:49:08 +0000 Subject: [PATCH 1/8] chore: make `Properties` more resilient to `null` --- .../src/main/kotlin/com/increase/api/core/Properties.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/increase-java-core/src/main/kotlin/com/increase/api/core/Properties.kt b/increase-java-core/src/main/kotlin/com/increase/api/core/Properties.kt index a753e8340..f3e51edcf 100644 --- a/increase-java-core/src/main/kotlin/com/increase/api/core/Properties.kt +++ b/increase-java-core/src/main/kotlin/com/increase/api/core/Properties.kt @@ -34,9 +34,9 @@ fun getOsName(): String { } } -fun getOsVersion(): String = System.getProperty("os.version", "unknown") +fun getOsVersion(): String = System.getProperty("os.version", "unknown") ?: "unknown" fun getPackageVersion(): String = - IncreaseClient::class.java.`package`.implementationVersion ?: "unknown" + IncreaseClient::class.java.`package`?.implementationVersion ?: "unknown" -fun getJavaVersion(): String = System.getProperty("java.version", "unknown") +fun getJavaVersion(): String = System.getProperty("java.version", "unknown") ?: "unknown" From 12df111e728f51766ffc2de3721453745a869339 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 04:59:26 +0000 Subject: [PATCH 2/8] chore: drop apache dependency --- increase-java-core/build.gradle.kts | 2 - .../api/core/http/HttpRequestBodies.kt | 295 +++++-- .../api/core/http/HttpRequestBodiesTest.kt | 729 ++++++++++++++++++ 3 files changed, 946 insertions(+), 80 deletions(-) create mode 100644 increase-java-core/src/test/kotlin/com/increase/api/core/http/HttpRequestBodiesTest.kt diff --git a/increase-java-core/build.gradle.kts b/increase-java-core/build.gradle.kts index b23d01548..4bc845bc9 100644 --- a/increase-java-core/build.gradle.kts +++ b/increase-java-core/build.gradle.kts @@ -28,8 +28,6 @@ dependencies { implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2") - implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4") - implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1") implementation("com.google.guava:guava:33.0.0-jre") testImplementation(kotlin("test")) diff --git a/increase-java-core/src/main/kotlin/com/increase/api/core/http/HttpRequestBodies.kt b/increase-java-core/src/main/kotlin/com/increase/api/core/http/HttpRequestBodies.kt index 19fbddc53..df50668ae 100644 --- a/increase-java-core/src/main/kotlin/com/increase/api/core/http/HttpRequestBodies.kt +++ b/increase-java-core/src/main/kotlin/com/increase/api/core/http/HttpRequestBodies.kt @@ -8,13 +8,13 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.node.JsonNodeType import com.increase.api.core.MultipartField +import com.increase.api.core.toImmutable import com.increase.api.errors.IncreaseInvalidDataException +import java.io.ByteArrayInputStream import java.io.InputStream import java.io.OutputStream +import java.util.UUID import kotlin.jvm.optionals.getOrNull -import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder -import org.apache.hc.core5.http.ContentType -import org.apache.hc.core5.http.HttpEntity @JvmSynthetic internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody = @@ -37,92 +37,231 @@ internal fun multipartFormData( jsonMapper: JsonMapper, fields: Map>, ): HttpRequestBody = - object : HttpRequestBody { - private val entity: HttpEntity by lazy { - MultipartEntityBuilder.create() - .apply { - fields.forEach { (name, field) -> - val knownValue = field.value.asKnown().getOrNull() - val parts = - if (knownValue is InputStream) { - // Read directly from the `InputStream` instead of reading it all - // into memory due to the `jsonMapper` serialization below. - sequenceOf(name to knownValue) - } else { - val node = jsonMapper.valueToTree(field.value) - serializePart(name, node) + MultipartBody.Builder() + .apply { + fields.forEach { (name, field) -> + val knownValue = field.value.asKnown().getOrNull() + val parts = + if (knownValue is InputStream) { + // Read directly from the `InputStream` instead of reading it all + // into memory due to the `jsonMapper` serialization below. + sequenceOf(name to knownValue) + } else { + val node = jsonMapper.valueToTree(field.value) + serializePart(name, node) + } + + parts.forEach { (name, bytes) -> + val partBody = + if (bytes is ByteArrayInputStream) { + val byteArray = bytes.readBytes() + + object : HttpRequestBody { + + override fun writeTo(outputStream: OutputStream) { + outputStream.write(byteArray) + } + + override fun contentType(): String = field.contentType + + override fun contentLength(): Long = byteArray.size.toLong() + + override fun repeatable(): Boolean = true + + override fun close() {} } + } else { + object : HttpRequestBody { + + override fun writeTo(outputStream: OutputStream) { + bytes.copyTo(outputStream) + } + + override fun contentType(): String = field.contentType + + override fun contentLength(): Long = -1L - parts.forEach { (name, bytes) -> - addBinaryBody( - name, - bytes, - ContentType.parseLenient(field.contentType), - field.filename().getOrNull(), - ) + override fun repeatable(): Boolean = false + + override fun close() = bytes.close() + } } - } + + addPart( + MultipartBody.Part.create( + name, + field.filename().getOrNull(), + field.contentType, + partBody, + ) + ) } - .build() + } } + .build() - private fun serializePart( - name: String, - node: JsonNode, - ): Sequence> = - when (node.nodeType) { - JsonNodeType.MISSING, - JsonNodeType.NULL -> emptySequence() - JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream()) - JsonNodeType.STRING -> sequenceOf(name to node.textValue().inputStream()) - JsonNodeType.BOOLEAN -> - sequenceOf(name to node.booleanValue().toString().inputStream()) - JsonNodeType.NUMBER -> - sequenceOf(name to node.numberValue().toString().inputStream()) - JsonNodeType.ARRAY -> - sequenceOf( - name to - node - .elements() - .asSequence() - .mapNotNull { element -> - when (element.nodeType) { - JsonNodeType.MISSING, - JsonNodeType.NULL -> null - JsonNodeType.STRING -> node.textValue() - JsonNodeType.BOOLEAN -> node.booleanValue().toString() - JsonNodeType.NUMBER -> node.numberValue().toString() - null, - JsonNodeType.BINARY, - JsonNodeType.ARRAY, - JsonNodeType.OBJECT, - JsonNodeType.POJO -> - throw IncreaseInvalidDataException( - "Unexpected JsonNode type in array: ${node.nodeType}" - ) - } - } - .joinToString(",") - .inputStream() - ) - JsonNodeType.OBJECT -> - node.fields().asSequence().flatMap { (key, value) -> - serializePart("$name.$key", value) - } - JsonNodeType.POJO, - null -> - throw IncreaseInvalidDataException("Unexpected JsonNode type: ${node.nodeType}") +private fun serializePart(name: String, node: JsonNode): Sequence> = + when (node.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> emptySequence() + JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream()) + JsonNodeType.STRING -> sequenceOf(name to node.textValue().byteInputStream()) + JsonNodeType.BOOLEAN -> sequenceOf(name to node.booleanValue().toString().byteInputStream()) + JsonNodeType.NUMBER -> sequenceOf(name to node.numberValue().toString().byteInputStream()) + JsonNodeType.ARRAY -> + sequenceOf( + name to + node + .elements() + .asSequence() + .mapNotNull { element -> + when (element.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> null + JsonNodeType.STRING -> element.textValue() + JsonNodeType.BOOLEAN -> element.booleanValue().toString() + JsonNodeType.NUMBER -> element.numberValue().toString() + null, + JsonNodeType.BINARY, + JsonNodeType.ARRAY, + JsonNodeType.OBJECT, + JsonNodeType.POJO -> + throw IncreaseInvalidDataException( + "Unexpected JsonNode type in array: ${element.nodeType}" + ) + } + } + .joinToString(",") + .byteInputStream() + ) + JsonNodeType.OBJECT -> + node.fields().asSequence().flatMap { (key, value) -> + serializePart("$name.$key", value) + } + JsonNodeType.POJO, + null -> throw IncreaseInvalidDataException("Unexpected JsonNode type: ${node.nodeType}") + } + +private class MultipartBody +private constructor(private val boundary: String, private val parts: List) : HttpRequestBody { + private val boundaryBytes: ByteArray = boundary.toByteArray() + private val contentType = "multipart/form-data; boundary=$boundary" + + // This must remain in sync with `contentLength`. + override fun writeTo(outputStream: OutputStream) { + parts.forEach { part -> + outputStream.write(DASHDASH) + outputStream.write(boundaryBytes) + outputStream.write(CRLF) + + outputStream.write(CONTENT_DISPOSITION) + outputStream.write(part.contentDisposition.toByteArray()) + outputStream.write(CRLF) + + outputStream.write(CONTENT_TYPE) + outputStream.write(part.contentType.toByteArray()) + outputStream.write(CRLF) + + outputStream.write(CRLF) + part.body.writeTo(outputStream) + outputStream.write(CRLF) + } + + outputStream.write(DASHDASH) + outputStream.write(boundaryBytes) + outputStream.write(DASHDASH) + outputStream.write(CRLF) + } + + override fun contentType(): String = contentType + + // This must remain in sync with `writeTo`. + override fun contentLength(): Long { + var byteCount = 0L + + parts.forEach { part -> + val contentLength = part.body.contentLength() + if (contentLength == -1L) { + return -1L } - private fun String.inputStream(): InputStream = toByteArray().inputStream() + byteCount += + DASHDASH.size + + boundaryBytes.size + + CRLF.size + + CONTENT_DISPOSITION.size + + part.contentDisposition.toByteArray().size + + CRLF.size + + CONTENT_TYPE.size + + part.contentType.toByteArray().size + + CRLF.size + + CRLF.size + + contentLength + + CRLF.size + } - override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream) + byteCount += DASHDASH.size + boundaryBytes.size + DASHDASH.size + CRLF.size + return byteCount + } - override fun contentType(): String = entity.contentType + override fun repeatable(): Boolean = parts.all { it.body.repeatable() } - override fun contentLength(): Long = entity.contentLength + override fun close() { + parts.forEach { it.body.close() } + } - override fun repeatable(): Boolean = entity.isRepeatable + class Builder { + private val boundary = UUID.randomUUID().toString() + private val parts: MutableList = mutableListOf() - override fun close() = entity.close() + fun addPart(part: Part) = apply { parts.add(part) } + + fun build() = MultipartBody(boundary, parts.toImmutable()) + } + + class Part + private constructor( + val contentDisposition: String, + val contentType: String, + val body: HttpRequestBody, + ) { + companion object { + fun create( + name: String, + filename: String?, + contentType: String, + body: HttpRequestBody, + ): Part { + val disposition = buildString { + append("form-data; name=") + appendQuotedString(name) + if (filename != null) { + append("; filename=") + appendQuotedString(filename) + } + } + return Part(disposition, contentType, body) + } + } + } + + companion object { + private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte()) + private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte()) + private val CONTENT_DISPOSITION = "Content-Disposition: ".toByteArray() + private val CONTENT_TYPE = "Content-Type: ".toByteArray() + + private fun StringBuilder.appendQuotedString(key: String) { + append('"') + for (ch in key) { + when (ch) { + '\n' -> append("%0A") + '\r' -> append("%0D") + '"' -> append("%22") + else -> append(ch) + } + } + append('"') + } } +} diff --git a/increase-java-core/src/test/kotlin/com/increase/api/core/http/HttpRequestBodiesTest.kt b/increase-java-core/src/test/kotlin/com/increase/api/core/http/HttpRequestBodiesTest.kt new file mode 100644 index 000000000..0b5b83440 --- /dev/null +++ b/increase-java-core/src/test/kotlin/com/increase/api/core/http/HttpRequestBodiesTest.kt @@ -0,0 +1,729 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.increase.api.core.http + +import com.increase.api.core.MultipartField +import com.increase.api.core.jsonMapper +import java.io.ByteArrayOutputStream +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class HttpRequestBodiesTest { + + @Test + fun multipartFormData_serializesFieldWithFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "file" to + MultipartField.builder() + .value("hello") + .filename("hello.txt") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(output.size().toLong()).isEqualTo(body.contentLength()) + val boundary = body.contentType()!!.substringAfter("multipart/form-data; boundary=") + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="file"; filename="hello.txt" + |Content-Type: text/plain + | + |hello + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesFieldWithoutFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field" to + MultipartField.builder() + .value("value") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(output.size().toLong()).isEqualTo(body.contentLength()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="field" + |Content-Type: text/plain + | + |value + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesInputStream() { + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the non-repeatable code + // path. + val inputStream = "stream content".byteInputStream().buffered() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder() + .value(inputStream) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data" + |Content-Type: application/octet-stream + | + |stream content + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesByteArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "binary" to + MultipartField.builder() + .value("abc".toByteArray()) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="binary" + |Content-Type: application/octet-stream + | + |abc + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesBooleanValue() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "flag" to + MultipartField.builder() + .value(true) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="flag" + |Content-Type: text/plain + | + |true + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNumberValue() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "count" to + MultipartField.builder().value(42).contentType("text/plain").build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="count" + |Content-Type: text/plain + | + |42 + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNullValueAsNoParts() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "present" to + MultipartField.builder() + .value("yes") + .contentType("text/plain") + .build(), + "absent" to + MultipartField.builder() + .value(null as String?) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="present" + |Content-Type: text/plain + | + |yes + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "items" to + MultipartField.builder>() + .value(listOf("alpha", "beta", "gamma")) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="items" + |Content-Type: text/plain + | + |alpha,beta,gamma + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesObjectAsNestedParts() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "meta" to + MultipartField.builder>() + .value(mapOf("key1" to "val1", "key2" to "val2")) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="meta.key1" + |Content-Type: text/plain + | + |val1 + |--$boundary + |Content-Disposition: form-data; name="meta.key2" + |Content-Type: text/plain + | + |val2 + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesMultipleFields() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "name" to + MultipartField.builder() + .value("Alice") + .contentType("text/plain") + .build(), + "age" to + MultipartField.builder().value(30).contentType("text/plain").build(), + "file" to + MultipartField.builder() + .value("file contents") + .filename("doc.txt") + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="name" + |Content-Type: text/plain + | + |Alice + |--$boundary + |Content-Disposition: form-data; name="age" + |Content-Type: text/plain + | + |30 + |--$boundary + |Content-Disposition: form-data; name="file"; filename="doc.txt" + |Content-Type: text/plain + | + |file contents + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_quotesSpecialCharactersInNameAndFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field\nname" to + MultipartField.builder() + .value("value") + .filename("file\r\"name.txt") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="field%0Aname"; filename="file%0D%22name.txt" + |Content-Type: text/plain + | + |value + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_writeIsRepeatable() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field" to + MultipartField.builder() + .value("repeatable") + .contentType("text/plain") + .build() + ), + ) + + val output1 = ByteArrayOutputStream() + body.writeTo(output1) + val output2 = ByteArrayOutputStream() + body.writeTo(output2) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output1.size().toLong()) + val boundary = boundary(body) + val expected = + """ + |--$boundary + |Content-Disposition: form-data; name="field" + |Content-Type: text/plain + | + |repeatable + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + assertThat(output1.toString("UTF-8")).isEqualTo(expected) + assertThat(output2.toString("UTF-8")).isEqualTo(expected) + } + + @Test + fun multipartFormData_serializesByteArrayInputStream() { + // ByteArrayInputStream is specifically handled as repeatable with known content length. + val inputStream = "byte array stream".byteInputStream() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder() + .value(inputStream) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data" + |Content-Type: application/octet-stream + | + |byte array stream + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesInputStreamWithFilename() { + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the non-repeatable code + // path. + val inputStream = "file data".byteInputStream().buffered() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "upload" to + MultipartField.builder() + .value(inputStream) + .filename("upload.bin") + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="upload"; filename="upload.bin" + |Content-Type: application/octet-stream + | + |file data + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNestedArrayInObject() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder>>() + .value(mapOf("tags" to listOf("a", "b"))) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data.tags" + |Content-Type: text/plain + | + |a,b + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_contentLengthIsUnknownWhenInputStreamPresent() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "text" to + MultipartField.builder() + .value("hello") + .contentType("text/plain") + .build(), + "stream" to + MultipartField.builder() + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the + // non-repeatable code path. + .value("data".byteInputStream().buffered()) + .contentType("application/octet-stream") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="text" + |Content-Type: text/plain + | + |hello + |--$boundary + |Content-Disposition: form-data; name="stream" + |Content-Type: application/octet-stream + | + |data + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesEmptyArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "required" to + MultipartField.builder() + .value("present") + .contentType("text/plain") + .build(), + "items" to + MultipartField.builder>() + .value(emptyList()) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="required" + |Content-Type: text/plain + | + |present + |--$boundary + |Content-Disposition: form-data; name="items" + |Content-Type: text/plain + | + | + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesEmptyObject() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "required" to + MultipartField.builder() + .value("present") + .contentType("text/plain") + .build(), + "meta" to + MultipartField.builder>() + .value(emptyMap()) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="required" + |Content-Type: text/plain + | + |present + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + private fun boundary(body: HttpRequestBody): String = + body.contentType()!!.substringAfter("multipart/form-data; boundary=") +} From ced5b7ff9be983a13a2f1b78d751c94b04b44e59 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:07:55 +0000 Subject: [PATCH 3/8] feat(api): api update --- .stats.yml | 4 ++-- .../increase/api/models/checkdeposits/CheckDeposit.kt | 9 +++++++++ .../models/declinedtransactions/DeclinedTransaction.kt | 9 +++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8da50141e..702aef7c1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 232 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-74ade94ecfd7ae3c09e8eb53591e3a346d7a1a8e4fdcc88468aaf732e550d360.yml -openapi_spec_hash: bd4e4f623f593b9d0b9c139fde8245e4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-ff40c6e135e905af36aac783b7524ae0d9f13cd1e165ee56da3b244ae76ab94e.yml +openapi_spec_hash: b0c7738dc63f7362a11b27c7369d7441 config_hash: 4b562e97b3d8b4cba758a87d4927a76d diff --git a/increase-java-core/src/main/kotlin/com/increase/api/models/checkdeposits/CheckDeposit.kt b/increase-java-core/src/main/kotlin/com/increase/api/models/checkdeposits/CheckDeposit.kt index e8ee15396..dcde8c370 100644 --- a/increase-java-core/src/main/kotlin/com/increase/api/models/checkdeposits/CheckDeposit.kt +++ b/increase-java-core/src/main/kotlin/com/increase/api/models/checkdeposits/CheckDeposit.kt @@ -2109,6 +2109,9 @@ private constructor( /** The check was rejected at the user's request. */ @JvmField val REQUESTED_BY_USER = of("requested_by_user") + /** The check is not a U.S. domestic check and cannot be processed. */ + @JvmField val INTERNATIONAL = of("international") + /** The check was rejected for an unknown reason. */ @JvmField val UNKNOWN = of("unknown") @@ -2137,6 +2140,8 @@ private constructor( DEPOSIT_WINDOW_EXPIRED, /** The check was rejected at the user's request. */ REQUESTED_BY_USER, + /** The check is not a U.S. domestic check and cannot be processed. */ + INTERNATIONAL, /** The check was rejected for an unknown reason. */ UNKNOWN, } @@ -2171,6 +2176,8 @@ private constructor( DEPOSIT_WINDOW_EXPIRED, /** The check was rejected at the user's request. */ REQUESTED_BY_USER, + /** The check is not a U.S. domestic check and cannot be processed. */ + INTERNATIONAL, /** The check was rejected for an unknown reason. */ UNKNOWN, /** @@ -2198,6 +2205,7 @@ private constructor( SUSPECTED_FRAUD -> Value.SUSPECTED_FRAUD DEPOSIT_WINDOW_EXPIRED -> Value.DEPOSIT_WINDOW_EXPIRED REQUESTED_BY_USER -> Value.REQUESTED_BY_USER + INTERNATIONAL -> Value.INTERNATIONAL UNKNOWN -> Value.UNKNOWN else -> Value._UNKNOWN } @@ -2223,6 +2231,7 @@ private constructor( SUSPECTED_FRAUD -> Known.SUSPECTED_FRAUD DEPOSIT_WINDOW_EXPIRED -> Known.DEPOSIT_WINDOW_EXPIRED REQUESTED_BY_USER -> Known.REQUESTED_BY_USER + INTERNATIONAL -> Known.INTERNATIONAL UNKNOWN -> Known.UNKNOWN else -> throw IncreaseInvalidDataException("Unknown Reason: $value") } diff --git a/increase-java-core/src/main/kotlin/com/increase/api/models/declinedtransactions/DeclinedTransaction.kt b/increase-java-core/src/main/kotlin/com/increase/api/models/declinedtransactions/DeclinedTransaction.kt index 1ac579da9..1ec7c699c 100644 --- a/increase-java-core/src/main/kotlin/com/increase/api/models/declinedtransactions/DeclinedTransaction.kt +++ b/increase-java-core/src/main/kotlin/com/increase/api/models/declinedtransactions/DeclinedTransaction.kt @@ -13936,6 +13936,9 @@ private constructor( /** The check was rejected at the user's request. */ @JvmField val REQUESTED_BY_USER = of("requested_by_user") + /** The check is not a U.S. domestic check and cannot be processed. */ + @JvmField val INTERNATIONAL = of("international") + /** The check was rejected for an unknown reason. */ @JvmField val UNKNOWN = of("unknown") @@ -13964,6 +13967,8 @@ private constructor( DEPOSIT_WINDOW_EXPIRED, /** The check was rejected at the user's request. */ REQUESTED_BY_USER, + /** The check is not a U.S. domestic check and cannot be processed. */ + INTERNATIONAL, /** The check was rejected for an unknown reason. */ UNKNOWN, } @@ -13998,6 +14003,8 @@ private constructor( DEPOSIT_WINDOW_EXPIRED, /** The check was rejected at the user's request. */ REQUESTED_BY_USER, + /** The check is not a U.S. domestic check and cannot be processed. */ + INTERNATIONAL, /** The check was rejected for an unknown reason. */ UNKNOWN, /** @@ -14026,6 +14033,7 @@ private constructor( SUSPECTED_FRAUD -> Value.SUSPECTED_FRAUD DEPOSIT_WINDOW_EXPIRED -> Value.DEPOSIT_WINDOW_EXPIRED REQUESTED_BY_USER -> Value.REQUESTED_BY_USER + INTERNATIONAL -> Value.INTERNATIONAL UNKNOWN -> Value.UNKNOWN else -> Value._UNKNOWN } @@ -14051,6 +14059,7 @@ private constructor( SUSPECTED_FRAUD -> Known.SUSPECTED_FRAUD DEPOSIT_WINDOW_EXPIRED -> Known.DEPOSIT_WINDOW_EXPIRED REQUESTED_BY_USER -> Known.REQUESTED_BY_USER + INTERNATIONAL -> Known.INTERNATIONAL UNKNOWN -> Known.UNKNOWN else -> throw IncreaseInvalidDataException("Unknown Reason: $value") } From 8fcb947fdf1b224de813833aaa40ad26d955bd58 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:31:07 +0000 Subject: [PATCH 4/8] feat(api): api update --- .stats.yml | 4 +- .../api/models/cardpayments/CardPayment.kt | 1304 ++++++++++++++++- .../CardPaymentListPageResponseTest.kt | 34 + .../models/cardpayments/CardPaymentTest.kt | 31 + 4 files changed, 1370 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 702aef7c1..7d09a9347 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 232 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-ff40c6e135e905af36aac783b7524ae0d9f13cd1e165ee56da3b244ae76ab94e.yml -openapi_spec_hash: b0c7738dc63f7362a11b27c7369d7441 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-949ee3a47313a37ba0147670d429c72ff0cfb477f7abe8f1212dc892ed4ad6d2.yml +openapi_spec_hash: b7a3d5406d046298e53103020cec545d config_hash: 4b562e97b3d8b4cba758a87d4927a76d diff --git a/increase-java-core/src/main/kotlin/com/increase/api/models/cardpayments/CardPayment.kt b/increase-java-core/src/main/kotlin/com/increase/api/models/cardpayments/CardPayment.kt index 339516563..44c86bd3d 100644 --- a/increase-java-core/src/main/kotlin/com/increase/api/models/cardpayments/CardPayment.kt +++ b/increase-java-core/src/main/kotlin/com/increase/api/models/cardpayments/CardPayment.kt @@ -36,6 +36,7 @@ private constructor( private val digitalWalletTokenId: JsonField, private val elements: JsonField>, private val physicalCardId: JsonField, + private val schemeFees: JsonField>, private val state: JsonField, private val type: JsonField, private val additionalProperties: MutableMap, @@ -58,6 +59,9 @@ private constructor( @JsonProperty("physical_card_id") @ExcludeMissing physicalCardId: JsonField = JsonMissing.of(), + @JsonProperty("scheme_fees") + @ExcludeMissing + schemeFees: JsonField> = JsonMissing.of(), @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), ) : this( @@ -68,6 +72,7 @@ private constructor( digitalWalletTokenId, elements, physicalCardId, + schemeFees, state, type, mutableMapOf(), @@ -131,6 +136,14 @@ private constructor( */ fun physicalCardId(): Optional = physicalCardId.getOptional("physical_card_id") + /** + * The scheme fees associated with this card payment. + * + * @throws IncreaseInvalidDataException 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 schemeFees(): List = schemeFees.getRequired("scheme_fees") + /** * The summarized state of this card payment. * @@ -204,6 +217,15 @@ private constructor( @ExcludeMissing fun _physicalCardId(): JsonField = physicalCardId + /** + * Returns the raw JSON value of [schemeFees]. + * + * Unlike [schemeFees], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("scheme_fees") + @ExcludeMissing + fun _schemeFees(): JsonField> = schemeFees + /** * Returns the raw JSON value of [state]. * @@ -244,6 +266,7 @@ private constructor( * .digitalWalletTokenId() * .elements() * .physicalCardId() + * .schemeFees() * .state() * .type() * ``` @@ -261,6 +284,7 @@ private constructor( private var digitalWalletTokenId: JsonField? = null private var elements: JsonField>? = null private var physicalCardId: JsonField? = null + private var schemeFees: JsonField>? = null private var state: JsonField? = null private var type: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @@ -274,6 +298,7 @@ private constructor( digitalWalletTokenId = cardPayment.digitalWalletTokenId elements = cardPayment.elements.map { it.toMutableList() } physicalCardId = cardPayment.physicalCardId + schemeFees = cardPayment.schemeFees.map { it.toMutableList() } state = cardPayment.state type = cardPayment.type additionalProperties = cardPayment.additionalProperties.toMutableMap() @@ -395,6 +420,32 @@ private constructor( this.physicalCardId = physicalCardId } + /** The scheme fees associated with this card payment. */ + fun schemeFees(schemeFees: List) = schemeFees(JsonField.of(schemeFees)) + + /** + * Sets [Builder.schemeFees] to an arbitrary JSON value. + * + * You should usually call [Builder.schemeFees] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun schemeFees(schemeFees: JsonField>) = apply { + this.schemeFees = schemeFees.map { it.toMutableList() } + } + + /** + * Adds a single [SchemeFee] to [schemeFees]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSchemeFee(schemeFee: SchemeFee) = apply { + schemeFees = + (schemeFees ?: JsonField.of(mutableListOf())).also { + checkKnown("schemeFees", it).add(schemeFee) + } + } + /** The summarized state of this card payment. */ fun state(state: State) = state(JsonField.of(state)) @@ -453,6 +504,7 @@ private constructor( * .digitalWalletTokenId() * .elements() * .physicalCardId() + * .schemeFees() * .state() * .type() * ``` @@ -468,6 +520,7 @@ private constructor( checkRequired("digitalWalletTokenId", digitalWalletTokenId), checkRequired("elements", elements).map { it.toImmutable() }, checkRequired("physicalCardId", physicalCardId), + checkRequired("schemeFees", schemeFees).map { it.toImmutable() }, checkRequired("state", state), checkRequired("type", type), additionalProperties.toMutableMap(), @@ -488,6 +541,7 @@ private constructor( digitalWalletTokenId() elements().forEach { it.validate() } physicalCardId() + schemeFees().forEach { it.validate() } state().validate() type().validate() validated = true @@ -515,6 +569,7 @@ private constructor( (if (digitalWalletTokenId.asKnown().isPresent) 1 else 0) + (elements.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + (if (physicalCardId.asKnown().isPresent) 1 else 0) + + (schemeFees.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + (state.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) @@ -78118,6 +78173,1251 @@ private constructor( "Element{category=$category, createdAt=$createdAt, cardAuthentication=$cardAuthentication, cardAuthorization=$cardAuthorization, cardAuthorizationExpiration=$cardAuthorizationExpiration, cardBalanceInquiry=$cardBalanceInquiry, cardDecline=$cardDecline, cardFinancial=$cardFinancial, cardFuelConfirmation=$cardFuelConfirmation, cardIncrement=$cardIncrement, cardRefund=$cardRefund, cardReversal=$cardReversal, cardSettlement=$cardSettlement, cardValidation=$cardValidation, other=$other, additionalProperties=$additionalProperties}" } + class SchemeFee + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val amount: JsonField, + private val createdAt: JsonField, + private val currency: JsonField, + private val feeType: JsonField, + private val fixedComponent: JsonField, + private val variableRate: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("amount") @ExcludeMissing amount: JsonField = JsonMissing.of(), + @JsonProperty("created_at") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("currency") + @ExcludeMissing + currency: JsonField = JsonMissing.of(), + @JsonProperty("fee_type") + @ExcludeMissing + feeType: JsonField = JsonMissing.of(), + @JsonProperty("fixed_component") + @ExcludeMissing + fixedComponent: JsonField = JsonMissing.of(), + @JsonProperty("variable_rate") + @ExcludeMissing + variableRate: JsonField = JsonMissing.of(), + ) : this(amount, createdAt, currency, feeType, fixedComponent, variableRate, mutableMapOf()) + + /** + * The fee amount given as a string containing a decimal number. + * + * @throws IncreaseInvalidDataException 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 amount(): String = amount.getRequired("amount") + + /** + * The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) time at which the fee was created. + * + * @throws IncreaseInvalidDataException 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 createdAt(): OffsetDateTime = createdAt.getRequired("created_at") + + /** + * The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) code for the fee reimbursement. + * + * @throws IncreaseInvalidDataException 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 currency(): Currency = currency.getRequired("currency") + + /** + * The type of fee being assessed. + * + * @throws IncreaseInvalidDataException 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 feeType(): FeeType = feeType.getRequired("fee_type") + + /** + * The fixed component of the fee, if applicable, given in major units of the fee amount. + * + * @throws IncreaseInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun fixedComponent(): Optional = fixedComponent.getOptional("fixed_component") + + /** + * The variable rate component of the fee, if applicable, given as a decimal (e.g., 0.015 + * for 1.5%). + * + * @throws IncreaseInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun variableRate(): Optional = variableRate.getOptional("variable_rate") + + /** + * Returns the raw JSON value of [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("amount") @ExcludeMissing fun _amount(): JsonField = amount + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("created_at") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [feeType]. + * + * Unlike [feeType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fee_type") @ExcludeMissing fun _feeType(): JsonField = feeType + + /** + * Returns the raw JSON value of [fixedComponent]. + * + * Unlike [fixedComponent], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fixed_component") + @ExcludeMissing + fun _fixedComponent(): JsonField = fixedComponent + + /** + * Returns the raw JSON value of [variableRate]. + * + * Unlike [variableRate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("variable_rate") + @ExcludeMissing + fun _variableRate(): JsonField = variableRate + + @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 [SchemeFee]. + * + * The following fields are required: + * ```java + * .amount() + * .createdAt() + * .currency() + * .feeType() + * .fixedComponent() + * .variableRate() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SchemeFee]. */ + class Builder internal constructor() { + + private var amount: JsonField? = null + private var createdAt: JsonField? = null + private var currency: JsonField? = null + private var feeType: JsonField? = null + private var fixedComponent: JsonField? = null + private var variableRate: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(schemeFee: SchemeFee) = apply { + amount = schemeFee.amount + createdAt = schemeFee.createdAt + currency = schemeFee.currency + feeType = schemeFee.feeType + fixedComponent = schemeFee.fixedComponent + variableRate = schemeFee.variableRate + additionalProperties = schemeFee.additionalProperties.toMutableMap() + } + + /** The fee amount given as a string containing a decimal number. */ + fun amount(amount: String) = amount(JsonField.of(amount)) + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun amount(amount: JsonField) = apply { this.amount = amount } + + /** + * The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) time at which the fee was + * created. + */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** + * The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) code for the fee + * reimbursement. + */ + fun currency(currency: Currency) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [Currency] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + /** The type of fee being assessed. */ + fun feeType(feeType: FeeType) = feeType(JsonField.of(feeType)) + + /** + * Sets [Builder.feeType] to an arbitrary JSON value. + * + * You should usually call [Builder.feeType] with a well-typed [FeeType] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun feeType(feeType: JsonField) = apply { this.feeType = feeType } + + /** + * The fixed component of the fee, if applicable, given in major units of the fee + * amount. + */ + fun fixedComponent(fixedComponent: String?) = + fixedComponent(JsonField.ofNullable(fixedComponent)) + + /** Alias for calling [Builder.fixedComponent] with `fixedComponent.orElse(null)`. */ + fun fixedComponent(fixedComponent: Optional) = + fixedComponent(fixedComponent.getOrNull()) + + /** + * Sets [Builder.fixedComponent] to an arbitrary JSON value. + * + * You should usually call [Builder.fixedComponent] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fixedComponent(fixedComponent: JsonField) = apply { + this.fixedComponent = fixedComponent + } + + /** + * The variable rate component of the fee, if applicable, given as a decimal (e.g., + * 0.015 for 1.5%). + */ + fun variableRate(variableRate: String?) = + variableRate(JsonField.ofNullable(variableRate)) + + /** Alias for calling [Builder.variableRate] with `variableRate.orElse(null)`. */ + fun variableRate(variableRate: Optional) = + variableRate(variableRate.getOrNull()) + + /** + * Sets [Builder.variableRate] to an arbitrary JSON value. + * + * You should usually call [Builder.variableRate] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun variableRate(variableRate: JsonField) = apply { + this.variableRate = variableRate + } + + 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 [SchemeFee]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .amount() + * .createdAt() + * .currency() + * .feeType() + * .fixedComponent() + * .variableRate() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SchemeFee = + SchemeFee( + checkRequired("amount", amount), + checkRequired("createdAt", createdAt), + checkRequired("currency", currency), + checkRequired("feeType", feeType), + checkRequired("fixedComponent", fixedComponent), + checkRequired("variableRate", variableRate), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SchemeFee = apply { + if (validated) { + return@apply + } + + amount() + createdAt() + currency().validate() + feeType().validate() + fixedComponent() + variableRate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: IncreaseInvalidDataException) { + 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 (amount.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (currency.asKnown().getOrNull()?.validity() ?: 0) + + (feeType.asKnown().getOrNull()?.validity() ?: 0) + + (if (fixedComponent.asKnown().isPresent) 1 else 0) + + (if (variableRate.asKnown().isPresent) 1 else 0) + + /** + * The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) code for the fee reimbursement. + */ + class Currency @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 { + + /** US Dollar (USD) */ + @JvmField val USD = of("USD") + + @JvmStatic fun of(value: String) = Currency(JsonField.of(value)) + } + + /** An enum containing [Currency]'s known values. */ + enum class Known { + /** US Dollar (USD) */ + USD + } + + /** + * An enum containing [Currency]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Currency] 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 { + /** US Dollar (USD) */ + USD, + /** + * An enum member indicating that [Currency] 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) { + USD -> Value.USD + 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 IncreaseInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + USD -> Known.USD + else -> throw IncreaseInvalidDataException("Unknown Currency: $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 IncreaseInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + IncreaseInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + fun validate(): Currency = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: IncreaseInvalidDataException) { + 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 Currency && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The type of fee being assessed. */ + class FeeType @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 { + + /** + * International Service Assessment (ISA) single-currency is a fee assessed by the + * card network for cross-border transactions presented and settled in the same + * currency. + */ + @JvmField + val VISA_INTERNATIONAL_SERVICE_ASSESSMENT_SINGLE_CURRENCY = + of("visa_international_service_assessment_single_currency") + + /** + * International Service Assessment (ISA) cross-currency is a fee assessed by the + * card network for cross-border transactions presented and settled in different + * currencies. + */ + @JvmField + val VISA_INTERNATIONAL_SERVICE_ASSESSMENT_CROSS_CURRENCY = + of("visa_international_service_assessment_cross_currency") + + /** + * Activity and charges for Visa Settlement System processing for POS + * (Point-Of-Sale) authorization transactions. Authorization is the process of + * approving or declining the transaction amount specified. The fee is assessed to + * the Issuer. + */ + @JvmField + val VISA_AUTHORIZATION_DOMESTIC_POINT_OF_SALE = + of("visa_authorization_domestic_point_of_sale") + + /** + * Activity and charges for Visa Settlement System processing for POS + * (Point-Of-Sale) International authorization transactions. Authorization is the + * process of approving or declining the transaction amount specified. The fee is + * assessed to the Issuer. + */ + @JvmField + val VISA_AUTHORIZATION_INTERNATIONAL_POINT_OF_SALE = + of("visa_authorization_international_point_of_sale") + + /** + * Activity and charges for Visa Settlement System processing for Canada Region POS + * (Point-of-Sale) authorization transactions. Authorization is the process of + * approving or declining the transaction amount specified. + */ + @JvmField + val VISA_AUTHORIZATION_CANADA_POINT_OF_SALE = + of("visa_authorization_canada_point_of_sale") + + /** + * Activity only for Visa Settlement System authorization processing of POS + * (Point-Of-Sale) reversal transactions. Authorization reversal represents a VSS + * message that undoes the complete or partial actions of a previous authorization + * request. + */ + @JvmField + val VISA_AUTHORIZATION_REVERSAL_POINT_OF_SALE = + of("visa_authorization_reversal_point_of_sale") + + /** + * Activity only for Visa Settlement System authorization processing of POS + * (Point-Of-Sale) International reversal transactions. Authorization reversal + * represents a VSS message that undoes the complete or partial actions of a + * previous authorization request. + */ + @JvmField + val VISA_AUTHORIZATION_REVERSAL_INTERNATIONAL_POINT_OF_SALE = + of("visa_authorization_reversal_international_point_of_sale") + + /** + * A per Address Verification Service (AVS) result fee. Applies to all usable AVS + * result codes. + */ + @JvmField + val VISA_AUTHORIZATION_ADDRESS_VERIFICATION_SERVICE = + of("visa_authorization_address_verification_service") + + /** + * Advanced Authorization is a fraud detection tool that monitors and risk evaluates + * 100 percent of US VisaNet authorizations in real-time. Activity related to + * Purchase (includes Signature Authenticated Visa and PIN Authenticated Visa Debit + * (PAVD) transactions). + */ + @JvmField val VISA_ADVANCED_AUTHORIZATION = of("visa_advanced_authorization") + + /** + * Issuer Transactions Visa represents a charge based on total actual monthly + * processing (Visa transactions only) through a VisaNet Access Point (VAP). Charges + * are assessed to the processor for each VisaNet Access Point. + */ + @JvmField val VISA_MESSAGE_TRANSMISSION = of("visa_message_transmission") + + /** + * Activity, per inquiry, related to the domestic Issuer for Account Number + * Verification. + */ + @JvmField + val VISA_ACCOUNT_VERIFICATION_DOMESTIC = of("visa_account_verification_domestic") + + /** + * Activity, per inquiry, related to the international Issuer for Account Number + * Verification. + */ + @JvmField + val VISA_ACCOUNT_VERIFICATION_INTERNATIONAL = + of("visa_account_verification_international") + + /** + * Activity, per inquiry, related to the US-Canada Issuer for Account Number + * Verification. + */ + @JvmField + val VISA_ACCOUNT_VERIFICATION_CANADA = of("visa_account_verification_canada") + + /** + * The Corporate Acceptance Fee is charged to issuers and is based on the monthly + * sales volume on Commercial and Government Debit, Prepaid, Credit, Charge, or + * Deferred Debit card transactions. + */ + @JvmField val VISA_CORPORATE_ACCEPTANCE_FEE = of("visa_corporate_acceptance_fee") + + /** + * The Consumer Debit Acceptance Fee is charged to issuers and is based on the + * monthly sales volume of Consumer Debit or Prepaid card transactions. The cashback + * portion of a Debit and Prepaid card transaction is excluded from the sales volume + * calculation. + */ + @JvmField + val VISA_CONSUMER_DEBIT_ACCEPTANCE_FEE = of("visa_consumer_debit_acceptance_fee") + + /** + * The Business Acceptance Fee is charged to issuers and is based on the monthly + * sales volume on Business Debit, Prepaid, Credit, Charge, or Deferred Debit card + * transactions. The cashback portion is included in the sales volume calculation + * with the exception of a Debit and Prepaid card transactions. + */ + @JvmField + val VISA_BUSINESS_DEBIT_ACCEPTANCE_FEE = of("visa_business_debit_acceptance_fee") + + /** + * The Purchasing Card Acceptance Fee is charged to issuers and is based on the + * monthly sales volume on Commercial and Government Debit, Prepaid, Credit, Charge, + * or Deferred Debit card transactions. + */ + @JvmField val VISA_PURCHASING_ACCEPTANCE_FEE = of("visa_purchasing_acceptance_fee") + + /** + * Activity and fees for the processing of a sales draft original for a purchase + * transaction. + */ + @JvmField val VISA_PURCHASE_DOMESTIC = of("visa_purchase_domestic") + + /** + * Activity and fees for the processing of an international sales draft original for + * a purchase transaction. + */ + @JvmField val VISA_PURCHASE_INTERNATIONAL = of("visa_purchase_international") + + /** + * Apple Pay Credit Product Token Purchase Original Transactions. This fee is billed + * by Visa on behalf of Apple Inc. for Apple Pay transactions. + */ + @JvmField val VISA_CREDIT_PURCHASE_TOKEN = of("visa_credit_purchase_token") + + /** + * Apple Pay Debit Product Token Purchase Original Transactions. This fee is billed + * by Visa on behalf of Apple Inc. for Apple Pay transactions. + */ + @JvmField val VISA_DEBIT_PURCHASE_TOKEN = of("visa_debit_purchase_token") + + /** A per transaction fee assessed for Base II financial draft - Issuer. */ + @JvmField val VISA_CLEARING_TRANSMISSION = of("visa_clearing_transmission") + + /** + * Issuer charge for Non-Financial OCT/AFT Authorization 0100 and Declined Financial + * OCT/AFT 0200 transactions. + */ + @JvmField val VISA_DIRECT_AUTHORIZATION = of("visa_direct_authorization") + + /** + * Data processing charge for Visa Direct OCTs for all business application + * identifiers (BAIs) other than money transfer-bank initiated (BI). BASE II + * transactions. + */ + @JvmField + val VISA_DIRECT_TRANSACTION_DOMESTIC = of("visa_direct_transaction_domestic") + + /** Issuer card service fee for Commercial Credit cards. */ + @JvmField val VISA_SERVICE_COMMERCIAL_CREDIT = of("visa_service_commercial_credit") + + /** Issuer Advertising Service Fee for Commercial Credit cards. */ + @JvmField + val VISA_ADVERTISING_SERVICE_COMMERCIAL_CREDIT = + of("visa_advertising_service_commercial_credit") + + /** Issuer Community Growth Acceleration Program Fee. */ + @JvmField + val VISA_COMMUNITY_GROWTH_ACCELERATION_PROGRAM = + of("visa_community_growth_acceleration_program") + + /** Issuer Processing Guarantee for Commercial Credit cards. */ + @JvmField + val VISA_PROCESSING_GUARANTEE_COMMERCIAL_CREDIT = + of("visa_processing_guarantee_commercial_credit") + + /** + * Pulse Switch Fee is a fee charged by the Pulse network for processing + * transactions on its network. + */ + @JvmField val PULSE_SWITCH_FEE = of("pulse_switch_fee") + + @JvmStatic fun of(value: String) = FeeType(JsonField.of(value)) + } + + /** An enum containing [FeeType]'s known values. */ + enum class Known { + /** + * International Service Assessment (ISA) single-currency is a fee assessed by the + * card network for cross-border transactions presented and settled in the same + * currency. + */ + VISA_INTERNATIONAL_SERVICE_ASSESSMENT_SINGLE_CURRENCY, + /** + * International Service Assessment (ISA) cross-currency is a fee assessed by the + * card network for cross-border transactions presented and settled in different + * currencies. + */ + VISA_INTERNATIONAL_SERVICE_ASSESSMENT_CROSS_CURRENCY, + /** + * Activity and charges for Visa Settlement System processing for POS + * (Point-Of-Sale) authorization transactions. Authorization is the process of + * approving or declining the transaction amount specified. The fee is assessed to + * the Issuer. + */ + VISA_AUTHORIZATION_DOMESTIC_POINT_OF_SALE, + /** + * Activity and charges for Visa Settlement System processing for POS + * (Point-Of-Sale) International authorization transactions. Authorization is the + * process of approving or declining the transaction amount specified. The fee is + * assessed to the Issuer. + */ + VISA_AUTHORIZATION_INTERNATIONAL_POINT_OF_SALE, + /** + * Activity and charges for Visa Settlement System processing for Canada Region POS + * (Point-of-Sale) authorization transactions. Authorization is the process of + * approving or declining the transaction amount specified. + */ + VISA_AUTHORIZATION_CANADA_POINT_OF_SALE, + /** + * Activity only for Visa Settlement System authorization processing of POS + * (Point-Of-Sale) reversal transactions. Authorization reversal represents a VSS + * message that undoes the complete or partial actions of a previous authorization + * request. + */ + VISA_AUTHORIZATION_REVERSAL_POINT_OF_SALE, + /** + * Activity only for Visa Settlement System authorization processing of POS + * (Point-Of-Sale) International reversal transactions. Authorization reversal + * represents a VSS message that undoes the complete or partial actions of a + * previous authorization request. + */ + VISA_AUTHORIZATION_REVERSAL_INTERNATIONAL_POINT_OF_SALE, + /** + * A per Address Verification Service (AVS) result fee. Applies to all usable AVS + * result codes. + */ + VISA_AUTHORIZATION_ADDRESS_VERIFICATION_SERVICE, + /** + * Advanced Authorization is a fraud detection tool that monitors and risk evaluates + * 100 percent of US VisaNet authorizations in real-time. Activity related to + * Purchase (includes Signature Authenticated Visa and PIN Authenticated Visa Debit + * (PAVD) transactions). + */ + VISA_ADVANCED_AUTHORIZATION, + /** + * Issuer Transactions Visa represents a charge based on total actual monthly + * processing (Visa transactions only) through a VisaNet Access Point (VAP). Charges + * are assessed to the processor for each VisaNet Access Point. + */ + VISA_MESSAGE_TRANSMISSION, + /** + * Activity, per inquiry, related to the domestic Issuer for Account Number + * Verification. + */ + VISA_ACCOUNT_VERIFICATION_DOMESTIC, + /** + * Activity, per inquiry, related to the international Issuer for Account Number + * Verification. + */ + VISA_ACCOUNT_VERIFICATION_INTERNATIONAL, + /** + * Activity, per inquiry, related to the US-Canada Issuer for Account Number + * Verification. + */ + VISA_ACCOUNT_VERIFICATION_CANADA, + /** + * The Corporate Acceptance Fee is charged to issuers and is based on the monthly + * sales volume on Commercial and Government Debit, Prepaid, Credit, Charge, or + * Deferred Debit card transactions. + */ + VISA_CORPORATE_ACCEPTANCE_FEE, + /** + * The Consumer Debit Acceptance Fee is charged to issuers and is based on the + * monthly sales volume of Consumer Debit or Prepaid card transactions. The cashback + * portion of a Debit and Prepaid card transaction is excluded from the sales volume + * calculation. + */ + VISA_CONSUMER_DEBIT_ACCEPTANCE_FEE, + /** + * The Business Acceptance Fee is charged to issuers and is based on the monthly + * sales volume on Business Debit, Prepaid, Credit, Charge, or Deferred Debit card + * transactions. The cashback portion is included in the sales volume calculation + * with the exception of a Debit and Prepaid card transactions. + */ + VISA_BUSINESS_DEBIT_ACCEPTANCE_FEE, + /** + * The Purchasing Card Acceptance Fee is charged to issuers and is based on the + * monthly sales volume on Commercial and Government Debit, Prepaid, Credit, Charge, + * or Deferred Debit card transactions. + */ + VISA_PURCHASING_ACCEPTANCE_FEE, + /** + * Activity and fees for the processing of a sales draft original for a purchase + * transaction. + */ + VISA_PURCHASE_DOMESTIC, + /** + * Activity and fees for the processing of an international sales draft original for + * a purchase transaction. + */ + VISA_PURCHASE_INTERNATIONAL, + /** + * Apple Pay Credit Product Token Purchase Original Transactions. This fee is billed + * by Visa on behalf of Apple Inc. for Apple Pay transactions. + */ + VISA_CREDIT_PURCHASE_TOKEN, + /** + * Apple Pay Debit Product Token Purchase Original Transactions. This fee is billed + * by Visa on behalf of Apple Inc. for Apple Pay transactions. + */ + VISA_DEBIT_PURCHASE_TOKEN, + /** A per transaction fee assessed for Base II financial draft - Issuer. */ + VISA_CLEARING_TRANSMISSION, + /** + * Issuer charge for Non-Financial OCT/AFT Authorization 0100 and Declined Financial + * OCT/AFT 0200 transactions. + */ + VISA_DIRECT_AUTHORIZATION, + /** + * Data processing charge for Visa Direct OCTs for all business application + * identifiers (BAIs) other than money transfer-bank initiated (BI). BASE II + * transactions. + */ + VISA_DIRECT_TRANSACTION_DOMESTIC, + /** Issuer card service fee for Commercial Credit cards. */ + VISA_SERVICE_COMMERCIAL_CREDIT, + /** Issuer Advertising Service Fee for Commercial Credit cards. */ + VISA_ADVERTISING_SERVICE_COMMERCIAL_CREDIT, + /** Issuer Community Growth Acceleration Program Fee. */ + VISA_COMMUNITY_GROWTH_ACCELERATION_PROGRAM, + /** Issuer Processing Guarantee for Commercial Credit cards. */ + VISA_PROCESSING_GUARANTEE_COMMERCIAL_CREDIT, + /** + * Pulse Switch Fee is a fee charged by the Pulse network for processing + * transactions on its network. + */ + PULSE_SWITCH_FEE, + } + + /** + * An enum containing [FeeType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [FeeType] 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 { + /** + * International Service Assessment (ISA) single-currency is a fee assessed by the + * card network for cross-border transactions presented and settled in the same + * currency. + */ + VISA_INTERNATIONAL_SERVICE_ASSESSMENT_SINGLE_CURRENCY, + /** + * International Service Assessment (ISA) cross-currency is a fee assessed by the + * card network for cross-border transactions presented and settled in different + * currencies. + */ + VISA_INTERNATIONAL_SERVICE_ASSESSMENT_CROSS_CURRENCY, + /** + * Activity and charges for Visa Settlement System processing for POS + * (Point-Of-Sale) authorization transactions. Authorization is the process of + * approving or declining the transaction amount specified. The fee is assessed to + * the Issuer. + */ + VISA_AUTHORIZATION_DOMESTIC_POINT_OF_SALE, + /** + * Activity and charges for Visa Settlement System processing for POS + * (Point-Of-Sale) International authorization transactions. Authorization is the + * process of approving or declining the transaction amount specified. The fee is + * assessed to the Issuer. + */ + VISA_AUTHORIZATION_INTERNATIONAL_POINT_OF_SALE, + /** + * Activity and charges for Visa Settlement System processing for Canada Region POS + * (Point-of-Sale) authorization transactions. Authorization is the process of + * approving or declining the transaction amount specified. + */ + VISA_AUTHORIZATION_CANADA_POINT_OF_SALE, + /** + * Activity only for Visa Settlement System authorization processing of POS + * (Point-Of-Sale) reversal transactions. Authorization reversal represents a VSS + * message that undoes the complete or partial actions of a previous authorization + * request. + */ + VISA_AUTHORIZATION_REVERSAL_POINT_OF_SALE, + /** + * Activity only for Visa Settlement System authorization processing of POS + * (Point-Of-Sale) International reversal transactions. Authorization reversal + * represents a VSS message that undoes the complete or partial actions of a + * previous authorization request. + */ + VISA_AUTHORIZATION_REVERSAL_INTERNATIONAL_POINT_OF_SALE, + /** + * A per Address Verification Service (AVS) result fee. Applies to all usable AVS + * result codes. + */ + VISA_AUTHORIZATION_ADDRESS_VERIFICATION_SERVICE, + /** + * Advanced Authorization is a fraud detection tool that monitors and risk evaluates + * 100 percent of US VisaNet authorizations in real-time. Activity related to + * Purchase (includes Signature Authenticated Visa and PIN Authenticated Visa Debit + * (PAVD) transactions). + */ + VISA_ADVANCED_AUTHORIZATION, + /** + * Issuer Transactions Visa represents a charge based on total actual monthly + * processing (Visa transactions only) through a VisaNet Access Point (VAP). Charges + * are assessed to the processor for each VisaNet Access Point. + */ + VISA_MESSAGE_TRANSMISSION, + /** + * Activity, per inquiry, related to the domestic Issuer for Account Number + * Verification. + */ + VISA_ACCOUNT_VERIFICATION_DOMESTIC, + /** + * Activity, per inquiry, related to the international Issuer for Account Number + * Verification. + */ + VISA_ACCOUNT_VERIFICATION_INTERNATIONAL, + /** + * Activity, per inquiry, related to the US-Canada Issuer for Account Number + * Verification. + */ + VISA_ACCOUNT_VERIFICATION_CANADA, + /** + * The Corporate Acceptance Fee is charged to issuers and is based on the monthly + * sales volume on Commercial and Government Debit, Prepaid, Credit, Charge, or + * Deferred Debit card transactions. + */ + VISA_CORPORATE_ACCEPTANCE_FEE, + /** + * The Consumer Debit Acceptance Fee is charged to issuers and is based on the + * monthly sales volume of Consumer Debit or Prepaid card transactions. The cashback + * portion of a Debit and Prepaid card transaction is excluded from the sales volume + * calculation. + */ + VISA_CONSUMER_DEBIT_ACCEPTANCE_FEE, + /** + * The Business Acceptance Fee is charged to issuers and is based on the monthly + * sales volume on Business Debit, Prepaid, Credit, Charge, or Deferred Debit card + * transactions. The cashback portion is included in the sales volume calculation + * with the exception of a Debit and Prepaid card transactions. + */ + VISA_BUSINESS_DEBIT_ACCEPTANCE_FEE, + /** + * The Purchasing Card Acceptance Fee is charged to issuers and is based on the + * monthly sales volume on Commercial and Government Debit, Prepaid, Credit, Charge, + * or Deferred Debit card transactions. + */ + VISA_PURCHASING_ACCEPTANCE_FEE, + /** + * Activity and fees for the processing of a sales draft original for a purchase + * transaction. + */ + VISA_PURCHASE_DOMESTIC, + /** + * Activity and fees for the processing of an international sales draft original for + * a purchase transaction. + */ + VISA_PURCHASE_INTERNATIONAL, + /** + * Apple Pay Credit Product Token Purchase Original Transactions. This fee is billed + * by Visa on behalf of Apple Inc. for Apple Pay transactions. + */ + VISA_CREDIT_PURCHASE_TOKEN, + /** + * Apple Pay Debit Product Token Purchase Original Transactions. This fee is billed + * by Visa on behalf of Apple Inc. for Apple Pay transactions. + */ + VISA_DEBIT_PURCHASE_TOKEN, + /** A per transaction fee assessed for Base II financial draft - Issuer. */ + VISA_CLEARING_TRANSMISSION, + /** + * Issuer charge for Non-Financial OCT/AFT Authorization 0100 and Declined Financial + * OCT/AFT 0200 transactions. + */ + VISA_DIRECT_AUTHORIZATION, + /** + * Data processing charge for Visa Direct OCTs for all business application + * identifiers (BAIs) other than money transfer-bank initiated (BI). BASE II + * transactions. + */ + VISA_DIRECT_TRANSACTION_DOMESTIC, + /** Issuer card service fee for Commercial Credit cards. */ + VISA_SERVICE_COMMERCIAL_CREDIT, + /** Issuer Advertising Service Fee for Commercial Credit cards. */ + VISA_ADVERTISING_SERVICE_COMMERCIAL_CREDIT, + /** Issuer Community Growth Acceleration Program Fee. */ + VISA_COMMUNITY_GROWTH_ACCELERATION_PROGRAM, + /** Issuer Processing Guarantee for Commercial Credit cards. */ + VISA_PROCESSING_GUARANTEE_COMMERCIAL_CREDIT, + /** + * Pulse Switch Fee is a fee charged by the Pulse network for processing + * transactions on its network. + */ + PULSE_SWITCH_FEE, + /** + * An enum member indicating that [FeeType] 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) { + VISA_INTERNATIONAL_SERVICE_ASSESSMENT_SINGLE_CURRENCY -> + Value.VISA_INTERNATIONAL_SERVICE_ASSESSMENT_SINGLE_CURRENCY + VISA_INTERNATIONAL_SERVICE_ASSESSMENT_CROSS_CURRENCY -> + Value.VISA_INTERNATIONAL_SERVICE_ASSESSMENT_CROSS_CURRENCY + VISA_AUTHORIZATION_DOMESTIC_POINT_OF_SALE -> + Value.VISA_AUTHORIZATION_DOMESTIC_POINT_OF_SALE + VISA_AUTHORIZATION_INTERNATIONAL_POINT_OF_SALE -> + Value.VISA_AUTHORIZATION_INTERNATIONAL_POINT_OF_SALE + VISA_AUTHORIZATION_CANADA_POINT_OF_SALE -> + Value.VISA_AUTHORIZATION_CANADA_POINT_OF_SALE + VISA_AUTHORIZATION_REVERSAL_POINT_OF_SALE -> + Value.VISA_AUTHORIZATION_REVERSAL_POINT_OF_SALE + VISA_AUTHORIZATION_REVERSAL_INTERNATIONAL_POINT_OF_SALE -> + Value.VISA_AUTHORIZATION_REVERSAL_INTERNATIONAL_POINT_OF_SALE + VISA_AUTHORIZATION_ADDRESS_VERIFICATION_SERVICE -> + Value.VISA_AUTHORIZATION_ADDRESS_VERIFICATION_SERVICE + VISA_ADVANCED_AUTHORIZATION -> Value.VISA_ADVANCED_AUTHORIZATION + VISA_MESSAGE_TRANSMISSION -> Value.VISA_MESSAGE_TRANSMISSION + VISA_ACCOUNT_VERIFICATION_DOMESTIC -> Value.VISA_ACCOUNT_VERIFICATION_DOMESTIC + VISA_ACCOUNT_VERIFICATION_INTERNATIONAL -> + Value.VISA_ACCOUNT_VERIFICATION_INTERNATIONAL + VISA_ACCOUNT_VERIFICATION_CANADA -> Value.VISA_ACCOUNT_VERIFICATION_CANADA + VISA_CORPORATE_ACCEPTANCE_FEE -> Value.VISA_CORPORATE_ACCEPTANCE_FEE + VISA_CONSUMER_DEBIT_ACCEPTANCE_FEE -> Value.VISA_CONSUMER_DEBIT_ACCEPTANCE_FEE + VISA_BUSINESS_DEBIT_ACCEPTANCE_FEE -> Value.VISA_BUSINESS_DEBIT_ACCEPTANCE_FEE + VISA_PURCHASING_ACCEPTANCE_FEE -> Value.VISA_PURCHASING_ACCEPTANCE_FEE + VISA_PURCHASE_DOMESTIC -> Value.VISA_PURCHASE_DOMESTIC + VISA_PURCHASE_INTERNATIONAL -> Value.VISA_PURCHASE_INTERNATIONAL + VISA_CREDIT_PURCHASE_TOKEN -> Value.VISA_CREDIT_PURCHASE_TOKEN + VISA_DEBIT_PURCHASE_TOKEN -> Value.VISA_DEBIT_PURCHASE_TOKEN + VISA_CLEARING_TRANSMISSION -> Value.VISA_CLEARING_TRANSMISSION + VISA_DIRECT_AUTHORIZATION -> Value.VISA_DIRECT_AUTHORIZATION + VISA_DIRECT_TRANSACTION_DOMESTIC -> Value.VISA_DIRECT_TRANSACTION_DOMESTIC + VISA_SERVICE_COMMERCIAL_CREDIT -> Value.VISA_SERVICE_COMMERCIAL_CREDIT + VISA_ADVERTISING_SERVICE_COMMERCIAL_CREDIT -> + Value.VISA_ADVERTISING_SERVICE_COMMERCIAL_CREDIT + VISA_COMMUNITY_GROWTH_ACCELERATION_PROGRAM -> + Value.VISA_COMMUNITY_GROWTH_ACCELERATION_PROGRAM + VISA_PROCESSING_GUARANTEE_COMMERCIAL_CREDIT -> + Value.VISA_PROCESSING_GUARANTEE_COMMERCIAL_CREDIT + PULSE_SWITCH_FEE -> Value.PULSE_SWITCH_FEE + 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 IncreaseInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + VISA_INTERNATIONAL_SERVICE_ASSESSMENT_SINGLE_CURRENCY -> + Known.VISA_INTERNATIONAL_SERVICE_ASSESSMENT_SINGLE_CURRENCY + VISA_INTERNATIONAL_SERVICE_ASSESSMENT_CROSS_CURRENCY -> + Known.VISA_INTERNATIONAL_SERVICE_ASSESSMENT_CROSS_CURRENCY + VISA_AUTHORIZATION_DOMESTIC_POINT_OF_SALE -> + Known.VISA_AUTHORIZATION_DOMESTIC_POINT_OF_SALE + VISA_AUTHORIZATION_INTERNATIONAL_POINT_OF_SALE -> + Known.VISA_AUTHORIZATION_INTERNATIONAL_POINT_OF_SALE + VISA_AUTHORIZATION_CANADA_POINT_OF_SALE -> + Known.VISA_AUTHORIZATION_CANADA_POINT_OF_SALE + VISA_AUTHORIZATION_REVERSAL_POINT_OF_SALE -> + Known.VISA_AUTHORIZATION_REVERSAL_POINT_OF_SALE + VISA_AUTHORIZATION_REVERSAL_INTERNATIONAL_POINT_OF_SALE -> + Known.VISA_AUTHORIZATION_REVERSAL_INTERNATIONAL_POINT_OF_SALE + VISA_AUTHORIZATION_ADDRESS_VERIFICATION_SERVICE -> + Known.VISA_AUTHORIZATION_ADDRESS_VERIFICATION_SERVICE + VISA_ADVANCED_AUTHORIZATION -> Known.VISA_ADVANCED_AUTHORIZATION + VISA_MESSAGE_TRANSMISSION -> Known.VISA_MESSAGE_TRANSMISSION + VISA_ACCOUNT_VERIFICATION_DOMESTIC -> Known.VISA_ACCOUNT_VERIFICATION_DOMESTIC + VISA_ACCOUNT_VERIFICATION_INTERNATIONAL -> + Known.VISA_ACCOUNT_VERIFICATION_INTERNATIONAL + VISA_ACCOUNT_VERIFICATION_CANADA -> Known.VISA_ACCOUNT_VERIFICATION_CANADA + VISA_CORPORATE_ACCEPTANCE_FEE -> Known.VISA_CORPORATE_ACCEPTANCE_FEE + VISA_CONSUMER_DEBIT_ACCEPTANCE_FEE -> Known.VISA_CONSUMER_DEBIT_ACCEPTANCE_FEE + VISA_BUSINESS_DEBIT_ACCEPTANCE_FEE -> Known.VISA_BUSINESS_DEBIT_ACCEPTANCE_FEE + VISA_PURCHASING_ACCEPTANCE_FEE -> Known.VISA_PURCHASING_ACCEPTANCE_FEE + VISA_PURCHASE_DOMESTIC -> Known.VISA_PURCHASE_DOMESTIC + VISA_PURCHASE_INTERNATIONAL -> Known.VISA_PURCHASE_INTERNATIONAL + VISA_CREDIT_PURCHASE_TOKEN -> Known.VISA_CREDIT_PURCHASE_TOKEN + VISA_DEBIT_PURCHASE_TOKEN -> Known.VISA_DEBIT_PURCHASE_TOKEN + VISA_CLEARING_TRANSMISSION -> Known.VISA_CLEARING_TRANSMISSION + VISA_DIRECT_AUTHORIZATION -> Known.VISA_DIRECT_AUTHORIZATION + VISA_DIRECT_TRANSACTION_DOMESTIC -> Known.VISA_DIRECT_TRANSACTION_DOMESTIC + VISA_SERVICE_COMMERCIAL_CREDIT -> Known.VISA_SERVICE_COMMERCIAL_CREDIT + VISA_ADVERTISING_SERVICE_COMMERCIAL_CREDIT -> + Known.VISA_ADVERTISING_SERVICE_COMMERCIAL_CREDIT + VISA_COMMUNITY_GROWTH_ACCELERATION_PROGRAM -> + Known.VISA_COMMUNITY_GROWTH_ACCELERATION_PROGRAM + VISA_PROCESSING_GUARANTEE_COMMERCIAL_CREDIT -> + Known.VISA_PROCESSING_GUARANTEE_COMMERCIAL_CREDIT + PULSE_SWITCH_FEE -> Known.PULSE_SWITCH_FEE + else -> throw IncreaseInvalidDataException("Unknown FeeType: $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 IncreaseInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + IncreaseInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + fun validate(): FeeType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: IncreaseInvalidDataException) { + 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 FeeType && 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 SchemeFee && + amount == other.amount && + createdAt == other.createdAt && + currency == other.currency && + feeType == other.feeType && + fixedComponent == other.fixedComponent && + variableRate == other.variableRate && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + amount, + createdAt, + currency, + feeType, + fixedComponent, + variableRate, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SchemeFee{amount=$amount, createdAt=$createdAt, currency=$currency, feeType=$feeType, fixedComponent=$fixedComponent, variableRate=$variableRate, additionalProperties=$additionalProperties}" + } + /** The summarized state of this card payment. */ class State @JsonCreator(mode = JsonCreator.Mode.DISABLED) @@ -78742,6 +80042,7 @@ private constructor( digitalWalletTokenId == other.digitalWalletTokenId && elements == other.elements && physicalCardId == other.physicalCardId && + schemeFees == other.schemeFees && state == other.state && type == other.type && additionalProperties == other.additionalProperties @@ -78756,6 +80057,7 @@ private constructor( digitalWalletTokenId, elements, physicalCardId, + schemeFees, state, type, additionalProperties, @@ -78765,5 +80067,5 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "CardPayment{id=$id, accountId=$accountId, cardId=$cardId, createdAt=$createdAt, digitalWalletTokenId=$digitalWalletTokenId, elements=$elements, physicalCardId=$physicalCardId, state=$state, type=$type, additionalProperties=$additionalProperties}" + "CardPayment{id=$id, accountId=$accountId, cardId=$cardId, createdAt=$createdAt, digitalWalletTokenId=$digitalWalletTokenId, elements=$elements, physicalCardId=$physicalCardId, schemeFees=$schemeFees, state=$state, type=$type, additionalProperties=$additionalProperties}" } diff --git a/increase-java-core/src/test/kotlin/com/increase/api/models/cardpayments/CardPaymentListPageResponseTest.kt b/increase-java-core/src/test/kotlin/com/increase/api/models/cardpayments/CardPaymentListPageResponseTest.kt index 647a0937f..c195b8482 100644 --- a/increase-java-core/src/test/kotlin/com/increase/api/models/cardpayments/CardPaymentListPageResponseTest.kt +++ b/increase-java-core/src/test/kotlin/com/increase/api/models/cardpayments/CardPaymentListPageResponseTest.kt @@ -8283,6 +8283,18 @@ internal class CardPaymentListPageResponseTest { ) ) .physicalCardId(null) + .addSchemeFee( + CardPayment.SchemeFee.builder() + .amount("0.137465") + .createdAt(OffsetDateTime.parse("2020-01-31T23:59:59Z")) + .currency(CardPayment.SchemeFee.Currency.USD) + .feeType( + CardPayment.SchemeFee.FeeType.VISA_CORPORATE_ACCEPTANCE_FEE + ) + .fixedComponent(null) + .variableRate("0.0002") + .build() + ) .state( CardPayment.State.builder() .authorizedAmount(100L) @@ -16101,6 +16113,16 @@ internal class CardPaymentListPageResponseTest { ) ) .physicalCardId(null) + .addSchemeFee( + CardPayment.SchemeFee.builder() + .amount("0.137465") + .createdAt(OffsetDateTime.parse("2020-01-31T23:59:59Z")) + .currency(CardPayment.SchemeFee.Currency.USD) + .feeType(CardPayment.SchemeFee.FeeType.VISA_CORPORATE_ACCEPTANCE_FEE) + .fixedComponent(null) + .variableRate("0.0002") + .build() + ) .state( CardPayment.State.builder() .authorizedAmount(100L) @@ -24391,6 +24413,18 @@ internal class CardPaymentListPageResponseTest { ) ) .physicalCardId(null) + .addSchemeFee( + CardPayment.SchemeFee.builder() + .amount("0.137465") + .createdAt(OffsetDateTime.parse("2020-01-31T23:59:59Z")) + .currency(CardPayment.SchemeFee.Currency.USD) + .feeType( + CardPayment.SchemeFee.FeeType.VISA_CORPORATE_ACCEPTANCE_FEE + ) + .fixedComponent(null) + .variableRate("0.0002") + .build() + ) .state( CardPayment.State.builder() .authorizedAmount(100L) diff --git a/increase-java-core/src/test/kotlin/com/increase/api/models/cardpayments/CardPaymentTest.kt b/increase-java-core/src/test/kotlin/com/increase/api/models/cardpayments/CardPaymentTest.kt index 3d6adfb87..f34e8df2f 100644 --- a/increase-java-core/src/test/kotlin/com/increase/api/models/cardpayments/CardPaymentTest.kt +++ b/increase-java-core/src/test/kotlin/com/increase/api/models/cardpayments/CardPaymentTest.kt @@ -7329,6 +7329,16 @@ internal class CardPaymentTest { ) ) .physicalCardId(null) + .addSchemeFee( + CardPayment.SchemeFee.builder() + .amount("0.137465") + .createdAt(OffsetDateTime.parse("2020-01-31T23:59:59Z")) + .currency(CardPayment.SchemeFee.Currency.USD) + .feeType(CardPayment.SchemeFee.FeeType.VISA_CORPORATE_ACCEPTANCE_FEE) + .fixedComponent(null) + .variableRate("0.0002") + .build() + ) .state( CardPayment.State.builder() .authorizedAmount(100L) @@ -13908,6 +13918,17 @@ internal class CardPaymentTest { .build(), ) assertThat(cardPayment.physicalCardId()).isEmpty + assertThat(cardPayment.schemeFees()) + .containsExactly( + CardPayment.SchemeFee.builder() + .amount("0.137465") + .createdAt(OffsetDateTime.parse("2020-01-31T23:59:59Z")) + .currency(CardPayment.SchemeFee.Currency.USD) + .feeType(CardPayment.SchemeFee.FeeType.VISA_CORPORATE_ACCEPTANCE_FEE) + .fixedComponent(null) + .variableRate("0.0002") + .build() + ) assertThat(cardPayment.state()) .isEqualTo( CardPayment.State.builder() @@ -21242,6 +21263,16 @@ internal class CardPaymentTest { ) ) .physicalCardId(null) + .addSchemeFee( + CardPayment.SchemeFee.builder() + .amount("0.137465") + .createdAt(OffsetDateTime.parse("2020-01-31T23:59:59Z")) + .currency(CardPayment.SchemeFee.Currency.USD) + .feeType(CardPayment.SchemeFee.FeeType.VISA_CORPORATE_ACCEPTANCE_FEE) + .fixedComponent(null) + .variableRate("0.0002") + .build() + ) .state( CardPayment.State.builder() .authorizedAmount(100L) From 6ed23f1144293e561338e597e92d75efa3b9e4d9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:22:55 +0000 Subject: [PATCH 5/8] chore(internal): expand imports --- .../increase/api/core/http/RetryingHttpClient.kt | 2 ++ .../api/core/http/RetryingHttpClientTest.kt | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/increase-java-core/src/main/kotlin/com/increase/api/core/http/RetryingHttpClient.kt b/increase-java-core/src/main/kotlin/com/increase/api/core/http/RetryingHttpClient.kt index 581b2b89b..ff1479ee5 100644 --- a/increase-java-core/src/main/kotlin/com/increase/api/core/http/RetryingHttpClient.kt +++ b/increase-java-core/src/main/kotlin/com/increase/api/core/http/RetryingHttpClient.kt @@ -1,3 +1,5 @@ +// File generated from our OpenAPI spec by Stainless. + package com.increase.api.core.http import com.increase.api.core.DefaultSleeper diff --git a/increase-java-core/src/test/kotlin/com/increase/api/core/http/RetryingHttpClientTest.kt b/increase-java-core/src/test/kotlin/com/increase/api/core/http/RetryingHttpClientTest.kt index 7acabbd07..15bacd974 100644 --- a/increase-java-core/src/test/kotlin/com/increase/api/core/http/RetryingHttpClientTest.kt +++ b/increase-java-core/src/test/kotlin/com/increase/api/core/http/RetryingHttpClientTest.kt @@ -1,6 +1,17 @@ +// File generated from our OpenAPI spec by Stainless. + package com.increase.api.core.http -import com.github.tomakehurst.wiremock.client.WireMock.* +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.resetAllScenarios +import com.github.tomakehurst.wiremock.client.WireMock.serviceUnavailable +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.github.tomakehurst.wiremock.client.WireMock.verify import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo import com.github.tomakehurst.wiremock.junit5.WireMockTest import com.github.tomakehurst.wiremock.stubbing.Scenario From 466bfa9b9808b1f31c8e6f5e60e3982bd8855eff Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 21:17:56 +0000 Subject: [PATCH 6/8] feat(api): api update --- .stats.yml | 4 +- .../WireDrawdownRequestCreateParams.kt | 89 ++++++++++++++++++- .../WireDrawdownRequestCreateParamsTest.kt | 3 + .../WireDrawdownRequestServiceAsyncTest.kt | 1 + .../WireDrawdownRequestServiceTest.kt | 1 + 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7d09a9347..42c775db0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 232 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-949ee3a47313a37ba0147670d429c72ff0cfb477f7abe8f1212dc892ed4ad6d2.yml -openapi_spec_hash: b7a3d5406d046298e53103020cec545d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-5a71b48efd87b38f1e7fd01e83a58b902b2c262b2606c4bf8e1ad0d7f562772e.yml +openapi_spec_hash: a159f26827b08d4900b7b354854535c7 config_hash: 4b562e97b3d8b4cba758a87d4927a76d diff --git a/increase-java-core/src/main/kotlin/com/increase/api/models/wiredrawdownrequests/WireDrawdownRequestCreateParams.kt b/increase-java-core/src/main/kotlin/com/increase/api/models/wiredrawdownrequests/WireDrawdownRequestCreateParams.kt index ad08d67ca..5ab5bae73 100644 --- a/increase-java-core/src/main/kotlin/com/increase/api/models/wiredrawdownrequests/WireDrawdownRequestCreateParams.kt +++ b/increase-java-core/src/main/kotlin/com/increase/api/models/wiredrawdownrequests/WireDrawdownRequestCreateParams.kt @@ -109,6 +109,14 @@ private constructor( */ fun debtorRoutingNumber(): Optional = body.debtorRoutingNumber() + /** + * A free-form reference string set by the sender mirrored back in the subsequent wire transfer. + * + * @throws IncreaseInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun endToEndIdentification(): Optional = body.endToEndIdentification() + /** * Returns the raw JSON value of [accountNumberId]. * @@ -184,6 +192,14 @@ private constructor( */ fun _debtorRoutingNumber(): JsonField = body._debtorRoutingNumber() + /** + * Returns the raw JSON value of [endToEndIdentification]. + * + * Unlike [endToEndIdentification], this method doesn't throw if the JSON field has an + * unexpected type. + */ + fun _endToEndIdentification(): JsonField = body._endToEndIdentification() + fun _additionalBodyProperties(): Map = body._additionalProperties() /** Additional headers to send with the request. */ @@ -396,6 +412,25 @@ private constructor( body.debtorRoutingNumber(debtorRoutingNumber) } + /** + * A free-form reference string set by the sender mirrored back in the subsequent wire + * transfer. + */ + fun endToEndIdentification(endToEndIdentification: String) = apply { + body.endToEndIdentification(endToEndIdentification) + } + + /** + * Sets [Builder.endToEndIdentification] to an arbitrary JSON value. + * + * You should usually call [Builder.endToEndIdentification] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun endToEndIdentification(endToEndIdentification: JsonField) = apply { + body.endToEndIdentification(endToEndIdentification) + } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { body.additionalProperties(additionalBodyProperties) } @@ -558,6 +593,7 @@ private constructor( private val debtorAccountNumber: JsonField, private val debtorExternalAccountId: JsonField, private val debtorRoutingNumber: JsonField, + private val endToEndIdentification: JsonField, private val additionalProperties: MutableMap, ) { @@ -591,6 +627,9 @@ private constructor( @JsonProperty("debtor_routing_number") @ExcludeMissing debtorRoutingNumber: JsonField = JsonMissing.of(), + @JsonProperty("end_to_end_identification") + @ExcludeMissing + endToEndIdentification: JsonField = JsonMissing.of(), ) : this( accountNumberId, amount, @@ -602,6 +641,7 @@ private constructor( debtorAccountNumber, debtorExternalAccountId, debtorRoutingNumber, + endToEndIdentification, mutableMapOf(), ) @@ -690,6 +730,16 @@ private constructor( fun debtorRoutingNumber(): Optional = debtorRoutingNumber.getOptional("debtor_routing_number") + /** + * A free-form reference string set by the sender mirrored back in the subsequent wire + * transfer. + * + * @throws IncreaseInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun endToEndIdentification(): Optional = + endToEndIdentification.getOptional("end_to_end_identification") + /** * Returns the raw JSON value of [accountNumberId]. * @@ -787,6 +837,16 @@ private constructor( @ExcludeMissing fun _debtorRoutingNumber(): JsonField = debtorRoutingNumber + /** + * Returns the raw JSON value of [endToEndIdentification]. + * + * Unlike [endToEndIdentification], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("end_to_end_identification") + @ExcludeMissing + fun _endToEndIdentification(): JsonField = endToEndIdentification + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -831,6 +891,7 @@ private constructor( private var debtorAccountNumber: JsonField = JsonMissing.of() private var debtorExternalAccountId: JsonField = JsonMissing.of() private var debtorRoutingNumber: JsonField = JsonMissing.of() + private var endToEndIdentification: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -845,6 +906,7 @@ private constructor( debtorAccountNumber = body.debtorAccountNumber debtorExternalAccountId = body.debtorExternalAccountId debtorRoutingNumber = body.debtorRoutingNumber + endToEndIdentification = body.endToEndIdentification additionalProperties = body.additionalProperties.toMutableMap() } @@ -994,6 +1056,24 @@ private constructor( this.debtorRoutingNumber = debtorRoutingNumber } + /** + * A free-form reference string set by the sender mirrored back in the subsequent wire + * transfer. + */ + fun endToEndIdentification(endToEndIdentification: String) = + endToEndIdentification(JsonField.of(endToEndIdentification)) + + /** + * Sets [Builder.endToEndIdentification] to an arbitrary JSON value. + * + * You should usually call [Builder.endToEndIdentification] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun endToEndIdentification(endToEndIdentification: JsonField) = apply { + this.endToEndIdentification = endToEndIdentification + } + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -1046,6 +1126,7 @@ private constructor( debtorAccountNumber, debtorExternalAccountId, debtorRoutingNumber, + endToEndIdentification, additionalProperties.toMutableMap(), ) } @@ -1067,6 +1148,7 @@ private constructor( debtorAccountNumber() debtorExternalAccountId() debtorRoutingNumber() + endToEndIdentification() validated = true } @@ -1095,7 +1177,8 @@ private constructor( (if (unstructuredRemittanceInformation.asKnown().isPresent) 1 else 0) + (if (debtorAccountNumber.asKnown().isPresent) 1 else 0) + (if (debtorExternalAccountId.asKnown().isPresent) 1 else 0) + - (if (debtorRoutingNumber.asKnown().isPresent) 1 else 0) + (if (debtorRoutingNumber.asKnown().isPresent) 1 else 0) + + (if (endToEndIdentification.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { @@ -1113,6 +1196,7 @@ private constructor( debtorAccountNumber == other.debtorAccountNumber && debtorExternalAccountId == other.debtorExternalAccountId && debtorRoutingNumber == other.debtorRoutingNumber && + endToEndIdentification == other.endToEndIdentification && additionalProperties == other.additionalProperties } @@ -1128,6 +1212,7 @@ private constructor( debtorAccountNumber, debtorExternalAccountId, debtorRoutingNumber, + endToEndIdentification, additionalProperties, ) } @@ -1135,7 +1220,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Body{accountNumberId=$accountNumberId, amount=$amount, creditorAddress=$creditorAddress, creditorName=$creditorName, debtorAddress=$debtorAddress, debtorName=$debtorName, unstructuredRemittanceInformation=$unstructuredRemittanceInformation, debtorAccountNumber=$debtorAccountNumber, debtorExternalAccountId=$debtorExternalAccountId, debtorRoutingNumber=$debtorRoutingNumber, additionalProperties=$additionalProperties}" + "Body{accountNumberId=$accountNumberId, amount=$amount, creditorAddress=$creditorAddress, creditorName=$creditorName, debtorAddress=$debtorAddress, debtorName=$debtorName, unstructuredRemittanceInformation=$unstructuredRemittanceInformation, debtorAccountNumber=$debtorAccountNumber, debtorExternalAccountId=$debtorExternalAccountId, debtorRoutingNumber=$debtorRoutingNumber, endToEndIdentification=$endToEndIdentification, additionalProperties=$additionalProperties}" } /** The creditor's address. */ diff --git a/increase-java-core/src/test/kotlin/com/increase/api/models/wiredrawdownrequests/WireDrawdownRequestCreateParamsTest.kt b/increase-java-core/src/test/kotlin/com/increase/api/models/wiredrawdownrequests/WireDrawdownRequestCreateParamsTest.kt index 953c6cbd9..ae46fdbc6 100644 --- a/increase-java-core/src/test/kotlin/com/increase/api/models/wiredrawdownrequests/WireDrawdownRequestCreateParamsTest.kt +++ b/increase-java-core/src/test/kotlin/com/increase/api/models/wiredrawdownrequests/WireDrawdownRequestCreateParamsTest.kt @@ -38,6 +38,7 @@ internal class WireDrawdownRequestCreateParamsTest { .debtorAccountNumber("987654321") .debtorExternalAccountId("debtor_external_account_id") .debtorRoutingNumber("101050001") + .endToEndIdentification("x") .build() } @@ -73,6 +74,7 @@ internal class WireDrawdownRequestCreateParamsTest { .debtorAccountNumber("987654321") .debtorExternalAccountId("debtor_external_account_id") .debtorRoutingNumber("101050001") + .endToEndIdentification("x") .build() val body = params._body() @@ -107,6 +109,7 @@ internal class WireDrawdownRequestCreateParamsTest { assertThat(body.debtorAccountNumber()).contains("987654321") assertThat(body.debtorExternalAccountId()).contains("debtor_external_account_id") assertThat(body.debtorRoutingNumber()).contains("101050001") + assertThat(body.endToEndIdentification()).contains("x") } @Test diff --git a/increase-java-core/src/test/kotlin/com/increase/api/services/async/WireDrawdownRequestServiceAsyncTest.kt b/increase-java-core/src/test/kotlin/com/increase/api/services/async/WireDrawdownRequestServiceAsyncTest.kt index 68f966318..5fbafff88 100644 --- a/increase-java-core/src/test/kotlin/com/increase/api/services/async/WireDrawdownRequestServiceAsyncTest.kt +++ b/increase-java-core/src/test/kotlin/com/increase/api/services/async/WireDrawdownRequestServiceAsyncTest.kt @@ -51,6 +51,7 @@ internal class WireDrawdownRequestServiceAsyncTest { .debtorAccountNumber("987654321") .debtorExternalAccountId("debtor_external_account_id") .debtorRoutingNumber("101050001") + .endToEndIdentification("x") .build() ) diff --git a/increase-java-core/src/test/kotlin/com/increase/api/services/blocking/WireDrawdownRequestServiceTest.kt b/increase-java-core/src/test/kotlin/com/increase/api/services/blocking/WireDrawdownRequestServiceTest.kt index 2b12197e8..cb7bac691 100644 --- a/increase-java-core/src/test/kotlin/com/increase/api/services/blocking/WireDrawdownRequestServiceTest.kt +++ b/increase-java-core/src/test/kotlin/com/increase/api/services/blocking/WireDrawdownRequestServiceTest.kt @@ -51,6 +51,7 @@ internal class WireDrawdownRequestServiceTest { .debtorAccountNumber("987654321") .debtorExternalAccountId("debtor_external_account_id") .debtorRoutingNumber("101050001") + .endToEndIdentification("x") .build() ) From b45ebad8a4c7c92df713179c0a3d5164134a493d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:26:29 +0000 Subject: [PATCH 7/8] feat(api): api update --- .stats.yml | 2 +- .../kotlin/com/increase/api/core/http/HttpResponse.kt | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 42c775db0..b138946e6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 232 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-5a71b48efd87b38f1e7fd01e83a58b902b2c262b2606c4bf8e1ad0d7f562772e.yml openapi_spec_hash: a159f26827b08d4900b7b354854535c7 -config_hash: 4b562e97b3d8b4cba758a87d4927a76d +config_hash: b881baa4e0bc537d0caf02152712bd28 diff --git a/increase-java-core/src/main/kotlin/com/increase/api/core/http/HttpResponse.kt b/increase-java-core/src/main/kotlin/com/increase/api/core/http/HttpResponse.kt index 4b78e3de2..46d38f21a 100644 --- a/increase-java-core/src/main/kotlin/com/increase/api/core/http/HttpResponse.kt +++ b/increase-java-core/src/main/kotlin/com/increase/api/core/http/HttpResponse.kt @@ -3,6 +3,7 @@ package com.increase.api.core.http import java.io.InputStream +import java.util.Optional interface HttpResponse : AutoCloseable { @@ -10,6 +11,13 @@ interface HttpResponse : AutoCloseable { fun headers(): Headers + /** + * Returns the value of the `Idempotent-Replayed` header, or an empty [Optional] if there's no + * such header in the response. + */ + fun idempotentReplayed(): Optional = + Optional.ofNullable(headers().values("Idempotent-Replayed").firstOrNull()) + fun body(): InputStream /** Overridden from [AutoCloseable] to not have a checked exception in its signature. */ From f59c29eccfe32802ec90a71bab5b9a4295b9647c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 05:00:14 +0000 Subject: [PATCH 8/8] release: 0.435.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 10 +++++----- build.gradle.kts | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a59deb77e..d260e00dd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.434.0" + ".": "0.435.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3549b54fc..738f0b61d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 0.435.0 (2026-02-25) + +Full Changelog: [v0.434.0...v0.435.0](https://github.com/Increase/increase-java/compare/v0.434.0...v0.435.0) + +### Features + +* **api:** api update ([b45ebad](https://github.com/Increase/increase-java/commit/b45ebad8a4c7c92df713179c0a3d5164134a493d)) +* **api:** api update ([466bfa9](https://github.com/Increase/increase-java/commit/466bfa9b9808b1f31c8e6f5e60e3982bd8855eff)) +* **api:** api update ([8fcb947](https://github.com/Increase/increase-java/commit/8fcb947fdf1b224de813833aaa40ad26d955bd58)) +* **api:** api update ([ced5b7f](https://github.com/Increase/increase-java/commit/ced5b7ff9be983a13a2f1b78d751c94b04b44e59)) + + +### Chores + +* drop apache dependency ([12df111](https://github.com/Increase/increase-java/commit/12df111e728f51766ffc2de3721453745a869339)) +* **internal:** expand imports ([6ed23f1](https://github.com/Increase/increase-java/commit/6ed23f1144293e561338e597e92d75efa3b9e4d9)) +* make `Properties` more resilient to `null` ([ee870a0](https://github.com/Increase/increase-java/commit/ee870a039a548a5c13dd05d5757c72634b744e69)) + ## 0.434.0 (2026-02-23) Full Changelog: [v0.433.0...v0.434.0](https://github.com/Increase/increase-java/compare/v0.433.0...v0.434.0) diff --git a/README.md b/README.md index ca4c4cebd..638071c00 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.increase.api/increase-java)](https://central.sonatype.com/artifact/com.increase.api/increase-java/0.434.0) -[![javadoc](https://javadoc.io/badge2/com.increase.api/increase-java/0.434.0/javadoc.svg)](https://javadoc.io/doc/com.increase.api/increase-java/0.434.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.increase.api/increase-java)](https://central.sonatype.com/artifact/com.increase.api/increase-java/0.435.0) +[![javadoc](https://javadoc.io/badge2/com.increase.api/increase-java/0.435.0/javadoc.svg)](https://javadoc.io/doc/com.increase.api/increase-java/0.435.0) @@ -13,7 +13,7 @@ The Increase Java SDK is similar to the Increase Kotlin SDK but with minor diffe -The REST API documentation can be found on [increase.com](https://increase.com/documentation). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.increase.api/increase-java/0.434.0). +The REST API documentation can be found on [increase.com](https://increase.com/documentation). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.increase.api/increase-java/0.435.0). @@ -24,7 +24,7 @@ The REST API documentation can be found on [increase.com](https://increase.com/d ### Gradle ```kotlin -implementation("com.increase.api:increase-java:0.434.0") +implementation("com.increase.api:increase-java:0.435.0") ``` ### Maven @@ -33,7 +33,7 @@ implementation("com.increase.api:increase-java:0.434.0") com.increase.api increase-java - 0.434.0 + 0.435.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index d94166cef..f59e31b38 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.increase.api" - version = "0.434.0" // x-release-please-version + version = "0.435.0" // x-release-please-version } subprojects {