Skip to content

Commit

Permalink
Card Vault iOS Alignment (#195)
Browse files Browse the repository at this point in the history
* Rename card vaulting types to have Card prefix.

* Clean up detekt lint errors.

* Update CHANGELOG.
  • Loading branch information
sshropshire authored Sep 28, 2023
1 parent 9857d30 commit c31da5e
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 46 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
* Make `ThreeDSecureResult` class internal
* Make `Environment` enum associated values internal
* Remove `OrderRequest` class
* Rename `VaultRequest` to `CardVaultRequest`
* Rename `VaultResult` to `CardVaultResult`
* Rename `VaultListener` to `CardVaultListener`
* CorePayments
* Remove `open` modifier on `PayPalSDKError`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import kotlinx.coroutines.launch
* Use this client to approve an order with a [Card].
*
* @property approveOrderListener listener to receive callbacks from [CardClient.approveOrder].
* @property vaultListener listener to receive callbacks form [CardClient.vault].
* @property cardVaultListener listener to receive callbacks form [CardClient.vault].
*/
class CardClient internal constructor(
activity: FragmentActivity,
Expand All @@ -37,7 +37,7 @@ class CardClient internal constructor(
/**
* @suppress
*/
var vaultListener: VaultListener? = null
var cardVaultListener: CardVaultListener? = null

private val lifeCycleObserver = CardLifeCycleObserver(this)

Expand All @@ -46,7 +46,7 @@ class CardClient internal constructor(
}

private val vaultExceptionHandler = CoreCoroutineExceptionHandler { error ->
vaultListener?.onVaultFailure(error)
cardVaultListener?.onVaultFailure(error)
}

private var orderId: String? = null
Expand Down Expand Up @@ -130,20 +130,21 @@ class CardClient internal constructor(
* Call this method to attach a payment source to a setup token.
*
* @param context [Context] Android context
* @param vaultRequest [VaultRequest] request containing details about the setup token and card to use for vaulting.
* @param cardVaultRequest [CardVaultRequest] request containing details about the setup token
* and card to use for vaulting.
*/
fun vault(context: Context, vaultRequest: VaultRequest) {
fun vault(context: Context, cardVaultRequest: CardVaultRequest) {
val applicationContext = context.applicationContext
CoroutineScope(dispatcher).launch(vaultExceptionHandler) {
updateSetupToken(applicationContext, vaultRequest)
updateSetupToken(applicationContext, cardVaultRequest)
}
}

private suspend fun updateSetupToken(context: Context, vaultRequest: VaultRequest) {
val result = vaultRequest.run {
private suspend fun updateSetupToken(context: Context, cardVaultRequest: CardVaultRequest) {
val result = cardVaultRequest.run {
paymentMethodTokensAPI.updateSetupToken(context, setupTokenId, card)
}
vaultListener?.onVaultSuccess(result)
cardVaultListener?.onVaultSuccess(result)
}

internal fun handleBrowserSwitchResult(activity: FragmentActivity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import com.paypal.android.corepayments.PayPalSDKError
*
* Listener to receive callbacks form [CardClient.vault].
*/
interface VaultListener {
interface CardVaultListener {

/**
* Called when a successful vault has occurred.
*/
@MainThread
fun onVaultSuccess(result: VaultResult)
fun onVaultSuccess(result: CardVaultResult)

/**
* Called when a vault failure has occurred.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import kotlinx.parcelize.Parcelize
* @property card card payment source to attach to the setup token.
*/
@Parcelize
data class VaultRequest(
data class CardVaultRequest(
val setupTokenId: String,
val card: Card,
) : Parcelable
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import kotlinx.parcelize.Parcelize
* @param status the status of the updated setup token
*/
@Parcelize
data class VaultResult(
data class CardVaultResult(
val setupTokenId: String,
val status: String
) : Parcelable
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal class DataVaultPaymentMethodTokensAPI internal constructor(
ResourceLoader()
)

suspend fun updateSetupToken(context: Context, setupTokenId: String, card: Card): VaultResult {
suspend fun updateSetupToken(context: Context, setupTokenId: String, card: Card): CardVaultResult {
val query = resourceLoader.loadRawResource(context, R.raw.graphql_query_update_setup_token)

val cardNumber = card.number.replace("\\s".toRegex(), "")
Expand Down Expand Up @@ -58,7 +58,7 @@ internal class DataVaultPaymentMethodTokensAPI internal constructor(
graphQLClient.send(graphQLRequest, queryName = "UpdateVaultSetupToken")
graphQLResponse.data?.let { responseJSON ->
val setupToken = responseJSON.getJSONObject("updateVaultSetupToken")
return VaultResult(
return CardVaultResult(
setupTokenId = setupToken.getString("id"),
status = setupToken.getString("status")
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class CardClientUnitTest {
private val orderId = "sample-order-id"

private val cardRequest = CardRequest(orderId, card, "return_url")
private val vaultRequest = VaultRequest(setupTokenId = "fake-setup-token-id", card = card)
private val cardVaultRequest = CardVaultRequest(setupTokenId = "fake-setup-token-id", card = card)

private val checkoutOrdersAPI = mockk<CheckoutOrdersAPI>(relaxed = true)
private val paymentMethodTokensAPI = mockk<DataVaultPaymentMethodTokensAPI>(relaxed = true)
Expand All @@ -59,7 +59,7 @@ class CardClientUnitTest {
private val browserSwitchClient = mockk<BrowserSwitchClient>(relaxed = true)

private val approveOrderListener = mockk<ApproveOrderListener>(relaxed = true)
private val vaultListener = mockk<VaultListener>(relaxed = true)
private val cardVaultListener = mockk<CardVaultListener>(relaxed = true)

private val applicationContext = ApplicationProvider.getApplicationContext<Application>()

Expand Down Expand Up @@ -180,19 +180,19 @@ class CardClientUnitTest {
fun `vault notifies listener of update setup token success`() = runTest {
val sut = createCardClient(testScheduler)

val vaultResult = VaultResult("fake-setup-token-id-from-result", "fake-status")
val cardVaultResult = CardVaultResult("fake-setup-token-id-from-result", "fake-status")
coEvery {
paymentMethodTokensAPI.updateSetupToken(applicationContext, "fake-setup-token-id", card)
} returns vaultResult
} returns cardVaultResult

sut.vault(activity, vaultRequest)
sut.vault(activity, cardVaultRequest)
advanceUntilIdle()

val resultSlot = slot<VaultResult>()
verify(exactly = 1) { vaultListener.onVaultSuccess(capture(resultSlot)) }
val resultSlot = slot<CardVaultResult>()
verify(exactly = 1) { cardVaultListener.onVaultSuccess(capture(resultSlot)) }

val actual = resultSlot.captured
assertEquals(vaultResult, actual)
assertEquals(cardVaultResult, actual)
}

@Test
Expand All @@ -204,11 +204,11 @@ class CardClientUnitTest {
paymentMethodTokensAPI.updateSetupToken(applicationContext, "fake-setup-token-id", card)
} throws error

sut.vault(activity, vaultRequest)
sut.vault(activity, cardVaultRequest)
advanceUntilIdle()

val errorSlot = slot<PayPalSDKError>()
verify(exactly = 1) { vaultListener.onVaultFailure(capture(errorSlot)) }
verify(exactly = 1) { cardVaultListener.onVaultFailure(capture(errorSlot)) }

val capturedError = errorSlot.captured
assertEquals("mock_error_message", capturedError.errorDescription)
Expand All @@ -225,7 +225,7 @@ class CardClientUnitTest {
dispatcher
)
sut.approveOrderListener = approveOrderListener
sut.vaultListener = vaultListener
sut.cardVaultListener = cardVaultListener
return sut
}

Expand Down
28 changes: 14 additions & 14 deletions Demo/src/main/java/com/paypal/android/ui/vault/VaultFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ import com.paypal.android.api.model.SetupToken
import com.paypal.android.api.services.SDKSampleServerAPI
import com.paypal.android.cardpayments.Card
import com.paypal.android.cardpayments.CardClient
import com.paypal.android.cardpayments.VaultListener
import com.paypal.android.cardpayments.VaultRequest
import com.paypal.android.cardpayments.VaultResult
import com.paypal.android.cardpayments.CardVaultListener
import com.paypal.android.cardpayments.CardVaultRequest
import com.paypal.android.cardpayments.CardVaultResult
import com.paypal.android.corepayments.CoreConfig
import com.paypal.android.corepayments.PayPalSDKError
import com.paypal.android.models.TestCard
Expand Down Expand Up @@ -119,10 +119,10 @@ class VaultFragment : Fragment() {

val configuration = CoreConfig(clientId = clientId)
cardClient = CardClient(requireActivity(), configuration)
cardClient.vaultListener = object : VaultListener {
override fun onVaultSuccess(result: VaultResult) {
cardClient.cardVaultListener = object : CardVaultListener {
override fun onVaultSuccess(result: CardVaultResult) {
viewModel.isUpdateSetupTokenLoading = false
viewModel.vaultResult = result
viewModel.cardVaultResult = result
}

override fun onVaultFailure(error: PayPalSDKError) {
Expand All @@ -132,8 +132,8 @@ class VaultFragment : Fragment() {
}

val card = parseCard(viewModel.uiState.value)
val vaultRequest = VaultRequest(viewModel.setupToken!!.id, card)
cardClient.vault(requireContext(), vaultRequest)
val cardVaultRequest = CardVaultRequest(viewModel.setupToken!!.id, card)
cardClient.vault(requireContext(), cardVaultRequest)
}
}

Expand Down Expand Up @@ -217,9 +217,9 @@ class VaultFragment : Fragment() {
onSubmit = { onAttachCardToSetupTokenSubmit() }
)
}
uiState.vaultResult?.let { vaultResult ->
uiState.cardVaultResult?.let { vaultResult ->
Spacer(modifier = Modifier.size(8.dp))
VaultSuccessView(vaultResult = vaultResult)
VaultSuccessView(cardVaultResult = vaultResult)
Spacer(modifier = Modifier.size(8.dp))
CreatePaymentTokenForm(
uiState = uiState,
Expand All @@ -234,7 +234,7 @@ class VaultFragment : Fragment() {
}

@Composable
fun VaultSuccessView(vaultResult: VaultResult) {
fun VaultSuccessView(cardVaultResult: CardVaultResult) {
OutlinedCard(
modifier = Modifier.fillMaxWidth()
) {
Expand All @@ -245,8 +245,8 @@ class VaultFragment : Fragment() {
text = "Vault Success",
style = MaterialTheme.typography.titleLarge,
)
PropertyView(name = "Setup Token Id", value = vaultResult.setupTokenId)
PropertyView(name = "Status", value = vaultResult.status)
PropertyView(name = "Setup Token Id", value = cardVaultResult.setupTokenId)
PropertyView(name = "Status", value = cardVaultResult.status)
}
}
}
Expand All @@ -270,7 +270,7 @@ class VaultFragment : Fragment() {
"1234",
"fake-card-brand"
),
vaultResult = VaultResult("456", "fake-status")
cardVaultResult = CardVaultResult("456", "fake-status")
),
onCreateSetupTokenSubmit = {},
onAttachCardToSetupTokenSubmit = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.paypal.android.ui.vault

import com.paypal.android.api.model.PaymentToken
import com.paypal.android.api.model.SetupToken
import com.paypal.android.cardpayments.VaultResult
import com.paypal.android.cardpayments.CardVaultResult

data class VaultUiState(
val setupToken: SetupToken? = null,
Expand All @@ -14,5 +14,5 @@ data class VaultUiState(
val cardNumber: String = "",
val cardExpirationDate: String = "",
val cardSecurityCode: String = "",
val vaultResult: VaultResult? = null
val cardVaultResult: CardVaultResult? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.paypal.android.ui.vault
import androidx.lifecycle.ViewModel
import com.paypal.android.api.model.PaymentToken
import com.paypal.android.api.model.SetupToken
import com.paypal.android.cardpayments.VaultResult
import com.paypal.android.cardpayments.CardVaultResult
import com.paypal.android.models.TestCard
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand Down Expand Up @@ -67,10 +67,10 @@ class VaultViewModel : ViewModel() {
_uiState.update { it.copy(cardSecurityCode = value) }
}

var vaultResult: VaultResult?
get() = _uiState.value.vaultResult
var cardVaultResult: CardVaultResult?
get() = _uiState.value.cardVaultResult
set(value) {
_uiState.update { it.copy(vaultResult = value) }
_uiState.update { it.copy(cardVaultResult = value) }
}

fun prefillCard(testCard: TestCard) {
Expand Down

0 comments on commit c31da5e

Please sign in to comment.