diff --git a/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.kt b/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.kt index b7cd5835b50..b53674be83e 100644 --- a/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.kt @@ -85,6 +85,7 @@ class PaymentSessionActivity : AppCompatActivity() { // Optionally specify the `PaymentMethod.Type` values to use. // Defaults to `PaymentMethod.Type.Card` .setPaymentMethodTypes(listOf(PaymentMethod.Type.Card)) + .setShouldShowGooglePay(true) .setAllowedShippingCountryCodes(setOf("US", "CA")) .setShippingInformationValidator(ShippingInformationValidator()) .setShippingMethodsFactory(ShippingMethodsFactory()) @@ -102,11 +103,20 @@ class PaymentSessionActivity : AppCompatActivity() { } private fun createPaymentMethodDescription(data: PaymentSessionData): String { - return data.paymentMethod?.let { paymentMethod -> - paymentMethod.card?.let { card -> - "${card.brand} ending in ${card.last4}" - } ?: paymentMethod.type - } ?: notSelectedText + val paymentMethod = data.paymentMethod + return when { + paymentMethod != null -> { + paymentMethod.card?.let { card -> + "${card.brand} ending in ${card.last4}" + } ?: paymentMethod.type.orEmpty() + } + data.useGooglePay -> { + "Use Google Pay" + } + else -> { + notSelectedText + } + } } private fun createShippingInfoDescription(shippingInformation: ShippingInformation?): String { diff --git a/stripe/src/main/java/com/stripe/android/PaymentSession.kt b/stripe/src/main/java/com/stripe/android/PaymentSession.kt index 31ffb0bd0f5..5242b521042 100644 --- a/stripe/src/main/java/com/stripe/android/PaymentSession.kt +++ b/stripe/src/main/java/com/stripe/android/PaymentSession.kt @@ -118,9 +118,11 @@ class PaymentSession @VisibleForTesting internal constructor( } private fun onPaymentMethodResult(data: Intent) { - val paymentMethod: PaymentMethod? = - PaymentMethodsActivityStarter.Result.fromIntent(data)?.paymentMethod - persistPaymentMethod(paymentMethod) + val result = PaymentMethodsActivityStarter.Result.fromIntent(data) + persistPaymentMethodResult( + paymentMethod = result?.paymentMethod, + useGooglePay = result?.useGooglePay ?: false + ) dispatchUpdates() } @@ -129,11 +131,17 @@ class PaymentSession @VisibleForTesting internal constructor( paymentSessionListener?.onCommunicatingStateChanged(false) } - private fun persistPaymentMethod(paymentMethod: PaymentMethod?) { + private fun persistPaymentMethodResult( + paymentMethod: PaymentMethod?, + useGooglePay: Boolean + ) { customerSession.cachedCustomer?.id?.let { customerId -> paymentSessionPrefs.saveSelectedPaymentMethodId(customerId, paymentMethod?.id) } - paymentSessionData = paymentSessionData.copy(paymentMethod = paymentMethod) + paymentSessionData = paymentSessionData.copy( + paymentMethod = paymentMethod, + useGooglePay = useGooglePay + ) } /** @@ -260,6 +268,7 @@ class PaymentSession @VisibleForTesting internal constructor( .setIsPaymentSessionActive(true) .setPaymentConfiguration(PaymentConfiguration.getInstance(context)) .setPaymentMethodTypes(config?.paymentMethodTypes.orEmpty()) + .setShouldShowGooglePay(config?.shouldShowGooglePay ?: false) .setWindowFlags(config?.windowFlags) .setBillingAddressFields(config?.billingAddressFields ?: BillingAddressFields.None) .build() diff --git a/stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt b/stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt index 6fd49a8a04e..3a00f86ae5a 100644 --- a/stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt +++ b/stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt @@ -32,6 +32,7 @@ data class PaymentSessionConfig internal constructor( @get:LayoutRes val addPaymentMethodFooterLayoutId: Int = 0, val paymentMethodTypes: List<PaymentMethod.Type> = listOf(PaymentMethod.Type.Card), + val shouldShowGooglePay: Boolean, val allowedShippingCountryCodes: Set<String> = emptySet(), val billingAddressFields: BillingAddressFields = BillingAddressFields.None, @@ -95,6 +96,7 @@ data class PaymentSessionConfig internal constructor( private var optionalShippingInfoFields: List<String>? = null private var shippingInformation: ShippingInformation? = null private var paymentMethodTypes: List<PaymentMethod.Type> = listOf(PaymentMethod.Type.Card) + private var shouldShowGooglePay: Boolean = false private var allowedShippingCountryCodes: Set<String> = emptySet() private var shippingInformationValidator: ShippingInformationValidator? = null private var shippingMethodsFactory: ShippingMethodsFactory? = null @@ -186,6 +188,15 @@ data class PaymentSessionConfig internal constructor( this.paymentMethodTypes = paymentMethodTypes } + /** + * @param shouldShowGooglePay if `true`, will show "Google Pay" as an option on the + * Payment Methods selection screen. If a user selects the Google Pay option, + * [PaymentSessionData.useGooglePay] will be `true`. + */ + fun setShouldShowGooglePay(shouldShowGooglePay: Boolean): Builder = apply { + this.shouldShowGooglePay = shouldShowGooglePay + } + /** * @param allowedShippingCountryCodes A set of allowed country codes for the * customer's shipping address. Will be ignored if empty. @@ -250,6 +261,7 @@ data class PaymentSessionConfig internal constructor( isShippingMethodRequired = shippingMethodsRequired, addPaymentMethodFooterLayoutId = addPaymentMethodFooterLayoutId, paymentMethodTypes = paymentMethodTypes, + shouldShowGooglePay = shouldShowGooglePay, allowedShippingCountryCodes = allowedShippingCountryCodes, shippingInformationValidator = shippingInformationValidator, shippingMethodsFactory = shippingMethodsFactory, diff --git a/stripe/src/main/java/com/stripe/android/PaymentSessionData.kt b/stripe/src/main/java/com/stripe/android/PaymentSessionData.kt index 18d61f058e2..d46bcc6d3c9 100644 --- a/stripe/src/main/java/com/stripe/android/PaymentSessionData.kt +++ b/stripe/src/main/java/com/stripe/android/PaymentSessionData.kt @@ -36,7 +36,14 @@ data class PaymentSessionData internal constructor( /** * @return the selected payment method for the associated [PaymentSession] */ - val paymentMethod: PaymentMethod? = null + val paymentMethod: PaymentMethod? = null, + + /** + * When `true`, the customer has indicated their intent to pay with Google Pay. Use the + * [Google Pay API](https://developers.google.com/pay/api/android/overview) to complete + * payment with Google Pay. + */ + val useGooglePay: Boolean = false ) : Parcelable { /** @@ -45,7 +52,7 @@ data class PaymentSessionData internal constructor( */ val isPaymentReadyToCharge: Boolean get() = - paymentMethod != null && config != null && + (paymentMethod != null || useGooglePay) && config != null && (!config.isShippingInfoRequired || shippingInformation != null) && (!config.isShippingMethodRequired || shippingMethod != null) } diff --git a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt index b0835004f50..b52bde08fc8 100644 --- a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt +++ b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt @@ -194,7 +194,11 @@ class PaymentMethodsActivity : AppCompatActivity() { } private fun finishWithGooglePay() { - // TODO(mshafrir-stripe): set correct result - ANDROID-457 + setResult(Activity.RESULT_OK, + Intent().putExtras( + PaymentMethodsActivityStarter.Result(useGooglePay = true).toBundle() + ) + ) finish() } diff --git a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivityStarter.kt b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivityStarter.kt index 1473442c87d..8b74857ae6b 100644 --- a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivityStarter.kt +++ b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivityStarter.kt @@ -112,8 +112,7 @@ class PaymentMethodsActivityStarter : ActivityStarter<PaymentMethodsActivity, Ar * Payment Methods selection screen. If a user selects the Google Pay option, * [PaymentMethodsActivityStarter.Result.useGooglePay] will be `true`. */ - @JvmSynthetic - internal fun setShouldShowGooglePay(shouldShowGooglePay: Boolean): Builder = apply { + fun setShouldShowGooglePay(shouldShowGooglePay: Boolean): Builder = apply { this.shouldShowGooglePay = shouldShowGooglePay } @@ -168,8 +167,8 @@ class PaymentMethodsActivityStarter : ActivityStarter<PaymentMethodsActivity, Ar */ @Parcelize data class Result internal constructor( - @JvmField val paymentMethod: PaymentMethod?, - private val useGooglePay: Boolean = false + @JvmField val paymentMethod: PaymentMethod? = null, + val useGooglePay: Boolean = false ) : ActivityStarter.Result { override fun toBundle(): Bundle { val bundle = Bundle() diff --git a/stripe/src/test/java/com/stripe/android/PaymentSessionTest.kt b/stripe/src/test/java/com/stripe/android/PaymentSessionTest.kt index 473d3ed3368..6fc5c075f1a 100644 --- a/stripe/src/test/java/com/stripe/android/PaymentSessionTest.kt +++ b/stripe/src/test/java/com/stripe/android/PaymentSessionTest.kt @@ -119,7 +119,7 @@ class PaymentSessionTest { } @Test - fun handlePaymentData_whenPaymentMethodRequest_notifiesListenerAndFetchesCustomer() { + fun handlePaymentData_whenPaymentMethodSelected_notifiesListenerAndFetchesCustomer() { CustomerSession.instance = createCustomerSession() val paymentSession = PaymentSession(activity) @@ -128,7 +128,9 @@ class PaymentSessionTest { // We have already tested the functionality up to here. reset(paymentSessionListener) - val result = PaymentMethodsActivityStarter.Result(PaymentMethodFixtures.CARD_PAYMENT_METHOD) + val result = PaymentMethodsActivityStarter.Result( + paymentMethod = PaymentMethodFixtures.CARD_PAYMENT_METHOD + ) val handled = paymentSession.handlePaymentData( PaymentMethodsActivityStarter.REQUEST_CODE, RESULT_OK, Intent().putExtras(result.toBundle())) @@ -138,6 +140,32 @@ class PaymentSessionTest { .onPaymentSessionDataChanged(paymentSessionDataArgumentCaptor.capture()) val data = paymentSessionDataArgumentCaptor.firstValue assertEquals(PaymentMethodFixtures.CARD_PAYMENT_METHOD, data.paymentMethod) + assertFalse(data.useGooglePay) + } + + @Test + fun handlePaymentData_whenGooglePaySelected_notifiesListenerAndFetchesCustomer() { + CustomerSession.instance = createCustomerSession() + + val paymentSession = PaymentSession(activity) + paymentSession.init(paymentSessionListener, PaymentSessionConfig.Builder().build()) + + // We have already tested the functionality up to here. + reset(paymentSessionListener) + + val result = PaymentMethodsActivityStarter.Result( + useGooglePay = true + ) + val handled = paymentSession.handlePaymentData( + PaymentMethodsActivityStarter.REQUEST_CODE, RESULT_OK, + Intent().putExtras(result.toBundle())) + assertTrue(handled) + + verify(paymentSessionListener) + .onPaymentSessionDataChanged(paymentSessionDataArgumentCaptor.capture()) + val data = paymentSessionDataArgumentCaptor.firstValue + assertNull(data.paymentMethod) + assertTrue(data.useGooglePay) } @Test