Skip to content

Commit

Permalink
PIMOB:2168 - Tokenization Logging was implemented for CVV (#244)
Browse files Browse the repository at this point in the history
  • Loading branch information
chintan-soni-cko authored Oct 11, 2023
1 parent 9013c4a commit 1236bb1
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.checkout.logging.utils.TOKEN_ID
import com.checkout.logging.utils.TOKEN_TYPE
import com.checkout.logging.utils.putErrorAttributes
import com.checkout.network.response.ErrorResponse
import com.checkout.tokenization.response.CVVTokenDetailsResponse
import com.checkout.tokenization.response.TokenDetailsResponse

internal class TokenizationEventLogger(private val logger: Logger<LoggingEvent>) : TokenizationLogger {
Expand All @@ -29,9 +30,19 @@ internal class TokenizationEventLogger(private val logger: Logger<LoggingEvent>)
tokenType: String,
publicKey: String,
tokenDetails: TokenDetailsResponse?,
cvvTokenDetailsResponse: CVVTokenDetailsResponse?,
code: Int?,
errorResponse: ErrorResponse?,
) = logEvent(TokenizationEventType.TOKEN_RESPONSE, tokenType, publicKey, null, tokenDetails, code, errorResponse)
) = logEvent(
tokenizationEventType = TokenizationEventType.TOKEN_RESPONSE,
tokenType = tokenType,
publicKey = publicKey,
error = null,
tokenDetails = tokenDetails,
cvvTokenDetailsResponse = cvvTokenDetailsResponse,
code = code,
errorResponse = errorResponse,
)

override fun resetSession() = logger.resetSession()

Expand All @@ -41,17 +52,19 @@ internal class TokenizationEventLogger(private val logger: Logger<LoggingEvent>)
publicKey: String,
error: Throwable? = null,
tokenDetails: TokenDetailsResponse? = null,
cvvTokenDetailsResponse: CVVTokenDetailsResponse? = null,
code: Int? = null,
errorResponse: ErrorResponse? = null,
) = logger.log(
provideLoggingEvent(
tokenizationEventType,
tokenType,
publicKey,
error,
tokenDetails,
code,
errorResponse,
tokenizationEventType = tokenizationEventType,
tokenType = tokenType,
publicKey = publicKey,
error = error,
tokenDetails = tokenDetails,
cvvTokenDetails = cvvTokenDetailsResponse,
code = code,
errorResponse = errorResponse,
),
)

Expand All @@ -61,6 +74,7 @@ internal class TokenizationEventLogger(private val logger: Logger<LoggingEvent>)
publicKey: String,
error: Throwable?,
tokenDetails: TokenDetailsResponse?,
cvvTokenDetails: CVVTokenDetailsResponse?,
code: Int?,
errorResponse: ErrorResponse?,
): LoggingEvent {
Expand All @@ -75,6 +89,10 @@ internal class TokenizationEventLogger(private val logger: Logger<LoggingEvent>)
properties[SCHEME] = it.scheme ?: ""
}

cvvTokenDetails?.let {
properties[TOKEN_ID] = it.token
}

