-
Notifications
You must be signed in to change notification settings - Fork 659
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
[PaymentSheet] Fix issue when used with hyperion and mochi #5321
Changes from all commits
a9a4adb
bdd3983
3b0ec9f
3de105a
13289cc
7942082
1037db0
fe084c1
ab21583
08238d4
7edd9c1
5f3ed37
ccf2327
5ed9be5
d9d4d54
759f9de
9cd020c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,63 +12,81 @@ import com.stripe.android.ui.core.elements.SectionSingleFieldElement | |
import com.stripe.android.ui.core.elements.SimpleTextElement | ||
import com.stripe.android.ui.core.elements.SimpleTextFieldConfig | ||
import com.stripe.android.ui.core.elements.SimpleTextFieldController | ||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.decodeFromString | ||
import kotlinx.serialization.descriptors.PrimitiveKind | ||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor | ||
import kotlinx.serialization.descriptors.SerialDescriptor | ||
import kotlinx.serialization.encoding.Decoder | ||
import kotlinx.serialization.encoding.Encoder | ||
import kotlinx.serialization.json.Json | ||
import java.io.InputStream | ||
import java.util.UUID | ||
|
||
@Serializable(with = FieldTypeAsStringSerializer::class) | ||
@Serializable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactored this so we don't require a special deserializer. |
||
internal enum class FieldType( | ||
val serializedValue: String, | ||
val identifierSpec: IdentifierSpec, | ||
@StringRes val defaultLabel: Int, | ||
val capitalization: KeyboardCapitalization | ||
@StringRes val defaultLabel: Int | ||
) { | ||
@SerialName("addressLine1") | ||
AddressLine1( | ||
"addressLine1", | ||
IdentifierSpec.Line1, | ||
R.string.address_label_address_line1, | ||
KeyboardCapitalization.Words | ||
R.string.address_label_address_line1 | ||
), | ||
|
||
@SerialName("addressLine2") | ||
AddressLine2( | ||
"addressLine2", | ||
IdentifierSpec.Line2, | ||
R.string.address_label_address_line2, | ||
KeyboardCapitalization.Words | ||
R.string.address_label_address_line2 | ||
), | ||
|
||
@SerialName("locality") | ||
Locality( | ||
"locality", | ||
IdentifierSpec.City, | ||
R.string.address_label_city, | ||
KeyboardCapitalization.Words | ||
R.string.address_label_city | ||
), | ||
|
||
@SerialName("dependentLocality") | ||
DependentLocality( | ||
"dependentLocality", | ||
IdentifierSpec.DependentLocality, | ||
R.string.address_label_city | ||
), | ||
|
||
@SerialName("postalCode") | ||
PostalCode( | ||
"postalCode", | ||
IdentifierSpec.PostalCode, | ||
R.string.address_label_postal_code, | ||
KeyboardCapitalization.None | ||
), | ||
R.string.address_label_postal_code | ||
) { | ||
override fun capitalization() = KeyboardCapitalization.None | ||
}, | ||
|
||
@SerialName("sortingCode") | ||
SortingCode( | ||
"sortingCode", | ||
IdentifierSpec.SortingCode, | ||
R.string.address_label_postal_code | ||
) { | ||
override fun capitalization() = KeyboardCapitalization.None | ||
}, | ||
|
||
@SerialName("administrativeArea") | ||
AdministrativeArea( | ||
"administrativeArea", | ||
IdentifierSpec.State, | ||
NameType.State.stringResId, | ||
KeyboardCapitalization.Words | ||
NameType.State.stringResId | ||
), | ||
|
||
@SerialName("name") | ||
Name( | ||
"name", | ||
IdentifierSpec.Name, | ||
R.string.address_label_name, | ||
KeyboardCapitalization.Words | ||
R.string.address_label_name | ||
); | ||
|
||
open fun capitalization() = KeyboardCapitalization.Words | ||
|
||
companion object { | ||
fun from(value: String) = values().firstOrNull { | ||
it.serializedValue == value | ||
|
@@ -167,7 +185,7 @@ internal class FieldSchema( | |
@SerialName("isNumeric") | ||
val isNumeric: Boolean = false, | ||
@SerialName("examples") | ||
val examples: List<String> = emptyList(), | ||
val examples: ArrayList<String> = arrayListOf(), | ||
@SerialName("nameType") | ||
val nameType: NameType // label, | ||
) | ||
|
@@ -186,43 +204,35 @@ private val format = Json { ignoreUnknownKeys = true } | |
|
||
internal fun parseAddressesSchema(inputStream: InputStream?) = | ||
getJsonStringFromInputStream(inputStream)?.let { | ||
format.decodeFromString<List<CountryAddressSchema>>( | ||
format.decodeFromString<ArrayList<CountryAddressSchema>>( | ||
it | ||
) | ||
} | ||
|
||
private object FieldTypeAsStringSerializer : KSerializer<FieldType?> { | ||
override val descriptor: SerialDescriptor = | ||
PrimitiveSerialDescriptor("FieldType", PrimitiveKind.STRING) | ||
|
||
override fun serialize(encoder: Encoder, value: FieldType?) { | ||
encoder.encodeString(value?.serializedValue ?: "") | ||
} | ||
|
||
override fun deserialize(decoder: Decoder): FieldType? { | ||
return FieldType.from(decoder.decodeString()) | ||
} | ||
} | ||
|
||
private fun getJsonStringFromInputStream(inputStream: InputStream?) = | ||
inputStream?.bufferedReader().use { it?.readText() } | ||
|
||
internal fun List<CountryAddressSchema>.transformToElementList(): List<SectionFieldElement> { | ||
val countryAddressElements = this.mapNotNull { addressField -> | ||
addressField.type?.let { | ||
SimpleTextElement( | ||
addressField.type.identifierSpec, | ||
SimpleTextFieldController( | ||
SimpleTextFieldConfig( | ||
label = addressField.schema?.nameType?.stringResId ?: it.defaultLabel, | ||
capitalization = it.capitalization, | ||
keyboard = getKeyboard(addressField.schema) | ||
), | ||
showOptionalLabel = !addressField.required | ||
val countryAddressElements = this | ||
.filterNot { | ||
it.type == FieldType.SortingCode || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are two new types that are valid, but we don't support showing. |
||
it.type == FieldType.DependentLocality | ||
} | ||
.mapNotNull { addressField -> | ||
addressField.type?.let { | ||
SimpleTextElement( | ||
addressField.type.identifierSpec, | ||
SimpleTextFieldController( | ||
SimpleTextFieldConfig( | ||
label = addressField.schema?.nameType?.stringResId ?: it.defaultLabel, | ||
capitalization = it.capitalization(), | ||
keyboard = getKeyboard(addressField.schema) | ||
), | ||
showOptionalLabel = !addressField.required | ||
) | ||
) | ||
) | ||
} | ||
} | ||
} | ||
|
||
// Put it in a single row | ||
return combineCityAndPostal(countryAddressElements) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,7 @@ internal class LpmSerializer { | |
emptyList() | ||
} else { | ||
try { | ||
format.decodeFromString<List<SharedDataSpec>>(serializer(), str) | ||
format.decodeFromString<ArrayList<SharedDataSpec>>(serializer(), str) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ArrayList works for finding the deserializer |
||
} catch (e: Exception) { | ||
Log.w("STRIPE", "Error parsing LPMs", e) | ||
emptyList() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,7 +103,10 @@ class LpmRepository constructor( | |
serverLpmSpecs: String?, | ||
force: Boolean = false | ||
) { | ||
if (!isLoaded() || force) { | ||
// If the expectedLpms is different form last time, we still need to reload. | ||
var lpmsNotParsedFromServerSpec = expectedLpms | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we get passed two different PMs with different values, we might need to reparse in the library to deserialize the new LPMs that we need. |
||
.filter { !codeToSupportedPaymentMethod.containsKey(it) } | ||
if (!isLoaded() || force || lpmsNotParsedFromServerSpec.isNotEmpty()) { | ||
serverSpecLoadingState = ServerSpecState.NoServerSpec(serverLpmSpecs) | ||
if (!serverLpmSpecs.isNullOrEmpty()) { | ||
serverSpecLoadingState = ServerSpecState.ServerNotParsed(serverLpmSpecs) | ||
|
@@ -116,7 +119,7 @@ class LpmRepository constructor( | |
|
||
// If the server does not return specs, or they are not parsed successfully | ||
// we will use the LPM on disk if found | ||
val lpmsNotParsedFromServerSpec = expectedLpms | ||
lpmsNotParsedFromServerSpec = expectedLpms | ||
.filter { !codeToSupportedPaymentMethod.containsKey(it) } | ||
if (lpmsNotParsedFromServerSpec.isNotEmpty()) { | ||
val mapFromDisk: Map<String, SharedDataSpec>? = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -163,7 +163,12 @@ internal class PaymentSheetActivity : BaseSheetActivity<PaymentSheetResult>() { | |
if (config != null) { | ||
// We only want to do this if the loading fragment is shown. Otherwise this causes | ||
// a new fragment to be created if the activity was destroyed and recreated. | ||
if (supportFragmentManager.fragments.firstOrNull() is PaymentSheetLoadingFragment) { | ||
// If hyperion is an added dependency it is loaded on top of the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the fix for when hyperion is a dependency |
||
// PaymentSheetLoadingFragment | ||
if (supportFragmentManager.fragments | ||
.filterIsInstance<PaymentSheetLoadingFragment>() | ||
.isNotEmpty() | ||
) { | ||
val target = if (viewModel.paymentMethods.value.isNullOrEmpty()) { | ||
viewModel.updateSelection(null) | ||
PaymentSheetViewModel.TransitionTarget.AddPaymentMethodSheet(config) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
THis is a duplicate field, removing the duplicate