Skip to content

Commit

Permalink
Make SourceTypeModel.Card.ThreeDSecureStatus an enum (#2562)
Browse files Browse the repository at this point in the history
  • Loading branch information
mshafrir-stripe authored Jun 9, 2020
1 parent 1b70eec commit 655e75e
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 89 deletions.
2 changes: 2 additions & 0 deletions MIGRATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
- Changes to `AddPaymentMethodActivity`
- When `CustomerSession` is instantiated with a `stripeAccountId`, it will be used in `AddPaymentMethodActivity`
when creating a payment method
- Changes to `SourceTypeModel.Card`
- `SourceTypeModel.Card.ThreeDSecureStatus` is now an enum
- Changes to `BankAccount`
- public constructors have been removed
- `BankAccount#accountNumber` has been removed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,8 @@ class CreateCardSourceActivity : AppCompatActivity() {
// Making a note of the Card Source in our list.
sourcesAdapter.addSource(source)

// If we need to get 3DS verification for this card, we first create a
// 3DS Source.
if (SourceTypeModel.Card.ThreeDSecureStatus.REQUIRED == cardData.threeDSecureStatus) {
// If we need to get 3DS verification for this card, we first create a 3DS Source.
if (SourceTypeModel.Card.ThreeDSecureStatus.Required == cardData.threeDSecureStatus) {
// The card Source can be used to create a 3DS Source
createThreeDSecureSource(source)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ internal class SourcesAdapter : RecyclerView.Adapter<SourcesAdapter.ViewHolder>(

private fun getRedirectStatus(source: Source): String? {
return source.redirect?.status
?: (source.sourceTypeModel as SourceTypeModel.Card).threeDSecureStatus
?: (source.sourceTypeModel as SourceTypeModel.Card).threeDSecureStatus.toString()
}
}

Expand Down
46 changes: 21 additions & 25 deletions stripe/src/main/java/com/stripe/android/model/SourceTypeModel.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.stripe.android.model

import androidx.annotation.StringDef
import kotlinx.android.parcel.Parcelize

/**
Expand All @@ -9,33 +8,30 @@ import kotlinx.android.parcel.Parcelize
sealed class SourceTypeModel : StripeModel {
@Parcelize
data class Card internal constructor(
val addressLine1Check: String?,
val addressZipCheck: String?,
val addressLine1Check: String? = null,
val addressZipCheck: String? = null,
val brand: CardBrand,
val country: String?,
val cvcCheck: String?,
val dynamicLast4: String?,
val expiryMonth: Int?,
val expiryYear: Int?,
val funding: CardFunding?,
val last4: String?,
@ThreeDSecureStatus
@get:ThreeDSecureStatus
val threeDSecureStatus: String?,

val country: String? = null,
val cvcCheck: String? = null,
val dynamicLast4: String? = null,
val expiryMonth: Int? = null,
val expiryYear: Int? = null,
val funding: CardFunding? = null,
val last4: String? = null,
val threeDSecureStatus: ThreeDSecureStatus? = null,
val tokenizationMethod: TokenizationMethod? = null
) : SourceTypeModel() {
@Retention(AnnotationRetention.SOURCE)
@StringDef(ThreeDSecureStatus.REQUIRED, ThreeDSecureStatus.OPTIONAL,
ThreeDSecureStatus.NOT_SUPPORTED, ThreeDSecureStatus.RECOMMENDED,
ThreeDSecureStatus.UNKNOWN)
annotation class ThreeDSecureStatus {
companion object {
const val REQUIRED: String = "required"
const val OPTIONAL: String = "optional"
const val NOT_SUPPORTED: String = "not_supported"
const val RECOMMENDED: String = "recommended"
const val UNKNOWN: String = "unknown"
enum class ThreeDSecureStatus(private val code: String) {
Required("required"),
Optional("optional"),
NotSupported("not_supported"),
Recommended("recommended"),
Unknown("unknown");

override fun toString(): String = code

internal companion object {
fun fromCode(code: String?) = values().firstOrNull { it.code == code }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package com.stripe.android.model.parsers

import androidx.annotation.VisibleForTesting
import com.stripe.android.model.CardBrand
import com.stripe.android.model.CardFunding
import com.stripe.android.model.SourceTypeModel
import com.stripe.android.model.StripeJsonUtils
import com.stripe.android.model.TokenizationMethod
import java.util.Locale
import org.json.JSONObject

internal class SourceCardDataJsonParser : ModelJsonParser<SourceTypeModel.Card> {
Expand All @@ -22,7 +20,7 @@ internal class SourceCardDataJsonParser : ModelJsonParser<SourceTypeModel.Card>
expiryYear = StripeJsonUtils.optInteger(json, FIELD_EXP_YEAR),
funding = CardFunding.fromCode(StripeJsonUtils.optString(json, FIELD_FUNDING)),
last4 = StripeJsonUtils.optString(json, FIELD_LAST4),
threeDSecureStatus = asThreeDSecureStatus(
threeDSecureStatus = SourceTypeModel.Card.ThreeDSecureStatus.fromCode(
StripeJsonUtils.optString(json, FIELD_THREE_D_SECURE)
),
tokenizationMethod = TokenizationMethod.fromCode(
Expand All @@ -44,25 +42,5 @@ internal class SourceCardDataJsonParser : ModelJsonParser<SourceTypeModel.Card>
private const val FIELD_LAST4 = "last4"
private const val FIELD_THREE_D_SECURE = "three_d_secure"
private const val FIELD_TOKENIZATION_METHOD = "tokenization_method"

@JvmSynthetic
@VisibleForTesting
@SourceTypeModel.Card.ThreeDSecureStatus
internal fun asThreeDSecureStatus(threeDSecureStatus: String?): String? {
if (threeDSecureStatus == null || StripeJsonUtils.nullIfNullOrEmpty(threeDSecureStatus) == null) {
return null
}

return when (threeDSecureStatus.toLowerCase(Locale.ROOT)) {
SourceTypeModel.Card.ThreeDSecureStatus.REQUIRED -> SourceTypeModel.Card.ThreeDSecureStatus.REQUIRED
SourceTypeModel.Card.ThreeDSecureStatus.OPTIONAL ->
SourceTypeModel.Card.ThreeDSecureStatus.OPTIONAL
SourceTypeModel.Card.ThreeDSecureStatus.NOT_SUPPORTED ->
SourceTypeModel.Card.ThreeDSecureStatus.NOT_SUPPORTED
SourceTypeModel.Card.ThreeDSecureStatus.RECOMMENDED ->
SourceTypeModel.Card.ThreeDSecureStatus.RECOMMENDED
else -> SourceTypeModel.Card.ThreeDSecureStatus.UNKNOWN
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,58 +1,46 @@
package com.stripe.android.model.parsers

import com.google.common.truth.Truth.assertThat
import com.stripe.android.model.CardBrand
import com.stripe.android.model.CardFunding
import com.stripe.android.model.SourceFixtures
import com.stripe.android.model.SourceTypeModel
import com.stripe.android.model.TokenizationMethod
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

class SourceCardDataJsonParserTest {
@Test
fun testAsThreeDSecureStatus() {
assertEquals(SourceTypeModel.Card.ThreeDSecureStatus.REQUIRED,
SourceCardDataJsonParser.asThreeDSecureStatus("required"))
assertEquals(SourceTypeModel.Card.ThreeDSecureStatus.OPTIONAL,
SourceCardDataJsonParser.asThreeDSecureStatus("optional"))
assertEquals(SourceTypeModel.Card.ThreeDSecureStatus.NOT_SUPPORTED,
SourceCardDataJsonParser.asThreeDSecureStatus("not_supported"))
assertEquals(SourceTypeModel.Card.ThreeDSecureStatus.RECOMMENDED,
SourceCardDataJsonParser.asThreeDSecureStatus("recommended"))
assertEquals(SourceTypeModel.Card.ThreeDSecureStatus.UNKNOWN,
SourceCardDataJsonParser.asThreeDSecureStatus("unknown"))
assertNull(SourceCardDataJsonParser.asThreeDSecureStatus(""))
fun `should parse correctly`() {
assertThat(CARD_DATA)
.isEqualTo(
SourceTypeModel.Card(
brand = CardBrand.Visa,
funding = CardFunding.Credit,
last4 = "4242",
expiryMonth = 12,
expiryYear = 2050,
country = "US",
addressLine1Check = "unchecked",
addressZipCheck = "unchecked",
cvcCheck = "unchecked",
dynamicLast4 = "4242",
threeDSecureStatus = SourceTypeModel.Card.ThreeDSecureStatus.Optional,
tokenizationMethod = TokenizationMethod.ApplePay
)
)
}

@Test
fun fromExampleJsonCard_createsExpectedObject() {
assertEquals(CardBrand.Visa, CARD_DATA.brand)
assertEquals(CardFunding.Credit, CARD_DATA.funding)
assertEquals("4242", CARD_DATA.last4)
assertNotNull(CARD_DATA.expiryMonth)
assertNotNull(CARD_DATA.expiryYear)
assertEquals(12, CARD_DATA.expiryMonth)
assertEquals(2050, CARD_DATA.expiryYear)
assertEquals("US", CARD_DATA.country)
assertEquals("optional", CARD_DATA.threeDSecureStatus)
assertEquals(TokenizationMethod.ApplePay, CARD_DATA.tokenizationMethod)
fun `should implement equals correctly`() {
assertThat(PARSER.parse(SourceFixtures.SOURCE_CARD_DATA_WITH_APPLE_PAY_JSON))
.isEqualTo(CARD_DATA)
}

@Test
fun testEquals() {
assertEquals(CARD_DATA,
PARSER.parse(SourceFixtures.SOURCE_CARD_DATA_WITH_APPLE_PAY_JSON))
}

@Test
fun testHashCode() {
assertNotNull(CARD_DATA)
assertEquals(
CARD_DATA.hashCode(),
fun `should implement hashCode correctly`() {
assertThat(
PARSER.parse(SourceFixtures.SOURCE_CARD_DATA_WITH_APPLE_PAY_JSON).hashCode()
)
).isEqualTo(CARD_DATA.hashCode())
}

private companion object {
Expand Down

0 comments on commit 655e75e

Please sign in to comment.