Created
August 13, 2025 16:00
-
-
Save btoconnor/b53925500068b3ded8ab02fd06bbc5c5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/issuing-acs-service/service/src/main/kotlin/com/squareup/cash/issuingacsservice/clients/minos/FakeMinosClient.kt b/issuing-acs-service/service/src/main/kotlin/com/squareup/cash/issuingacsservice/clients/minos/FakeMinosClient.kt | |
index e4e4efd18fe9..403b63c5d726 100644 | |
--- a/issuing-acs-service/service/src/main/kotlin/com/squareup/cash/issuingacsservice/clients/minos/FakeMinosClient.kt | |
+++ b/issuing-acs-service/service/src/main/kotlin/com/squareup/cash/issuingacsservice/clients/minos/FakeMinosClient.kt | |
@@ -11,31 +11,34 @@ import com.squareup.protos.cash.minos.actions.v1.SubmitOTPThreeDSecureAuthentica | |
import com.squareup.protos.cash.minos.actions.v1.SubmitOTPThreeDSecureAuthenticationResultResponse | |
import jakarta.inject.Inject | |
import jakarta.inject.Singleton | |
@Singleton | |
open class FakeMinosClient @Inject constructor() : MinosClient { | |
override fun getAvailableThreeDSecureAuthenticationChannel( | |
request: GetAvailableThreeDSecureAuthenticationChannelRequest | |
): GetAvailableThreeDSecureAuthenticationChannelResponse { | |
if (request.customer_token == "C_user_token") { | |
return GetAvailableThreeDSecureAuthenticationChannelResponse.Builder() | |
.channel(Channel.Builder().type(ChannelType.SMS).identifier("1234567890").build()) | |
.build() | |
} else if (request.customer_token == "C_no_available_channel") { | |
return GetAvailableThreeDSecureAuthenticationChannelResponse.Builder().build() | |
+ } else if (request.customer_token.equals("C_service_unavailable")) { | |
+ throw ServiceUnavailableException("Failed to get authentication channel") | |
} | |
+ | |
return GetAvailableThreeDSecureAuthenticationChannelResponse.Builder() | |
.channel(Channel.Builder().type(ChannelType.EMAIL).identifier("[email protected]").build()) | |
.build() | |
} | |
override fun sendOTPToCustomerForThreeDSecureAuthentication( | |
request: SendOTPToCustomerForThreeDSecureAuthenticationRequest | |
): SendOTPToCustomerForThreeDSecureAuthenticationResponse { | |
if (request.request_token.equals("failed-response-expected")) { | |
throw ServiceUnavailableException("Failed to send OTP") | |
} | |
return SendOTPToCustomerForThreeDSecureAuthenticationResponse.Builder().build() | |
} | |
override fun submitOTPThreeDSecureAuthenticationResult( | |
diff --git a/issuing-acs-service/service/src/main/kotlin/com/squareup/cash/issuingacsservice/handlers/actions/entersekt/GetCardDetailsWebAction.kt b/issuing-acs-service/service/src/main/kotlin/com/squareup/cash/issuingacsservice/handlers/actions/entersekt/GetCardDetailsWebAction.kt | |
index fae2b7b1ddd6..1ae6cadf773a 100644 | |
--- a/issuing-acs-service/service/src/main/kotlin/com/squareup/cash/issuingacsservice/handlers/actions/entersekt/GetCardDetailsWebAction.kt | |
+++ b/issuing-acs-service/service/src/main/kotlin/com/squareup/cash/issuingacsservice/handlers/actions/entersekt/GetCardDetailsWebAction.kt | |
@@ -58,31 +58,47 @@ constructor( | |
issuingAcsServiceMetrics.incrementGetCardDetailsRequestedCount() | |
LogHelper.log( | |
logger, | |
LogLevel.INFO, | |
"Received getCardDetails request", | |
requestToken = requestToken, | |
messageId = getCardDetailsRequest.messageID, | |
) | |
val card = cardController.getCardDetails(pan, expiryMonth, expiryYear, requestToken) | |
// Retrieve the authentication channel only for active cards, as authentication with inactive cards will be | |
// declined before risk evaluation | |
val channel = | |
card?.let { | |
- if (it.state == CardState.ACTIVE) cardController.getAuthenticationChannel(it, requestToken) else null | |
+ if (it.state == CardState.ACTIVE) { | |
+ try { | |
+ cardController.getAuthenticationChannel(it, requestToken) | |
+ } catch (e: Exception) { | |
+ LogHelper.log( | |
+ logger, | |
+ LogLevel.ERROR, | |
+ "Failed to get authentication channel for card", | |
+ e = e, | |
+ requestToken = requestToken, | |
+ cardToken = it.cardToken, | |
+ ) | |
+ null | |
+ } | |
+ } else { | |
+ null | |
+ } | |
} | |
cardController.saveAuthenticationNotificationChannel(UUID.fromString(requestToken), card, channel) | |
issuingAcsServiceMetrics.incrementGetCardDetailsProcessedCount() | |
issuingAcsServiceMetrics.emitRequestProcessLatency( | |
requestName = javaClass.simpleName, | |
latency = Duration.between(receivedTime, Instant.now()).toMillis().toDouble(), | |
) | |
return buildCardDetailsResponse(getCardDetailsRequest, card, channel) | |
} catch (e: JOSEException) { | |
LogHelper.log(logger, LogLevel.ERROR, "Failed to decrypt JWE token", e = e) | |
throw DecryptionFailedException(e.message, e) | |
} catch (e: Exception) { | |
diff --git a/issuing-acs-service/service/src/test/kotlin/com/squareup/cash/issuingacsservice/actions/entersekt/GetCardDetailsWebActionTest.kt b/issuing-acs-service/service/src/test/kotlin/com/squareup/cash/issuingacsservice/actions/entersekt/GetCardDetailsWebActionTest.kt | |
index bdea6847e7b9..1e7980aa501b 100644 | |
--- a/issuing-acs-service/service/src/test/kotlin/com/squareup/cash/issuingacsservice/actions/entersekt/GetCardDetailsWebActionTest.kt | |
+++ b/issuing-acs-service/service/src/test/kotlin/com/squareup/cash/issuingacsservice/actions/entersekt/GetCardDetailsWebActionTest.kt | |
@@ -486,30 +486,57 @@ internal class GetCardDetailsWebActionTest : IssuingAcsServiceTest() { | |
assertEquals(response.card!!.status.toString(), "ACTIVE") | |
assertEquals(response.card!!.expiryMonth, "06") | |
assertEquals(response.card!!.expiryYear, "2099") | |
assertNotNull(response.customer!!.person?.name?.firstName, "first-name") | |
assertNotNull(response.customer!!.person?.name?.lastName, "last-name") | |
assertEquals( | |
response.customer!!.customerDigital!!.authenticationCommsOptions, | |
listOf(EntersektCustomerDigital.AuthenticationCommsOption.SMS), | |
) | |
assertEquals(response.customer!!.customerDigital!!.mobileNumber, TEST_MOBILE_NUMBER) | |
assertNotNull(response.result) | |
assertEquals(response.result!!.action, EntersektResult.EntersektAction.APPROVED) | |
} | |
+ @Test | |
+ fun `Handle error if minos unavailable`() { | |
+ fakeFeatureFlags.overrideKey( | |
+ IssuingAcsServiceFeatureFlags.ENABLE_AUTHENTICATION_NOTIFICATION_CHANNEL, | |
+ "C_user_token", | |
+ true, | |
+ ) | |
+ val request = EncryptedEntersektGetCardDetailsRequest.Builder().encryptedToken(ENTERSEKT_TEST_SUCCESS_JWE_TOKEN) | |
+ .build() | |
+ val response = | |
+ action.GetCardDetails(request) | |
+ | |
+ assertEquals(response.messageID, "messageID") | |
+ assertNotNull(response.card) | |
+ assertNull(response.card!!.pan) | |
+ assertEquals(response.card!!.panReferenceID, "test-token") | |
+ assertEquals(response.card!!.status.toString(), "ACTIVE") | |
+ assertNotNull(response.customer!!.person?.name?.firstName, "first-name") | |
+ assertNotNull(response.customer!!.person?.name?.lastName, "last-name") | |
+ assertEquals( | |
+ response.customer!!.customerDigital!!.authenticationCommsOptions, | |
+ listOf(EntersektCustomerDigital.AuthenticationCommsOption.SMS), | |
+ ) | |
+ assertEquals(response.customer!!.customerDigital!!.mobileNumber, TEST_MOBILE_NUMBER) | |
+ assertEquals(response.customer!!.customerBanking!!.bankingCustomerID, "test-token") | |
+ } | |
+ | |
private fun assertEntersektCustomer( | |
response: EntersektGetCardDetailsResponse, | |
customerToken: String? = "C_usertoken", | |
cardToken: String, | |
cardState: EntersektCard.Status? = EntersektCard.Status.ACTIVE, | |
) { | |
assertEquals(response.customer!!.customerBanking!!.bankingCustomerID, cardToken) | |
assertEquals(response.customer!!.customerDigital!!.digitalCustomerID, customerToken) | |
if (cardState == EntersektCard.Status.ACTIVE) { | |
assertEquals( | |
response.customer!!.customerDigital!!.authenticationCommsOptions, | |
listOf(EntersektCustomerDigital.AuthenticationCommsOption.SMS), | |
) | |
assertEquals(response.customer!!.customerDigital!!.mobileNumber, Card.MOBILE_NUMBER_PLACE_HOLDER) | |
} else { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment