Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2026 FinCore Engine Authors

package com.fincore.compliance.infrastructure.kyc

import com.fincore.compliance.application.kyc.KycCheckRequest
import com.fincore.compliance.application.kyc.KycCheckResult
import com.fincore.compliance.application.kyc.KycProvider
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Component

/**
* Deterministic in-tree sandbox KYC provider, off by default. Encodes no real provider logic; the outcome is keyed on
* documented case-insensitive markers in the opaque subject reference: "reject" -> Rejected, "pending" -> Pending,
* "insufficient" -> InsufficientData, otherwise -> Approved. The same reference always yields the same result.
*/
@Component
@ConditionalOnProperty(
prefix = "fincore.compliance.kyc.sandbox",
name = ["enabled"],
havingValue = "true",
matchIfMissing = false,
)
class SandboxKycProvider : KycProvider {
override fun check(request: KycCheckRequest): KycCheckResult {
val reference = request.subjectReference
val providerReference = "sbx-kyc-${reference.hashCode().toUInt()}"
return when {
reference.contains(REJECT_MARKER, ignoreCase = true) ->
KycCheckResult.Rejected("sandbox rejected by subject marker")
reference.contains(PENDING_MARKER, ignoreCase = true) ->
KycCheckResult.Pending(providerReference)
reference.contains(INSUFFICIENT_MARKER, ignoreCase = true) ->
KycCheckResult.InsufficientData(listOf("sandbox.insufficient"))
else -> KycCheckResult.Approved(providerReference)
}
}

private companion object {
const val REJECT_MARKER = "reject"
const val PENDING_MARKER = "pending"
const val INSUFFICIENT_MARKER = "insufficient"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2026 FinCore Engine Authors

package com.fincore.compliance.infrastructure.kyc

import com.fincore.compliance.application.kyc.KycCheckRequest
import com.fincore.compliance.application.kyc.KycCheckResult
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import org.junit.jupiter.api.Test

class SandboxKycProviderTest {
private val provider = SandboxKycProvider()

@Test
fun `should approve a plain subject reference`() {
provider.check(KycCheckRequest("subject-1")).shouldBeInstanceOf<KycCheckResult.Approved>()
}

@Test
fun `should reject when the reference carries the reject marker`() {
provider.check(KycCheckRequest("reject-subject")).shouldBeInstanceOf<KycCheckResult.Rejected>()
}

@Test
fun `should return pending when the reference carries the pending marker`() {
provider.check(KycCheckRequest("pending-subject")).shouldBeInstanceOf<KycCheckResult.Pending>()
}

@Test
fun `should return insufficient data with a non-empty list when marked`() {
val result = provider.check(KycCheckRequest("insufficient-subject"))

result.shouldBeInstanceOf<KycCheckResult.InsufficientData>().missing shouldBe listOf("sandbox.insufficient")
}

@Test
fun `should be deterministic for the same subject reference`() {
val first = provider.check(KycCheckRequest("subject-1"))
val second = provider.check(KycCheckRequest("subject-1"))

first shouldBe second
}
}
Loading