code?.let {
properties[HTTP_STATUS_CODE] = it
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.checkout.tokenization.logging

import com.checkout.network.response.ErrorResponse
import com.checkout.tokenization.response.CVVTokenDetailsResponse
import com.checkout.tokenization.response.TokenDetailsResponse

/**
Expand All @@ -25,6 +26,7 @@ internal interface TokenizationLogger {
tokenType: String,
publicKey: String,
tokenDetails: TokenDetailsResponse? = null,
cvvTokenDetailsResponse: CVVTokenDetailsResponse? = null,
code: Int? = null,
errorResponse: ErrorResponse? = null,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,32 @@ internal class TokenRepositoryImpl(

@Suppress("TooGenericExceptionCaught")
override fun sendCVVTokenizationRequest(cvvTokenizationRequest: CVVTokenizationRequest) {
var response: NetworkApiResponse<CVVTokenDetailsResponse>

with(cvvTokenizationRequest) {
networkCoroutineScope.launch {
val tokenType = TokenizationConstants.CVV
val validateCVVRequest = ValidateCVVTokenizationRequest(
cvv = cvv,
cardScheme = cardScheme,
)

val validationTokenizationDataResult = validateCVVTokenizationDataUseCase.execute(validateCVVRequest)

val response: NetworkApiResponse<CVVTokenDetailsResponse> = when (validationTokenizationDataResult) {
when (validationTokenizationDataResult) {
is ValidationResult.Failure -> {
NetworkApiResponse.InternalError(validationTokenizationDataResult.error)
response = NetworkApiResponse.InternalError(validationTokenizationDataResult.error)
}

is ValidationResult.Success -> {
networkApiClient.sendCVVTokenRequest(
logger.logTokenRequestEvent(tokenType, publicKey)

response = networkApiClient.sendCVVTokenRequest(
cvvToTokenNetworkRequestMapper.map(from = cvvTokenizationRequest),
)

logCVVTokenizationResponse(response)
logger.resetSession()
}
}

Expand Down Expand Up @@ -195,14 +203,41 @@ internal class TokenRepositoryImpl(
private fun logResponse(response: NetworkApiResponse<TokenDetailsResponse>, tokenType: String) {
when (response) {
is NetworkApiResponse.ServerError -> logger.logTokenResponseEvent(
tokenType,
publicKey,
null,
response.code,
response.body,
tokenType = tokenType,
publicKey = publicKey,
tokenDetails = null,
cvvTokenDetailsResponse = null,
code = response.code,
errorResponse = response.body,
)

is NetworkApiResponse.Success -> logger.logTokenResponseEvent(
tokenType = tokenType,
publicKey = publicKey,
tokenDetails = response.body,
)

else -> {}
}
}

private fun logCVVTokenizationResponse(response: NetworkApiResponse<CVVTokenDetailsResponse>) {
when (response) {
is NetworkApiResponse.ServerError -> logger.logTokenResponseEvent(
tokenType = TokenizationConstants.CVV,
publicKey = publicKey,
tokenDetails = null,
cvvTokenDetailsResponse = null,
code = response.code,
errorResponse = response.body,
)

is NetworkApiResponse.Success -> logger.logTokenResponseEvent(tokenType, publicKey, response.body)
is NetworkApiResponse.Success -> logger.logTokenResponseEvent(
tokenType = TokenizationConstants.CVV,
publicKey = publicKey,
tokenDetails = null,
cvvTokenDetailsResponse = response.body,
)

else -> {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,12 @@ internal class TokenizationEventLoggerTest {

// When
tokenizationEventLogger.logTokenResponseEvent(
tokenType,
"test_key",
null,
501,
TokenizationRequestTestData.errorResponse(),
tokenType = tokenType,
publicKey = "test_key",
tokenDetails = null,
cvvTokenDetailsResponse = null,
code = 501,
errorResponse = TokenizationRequestTestData.errorResponse(),
)

// Then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.checkout.tokenization.repository
import com.checkout.base.model.CardScheme
import com.checkout.base.usecase.UseCase
import com.checkout.mock.TokenizationRequestTestData
import com.checkout.mock.TokenizationRequestTestData.cvvTokenizationRequest
import com.checkout.network.response.ErrorResponse
import com.checkout.network.response.NetworkApiResponse
import com.checkout.tokenization.TokenNetworkApiClient
Expand Down Expand Up @@ -241,11 +242,12 @@ internal class TokenRepositoryImplTest {
} else {
verify(exactly = 1) {
mockTokenizationLogger.logTokenResponseEvent(
eq(TokenizationConstants.CARD),
eq("test_key"),
null,
501,
serverErrorBody,
tokenType = eq(TokenizationConstants.CARD),
publicKey = eq("test_key"),
tokenDetails = null,
cvvTokenDetailsResponse = null,
code = 501,
errorResponse = serverErrorBody,
)
}
}
Expand Down Expand Up @@ -507,11 +509,12 @@ internal class TokenRepositoryImplTest {
} else {
verify(exactly = 1) {
mockTokenizationLogger.logTokenResponseEvent(
eq(TokenizationConstants.GOOGLE_PAY),
eq("test_key"),
null,
501,
serverErrorBody,
tokenType = eq(TokenizationConstants.GOOGLE_PAY),
publicKey = eq("test_key"),
tokenDetails = null,
cvvTokenDetailsResponse = null,
code = 501,
errorResponse = serverErrorBody,
)
}
}
Expand Down Expand Up @@ -567,6 +570,45 @@ internal class TokenRepositoryImplTest {
)
}

@Test
fun `when sendCVVTokenizationRequest invoked with success then log tokenResponseEvent along with resetSession`() {
testCVVTokenizationEventInvocation(true)
}

@Test
fun `when sendCVVTokenizationRequest invoked with servererror then log tokenResponseEvent along with resetSession`() {
testCVVTokenizationEventInvocation(false)
}

@Test
fun `when sendCVVTokenizationRequest invoked then send cvv token request with correct data is invoked`() = runTest {
// Given
val response = mockk<NetworkApiResponse<CVVTokenDetailsResponse>>()

val testDispatcher = UnconfinedTestDispatcher(testScheduler)
Dispatchers.setMain(testDispatcher)

tokenRepositoryImpl.networkCoroutineScope = CoroutineScope(StandardTestDispatcher(testScheduler))

every { mockValidateCVVTokenizationDataUseCase.execute(any()) } returns ValidationResult.Success(Unit)
coEvery { mockTokenNetworkApiClient.sendCVVTokenRequest(any()) } returns response

// When
tokenRepositoryImpl.sendCVVTokenizationRequest(
cvvTokenizationRequest,
)

// Then
launch {
verify(exactly = 1) {
mockTokenizationLogger.logTokenRequestEvent(
TokenizationConstants.CVV,
"test_key",
)
}
}
}

private fun testCVVTokenResultInvocation(
successHandlerInvoked: Boolean,
response: NetworkApiResponse<CVVTokenDetailsResponse>,
Expand Down Expand Up @@ -603,5 +645,59 @@ internal class TokenRepositoryImplTest {
assertEquals(isSuccess.toString(), successHandlerInvoked.toString())
}
}

private fun testCVVTokenizationEventInvocation(isSuccessResponse: Boolean) =
runTest {
// Given
val successBody = mockk<CVVTokenDetailsResponse>()
val serverErrorBody = mockk<ErrorResponse>()

val response = if (isSuccessResponse) {
NetworkApiResponse.Success(successBody)
} else {
NetworkApiResponse.ServerError(serverErrorBody, 501)
}

val testDispatcher = UnconfinedTestDispatcher(testScheduler)
Dispatchers.setMain(testDispatcher)

tokenRepositoryImpl.networkCoroutineScope = CoroutineScope(StandardTestDispatcher(testScheduler))

every { mockValidateCVVTokenizationDataUseCase.execute(any()) } returns ValidationResult.Success(Unit)
coEvery { mockTokenNetworkApiClient.sendCVVTokenRequest(any()) } returns response

// When
tokenRepositoryImpl.sendCVVTokenizationRequest(
cvvTokenizationRequest,
)

// Then
launch {
if (isSuccessResponse) {
verify(exactly = 1) {
mockTokenizationLogger.logTokenResponseEvent(
tokenType = eq(TokenizationConstants.CVV),
publicKey = eq("test_key"),
tokenDetails = null,
cvvTokenDetailsResponse = eq(successBody),

)
}
} else {
verify(exactly = 1) {
mockTokenizationLogger.logTokenResponseEvent(
tokenType = eq(TokenizationConstants.CVV),
publicKey = eq("test_key"),
tokenDetails = null,
cvvTokenDetailsResponse = null,
code = 501,
errorResponse = serverErrorBody,
)
}
}

verify(exactly = 1) { mockTokenizationLogger.resetSession() }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object CustomCVVInputFieldStyle {
fun create() = InputFieldStyle(
placeholderTextId = R.string.enter_cvv_here,
textStyle = TextStyle(16, color = textColor, font = Font.Cursive),
placeholderStyle = TextStyle(16, color = placeHolderTextColor, font = Font.Cursive),
placeholderStyle = TextStyle(16, color = placeHolderTextColor, font = Font.Monospace),
containerStyle = ContainerStyle(
width = 250,
color = backgroundColor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ internal class BillingFormAddressToBillingAddressMapperTest {
}

companion object {
@Suppress("LongMethod")
@JvmStatic
fun testBillingAddressSummaryArguments(): Stream<Arguments> = Stream.of(
Arguments.of(
Expand Down

0 comments on commit 1236bb1

Please sign in to comment.