Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compose credit element #4358

Merged
merged 127 commits into from
Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from 123 commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
c5ef5ec
[WIP]
michelleb-stripe Aug 9, 2021
0c95106
Display and receive input for Credit Card number and CVC.
michelleb-stripe Aug 12, 2021
75e54d1
Add in the credit billing fields, need hidden visibility to work on a…
michelleb-stripe Aug 12, 2021
cd4ff51
Merge with master.
michelleb-stripe Aug 12, 2021
da41eb0
Billing code now shows the correct fields.
michelleb-stripe Aug 12, 2021
a9a04ac
Billing now added to the credit element.
michelleb-stripe Aug 12, 2021
2ceac10
Credit card number length verification corrected.
michelleb-stripe Aug 12, 2021
e35e2c7
Refactor Elements so each element (including sections) provide the fo…
michelleb-stripe Aug 17, 2021
f897f12
Add in the expiration date functionality.
michelleb-stripe Aug 18, 2021
53661e1
Merge branch 'master' into michelleb/credit-card-compose
michelleb-stripe Aug 21, 2021
4f20fb2
Refactor so that each element returns a flow of FormFieldEntries, so …
michelleb-stripe Aug 21, 2021
940f304
Fix unit test
michelleb-stripe Aug 23, 2021
e8fc0ff
Merge with master.
michelleb-stripe Aug 23, 2021
7e5ee6a
Rename class
michelleb-stripe Aug 23, 2021
c3d7b28
More rework
michelleb-stripe Aug 23, 2021
a61ea99
Merge with michelleb/refactor-section-form-fields
michelleb-stripe Aug 23, 2021
b3be29c
Get ready for merge with master
michelleb-stripe Aug 27, 2021
9a258ec
Merge with master.
michelleb-stripe Aug 29, 2021
57b617c
Merge with master
michelleb-stripe Sep 20, 2021
3e60136
Merge with master.
michelleb-stripe Nov 2, 2021
4d8ec20
Change from liveData to flow.
michelleb-stripe Nov 2, 2021
591a73e
Merge branch 'michelleb/convert-to-flow' into michelleb/credit-card-c…
michelleb-stripe Nov 2, 2021
2aeb3ef
Row is working but not expiry date
michelleb-stripe Nov 2, 2021
9161d4f
Make the keypad just numbers.
michelleb-stripe Nov 2, 2021
0441d0a
Make the keypad just numbers.
michelleb-stripe Nov 2, 2021
c41ec5d
Set the expiration month and date correctly in the formFieldValues
michelleb-stripe Nov 2, 2021
fdad41f
Update the focus to visit the CVC
michelleb-stripe Nov 2, 2021
43c9127
Handle a full text field state.
michelleb-stripe Nov 3, 2021
317a841
Merge with master
michelleb-stripe Nov 4, 2021
8e649c8
Unit tests
michelleb-stripe Nov 5, 2021
9ead541
ktlintFormat
michelleb-stripe Nov 5, 2021
939dba9
Format card number based on max pan for card number
michelleb-stripe Nov 5, 2021
60da73f
Cleanup date util for expiration month
michelleb-stripe Nov 5, 2021
f51f90b
Make the label flowable
michelleb-stripe Nov 5, 2021
fef63b3
Fix internals
michelleb-stripe Nov 9, 2021
f19cbe2
Cleanup
michelleb-stripe Nov 9, 2021
07cfa62
Cleanup
michelleb-stripe Nov 9, 2021
bd5d754
Merge with master
michelleb-stripe Nov 9, 2021
2722977
Fix unit tests
michelleb-stripe Nov 9, 2021
4bee0c1
ktlintFormat
michelleb-stripe Nov 9, 2021
4b78747
Undos
michelleb-stripe Nov 9, 2021
9242d80
ktlintFormat
michelleb-stripe Nov 9, 2021
b1cd5b0
Merge with master
michelleb-stripe Nov 11, 2021
239273d
Merge remote-tracking branch 'origin/master' into michelleb/credit-ca…
michelleb-stripe Nov 17, 2021
8eb4204
Merge with master
michelleb-stripe Nov 29, 2021
40fe6c0
Merge with master
michelleb-stripe Dec 2, 2021
adaed05
Ignore tests that cause other failures
michelleb-stripe Dec 5, 2021
667bd39
Merge branch 'master' into michelleb/credit-card-compose
michelleb-stripe Feb 3, 2022
7c5e474
Working on the merge.
michelleb-stripe Feb 4, 2022
1eee681
Almost building
michelleb-stripe Feb 4, 2022
7ed49f3
Get closer to master
michelleb-stripe Feb 4, 2022
e68a86a
Update files.
michelleb-stripe Feb 4, 2022
f0fa03a
Iterating
michelleb-stripe Feb 4, 2022
5ad3f10
Building
michelleb-stripe Feb 4, 2022
a3c9eae
Fix unit tests
michelleb-stripe Feb 4, 2022
e2f39c4
Cleanup
michelleb-stripe Feb 8, 2022
57677d8
Remove extra comment
michelleb-stripe Feb 8, 2022
6bee0a4
Fix failing tests
epan-stripe Feb 8, 2022
2c6e45c
Fix linting
epan-stripe Feb 8, 2022
8b5d2ae
Fix SaveForFutureUseController.label not showing
epan-stripe Feb 8, 2022
313f52a
Merge with master
michelleb-stripe Feb 8, 2022
c1e78f7
Cleanup
michelleb-stripe Feb 8, 2022
6c9cb94
apiDump
michelleb-stripe Feb 9, 2022
508b5ad
Merge with master
michelleb-stripe Feb 9, 2022
33b754a
apiDump
michelleb-stripe Feb 9, 2022
7819db6
Fix some failing tests, apiDump, ktlintFormat
michelleb-stripe Feb 10, 2022
b3917f9
Fix remaining failing tests, apiDump, ktlintFormat
michelleb-stripe Feb 10, 2022
95d8557
Fix todo
michelleb-stripe Feb 10, 2022
7af4ff5
Merge remote-tracking branch 'origin/master' into michelleb/credit-ca…
michelleb-stripe Feb 10, 2022
afd06c9
Simplify and add DateConfig tests and cleanup commented out code in T…
michelleb-stripe Feb 11, 2022
98043bd
Merge with master
michelleb-stripe Feb 11, 2022
8ca28d2
Merge remote-tracking branch 'origin/master' into michelleb/credit-ca…
michelleb-stripe Feb 14, 2022
9bf81f2
ktlintFormat apiDump
michelleb-stripe Feb 14, 2022
783039a
Merge with master
michelleb-stripe Feb 15, 2022
bf51053
Add icons to the IBAN, credit card and CVC fields. (#4359)
michelleb-stripe Feb 18, 2022
7979d18
Merge with master
michelleb-stripe Feb 22, 2022
bd3fb81
Merge with remote
michelleb-stripe Feb 22, 2022
a18c6c2
Add Card Metadata Service to CardNumberController (#4573)
epan-stripe Feb 25, 2022
b4cacba
Don't show cardbrand if there are multiple possibilities
epan-stripe Feb 25, 2022
06c5d00
Don't show cardbrand if there are multiple possibilities
epan-stripe Feb 25, 2022
c179b73
Merge branch 'michelleb/credit-card-compose' of github.com:stripe/str…
epan-stripe Feb 25, 2022
61200f9
Merge remote-tracking branch 'origin/master' into michelleb/credit-ca…
michelleb-stripe Feb 25, 2022
110ecd3
Merge with master
michelleb-stripe Feb 25, 2022
067ea3b
Material Theme information gathering.
michelleb-stripe Feb 25, 2022
36913d3
Revert "Material Theme information gathering."
michelleb-stripe Feb 25, 2022
167b00f
Immediately detect invalid card numbers (no brand)
epan-stripe Mar 1, 2022
8701f44
Fix invalid vs incomplete dateconfig error text
epan-stripe Mar 1, 2022
b6fff48
Fix CVC icon and next field focus
epan-stripe Mar 2, 2022
3ed0104
Fix expiry date accessibility reader
epan-stripe Mar 2, 2022
7f3d2f2
Merge branch 'master' into michelleb/credit-card-compose
epan-stripe Mar 2, 2022
d92df95
Fix merge issues
epan-stripe Mar 2, 2022
e2880f4
Add ability to move focus on delete
jameswoo-stripe Mar 3, 2022
81042c0
Merge remote-tracking branch 'origin/master' into michelleb/credit-ca…
michelleb-stripe Mar 3, 2022
219e999
Fix dark mode error underline
epan-stripe Mar 3, 2022
126de9a
Fix cursor and spacing position in card details
epan-stripe Mar 4, 2022
10f4f47
update icons and colors for future wardrobe work
skyler-stripe Mar 4, 2022
da16b49
Merge branch 'master' into michelleb/credit-card-compose
michelleb-stripe Mar 8, 2022
0d7f077
Merge branch 'michelleb/credit-card-compose' of github.com:stripe/str…
michelleb-stripe Mar 8, 2022
ba21e4a
Add card information title
epan-stripe Mar 8, 2022
0c87580
Add tests for card number formatting
epan-stripe Mar 9, 2022
7a60b61
Revert "Add tests for card number formatting"
epan-stripe Mar 9, 2022
d2ff7ba
Add country list to card and sepa billing spec. (#4669)
michelleb-stripe Mar 9, 2022
55a143b
Credit Card Address and focus bug fixes (#4665)
michelleb-stripe Mar 9, 2022
f111ac0
Fix width of rows (#4676)
epan-stripe Mar 11, 2022
f3d960b
Fix spacing for AMEX and Discover cards (#4672)
epan-stripe Mar 11, 2022
f8c7ec7
Don't show CVC error when CardBrand is unknown (#4681)
epan-stripe Mar 14, 2022
5fbad06
Stop accessibility talkback reading textfields twice (#4683)
epan-stripe Mar 14, 2022
c6363f2
Merge with master
michelleb-stripe Mar 14, 2022
c5c70e5
Merge branch 'michelleb/credit-card-compose' of github.com:stripe/str…
michelleb-stripe Mar 14, 2022
2ba55b8
ktformat
michelleb-stripe Mar 14, 2022
6fb7a3f
Fix detekt
michelleb-stripe Mar 14, 2022
b9095ff
checkbox colors read from theme object now
skyler-stripe Mar 15, 2022
23a2a48
Fix the delete button move to previous. (#4706)
michelleb-stripe Mar 15, 2022
e23283b
Improve performance of dropdown in forms. (#4705)
michelleb-stripe Mar 16, 2022
eafd4c3
Merge with master. (Sepa debit address is broken - the row fields ar…
michelleb-stripe Mar 16, 2022
e89c5c1
Card now showing up correctly.
michelleb-stripe Mar 16, 2022
3b818e7
Merge branch 'michelleb/credit-card-compose' of github.com:stripe/str…
michelleb-stripe Mar 16, 2022
4119def
Fix compile error.
michelleb-stripe Mar 16, 2022
33a004c
Don't show cardbrand if there are multiple possible (#4682)
epan-stripe Mar 16, 2022
cd76630
Merge branch 'master' into michelleb/credit-card-compose
michelleb-stripe Mar 17, 2022
bcc452e
Merge branch 'michelleb/credit-card-compose' of github.com:stripe/str…
michelleb-stripe Mar 17, 2022
e3c29bb
Merge remote-tracking branch 'origin/master' into michelleb/credit-ca…
michelleb-stripe Mar 20, 2022
e79cfdd
merge master
skyler-stripe Mar 24, 2022
8f8c288
Merge remote-tracking branch 'origin/master' into michelleb/credit-ca…
michelleb-stripe Mar 30, 2022
c777dfc
Merge with master
michelleb-stripe Mar 31, 2022
f2e59b9
Fix merge errors.
michelleb-stripe Mar 31, 2022
51dfa5d
Update the changedoc.
michelleb-stripe Mar 31, 2022
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This release enables a new configuration object to be defined for StripeCardScan

### PaymentSheet
* [FIXED] [4646](https://github.com/stripe/stripe-android/pull/4646) Update 3ds2 to latest version 6.1.4, see PR for specific issues addressed.
* [FIXED] [4669](https://github.com/stripe/stripe-android/pull/4669) Restrict the list of SEPA debit supported countries.

### CardScan
* [ADDED] [4689](https://github.com/stripe/stripe-android/pull/4689) The `CardImageVerificationSheet` initializer can now take an additional `Configuration` object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ private fun EmailCollectionSection(
listOf(emailElement.sectionFieldErrorController())
)
),
emptyList()
emptyList(),
emailElement.identifier
)
if (signUpState == SignUpState.VerifyingEmail) {
CircularProgressIndicator(
Expand Down
29 changes: 29 additions & 0 deletions payments-core/api/payments-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,32 @@ public final class com/stripe/android/StripeKtxKt {
public static synthetic fun retrieveSource$default (Lcom/stripe/android/Stripe;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public abstract interface class com/stripe/android/cards/CardAccountRangeRepository$Factory {
public abstract fun create ()Lcom/stripe/android/cards/CardAccountRangeRepository;
}

public abstract interface class com/stripe/android/cards/CardAccountRangeService$AccountRangeResultListener {
public abstract fun onAccountRangeResult (Lcom/stripe/android/model/AccountRange;)V
}

public final class com/stripe/android/cards/CardNumber$Companion {
}

public final class com/stripe/android/cards/CardNumber$Unvalidated : com/stripe/android/cards/CardNumber {
public static final field $stable I
public fun <init> (Ljava/lang/String;)V
public final fun copy (Ljava/lang/String;)Lcom/stripe/android/cards/CardNumber$Unvalidated;
public static synthetic fun copy$default (Lcom/stripe/android/cards/CardNumber$Unvalidated;Ljava/lang/String;ILjava/lang/Object;)Lcom/stripe/android/cards/CardNumber$Unvalidated;
public fun equals (Ljava/lang/Object;)Z
public final fun getBin ()Lcom/stripe/android/cards/Bin;
public final fun getLength ()I
public final fun getNormalized ()Ljava/lang/String;
public fun hashCode ()I
public final fun isMaxLength ()Z
public final fun isValidLuhn ()Z
public fun toString ()Ljava/lang/String;
}

public final class com/stripe/android/exception/AuthenticationException : com/stripe/android/core/exception/StripeException {
public static final field $stable I
}
Expand Down Expand Up @@ -5757,8 +5783,11 @@ public final class com/stripe/android/view/CardNumberEditText : com/stripe/andro
public fun <init> (Landroid/content/Context;Landroid/util/AttributeSet;)V
public fun <init> (Landroid/content/Context;Landroid/util/AttributeSet;I)V
public synthetic fun <init> (Landroid/content/Context;Landroid/util/AttributeSet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAccountRangeService ()Lcom/stripe/android/cards/CardAccountRangeService;
public final fun getCardBrand ()Lcom/stripe/android/model/CardBrand;
public final fun getWorkContext ()Lkotlin/coroutines/CoroutineContext;
public final fun isCardNumberValid ()Z
public final fun setWorkContext (Lkotlin/coroutines/CoroutineContext;)V
}

public abstract interface class com/stripe/android/view/CardValidCallback {
Expand Down
10 changes: 8 additions & 2 deletions payments-core/src/main/java/com/stripe/android/CardUtils.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.stripe.android

import androidx.annotation.RestrictTo
import com.stripe.android.cards.CardNumber
import com.stripe.android.model.CardBrand

Expand All @@ -13,7 +14,10 @@ object CardUtils {
* @return the [CardBrand] that matches the card number based on prefixes,
* or [CardBrand.Unknown] if it can't be determined
*/
@Deprecated("CardInputWidget and CardMultilineWidget handle card brand lookup. This method should not be relied on for determining CardBrand.")
@Deprecated(
"CardInputWidget and CardMultilineWidget handle card brand lookup. " +
"This method should not be relied on for determining CardBrand."
)
@JvmStatic
fun getPossibleCardBrand(cardNumber: String?): CardBrand {
return if (cardNumber.isNullOrBlank()) {
Expand All @@ -29,7 +33,9 @@ object CardUtils {
* @param cardNumber a String that may or may not represent a valid Luhn number
* @return `true` if and only if the input value is a valid Luhn number
*/
internal fun isValidLuhnNumber(cardNumber: String?): Boolean {
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@SuppressWarnings("ReturnCount")
fun isValidLuhnNumber(cardNumber: String?): Boolean {
if (cardNumber == null) {
return false
}
Expand Down
6 changes: 4 additions & 2 deletions payments-core/src/main/java/com/stripe/android/cards/Bin.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.stripe.android.cards

import androidx.annotation.RestrictTo
import com.stripe.android.core.model.StripeModel
import kotlinx.parcelize.Parcelize

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Parcelize
internal data class Bin internal constructor(
data class Bin internal constructor(
internal val value: String
) : StripeModel {
override fun toString() = value

companion object {
internal companion object {
fun create(cardNumber: String): Bin? {
return cardNumber
.take(BIN_LENGTH)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.stripe.android.cards

import androidx.annotation.RestrictTo
import com.stripe.android.model.AccountRange
import kotlinx.coroutines.flow.Flow

internal interface CardAccountRangeRepository {
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface CardAccountRangeRepository {
suspend fun getAccountRange(
cardNumber: CardNumber.Unvalidated
): AccountRange?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.stripe.android.cards

import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import com.stripe.android.model.AccountRange
import com.stripe.android.model.CardBrand
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class CardAccountRangeService constructor(
private val cardAccountRangeRepository: CardAccountRangeRepository,
private val workContext: CoroutineContext,
val staticCardAccountRanges: StaticCardAccountRanges,
private val accountRangeResultListener: AccountRangeResultListener
) {

val isLoading: Flow<Boolean> = cardAccountRangeRepository.loading

var accountRange: AccountRange? = null
private set

@VisibleForTesting
var accountRangeRepositoryJob: Job? = null

fun onCardNumberChanged(cardNumber: CardNumber.Unvalidated) {
val staticAccountRange = staticCardAccountRanges.filter(cardNumber)
.let { accountRanges ->
if (accountRanges.size == 1) {
accountRanges.first()
} else {
null
}
}
if (staticAccountRange == null || shouldQueryRepository(staticAccountRange)) {
// query for AccountRange data
queryAccountRangeRepository(cardNumber)
} else {
// use static AccountRange data
updateAccountRangeResult(staticAccountRange)
}
}

@JvmSynthetic
fun queryAccountRangeRepository(cardNumber: CardNumber.Unvalidated) {
if (shouldQueryAccountRange(cardNumber)) {
// cancel in-flight job
cancelAccountRangeRepositoryJob()

// invalidate accountRange before fetching
accountRange = null

accountRangeRepositoryJob = CoroutineScope(workContext).launch {
val bin = cardNumber.bin
val accountRange = if (bin != null) {
cardAccountRangeRepository.getAccountRange(cardNumber)
} else {
null
}

withContext(Dispatchers.Main) {
updateAccountRangeResult(accountRange)
}
}
}
}

fun cancelAccountRangeRepositoryJob() {
accountRangeRepositoryJob?.cancel()
accountRangeRepositoryJob = null
}

@JvmSynthetic
fun updateAccountRangeResult(
newAccountRange: AccountRange?
) {
accountRange = newAccountRange
accountRangeResultListener.onAccountRangeResult(accountRange)
}

private fun shouldQueryRepository(
accountRange: AccountRange
) = when (accountRange.brand) {
CardBrand.Unknown,
CardBrand.UnionPay -> true
else -> false
}

private fun shouldQueryAccountRange(cardNumber: CardNumber.Unvalidated): Boolean {
return accountRange == null ||
cardNumber.bin == null ||
accountRange?.binRange?.matches(cardNumber) == false
}

interface AccountRangeResultListener {
fun onAccountRangeResult(newAccountRange: AccountRange?)
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package com.stripe.android.cards

import androidx.annotation.RestrictTo
import com.stripe.android.CardUtils
import com.stripe.android.model.CardBrand

internal sealed class CardNumber {
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
sealed class CardNumber {

/**
* A representation of a partial or full card number that hasn't been validated.
*/
internal data class Unvalidated internal constructor(
data class Unvalidated constructor(
private val denormalized: String
) : CardNumber() {
val normalized = denormalized.filterNot { REJECT_CHARS.contains(it) }
Expand All @@ -21,7 +23,7 @@ internal sealed class CardNumber {

val isValidLuhn = CardUtils.isValidLuhnNumber(normalized)

fun validate(panLength: Int): Validated? {
internal fun validate(panLength: Int): Validated? {
return if (panLength >= MIN_PAN_LENGTH &&
normalized.length == panLength &&
isValidLuhn
Expand All @@ -41,7 +43,7 @@ internal sealed class CardNumber {
* `"424242"` with pan length `16` will return `"4242 42"`;
* `"4242424242424242"` with pan length `14` will return `"4242 424242 4242"`
*/
fun getFormatted(
internal fun getFormatted(
panLength: Int = DEFAULT_PAN_LENGTH
) = formatNumber(panLength)

Expand Down Expand Up @@ -96,17 +98,17 @@ internal sealed class CardNumber {
/**
* A representation of a client-side validated card number.
*/
internal data class Validated internal constructor(
internal data class Validated constructor(
internal val value: String
) : CardNumber()

internal companion object {
companion object {
internal fun getSpacePositions(panLength: Int) = SPACE_POSITIONS[panLength]
?: DEFAULT_SPACE_POSITIONS

internal const val MIN_PAN_LENGTH = 14
internal const val MAX_PAN_LENGTH = 19
internal const val DEFAULT_PAN_LENGTH = 16
const val DEFAULT_PAN_LENGTH = 16
private val DEFAULT_SPACE_POSITIONS = setOf(4, 9, 14)

private val SPACE_POSITIONS = mapOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.stripe.android.cards

import android.content.Context
import androidx.annotation.RestrictTo
import com.stripe.android.PaymentConfiguration
import com.stripe.android.core.networking.AnalyticsRequestExecutor
import com.stripe.android.core.networking.ApiRequest
Expand All @@ -17,7 +18,8 @@ import kotlinx.coroutines.flow.flowOf
*
* Will throw an exception if [PaymentConfiguration] has not been instantiated.
*/
internal class DefaultCardAccountRangeRepositoryFactory(
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class DefaultCardAccountRangeRepositoryFactory(
context: Context,
private val analyticsRequestExecutor: AnalyticsRequestExecutor
) : CardAccountRangeRepository.Factory {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.stripe.android.cards

import androidx.annotation.RestrictTo
import com.stripe.android.model.AccountRange
import com.stripe.android.model.BinRange

internal class DefaultStaticCardAccountRanges : StaticCardAccountRanges {
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class DefaultStaticCardAccountRanges : StaticCardAccountRanges {
override fun first(
cardNumber: CardNumber.Unvalidated
) = filter(cardNumber).firstOrNull()
Expand Down Expand Up @@ -99,9 +101,14 @@ internal class DefaultStaticCardAccountRanges : StaticCardAccountRanges {
)
}

private val UNIONPAY_ACCOUNTS = setOf(
private val UNIONPAY16_ACCOUNTS = setOf(
BinRange(
low = "6200000000000000",
high = "6216828049999999"
),

BinRange(
low = "6216828060000000",
high = "6299999999999999"
),

Expand All @@ -117,6 +124,19 @@ internal class DefaultStaticCardAccountRanges : StaticCardAccountRanges {
)
}

private val UNIONPAY19_ACCOUNTS = setOf(
BinRange(
low = "6216828050000000000",
high = "6216828059999999999"
)
).map {
AccountRange(
binRange = it,
panLength = 19,
brandInfo = AccountRange.BrandInfo.UnionPay
)
}

private val DINERSCLUB16_ACCOUNT_RANGES = setOf(
BinRange(
low = "3000000000000000",
Expand Down Expand Up @@ -159,7 +179,8 @@ internal class DefaultStaticCardAccountRanges : StaticCardAccountRanges {
.plus(AMEX_ACCOUNTS)
.plus(DISCOVER_ACCOUNTS)
.plus(JCB_ACCOUNTS)
.plus(UNIONPAY_ACCOUNTS)
.plus(UNIONPAY16_ACCOUNTS)
.plus(UNIONPAY19_ACCOUNTS)
.plus(DINERSCLUB16_ACCOUNT_RANGES)
.plus(DINERSCLUB14_ACCOUNT_RANGES)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.stripe.android.cards

import androidx.annotation.RestrictTo
import com.stripe.android.model.AccountRange
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf

/**
* A [CardAccountRangeSource] that uses a local, static source of BIN ranges.
*/
internal class StaticCardAccountRangeSource(
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class StaticCardAccountRangeSource(
private val accountRanges: StaticCardAccountRanges = DefaultStaticCardAccountRanges()
) : CardAccountRangeSource {
override val loading: Flow<Boolean> = flowOf(false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.stripe.android.cards

import androidx.annotation.RestrictTo
import com.stripe.android.model.AccountRange

internal interface StaticCardAccountRanges {
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface StaticCardAccountRanges {
/**
* Return the first [AccountRange] that contains the given [cardNumber], or `null`.
*/
Expand Down
Loading