Skip to content

Commit

Permalink
Fix Oxxo's next_action type enum (#3559)
Browse files Browse the repository at this point in the history
`display_oxxo_details` was used during the beta; the value is now
`oxxo_display_details`.

Update `SimplePaymentMethodConfirmationActivity` to support Oxxo.
  • Loading branch information
mshafrir-stripe authored Apr 5, 2021
1 parent 9c42461 commit cc1dc06
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 137 deletions.
6 changes: 2 additions & 4 deletions example/res/layout/simple_payment_method_activity.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Jack" />
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
Expand All @@ -42,8 +41,7 @@
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="jack@stripe.com" />
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Filter
import androidx.activity.viewModels
import androidx.annotation.DrawableRes
import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import com.stripe.android.model.PaymentMethod
import com.stripe.android.model.PaymentMethodCreateParams
import com.stripe.example.R
import com.stripe.example.databinding.DropdownMenuPopupItemBinding
import com.stripe.example.databinding.SimplePaymentMethodActivityBinding

class SimplePaymentMethodConfirmationActivity : StripeIntentActivity() {
private val simpleConfirmationViewModel: SimpleConfirmationViewModel by viewModels()

private val viewBinding: SimplePaymentMethodActivityBinding by lazy {
SimplePaymentMethodActivityBinding.inflate(layoutInflater)
Expand All @@ -38,10 +43,13 @@ class SimplePaymentMethodConfirmationActivity : StripeIntentActivity() {
return dropdownItem.createParams(billingDetails, null)
}

private fun onDropdownItemSelected() {
private fun onPaymentMethodSelected() {
val dropdownItem = this.dropdownItem
viewBinding.nameLayout.visibility = viewVisibility(dropdownItem.requiresName)
viewBinding.emailLayout.visibility = viewVisibility(dropdownItem.requiresEmail)

simpleConfirmationViewModel.selectedPaymentMethod = dropdownItem.name

viewBinding.nameLayout.isVisible = dropdownItem.requiresName
viewBinding.emailLayout.isVisible = dropdownItem.requiresEmail
viewBinding.paymentMethod.setCompoundDrawablesRelativeWithIntrinsicBounds(
dropdownItem.icon,
0,
Expand All @@ -50,50 +58,39 @@ class SimplePaymentMethodConfirmationActivity : StripeIntentActivity() {
)
}

private fun viewVisibility(visible: Boolean): Int {
return if (visible) {
View.VISIBLE
} else {
View.GONE
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(viewBinding.root)

viewModel.inProgress.observe(this, { enableUi(!it) })
viewModel.inProgress.observe(this) { enableUi(!it) }
viewModel.status.observe(this, Observer(viewBinding.status::setText))

val adapter = DropdownItemAdapter(this)
viewBinding.paymentMethod.setAdapter(adapter)
viewBinding.paymentMethod.setOnItemClickListener { _, _, _, _ ->
viewModel.status.value = ""
onDropdownItemSelected()
onPaymentMethodSelected()
}
viewBinding.paymentMethod.setText(DropdownItem.P24.name, false)
onDropdownItemSelected()

viewBinding.payNow.setOnClickListener {
createAndConfirmPaymentIntent(dropdownItem.country, paymentMethodCreateParams)
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(STATE_EMAIL, viewBinding.email.text.toString())
outState.putString(STATE_NAME, viewBinding.name.text.toString())
outState.putString(STATE_DROPDOWN_ITEM, viewBinding.paymentMethod.text.toString())
}
viewBinding.name.setText(simpleConfirmationViewModel.name)
viewBinding.email.setText(simpleConfirmationViewModel.email)

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
savedInstanceState.getString(STATE_EMAIL)?.let(viewBinding.email::setText)
savedInstanceState.getString(STATE_NAME)?.let(viewBinding.name::setText)
savedInstanceState.getString(STATE_DROPDOWN_ITEM)?.let {
viewBinding.name.doAfterTextChanged {
simpleConfirmationViewModel.email = it?.toString().orEmpty()
}
viewBinding.email.doAfterTextChanged {
simpleConfirmationViewModel.email = it?.toString().orEmpty()
}

simpleConfirmationViewModel.selectedPaymentMethod?.let {
viewBinding.paymentMethod.setText(it)
onDropdownItemSelected()
}
onPaymentMethodSelected()
}

private fun enableUi(enabled: Boolean) {
Expand All @@ -103,96 +100,102 @@ class SimplePaymentMethodConfirmationActivity : StripeIntentActivity() {
viewBinding.progressBar.visibility = if (enabled) View.INVISIBLE else View.VISIBLE
}

companion object {
private const val STATE_NAME = "name"
private const val STATE_EMAIL = "email"
private const val STATE_DROPDOWN_ITEM = "dropdown_item"

private enum class DropdownItem(
val country: String,
@DrawableRes val icon: Int,
val createParams:
(PaymentMethod.BillingDetails, Map<String, String>?) -> PaymentMethodCreateParams,
val requiresName: Boolean = true,
val requiresEmail: Boolean = false
) {
P24(
"pl",
R.drawable.ic_brandicon__p24,
PaymentMethodCreateParams.Companion::createP24,
requiresName = false,
requiresEmail = true
),
Bancontact(
"be",
R.drawable.ic_brandicon__bancontact,
PaymentMethodCreateParams.Companion::createBancontact
),
EPS(
"at",
R.drawable.ic_brandicon__eps,
PaymentMethodCreateParams.Companion::createEps
),
Giropay(
"de",
R.drawable.ic_brandicon__giropay,
PaymentMethodCreateParams.Companion::createGiropay
),
GrabPay(
"sg",
R.drawable.ic_brandicon_grabpay,
PaymentMethodCreateParams.Companion::createGrabPay
);
}
internal class SimpleConfirmationViewModel : ViewModel() {
var name = "Jenny Rosen"
var email = "jrosen@example.com"
var selectedPaymentMethod: String? = null
}

private class DropdownItemAdapter(
context: Context
) : ArrayAdapter<DropdownItem>(
context,
0
) {
private val layoutInflater = LayoutInflater.from(context)
private enum class DropdownItem(
val country: String,
@DrawableRes val icon: Int,
val createParams:
(PaymentMethod.BillingDetails, Map<String, String>?) -> PaymentMethodCreateParams,
val requiresName: Boolean = true,
val requiresEmail: Boolean = false
) {
P24(
"pl",
R.drawable.ic_brandicon__p24,
PaymentMethodCreateParams.Companion::createP24,
requiresName = false,
requiresEmail = true
),
Bancontact(
"be",
R.drawable.ic_brandicon__bancontact,
PaymentMethodCreateParams.Companion::createBancontact
),
EPS(
"at",
R.drawable.ic_brandicon__eps,
PaymentMethodCreateParams.Companion::createEps
),
Giropay(
"de",
R.drawable.ic_brandicon__giropay,
PaymentMethodCreateParams.Companion::createGiropay
),
GrabPay(
"sg",
R.drawable.ic_brandicon_grabpay,
PaymentMethodCreateParams.Companion::createGrabPay
),
Oxxo(
"mx",
0, // TODO (mshafrir-stripe): add icon for Oxxo
PaymentMethodCreateParams.Companion::createOxxo,
requiresEmail = true
)
}

init {
addAll(*DropdownItem.values())
}
private class DropdownItemAdapter(
context: Context
) : ArrayAdapter<DropdownItem>(
context,
0
) {
private val layoutInflater = LayoutInflater.from(context)

/*
The material ExposedDropdownMenu abuses an AutocompleteTextView as a Spinner.
When we want to set the selected item, the AutocompleteTextView tries to
filter the results to only those that start with that item.
We do not want this behaviour so we ignore AutocompleteTextView's filtering logic.
*/
override fun getFilter(): Filter {
return NullFilter
}
init {
addAll(*DropdownItem.values())
}

override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val viewBinding = convertView?.let {
DropdownMenuPopupItemBinding.bind(convertView)
} ?: DropdownMenuPopupItemBinding.inflate(layoutInflater, parent, false)

val dropdownItem = requireNotNull(getItem(position))
viewBinding.text.text = dropdownItem.name
viewBinding.text.setCompoundDrawablesRelativeWithIntrinsicBounds(
dropdownItem.icon,
0,
0,
0
)

return viewBinding.root
}
/**
* The material ExposedDropdownMenu abuses an AutocompleteTextView as a Spinner.
* When we want to set the selected item, the AutocompleteTextView tries to
* filter the results to only those that start with that item.
* We do not want this behaviour so we ignore AutocompleteTextView's filtering logic.
*/
override fun getFilter(): Filter {
return NullFilter
}

override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val viewBinding = convertView?.let {
DropdownMenuPopupItemBinding.bind(convertView)
} ?: DropdownMenuPopupItemBinding.inflate(layoutInflater, parent, false)

val dropdownItem = requireNotNull(getItem(position))
viewBinding.text.text = dropdownItem.name
viewBinding.text.setCompoundDrawablesRelativeWithIntrinsicBounds(
dropdownItem.icon,
0,
0,
0
)

private object NullFilter : Filter() {
private val emptyResult = FilterResults()
return viewBinding.root
}

private object NullFilter : Filter() {
private val emptyResult = FilterResults()

override fun performFiltering(prefix: CharSequence?): FilterResults {
return emptyResult
}
override fun performFiltering(prefix: CharSequence?): FilterResults {
return emptyResult
}

override fun publishResults(p0: CharSequence?, p1: FilterResults?) {
}
override fun publishResults(p0: CharSequence?, p1: FilterResults?) {
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ interface StripeIntent : StripeModel {
enum class NextActionType(val code: String) {
RedirectToUrl("redirect_to_url"),
UseStripeSdk("use_stripe_sdk"),
DisplayOxxoDetails("display_oxxo_details"),
DisplayOxxoDetails("oxxo_display_details"),
AlipayRedirect("alipay_handle_redirect"),
BlikAuthorize("blik_authorize");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ internal class StripePaymentControllerTest {
paymentAuthWebViewContract.parseArgs(intentArgumentCaptor.firstValue)
)
assertThat(args.url)
.isEqualTo("https://payments.stripe.com/oxxo/voucher/vchr_test_YWNjdF8xR1hhNUZIU0wxMEo5d3F2LHZjaHJfSGJIOGVMYmNmQlkyMUJ5OU1WTU5uMVYxdDNta1Q2RQ0000gtenGCef")
.isEqualTo("https://payments.stripe.com/oxxo/voucher/test_YWNjdF8xSWN1c1VMMzJLbFJvdDAxLF9KRlBtckVBMERWM0lBZEUyb")
assertThat(args.shouldCancelIntentOnUserNavigation).isFalse()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -716,31 +716,50 @@ internal object PaymentIntentFixtures {
val OXXO_REQUIRES_ACTION_JSON = JSONObject(
"""
{
"id": "pi_1Ga0nFLYnbCF8",
"id": "pi_1IcuwoL32KlRo",
"object": "payment_intent",
"amount": 1000,
"amount": 1099,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"client_secret": "pi_1Ga0nFLYnbCF8_secret_Vd75bObmsTyGR4k",
"client_secret": "pi_1IcuwoL32KlRo_secret_KC0YoHfna465TDVW",
"confirmation_method": "automatic",
"created": 1587393617,
"created": 1617638802,
"currency": "mxn",
"description": null,
"description": "Example PaymentIntent",
"last_payment_error": null,
"livemode": false,
"next_action": {
"display_oxxo_details": {
"expires_after": 1587704399,
"number": "12345678901234657890123456789012",
"hosted_voucher_url": "https://payments.stripe.com/oxxo/voucher/vchr_test_YWNjdF8xR1hhNUZIU0wxMEo5d3F2LHZjaHJfSGJIOGVMYmNmQlkyMUJ5OU1WTU5uMVYxdDNta1Q2RQ0000gtenGCef"
"oxxo_display_details": {
"expires_after": 1617944399,
"hosted_voucher_url": "https:\/\/payments.stripe.com\/oxxo\/voucher\/test_YWNjdF8xSWN1c1VMMzJLbFJvdDAxLF9KRlBtckVBMERWM0lBZEUyb",
"number": "12345678901234657890123456789012"
},
"type": "display_oxxo_details"
"type": "oxxo_display_details"
},
"payment_method": "pm_1Ga11MLYnbC",
"payment_method_types": [
"oxxo"
],
"payment_method": {
"id": "pm_1IcuwoL32KlRot01",
"object": "payment_method",
"billing_details": {
"address": {
"city": null,
"country": null,
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": "jrosen@example.com",
"name": "Jenny Rosen",
"phone": null
},
"created": 1617638802,
"customer": null,
"livemode": false,
"oxxo": {},
"type": "oxxo"
},
"payment_method_types": ["card", "oxxo"],
"receipt_email": null,
"setup_future_usage": null,
"shipping": null,
Expand All @@ -749,7 +768,7 @@ internal object PaymentIntentFixtures {
}
""".trimIndent()
)
val OXXO_REQUIES_ACTION = PARSER.parse(OXXO_REQUIRES_ACTION_JSON)!!
val OXXO_REQUIES_ACTION = requireNotNull(PARSER.parse(OXXO_REQUIRES_ACTION_JSON))

val ALIPAY_REQUIRES_ACTION_JSON = JSONObject(
"""
Expand Down
Loading

0 comments on commit cc1dc06

Please sign in to comment.