diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index efe3a28d75..79d070fcdd 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -8,7 +8,24 @@
[//]: # (## Deprecated)
[//]: # ( - Configurations public constructor are deprecated, please use each Configuration's builder to make a Configuration object)
+## New
+- Launch Google Pay with `submit()` to get rid of the deprecated activity result handling.
+- For drop-in, show a toolbar on every intermediary screen, so shoppers can always easily navigate back.
+
## Fixed
-- For the Address Lookup functionality:
- - Address data is now correctly saved to `PaymentComponentData`.
- - Address fields that were edited manually no longer lose their state when starting Lookup mode.
+
+## Improved
+
+## Changed
+- Dependency versions:
+ | Name | Version |
+ |--------------------------------------------------------------------------------------------------------|-------------------------------|
+ | | |
+
+## Deprecated
+- The styles and strings for the Cash App Pay loading indicator. Use the new styles and strings instead.
+| Previous | Now |
+|-----------------------------------------------------------|------------------------------------------------------------------|
+| `AdyenCheckout.CashAppPay.ProgressBar` | `AdyenCheckout.ProcessingPaymentView.ProgressBar` |
+| `AdyenCheckout.CashAppPay.WaitingDescriptionTextView` | `AdyenCheckout.ProcessingPaymentView.WaitingDescriptionTextView` |
+| `cash_app_pay_waiting_text` | `checkout_processing_payment` |
diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/CashAppPayViewProvider.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/CashAppPayViewProvider.kt
index de259f016b..a8f667decc 100644
--- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/CashAppPayViewProvider.kt
+++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/CashAppPayViewProvider.kt
@@ -11,13 +11,13 @@ package com.adyen.checkout.cashapppay.internal.ui
import android.content.Context
import com.adyen.checkout.cashapppay.internal.ui.view.CashAppPayButtonView
import com.adyen.checkout.cashapppay.internal.ui.view.CashAppPayView
-import com.adyen.checkout.cashapppay.internal.ui.view.CashAppPayWaitingView
import com.adyen.checkout.ui.core.internal.ui.ButtonComponentViewType
import com.adyen.checkout.ui.core.internal.ui.ButtonViewProvider
import com.adyen.checkout.ui.core.internal.ui.ComponentView
import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
import com.adyen.checkout.ui.core.internal.ui.ViewProvider
import com.adyen.checkout.ui.core.internal.ui.view.PayButton
+import com.adyen.checkout.ui.core.internal.ui.view.ProcessingPaymentView
internal object CashAppPayViewProvider : ViewProvider {
@@ -26,7 +26,7 @@ internal object CashAppPayViewProvider : ViewProvider {
context: Context,
): ComponentView = when (viewType) {
CashAppPayComponentViewType -> CashAppPayView(context)
- PaymentInProgressViewType -> CashAppPayWaitingView(context)
+ PaymentInProgressViewType -> ProcessingPaymentView(context)
else -> throw IllegalArgumentException("Unsupported view type")
}
}
diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/view/CashAppPayButtonView.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/view/CashAppPayButtonView.kt
index 61c0bedb14..727a1d8bc5 100644
--- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/view/CashAppPayButtonView.kt
+++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/view/CashAppPayButtonView.kt
@@ -12,7 +12,9 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import com.adyen.checkout.cashapppay.databinding.CashAppPayButtonViewBinding
+import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate
import com.adyen.checkout.ui.core.internal.ui.view.PayButton
+import kotlinx.coroutines.CoroutineScope
internal class CashAppPayButtonView @JvmOverloads constructor(
context: Context,
@@ -22,6 +24,8 @@ internal class CashAppPayButtonView @JvmOverloads constructor(
private val binding = CashAppPayButtonViewBinding.inflate(LayoutInflater.from(context), this)
+ override fun initialize(delegate: ButtonDelegate, coroutineScope: CoroutineScope) = Unit
+
override fun setEnabled(enabled: Boolean) {
binding.payButton.isEnabled = enabled
}
diff --git a/cashapppay/src/main/res/template/values/strings.xml.tt b/cashapppay/src/main/res/template/values/strings.xml.tt
index 3ce489f054..b936f73e1d 100644
--- a/cashapppay/src/main/res/template/values/strings.xml.tt
+++ b/cashapppay/src/main/res/template/values/strings.xml.tt
@@ -8,5 +8,4 @@
%%storeDetails%%
- %%paypal.processingPayment%%
diff --git a/cashapppay/src/main/res/values-ar/strings.xml b/cashapppay/src/main/res/values-ar/strings.xml
index 2b812ea78f..b0878541df 100644
--- a/cashapppay/src/main/res/values-ar/strings.xml
+++ b/cashapppay/src/main/res/values-ar/strings.xml
@@ -8,5 +8,4 @@
حفظ لمدفوعاتي القادمة
- جارِ معالجة المدفوعات…
diff --git a/cashapppay/src/main/res/values-bg-rBG/strings.xml b/cashapppay/src/main/res/values-bg-rBG/strings.xml
index 8f84adb4cf..49f695af31 100644
--- a/cashapppay/src/main/res/values-bg-rBG/strings.xml
+++ b/cashapppay/src/main/res/values-bg-rBG/strings.xml
@@ -8,5 +8,4 @@
Запазване за следващото ми плащане
- Обработка на плащането…
diff --git a/cashapppay/src/main/res/values-ca-rES/strings.xml b/cashapppay/src/main/res/values-ca-rES/strings.xml
index 0634aeb780..b638f6b993 100644
--- a/cashapppay/src/main/res/values-ca-rES/strings.xml
+++ b/cashapppay/src/main/res/values-ca-rES/strings.xml
@@ -8,5 +8,4 @@
Desa\'l per al meu proper pagament
- S\'esta processant el pagament…
diff --git a/cashapppay/src/main/res/values-cs-rCZ/strings.xml b/cashapppay/src/main/res/values-cs-rCZ/strings.xml
index 1801d976d9..4308eac985 100644
--- a/cashapppay/src/main/res/values-cs-rCZ/strings.xml
+++ b/cashapppay/src/main/res/values-cs-rCZ/strings.xml
@@ -8,5 +8,4 @@
Uložit pro příští platby
- Zpracování platby…
diff --git a/cashapppay/src/main/res/values-da-rDK/strings.xml b/cashapppay/src/main/res/values-da-rDK/strings.xml
index b5e13d4b70..7e5cefdd1b 100644
--- a/cashapppay/src/main/res/values-da-rDK/strings.xml
+++ b/cashapppay/src/main/res/values-da-rDK/strings.xml
@@ -8,5 +8,4 @@
Gem til min næste betaling
- Behandler betaling…
diff --git a/cashapppay/src/main/res/values-de-rDE/strings.xml b/cashapppay/src/main/res/values-de-rDE/strings.xml
index 1a33495c92..1ff4ddc1b4 100644
--- a/cashapppay/src/main/res/values-de-rDE/strings.xml
+++ b/cashapppay/src/main/res/values-de-rDE/strings.xml
@@ -8,5 +8,4 @@
Für zukünftige Zahlvorgänge speichern
- Zahlung wird verarbeitet…
diff --git a/cashapppay/src/main/res/values-el-rGR/strings.xml b/cashapppay/src/main/res/values-el-rGR/strings.xml
index c3da340648..2b59f67998 100644
--- a/cashapppay/src/main/res/values-el-rGR/strings.xml
+++ b/cashapppay/src/main/res/values-el-rGR/strings.xml
@@ -8,5 +8,4 @@
Αποθήκευση για την επόμενη πληρωμή μου
- Επεξεργασία πληρωμής…
diff --git a/cashapppay/src/main/res/values-es-rES/strings.xml b/cashapppay/src/main/res/values-es-rES/strings.xml
index a282fe5af7..79fef68395 100644
--- a/cashapppay/src/main/res/values-es-rES/strings.xml
+++ b/cashapppay/src/main/res/values-es-rES/strings.xml
@@ -8,5 +8,4 @@
Recordar para mi próximo pago
- Procesando pago…
diff --git a/cashapppay/src/main/res/values-et-rEE/strings.xml b/cashapppay/src/main/res/values-et-rEE/strings.xml
index b37ef99f09..52ae2947aa 100644
--- a/cashapppay/src/main/res/values-et-rEE/strings.xml
+++ b/cashapppay/src/main/res/values-et-rEE/strings.xml
@@ -8,5 +8,4 @@
Salvesta mu järgmise makse jaoks
- Makse töötlemine …
diff --git a/cashapppay/src/main/res/values-fi-rFI/strings.xml b/cashapppay/src/main/res/values-fi-rFI/strings.xml
index 47a7aebdc4..27ad6df965 100644
--- a/cashapppay/src/main/res/values-fi-rFI/strings.xml
+++ b/cashapppay/src/main/res/values-fi-rFI/strings.xml
@@ -8,5 +8,4 @@
Tallenna seuraavaa maksuani varten
- Maksua käsitellään…
diff --git a/cashapppay/src/main/res/values-fr-rFR/strings.xml b/cashapppay/src/main/res/values-fr-rFR/strings.xml
index 6615d2c753..aae6b252fc 100644
--- a/cashapppay/src/main/res/values-fr-rFR/strings.xml
+++ b/cashapppay/src/main/res/values-fr-rFR/strings.xml
@@ -8,5 +8,4 @@
Sauvegarder pour mon prochain paiement
- Traitement du paiement en cours…
diff --git a/cashapppay/src/main/res/values-hr-rHR/strings.xml b/cashapppay/src/main/res/values-hr-rHR/strings.xml
index 0c5a8a7e7b..f1a49b69e7 100644
--- a/cashapppay/src/main/res/values-hr-rHR/strings.xml
+++ b/cashapppay/src/main/res/values-hr-rHR/strings.xml
@@ -8,5 +8,4 @@
Pohrani za moje sljedeće plaćanje
- Obrada plaćanja u tijeku…
diff --git a/cashapppay/src/main/res/values-hu-rHU/strings.xml b/cashapppay/src/main/res/values-hu-rHU/strings.xml
index 37218519e2..db5ad56615 100644
--- a/cashapppay/src/main/res/values-hu-rHU/strings.xml
+++ b/cashapppay/src/main/res/values-hu-rHU/strings.xml
@@ -8,5 +8,4 @@
Mentés a következő fizetéshez
- Fizetés feldolgozása…
diff --git a/cashapppay/src/main/res/values-is-rIS/strings.xml b/cashapppay/src/main/res/values-is-rIS/strings.xml
index 8c84a651ed..f9beb5794b 100644
--- a/cashapppay/src/main/res/values-is-rIS/strings.xml
+++ b/cashapppay/src/main/res/values-is-rIS/strings.xml
@@ -8,5 +8,4 @@
Spara fyrir næstu greiðslu
- Unnið úr greiðslu…
diff --git a/cashapppay/src/main/res/values-it-rIT/strings.xml b/cashapppay/src/main/res/values-it-rIT/strings.xml
index cf28df04c9..9563785e4f 100644
--- a/cashapppay/src/main/res/values-it-rIT/strings.xml
+++ b/cashapppay/src/main/res/values-it-rIT/strings.xml
@@ -8,5 +8,4 @@
Salva per il prossimo pagamento
- Elaborazione del pagamento in corso…
diff --git a/cashapppay/src/main/res/values-ja-rJP/strings.xml b/cashapppay/src/main/res/values-ja-rJP/strings.xml
index 9cdc8febb8..8944ed199d 100644
--- a/cashapppay/src/main/res/values-ja-rJP/strings.xml
+++ b/cashapppay/src/main/res/values-ja-rJP/strings.xml
@@ -8,5 +8,4 @@
次回のお支払いのため詳細を保存
- 支払いを処理しています…
diff --git a/cashapppay/src/main/res/values-ko-rKR/strings.xml b/cashapppay/src/main/res/values-ko-rKR/strings.xml
index 7e21b66846..32aaeb06b9 100644
--- a/cashapppay/src/main/res/values-ko-rKR/strings.xml
+++ b/cashapppay/src/main/res/values-ko-rKR/strings.xml
@@ -8,5 +8,4 @@
다음 결제를 위해 이 수단 저장
- 결제 처리 중…
diff --git a/cashapppay/src/main/res/values-lt-rLT/strings.xml b/cashapppay/src/main/res/values-lt-rLT/strings.xml
index 8ff48b9685..431023b205 100644
--- a/cashapppay/src/main/res/values-lt-rLT/strings.xml
+++ b/cashapppay/src/main/res/values-lt-rLT/strings.xml
@@ -8,5 +8,4 @@
Išsaugoti kitam mokėjimui
- Mokėjimas apdorojamas…
diff --git a/cashapppay/src/main/res/values-lv-rLV/strings.xml b/cashapppay/src/main/res/values-lv-rLV/strings.xml
index b87ba14304..a78ccce57c 100644
--- a/cashapppay/src/main/res/values-lv-rLV/strings.xml
+++ b/cashapppay/src/main/res/values-lv-rLV/strings.xml
@@ -8,5 +8,4 @@
Saglabāt manam nākamajam maksājumam
- Notiek maksājuma apstrāde…
diff --git a/cashapppay/src/main/res/values-nb-rNO/strings.xml b/cashapppay/src/main/res/values-nb-rNO/strings.xml
index 0c036a6d1d..c863f1d07e 100644
--- a/cashapppay/src/main/res/values-nb-rNO/strings.xml
+++ b/cashapppay/src/main/res/values-nb-rNO/strings.xml
@@ -8,5 +8,4 @@
Lagre til min neste betaling
- Behandler betaling…
diff --git a/cashapppay/src/main/res/values-nl-rNL/strings.xml b/cashapppay/src/main/res/values-nl-rNL/strings.xml
index 9371e67494..9cd55a0534 100644
--- a/cashapppay/src/main/res/values-nl-rNL/strings.xml
+++ b/cashapppay/src/main/res/values-nl-rNL/strings.xml
@@ -8,5 +8,4 @@
Bewaar voor mijn volgende betaling
- Betaling wordt verwerkt…
diff --git a/cashapppay/src/main/res/values-pl-rPL/strings.xml b/cashapppay/src/main/res/values-pl-rPL/strings.xml
index 10614e4c13..a18f52a602 100644
--- a/cashapppay/src/main/res/values-pl-rPL/strings.xml
+++ b/cashapppay/src/main/res/values-pl-rPL/strings.xml
@@ -8,5 +8,4 @@
Zapisz na potrzeby następnej płatności
- Przetwarzanie płatności…
diff --git a/cashapppay/src/main/res/values-pt-rBR/strings.xml b/cashapppay/src/main/res/values-pt-rBR/strings.xml
index 89cc49afb1..e96e04a449 100644
--- a/cashapppay/src/main/res/values-pt-rBR/strings.xml
+++ b/cashapppay/src/main/res/values-pt-rBR/strings.xml
@@ -8,5 +8,4 @@
Salvar para meu próximo pagamento
- Processando pagamento…
diff --git a/cashapppay/src/main/res/values-pt-rPT/strings.xml b/cashapppay/src/main/res/values-pt-rPT/strings.xml
index 83e20e6bc5..225f8b4529 100644
--- a/cashapppay/src/main/res/values-pt-rPT/strings.xml
+++ b/cashapppay/src/main/res/values-pt-rPT/strings.xml
@@ -8,5 +8,4 @@
Guardar para o meu próximo pagamento
- A processar pagamento…
diff --git a/cashapppay/src/main/res/values-ro-rRO/strings.xml b/cashapppay/src/main/res/values-ro-rRO/strings.xml
index e90e4c3008..fb8655eb49 100644
--- a/cashapppay/src/main/res/values-ro-rRO/strings.xml
+++ b/cashapppay/src/main/res/values-ro-rRO/strings.xml
@@ -8,5 +8,4 @@
Salvează pentru următoarea mea plată
- Se prelucrează plata…
diff --git a/cashapppay/src/main/res/values-ru-rRU/strings.xml b/cashapppay/src/main/res/values-ru-rRU/strings.xml
index d01ef30545..c14a1aeedf 100644
--- a/cashapppay/src/main/res/values-ru-rRU/strings.xml
+++ b/cashapppay/src/main/res/values-ru-rRU/strings.xml
@@ -8,5 +8,4 @@
Сохранить для следующего платежа
- Платеж обрабатывается…
diff --git a/cashapppay/src/main/res/values-sk-rSK/strings.xml b/cashapppay/src/main/res/values-sk-rSK/strings.xml
index ff3c302d5a..8198dbd57e 100644
--- a/cashapppay/src/main/res/values-sk-rSK/strings.xml
+++ b/cashapppay/src/main/res/values-sk-rSK/strings.xml
@@ -8,5 +8,4 @@
Uložiť pre moju ďalšiu platbu
- Platba sa spracúva.
diff --git a/cashapppay/src/main/res/values-sl-rSI/strings.xml b/cashapppay/src/main/res/values-sl-rSI/strings.xml
index e799889891..09bde2f79c 100644
--- a/cashapppay/src/main/res/values-sl-rSI/strings.xml
+++ b/cashapppay/src/main/res/values-sl-rSI/strings.xml
@@ -8,5 +8,4 @@
Shrani za moje naslednje plačilo
- Obdelava plačila…
diff --git a/cashapppay/src/main/res/values-sv-rSE/strings.xml b/cashapppay/src/main/res/values-sv-rSE/strings.xml
index 085bc46c5c..3bf819354a 100644
--- a/cashapppay/src/main/res/values-sv-rSE/strings.xml
+++ b/cashapppay/src/main/res/values-sv-rSE/strings.xml
@@ -8,5 +8,4 @@
Spara till min nästa betalning
- Behandlar betalning…
diff --git a/cashapppay/src/main/res/values-zh-rCN/strings.xml b/cashapppay/src/main/res/values-zh-rCN/strings.xml
index 012a1a675a..27a63acc09 100644
--- a/cashapppay/src/main/res/values-zh-rCN/strings.xml
+++ b/cashapppay/src/main/res/values-zh-rCN/strings.xml
@@ -8,5 +8,4 @@
保存以便下次支付使用
- 正在处理付款…
diff --git a/cashapppay/src/main/res/values-zh-rTW/strings.xml b/cashapppay/src/main/res/values-zh-rTW/strings.xml
index 62265679dc..438cc5afb8 100644
--- a/cashapppay/src/main/res/values-zh-rTW/strings.xml
+++ b/cashapppay/src/main/res/values-zh-rTW/strings.xml
@@ -8,5 +8,4 @@
儲存以供下次付款使用
- 正在處理付款……
diff --git a/cashapppay/src/main/res/values/strings.xml b/cashapppay/src/main/res/values/strings.xml
index c3b37e4f2d..1243ed256a 100644
--- a/cashapppay/src/main/res/values/strings.xml
+++ b/cashapppay/src/main/res/values/strings.xml
@@ -8,5 +8,4 @@
Save for my next payment
- Processing payment…
diff --git a/cashapppay/src/main/res/values/styles.xml b/cashapppay/src/main/res/values/styles.xml
index 643a771043..52a4547201 100644
--- a/cashapppay/src/main/res/values/styles.xml
+++ b/cashapppay/src/main/res/values/styles.xml
@@ -15,18 +15,5 @@
- 18sp
-
-
-
-
diff --git a/components-core/api/components-core.api b/components-core/api/components-core.api
index 6665b06e63..d166078a63 100644
--- a/components-core/api/components-core.api
+++ b/components-core/api/components-core.api
@@ -935,6 +935,11 @@ public final class com/adyen/checkout/components/core/PaymentMethodTypes {
public final fun getUNSUPPORTED_PAYMENT_METHODS ()Ljava/util/List;
}
+public class com/adyen/checkout/components/core/PaymentMethodUnavailableException : com/adyen/checkout/core/exception/CheckoutException {
+ public fun (Ljava/lang/String;Ljava/lang/Throwable;)V
+ public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+}
+
public final class com/adyen/checkout/components/core/PaymentMethodsApiResponse : com/adyen/checkout/core/internal/data/model/ModelObject {
public static final field CREATOR Landroid/os/Parcelable$Creator;
public static final field Companion Lcom/adyen/checkout/components/core/PaymentMethodsApiResponse$Companion;
diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/ComponentAvailableCallback.kt b/components-core/src/main/java/com/adyen/checkout/components/core/ComponentAvailableCallback.kt
index f5f2c00f36..f187228cea 100644
--- a/components-core/src/main/java/com/adyen/checkout/components/core/ComponentAvailableCallback.kt
+++ b/components-core/src/main/java/com/adyen/checkout/components/core/ComponentAvailableCallback.kt
@@ -7,6 +7,6 @@
*/
package com.adyen.checkout.components.core
-interface ComponentAvailableCallback {
+fun interface ComponentAvailableCallback {
fun onAvailabilityResult(isAvailable: Boolean, paymentMethod: PaymentMethod)
}
diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodUnavailableException.kt b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodUnavailableException.kt
new file mode 100644
index 0000000000..4b529e03af
--- /dev/null
+++ b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodUnavailableException.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 30/10/2024.
+ */
+
+package com.adyen.checkout.components.core
+
+import com.adyen.checkout.core.exception.CheckoutException
+
+open class PaymentMethodUnavailableException(
+ message: String,
+ cause: Throwable? = null
+) : CheckoutException(message, cause)
diff --git a/docs/payment-methods/GOOGLE_PAY.md b/docs/payment-methods/GOOGLE_PAY.md
new file mode 100644
index 0000000000..1fc5b3fdf2
--- /dev/null
+++ b/docs/payment-methods/GOOGLE_PAY.md
@@ -0,0 +1,142 @@
+# Google Pay
+On this page, you can find additional configuration and a migration guide for Google Pay.
+
+## Drop-in
+### Sessions
+The integration works out of the box for sessions implementation. Check out the integration guide [here](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=Android&integration=Drop-in).
+
+### Advanced
+There is no additional configuration required. Follow the [Advanced flow integration guide](https://docs.adyen.com/online-payments/build-your-integration/advanced-flow/?platform=Android&integration=Drop-in).
+
+## Components
+Use the following module and component names:
+- To import the module use `googlepay`.
+
+```groovy
+implementation "com.adyen.checkout:googlepay:YOUR_VERSION"
+```
+
+- To launch and show the Component use `GooglePayComponent`.
+
+```kotlin
+val component = GooglePayComponent.PROVIDER.get(
+ activity = activity, // or fragment = fragment
+ checkoutSession = checkoutSession, // Should be passed only for sessions
+ paymentMethod = paymentMethod,
+ configuration = checkoutConfiguration,
+ componentCallback = callback,
+)
+```
+
+### Sessions
+Make sure to follow the Android Components integration guide for sessions integration [here](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow?platform=Android&integration=Components).
+
+### Advanced
+Make sure to follow the Android Components integration guide for advanced integration [here](https://docs.adyen.com/online-payments/build-your-integration/advanced-flow/?platform=Android&integration=Components).
+
+## Optional configurations
+
+```kotlin
+CheckoutConfiguration(
+ environment = environment,
+ clientKey = clientKey,
+ …
+) {
+ googlePay {
+ setSubmitButtonVisible(true)
+ setMerchantAccount("YOUR_MERCHANT_ACCOUNT")
+ setGooglePayEnvironment(WalletConstants.ENVIRONMENT_TEST)
+ setMerchantInfo(…)
+ setCountryCode("US")
+ setAllowedAuthMethods(listOf(AllowedAuthMethods.PAN_ONLY))
+ setAllowedCardNetworks(listOf("AMEX", "MASTERCARD"))
+ setAllowPrepaidCards(false)
+ setAllowCreditCards(true)
+ setAssuranceDetailsRequired(false)
+ setEmailRequired(true)
+ setExistingPaymentMethodRequired(false)
+ setShippingAddressRequired(true)
+ setShippingAddressParameters(…)
+ setBillingAddressRequired(true)
+ setBillingAddressParameters(…)
+ setTotalPriceStatus("FINAL")
+ setGooglePayButtonStyling(…)
+ }
+}
+```
+
+| Method | Description |
+|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------|
+| `setSubmitButtonVisible` | Set to `true` to display the Google Pay button in the component. The default value is `false`. |
+| `setMerchantAccount` | Sets the merchant account to be put in the payment token from Google to Adyen. |
+| `setGooglePayEnvironment` | Sets the environment to be used by Google Pay. |
+| `setMerchantInfo` | Sets the information about the merchant requesting the payment. |
+| `setCountryCode` | Sets the ISO 3166-1 alpha-2 country code where the transaction is processed. |
+| `setAllowedAuthMethods` | Sets the supported authentication methods. |
+| `setAllowedCardNetworks` | Sets the allowed card networks. The allowed networks are automatically configured based on your account settings, but you can override them here. |
+| `setAllowPrepaidCards` | Set to `true` if you support prepaid cards. |
+| `setAllowCreditCards` | Set to `true` if you support credit cards. |
+| `setAssuranceDetailsRequired` | Set to `true` if you want to request assurance details. |
+| `setEmailRequired` | Set to `true` if an email address is required. |
+| `setExistingPaymentMethodRequired` | Set to `true` if an existing payment method is required. |
+| `setShippingAddressRequired` | Set to `true` if a shipping address is required. |
+| `setShippingAddressParameters` | Allows to configure the shipping address parameters. |
+| `setBillingAddressRequired` | Set to `true` if a billing address is required. |
+| `setBillingAddressParameters` | Allows to configure the billing address parameters. |
+| `setTotalPriceStatus` | Sets the status of the total price used. |
+| `setGooglePayButtonStyling` | Allows to configure the styling of the Google Pay button. |
+
+## Migrating to 5.8.0+
+It is not necessary to migrate, but 5.8.0 introduced a simplified integration for Google Pay. This new integration among others gets rid of the deprecated `onActivityResult` and includes the Google Pay button. Follow the steps below to migrate from previous 5.x.x versions to 5.8.0:
+
+### 1. Remove deprecated Activity Result code
+
+Add `AdyenComponentView` to your layout and attach the component to it.
+```xml
+
+```
+```kotlin
+// Attach the component to the view
+binding.componentView.attach(googlePayComponent, lifecycleOwner)
+
+// Or if you use Jetpack Compose
+AdyenComponent(googlePayComponent)
+```
+
+Now you no longer need activity result related code, so you can clean it up. For example you can remove:
+```kotlin
+// This function can be deleted
+override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ googlePayComponent.handleActivityResult(resultCode, data)
+}
+```
+
+### 2. Display Google Pay button
+
+If you want to keep displaying a button yourself, then you have to replace the call to `googlePayComponent.startGooglePayScreen(…)` with `googlePayComponent.submit()`.
+
+To let the component display the Google Pay button inside the `AdyenComponentView` remove your own button and adjust your configuration:
+```kotlin
+CheckoutConfiguration(
+ environment = environment,
+ clientKey = clientKey,
+ …
+) {
+ googlePay {
+ setSubmitButtonVisible(true)
+ setGooglePayButtonStyling(…) // Optionally style the button
+ }
+}
+```
+
+The `com.google.pay.button:compose-pay-button` dependency can now also be removed from your `build.gradle`.
+
+### 3. Google Pay availability check
+
+You no longer need to call `GooglePayComponent.PROVIDER.isAvailable(…)`.
+
+The`GooglePayComponent` now checks if Google Pay is available when you initialize it. If Google Pay is not available, you get a `GooglePayUnavailableException` in `onError`.
diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt
index 609b6b8783..2228a69807 100644
--- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt
+++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt
@@ -182,22 +182,6 @@ internal class DropInActivity :
return baseContext.createLocalizedContext(locale)
}
- @Deprecated("Deprecated in Java")
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- checkGooglePayActivityResult(requestCode, resultCode, data)
- }
-
- private fun checkGooglePayActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (requestCode != GOOGLE_PAY_REQUEST_CODE) return
- val fragment = getFragmentByTag(COMPONENT_FRAGMENT_TAG) as? GooglePayComponentDialogFragment
- if (fragment == null) {
- adyenLog(AdyenLogLevel.ERROR) { "GooglePayComponentDialogFragment is not loaded" }
- return
- }
- fragment.handleActivityResult(resultCode, data)
- }
-
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
adyenLog(AdyenLogLevel.DEBUG) { "onNewIntent" }
diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt
index e3f2365467..b4b0fdfa33 100644
--- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt
+++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt
@@ -8,15 +8,13 @@
package com.adyen.checkout.dropin.internal.ui
-import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
-import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.adyen.checkout.components.core.ActionComponentData
import com.adyen.checkout.components.core.ComponentCallback
import com.adyen.checkout.components.core.ComponentError
@@ -24,47 +22,70 @@ import com.adyen.checkout.components.core.PaymentMethod
import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.exception.CheckoutException
import com.adyen.checkout.core.internal.util.adyenLog
-import com.adyen.checkout.dropin.R
+import com.adyen.checkout.dropin.databinding.FragmentGooglePayComponentBinding
import com.adyen.checkout.dropin.internal.provider.getComponentFor
import com.adyen.checkout.googlepay.GooglePayComponent
import com.adyen.checkout.googlepay.GooglePayComponentState
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
@Suppress("TooManyFunctions")
internal class GooglePayComponentDialogFragment :
DropInBottomSheetDialogFragment(),
ComponentCallback {
+ private var _binding: FragmentGooglePayComponentBinding? = null
+ private val binding: FragmentGooglePayComponentBinding get() = requireNotNull(_binding)
+
private val googlePayViewModel: GooglePayViewModel by viewModels()
private lateinit var paymentMethod: PaymentMethod
private lateinit var component: GooglePayComponent
+ private val toolbarMode: DropInBottomSheetToolbarMode
+ get() = when {
+ dropInViewModel.shouldSkipToSinglePaymentMethod() -> DropInBottomSheetToolbarMode.CLOSE_BUTTON
+ else -> DropInBottomSheetToolbarMode.BACK_BUTTON
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
adyenLog(AdyenLogLevel.DEBUG) { "onCreate" }
super.onCreate(savedInstanceState)
arguments?.let {
+ @Suppress("DEPRECATION")
paymentMethod = it.getParcelable(PAYMENT_METHOD) ?: throw IllegalArgumentException("Payment method is null")
}
-
- googlePayViewModel.fragmentLoaded()
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
adyenLog(AdyenLogLevel.DEBUG) { "onCreateView" }
- return inflater.inflate(R.layout.fragment_google_pay_component, container, false)
+ _binding = FragmentGooglePayComponentBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
adyenLog(AdyenLogLevel.DEBUG) { "onViewCreated" }
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- googlePayViewModel.eventsFlow.collect { handleEvent(it) }
- }
- }
+ initToolbar()
loadComponent()
+
+ binding.componentView.attach(component, viewLifecycleOwner)
+
+ googlePayViewModel.onFragmentLoaded()
+
+ googlePayViewModel.eventsFlow
+ .onEach(::handleEvent)
+ .flowWithLifecycle(viewLifecycleOwner.lifecycle)
+ .launchIn(viewLifecycleOwner.lifecycleScope)
+ }
+
+ private fun initToolbar() = with(binding.bottomSheetToolbar) {
+ setTitle(paymentMethod.name)
+ setOnButtonClickListener {
+ onBackPressed()
+ }
+ setMode(toolbarMode)
}
private fun loadComponent() {
@@ -102,7 +123,7 @@ internal class GooglePayComponentDialogFragment :
private fun handleEvent(event: GooglePayFragmentEvent) {
when (event) {
is GooglePayFragmentEvent.StartGooglePay -> {
- component.startGooglePayScreen(requireActivity(), DropInActivity.GOOGLE_PAY_REQUEST_CODE)
+ component.submit()
}
}
}
@@ -127,8 +148,9 @@ internal class GooglePayComponentDialogFragment :
return true
}
- fun handleActivityResult(resultCode: Int, data: Intent?) {
- component.handleActivityResult(resultCode, data)
+ override fun onDestroyView() {
+ _binding = null
+ super.onDestroyView()
}
companion object {
diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayViewModel.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayViewModel.kt
index 49e3654a28..27eb23376a 100644
--- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayViewModel.kt
+++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayViewModel.kt
@@ -10,7 +10,6 @@ package com.adyen.checkout.dropin.internal.ui
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
import com.adyen.checkout.components.core.internal.SavedStateHandleContainer
import com.adyen.checkout.components.core.internal.SavedStateHandleProperty
import com.adyen.checkout.components.core.internal.util.bufferedChannel
@@ -18,7 +17,6 @@ import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.internal.util.adyenLog
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
-import kotlinx.coroutines.launch
internal class GooglePayViewModel(
override val savedStateHandle: SavedStateHandle
@@ -29,13 +27,11 @@ internal class GooglePayViewModel(
private var isGooglePayStarted: Boolean? by SavedStateHandleProperty(IS_GOOGLE_PAY_STARTED)
- fun fragmentLoaded() {
+ fun onFragmentLoaded() {
+ adyenLog(AdyenLogLevel.DEBUG) { "onFragmentLoaded" }
if (isGooglePayStarted == true) return
isGooglePayStarted = true
- viewModelScope.launch {
- adyenLog(AdyenLogLevel.DEBUG) { "Sending start GooglePay event" }
- eventChannel.send(GooglePayFragmentEvent.StartGooglePay)
- }
+ eventChannel.trySend(GooglePayFragmentEvent.StartGooglePay)
}
companion object {
@@ -43,6 +39,6 @@ internal class GooglePayViewModel(
}
}
-internal sealed class GooglePayFragmentEvent {
- object StartGooglePay : GooglePayFragmentEvent()
+internal abstract class GooglePayFragmentEvent {
+ data object StartGooglePay : GooglePayFragmentEvent()
}
diff --git a/drop-in/src/main/res/layout/fragment_google_pay_component.xml b/drop-in/src/main/res/layout/fragment_google_pay_component.xml
index 07f50189f3..59b9c0ff67 100644
--- a/drop-in/src/main/res/layout/fragment_google_pay_component.xml
+++ b/drop-in/src/main/res/layout/fragment_google_pay_component.xml
@@ -1,5 +1,4 @@
-
-
-
\ No newline at end of file
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+
+
+
+
+
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/compose/ResultContent.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/compose/ResultContent.kt
index c3676375b6..1c15cc0408 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/compose/ResultContent.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/compose/ResultContent.kt
@@ -20,7 +20,9 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.adyen.checkout.example.ui.theme.ExampleTheme
@@ -35,19 +37,14 @@ internal fun ResultContent(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
- val tint = when (resultState) {
- ResultState.SUCCESS -> ExampleTheme.customColors.success
- ResultState.PENDING -> ExampleTheme.customColors.warning
- ResultState.FAILURE -> MaterialTheme.colorScheme.error
- }
Icon(
painter = painterResource(id = resultState.drawable),
contentDescription = null,
- tint = tint,
+ tint = Color.Unspecified,
modifier = Modifier.size(100.dp),
)
Spacer(modifier = Modifier.height(ExampleTheme.dimensions.grid_2))
- Text(text = resultState.text, style = MaterialTheme.typography.displaySmall)
+ Text(text = resultState.text, style = MaterialTheme.typography.displaySmall, textAlign = TextAlign.Center)
}
}
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/compose/ResultState.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/compose/ResultState.kt
index 2debb57da3..7ba1de3d56 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/compose/ResultState.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/compose/ResultState.kt
@@ -10,11 +10,14 @@ package com.adyen.checkout.example.ui.compose
import com.adyen.checkout.example.R
-enum class ResultState(
+data class ResultState(
val drawable: Int,
val text: String,
) {
- SUCCESS(R.drawable.ic_result_success, "Payment successful!"),
- PENDING(R.drawable.ic_result_pending, "Payment pending..."),
- FAILURE(R.drawable.ic_result_failure, "Payment failed..."),
+
+ companion object {
+ val SUCCESS = ResultState(R.drawable.ic_result_success, "Payment successful!")
+ val PENDING = ResultState(R.drawable.ic_result_pending, "Payment pending...")
+ val FAILURE = ResultState(R.drawable.ic_result_failure, "Payment failed...")
+ }
}
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt
index 4d35bcc87b..811d2b9489 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt
@@ -89,6 +89,7 @@ internal class CheckoutConfigurationProvider @Inject constructor(
}
googlePay {
+ setSubmitButtonVisible(true)
setCountryCode(keyValueStorage.getCountry())
}
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayActivityResult.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayActivityResult.kt
deleted file mode 100644
index 019178c53e..0000000000
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayActivityResult.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) 2023 Adyen N.V.
- *
- * This file is open source and available under the MIT license. See the LICENSE file for more info.
- *
- * Created by josephj on 13/12/2023.
- */
-
-package com.adyen.checkout.example.ui.googlepay
-
-import android.content.Intent
-import com.adyen.checkout.example.ui.googlepay.compose.SessionsGooglePayComponentData
-
-internal data class GooglePayActivityResult(
- val componentData: SessionsGooglePayComponentData,
- val resultCode: Int,
- val data: Intent?,
-)
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayFragment.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayFragment.kt
index 91609d18a1..0af398bdf5 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayFragment.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayFragment.kt
@@ -8,7 +8,6 @@
package com.adyen.checkout.example.ui.googlepay
-import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -25,8 +24,6 @@ import com.adyen.checkout.example.extensions.getLogTag
import com.adyen.checkout.example.ui.configuration.CheckoutConfigurationProvider
import com.adyen.checkout.googlepay.GooglePayComponent
import com.adyen.checkout.redirect.RedirectComponent
-import com.google.android.gms.wallet.button.ButtonConstants.ButtonType
-import com.google.android.gms.wallet.button.ButtonOptions
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
@@ -89,8 +86,6 @@ class GooglePayFragment : BottomSheetDialogFragment() {
this.googlePayComponent = googlePayComponent
binding.componentView.attach(googlePayComponent, viewLifecycleOwner)
-
- loadGooglePayButton()
}
private fun onViewState(state: GooglePayViewState) {
@@ -100,28 +95,18 @@ class GooglePayFragment : BottomSheetDialogFragment() {
binding.errorView.text = getString(state.message)
binding.componentView.isVisible = false
binding.progressIndicator.isVisible = false
- binding.googlePayButton.isVisible = false
}
GooglePayViewState.Loading -> {
binding.errorView.isVisible = false
binding.componentView.isVisible = false
binding.progressIndicator.isVisible = true
- binding.googlePayButton.isVisible = false
- }
-
- GooglePayViewState.ShowButton -> {
- binding.errorView.isVisible = false
- binding.componentView.isVisible = false
- binding.progressIndicator.isVisible = false
- binding.googlePayButton.isVisible = true
}
GooglePayViewState.ShowComponent -> {
binding.errorView.isVisible = false
binding.componentView.isVisible = true
binding.progressIndicator.isVisible = false
- binding.googlePayButton.isVisible = false
}
}
}
@@ -138,29 +123,6 @@ class GooglePayFragment : BottomSheetDialogFragment() {
dismiss()
}
- private fun loadGooglePayButton() {
- val allowedPaymentMethods = googlePayComponent?.getGooglePayButtonParameters()?.allowedPaymentMethods.orEmpty()
- val buttonOptions = ButtonOptions
- .newBuilder()
- .setButtonType(ButtonType.PAY)
- .setAllowedPaymentMethods(allowedPaymentMethods)
- .build()
- binding.googlePayButton.initialize(buttonOptions)
-
- binding.googlePayButton.setOnClickListener {
- googlePayComponent?.startGooglePayScreen(requireActivity(), ACTIVITY_RESULT_CODE)
- }
- }
-
- // It is required to use onActivityResult with the Google Pay library (AutoResolveHelper).
- @Deprecated("Deprecated in Java")
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == ACTIVITY_RESULT_CODE) {
- googlePayComponent?.handleActivityResult(resultCode, data)
- }
- }
-
override fun onDestroyView() {
super.onDestroyView()
_binding = null
@@ -172,7 +134,6 @@ class GooglePayFragment : BottomSheetDialogFragment() {
internal val TAG = getLogTag()
internal const val RETURN_URL_EXTRA = "RETURN_URL_EXTRA"
- internal const val ACTIVITY_RESULT_CODE = 1
fun show(fragmentManager: FragmentManager) {
GooglePayFragment().show(fragmentManager, TAG)
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayViewModel.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayViewModel.kt
index c4d3be339c..6f958732b6 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayViewModel.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayViewModel.kt
@@ -84,6 +84,7 @@ internal class GooglePayViewModel @Inject constructor(
?.firstOrNull { GooglePayComponent.PROVIDER.isPaymentMethodSupported(it) }
if (paymentMethod == null) {
+ @Suppress("RestrictedApi")
_viewState.emit(GooglePayViewState.Error(UICoreR.string.error_dialog_title))
return@withContext
}
@@ -114,7 +115,7 @@ internal class GooglePayViewModel @Inject constructor(
override fun onAvailabilityResult(isAvailable: Boolean, paymentMethod: PaymentMethod) {
viewModelScope.launch {
if (isAvailable) {
- _viewState.emit(GooglePayViewState.ShowButton)
+ _viewState.emit(GooglePayViewState.ShowComponent)
} else {
_viewState.emit(GooglePayViewState.Error(R.string.google_pay_unavailable_error))
}
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayViewState.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayViewState.kt
index d9bceb0940..d66232b067 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayViewState.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/GooglePayViewState.kt
@@ -14,8 +14,6 @@ internal sealed class GooglePayViewState {
data object Loading : GooglePayViewState()
- data object ShowButton : GooglePayViewState()
-
data object ShowComponent : GooglePayViewState()
data class Error(@StringRes val message: Int) : GooglePayViewState()
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayActivity.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayActivity.kt
index 2ded1978c8..0d66f76d3b 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayActivity.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayActivity.kt
@@ -13,6 +13,8 @@ import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.core.view.WindowCompat
import com.adyen.checkout.example.ui.theme.ExampleTheme
import com.adyen.checkout.example.ui.theme.UIThemeRepository
@@ -39,12 +41,14 @@ class SessionsGooglePayActivity : AppCompatActivity() {
intent = (intent ?: Intent()).putExtra(RETURN_URL_EXTRA, returnUrl)
setContent {
+ val googlePayState by sessionsGooglePayViewModel.googlePayState.collectAsState()
+ val eventsState by sessionsGooglePayViewModel.stateEvents.collectAsState()
val isDarkTheme = uiThemeRepository.isDarkTheme()
ExampleTheme(isDarkTheme) {
SessionsGooglePayScreen(
- useDarkTheme = isDarkTheme,
onBackPressed = { onBackPressedDispatcher.onBackPressed() },
- viewModel = sessionsGooglePayViewModel,
+ googlePayState = googlePayState,
+ eventsState = eventsState,
)
}
}
@@ -59,13 +63,6 @@ class SessionsGooglePayActivity : AppCompatActivity() {
}
}
- // It is required to use onActivityResult with the Google Pay library (AutoResolveHelper).
- @Deprecated("Deprecated in Java")
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- sessionsGooglePayViewModel.onActivityResult(requestCode, resultCode, data)
- }
-
companion object {
internal const val RETURN_URL_EXTRA = "RETURN_URL_EXTRA"
}
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayEvents.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayEvents.kt
new file mode 100644
index 0000000000..45df475e39
--- /dev/null
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayEvents.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 15/5/2024.
+ */
+
+package com.adyen.checkout.example.ui.googlepay.compose
+
+internal abstract class SessionsGooglePayEvents internal constructor() {
+ object None : SessionsGooglePayEvents()
+ class ComponentData(val data: SessionsGooglePayComponentData) : SessionsGooglePayEvents()
+ class Action(val action: com.adyen.checkout.components.core.action.Action) : SessionsGooglePayEvents()
+ class Intent(val intent: android.content.Intent) : SessionsGooglePayEvents()
+}
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayScreen.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayScreen.kt
index 41a3e65561..701fe4ec4f 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayScreen.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayScreen.kt
@@ -18,7 +18,7 @@ import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
@@ -28,25 +28,19 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.adyen.checkout.components.compose.AdyenComponent
import com.adyen.checkout.components.compose.get
import com.adyen.checkout.example.ui.compose.ResultContent
-import com.adyen.checkout.example.ui.googlepay.GooglePayActivityResult
import com.adyen.checkout.googlepay.GooglePayComponent
-import com.google.pay.button.ButtonTheme
-import com.google.pay.button.ButtonType
-import com.google.pay.button.PayButton
@Composable
internal fun SessionsGooglePayScreen(
- useDarkTheme: Boolean,
+ googlePayState: SessionsGooglePayState,
+ eventsState: SessionsGooglePayEvents,
onBackPressed: () -> Unit,
- viewModel: SessionsGooglePayViewModel,
) {
Scaffold(
modifier = Modifier.windowInsetsPadding(WindowInsets.ime),
@@ -55,26 +49,17 @@ internal fun SessionsGooglePayScreen(
title = { Text(text = "Google Pay with sessions") },
navigationIcon = {
IconButton(onClick = onBackPressed) {
- Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
}
},
)
},
) { innerPadding ->
- val googlePayState by viewModel.googlePayState.collectAsState()
SessionsGooglePayContent(
googlePayState = googlePayState,
- onButtonClicked = viewModel::onButtonClicked,
- useDarkTheme = useDarkTheme,
+ googlePayEvents = eventsState,
modifier = Modifier.padding(innerPadding),
)
-
- with(googlePayState) {
- HandleStartGooglePay(startGooglePay, viewModel::onGooglePayStarted)
- HandleActivityResult(activityResultToHandle, viewModel::onActivityResultHandled)
- HandleAction(actionToHandle, viewModel::onActionConsumed)
- HandleNewIntent(intentToHandle, viewModel::onNewIntentHandled)
- }
}
}
@@ -82,101 +67,50 @@ internal fun SessionsGooglePayScreen(
@Composable
private fun SessionsGooglePayContent(
googlePayState: SessionsGooglePayState,
- onButtonClicked: () -> Unit,
- useDarkTheme: Boolean,
+ googlePayEvents: SessionsGooglePayEvents,
modifier: Modifier = Modifier,
) {
+ val activity = LocalContext.current as Activity
+ lateinit var googlePayComponent: GooglePayComponent
+
+ when (googlePayEvents) {
+ is SessionsGooglePayEvents.ComponentData -> {
+ googlePayComponent = getGooglePayComponent(componentData = googlePayEvents.data)
+ }
+
+ is SessionsGooglePayEvents.Action -> {
+ LaunchedEffect(googlePayEvents.action) {
+ googlePayComponent.handleAction(googlePayEvents.action, activity)
+ }
+ }
+
+ is SessionsGooglePayEvents.Intent -> {
+ LaunchedEffect(googlePayEvents.intent) {
+ googlePayComponent.handleIntent(googlePayEvents.intent)
+ }
+ }
+ }
+
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
- when (val uiState = googlePayState.uiState) {
- SessionsGooglePayUIState.Loading -> {
+ when (googlePayState) {
+ SessionsGooglePayState.Loading -> {
CircularProgressIndicator()
}
- is SessionsGooglePayUIState.ShowButton -> {
- val googlePayComponent = getGooglePayComponent(componentData = uiState.componentData)
- PayButton(
- onClick = onButtonClicked,
- allowedPaymentMethods = googlePayComponent.getGooglePayButtonParameters().allowedPaymentMethods,
- theme = if (useDarkTheme) ButtonTheme.Light else ButtonTheme.Dark,
- type = ButtonType.Pay,
- )
- }
-
- is SessionsGooglePayUIState.ShowComponent -> {
- val googlePayComponent = getGooglePayComponent(componentData = uiState.componentData)
- AdyenComponent(
- googlePayComponent,
- modifier,
- )
+ is SessionsGooglePayState.ShowButton -> {
+ AdyenComponent(googlePayComponent)
}
- is SessionsGooglePayUIState.FinalResult -> {
- ResultContent(uiState.finalResult)
+ is SessionsGooglePayState.FinalResult -> {
+ ResultContent(googlePayState.finalResult)
}
}
}
}
-@Composable
-private fun HandleStartGooglePay(
- startGooglePayData: SessionsStartGooglePayData?,
- onGooglePayStarted: () -> Unit
-) {
- if (startGooglePayData == null) return
- val activity = LocalContext.current as Activity
- val googlePayComponent = getGooglePayComponent(componentData = startGooglePayData.componentData)
- LaunchedEffect(startGooglePayData) {
- googlePayComponent.startGooglePayScreen(
- activity,
- startGooglePayData.requestCode,
- )
- onGooglePayStarted()
- }
-}
-
-@Composable
-private fun HandleActivityResult(
- activityResultToHandle: GooglePayActivityResult?,
- onActivityResultHandled: () -> Unit
-) {
- if (activityResultToHandle == null) return
- val googlePayComponent = getGooglePayComponent(componentData = activityResultToHandle.componentData)
- LaunchedEffect(activityResultToHandle) {
- googlePayComponent.handleActivityResult(activityResultToHandle.resultCode, activityResultToHandle.data)
- onActivityResultHandled()
- }
-}
-
-@Composable
-private fun HandleAction(
- actionToHandle: SessionsGooglePayAction?,
- onActionConsumed: () -> Unit
-) {
- if (actionToHandle == null) return
- val activity = LocalContext.current as Activity
- val googlePayComponent = getGooglePayComponent(componentData = actionToHandle.componentData)
- LaunchedEffect(actionToHandle) {
- googlePayComponent.handleAction(actionToHandle.action, activity)
- onActionConsumed()
- }
-}
-
-@Composable
-private fun HandleNewIntent(
- intentToHandle: SessionsGooglePayIntent?,
- onNewIntentHandled: () -> Unit
-) {
- if (intentToHandle == null) return
- val googlePayComponent = getGooglePayComponent(componentData = intentToHandle.componentData)
- LaunchedEffect(intentToHandle) {
- googlePayComponent.handleIntent(intentToHandle.intent)
- onNewIntentHandled()
- }
-}
-
@Composable
private fun getGooglePayComponent(componentData: SessionsGooglePayComponentData): GooglePayComponent {
return with(componentData) {
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayState.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayState.kt
index af6755c00c..5c765a1427 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayState.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayState.kt
@@ -9,13 +9,16 @@
package com.adyen.checkout.example.ui.googlepay.compose
import androidx.compose.runtime.Immutable
-import com.adyen.checkout.example.ui.googlepay.GooglePayActivityResult
+import com.adyen.checkout.example.ui.compose.ResultState
-@Immutable
-internal data class SessionsGooglePayState(
- val uiState: SessionsGooglePayUIState,
- val startGooglePay: SessionsStartGooglePayData? = null,
- val activityResultToHandle: GooglePayActivityResult? = null,
- val actionToHandle: SessionsGooglePayAction? = null,
- val intentToHandle: SessionsGooglePayIntent? = null,
-)
+internal sealed class SessionsGooglePayState {
+
+ @Immutable
+ data object Loading : SessionsGooglePayState()
+
+ @Immutable
+ data object ShowButton : SessionsGooglePayState()
+
+ @Immutable
+ data class FinalResult(val finalResult: ResultState) : SessionsGooglePayState()
+}
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayViewModel.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayViewModel.kt
index 3939f75ef3..09a7fcfaa9 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayViewModel.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/googlepay/compose/SessionsGooglePayViewModel.kt
@@ -8,18 +8,16 @@
package com.adyen.checkout.example.ui.googlepay.compose
-import android.app.Application
import android.content.Intent
import android.util.Log
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.adyen.checkout.components.core.CheckoutConfiguration
-import com.adyen.checkout.components.core.ComponentAvailableCallback
import com.adyen.checkout.components.core.ComponentError
-import com.adyen.checkout.components.core.PaymentMethod
import com.adyen.checkout.components.core.PaymentMethodTypes
import com.adyen.checkout.components.core.action.Action
+import com.adyen.checkout.example.R
import com.adyen.checkout.example.data.storage.KeyValueStorage
import com.adyen.checkout.example.extensions.IODispatcher
import com.adyen.checkout.example.extensions.getLogTag
@@ -28,9 +26,8 @@ import com.adyen.checkout.example.service.getSessionRequest
import com.adyen.checkout.example.service.getSettingsInstallmentOptionsMode
import com.adyen.checkout.example.ui.compose.ResultState
import com.adyen.checkout.example.ui.configuration.CheckoutConfigurationProvider
-import com.adyen.checkout.example.ui.googlepay.GooglePayActivityResult
-import com.adyen.checkout.googlepay.GooglePayComponent
import com.adyen.checkout.googlepay.GooglePayComponentState
+import com.adyen.checkout.googlepay.GooglePayUnavailableException
import com.adyen.checkout.sessions.core.CheckoutSession
import com.adyen.checkout.sessions.core.CheckoutSessionProvider
import com.adyen.checkout.sessions.core.CheckoutSessionResult
@@ -50,22 +47,19 @@ import javax.inject.Inject
@HiltViewModel
internal class SessionsGooglePayViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
- private val application: Application,
private val paymentsRepository: PaymentsRepository,
private val keyValueStorage: KeyValueStorage,
checkoutConfigurationProvider: CheckoutConfigurationProvider,
) : ViewModel(),
- SessionComponentCallback,
- ComponentAvailableCallback {
+ SessionComponentCallback {
private val checkoutConfiguration = checkoutConfigurationProvider.checkoutConfig
- private val _googlePayState = MutableStateFlow(SessionsGooglePayState(SessionsGooglePayUIState.Loading))
+ private val _googlePayState = MutableStateFlow(SessionsGooglePayState.Loading)
val googlePayState: StateFlow = _googlePayState.asStateFlow()
- private var _componentData: SessionsGooglePayComponentData? = null
- private val componentData: SessionsGooglePayComponentData
- get() = requireNotNull(_componentData) { "component data should not be null" }
+ private val _stateEvents: MutableStateFlow = MutableStateFlow(SessionsGooglePayEvents.None)
+ val stateEvents: StateFlow = _stateEvents.asStateFlow()
init {
viewModelScope.launch { fetchSession() }
@@ -86,14 +80,15 @@ internal class SessionsGooglePayViewModel @Inject constructor(
return@withContext
}
- _componentData = SessionsGooglePayComponentData(
+ val componentData = SessionsGooglePayComponentData(
checkoutSession,
checkoutConfiguration,
paymentMethod,
this@SessionsGooglePayViewModel,
)
- checkGooglePayAvailability(paymentMethod, checkoutConfiguration)
+ updateEvent { SessionsGooglePayEvents.ComponentData(componentData) }
+ updateState { SessionsGooglePayState.ShowButton }
}
private suspend fun getSession(paymentMethodType: String): CheckoutSession? {
@@ -129,41 +124,23 @@ internal class SessionsGooglePayViewModel @Inject constructor(
}
}
- private fun checkGooglePayAvailability(
- paymentMethod: PaymentMethod,
- checkoutConfiguration: CheckoutConfiguration,
- ) {
- GooglePayComponent.PROVIDER.isAvailable(
- application = application,
- paymentMethod = paymentMethod,
- checkoutConfiguration = checkoutConfiguration,
- callback = this,
- )
- }
-
- override fun onAvailabilityResult(isAvailable: Boolean, paymentMethod: PaymentMethod) {
- viewModelScope.launch {
- if (isAvailable) {
- updateState { it.copy(uiState = SessionsGooglePayUIState.ShowButton(componentData)) }
- } else {
- onError()
- }
- }
- }
-
override fun onAction(action: Action) {
- updateState { it.copy(actionToHandle = SessionsGooglePayAction(componentData, action)) }
+ updateEvent { SessionsGooglePayEvents.Action(action) }
}
override fun onError(componentError: ComponentError) {
- Log.e(TAG, "Component error occurred")
- onError()
+ val exception = componentError.exception
+ Log.e(TAG, "Component error occurred", exception)
+
+ if (exception is GooglePayUnavailableException) {
+ onGooglePayUnavailable()
+ } else {
+ onError()
+ }
}
override fun onFinished(result: SessionPaymentResult) {
- updateState {
- it.copy(uiState = SessionsGooglePayUIState.FinalResult(getFinalResultState(result)))
- }
+ updateState { SessionsGooglePayState.FinalResult(getFinalResultState(result)) }
}
private fun getFinalResultState(result: SessionPaymentResult): ResultState = when (result.resultCode) {
@@ -174,50 +151,33 @@ internal class SessionsGooglePayViewModel @Inject constructor(
else -> ResultState.FAILURE
}
- private fun onError() {
- updateState { it.copy(uiState = SessionsGooglePayUIState.FinalResult(ResultState.FAILURE)) }
- }
-
- private fun updateState(block: (SessionsGooglePayState) -> SessionsGooglePayState) {
- _googlePayState.update(block)
- }
-
- fun onButtonClicked() {
+ private fun onGooglePayUnavailable() {
updateState {
- it.copy(
- uiState = SessionsGooglePayUIState.ShowComponent(componentData),
- startGooglePay = SessionsStartGooglePayData(componentData, ACTIVITY_RESULT_CODE),
+ val result = ResultState(
+ R.drawable.ic_result_failure,
+ "Google Pay is not available on this device",
)
+ SessionsGooglePayState.FinalResult(result)
}
}
- fun onGooglePayStarted() {
- updateState { it.copy(startGooglePay = null) }
- }
-
- fun onActionConsumed() {
- updateState { it.copy(actionToHandle = null) }
+ private fun onError() {
+ updateState { SessionsGooglePayState.FinalResult(ResultState.FAILURE) }
}
- fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (requestCode != ACTIVITY_RESULT_CODE) return
- updateState { it.copy(activityResultToHandle = GooglePayActivityResult(componentData, resultCode, data)) }
+ private fun updateState(block: (SessionsGooglePayState) -> SessionsGooglePayState) {
+ _googlePayState.update(block)
}
- fun onActivityResultHandled() {
- updateState { it.copy(activityResultToHandle = null) }
+ private fun updateEvent(block: (SessionsGooglePayEvents) -> SessionsGooglePayEvents) {
+ _stateEvents.update(block)
}
fun onNewIntent(intent: Intent) {
- updateState { it.copy(intentToHandle = SessionsGooglePayIntent(componentData, intent)) }
- }
-
- fun onNewIntentHandled() {
- updateState { it.copy(intentToHandle = null) }
+ updateEvent { SessionsGooglePayEvents.Intent(intent) }
}
companion object {
private val TAG = getLogTag()
- private const val ACTIVITY_RESULT_CODE = 1
}
}
diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/main/MainActivity.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/main/MainActivity.kt
index 37640c65fd..d4d6101634 100644
--- a/example-app/src/main/java/com/adyen/checkout/example/ui/main/MainActivity.kt
+++ b/example-app/src/main/java/com/adyen/checkout/example/ui/main/MainActivity.kt
@@ -223,16 +223,6 @@ class MainActivity : AppCompatActivity() {
binding.switchSessions.isChecked = isChecked
}
- // It is required to use onActivityResult with the Google Pay library (AutoResolveHelper).
- @Deprecated("Deprecated in Java")
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == GooglePayFragment.ACTIVITY_RESULT_CODE) {
- (supportFragmentManager.findFragmentByTag(GooglePayFragment.TAG) as? GooglePayFragment)
- ?.onActivityResult(requestCode, resultCode, data)
- }
- }
-
override fun onDestroy() {
super.onDestroy()
componentItemAdapter = null
diff --git a/example-app/src/main/res/layout/fragment_google_pay.xml b/example-app/src/main/res/layout/fragment_google_pay.xml
index fa75802986..2dce7d3b29 100644
--- a/example-app/src/main/res/layout/fragment_google_pay.xml
+++ b/example-app/src/main/res/layout/fragment_google_pay.xml
@@ -8,7 +8,6 @@
@@ -16,18 +15,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
-
-
-
diff --git a/googlepay/api/googlepay.api b/googlepay/api/googlepay.api
index dc8b4cabca..71125b8620 100644
--- a/googlepay/api/googlepay.api
+++ b/googlepay/api/googlepay.api
@@ -65,7 +65,59 @@ public final class com/adyen/checkout/googlepay/GooglePayButtonParameters {
public fun toString ()Ljava/lang/String;
}
-public final class com/adyen/checkout/googlepay/GooglePayComponent : androidx/lifecycle/ViewModel, com/adyen/checkout/action/core/internal/ActionHandlingComponent, com/adyen/checkout/components/core/internal/ActivityResultHandlingComponent, com/adyen/checkout/components/core/internal/PaymentComponent, com/adyen/checkout/ui/core/internal/ui/ViewableComponent {
+public final class com/adyen/checkout/googlepay/GooglePayButtonStyling : android/os/Parcelable {
+ public static final field CREATOR Landroid/os/Parcelable$Creator;
+ public fun ()V
+ public fun (Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;Lcom/adyen/checkout/googlepay/GooglePayButtonType;Ljava/lang/Integer;)V
+ public synthetic fun (Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;Lcom/adyen/checkout/googlepay/GooglePayButtonType;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;
+ public final fun component2 ()Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public final fun component3 ()Ljava/lang/Integer;
+ public final fun copy (Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;Lcom/adyen/checkout/googlepay/GooglePayButtonType;Ljava/lang/Integer;)Lcom/adyen/checkout/googlepay/GooglePayButtonStyling;
+ public static synthetic fun copy$default (Lcom/adyen/checkout/googlepay/GooglePayButtonStyling;Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;Lcom/adyen/checkout/googlepay/GooglePayButtonType;Ljava/lang/Integer;ILjava/lang/Object;)Lcom/adyen/checkout/googlepay/GooglePayButtonStyling;
+ public fun describeContents ()I
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getButtonTheme ()Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;
+ public final fun getButtonType ()Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public final fun getCornerRadius ()Ljava/lang/Integer;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+ public fun writeToParcel (Landroid/os/Parcel;I)V
+}
+
+public final class com/adyen/checkout/googlepay/GooglePayButtonStyling$Creator : android/os/Parcelable$Creator {
+ public fun ()V
+ public final fun createFromParcel (Landroid/os/Parcel;)Lcom/adyen/checkout/googlepay/GooglePayButtonStyling;
+ public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
+ public final fun newArray (I)[Lcom/adyen/checkout/googlepay/GooglePayButtonStyling;
+ public synthetic fun newArray (I)[Ljava/lang/Object;
+}
+
+public final class com/adyen/checkout/googlepay/GooglePayButtonTheme : java/lang/Enum {
+ public static final field DARK Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;
+ public static final field LIGHT Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public final fun getValue ()I
+ public static fun valueOf (Ljava/lang/String;)Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;
+ public static fun values ()[Lcom/adyen/checkout/googlepay/GooglePayButtonTheme;
+}
+
+public final class com/adyen/checkout/googlepay/GooglePayButtonType : java/lang/Enum {
+ public static final field BOOK Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static final field BUY Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static final field CHECKOUT Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static final field DONATE Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static final field ORDER Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static final field PAY Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static final field PLAIN Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static final field SUBSCRIBE Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public final fun getValue ()I
+ public static fun valueOf (Ljava/lang/String;)Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+ public static fun values ()[Lcom/adyen/checkout/googlepay/GooglePayButtonType;
+}
+
+public final class com/adyen/checkout/googlepay/GooglePayComponent : androidx/lifecycle/ViewModel, com/adyen/checkout/action/core/internal/ActionHandlingComponent, com/adyen/checkout/components/core/internal/ActivityResultHandlingComponent, com/adyen/checkout/components/core/internal/ButtonComponent, com/adyen/checkout/components/core/internal/PaymentComponent, com/adyen/checkout/ui/core/internal/ui/ViewableComponent {
public static final field Companion Lcom/adyen/checkout/googlepay/GooglePayComponent$Companion;
public static final field PAYMENT_METHOD_TYPES Ljava/util/List;
public static final field PROVIDER Lcom/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider;
@@ -76,9 +128,11 @@ public final class com/adyen/checkout/googlepay/GooglePayComponent : androidx/li
public fun handleAction (Lcom/adyen/checkout/components/core/action/Action;Landroid/app/Activity;)V
public fun handleActivityResult (ILandroid/content/Intent;)V
public fun handleIntent (Landroid/content/Intent;)V
+ public fun isConfirmationRequired ()Z
public fun setInteractionBlocked (Z)V
public fun setOnRedirectListener (Lkotlin/jvm/functions/Function0;)V
public final fun startGooglePayScreen (Landroid/app/Activity;I)V
+ public fun submit ()V
}
public final class com/adyen/checkout/googlepay/GooglePayComponent$Companion {
@@ -102,9 +156,9 @@ public final class com/adyen/checkout/googlepay/GooglePayComponentState : com/ad
public fun toString ()Ljava/lang/String;
}
-public final class com/adyen/checkout/googlepay/GooglePayConfiguration : com/adyen/checkout/components/core/internal/Configuration {
+public final class com/adyen/checkout/googlepay/GooglePayConfiguration : com/adyen/checkout/components/core/internal/ButtonConfiguration, com/adyen/checkout/components/core/internal/Configuration {
public static final field CREATOR Landroid/os/Parcelable$Creator;
- public synthetic fun (Ljava/util/Locale;Lcom/adyen/checkout/core/Environment;Ljava/lang/String;Lcom/adyen/checkout/components/core/AnalyticsConfiguration;Ljava/lang/String;Ljava/lang/Integer;Lcom/adyen/checkout/components/core/Amount;Ljava/lang/String;Ljava/lang/String;Lcom/adyen/checkout/googlepay/MerchantInfo;Ljava/util/List;Ljava/util/List;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Lcom/adyen/checkout/googlepay/ShippingAddressParameters;Ljava/lang/Boolean;Lcom/adyen/checkout/googlepay/BillingAddressParameters;Lcom/adyen/checkout/action/core/GenericActionConfiguration;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (Ljava/util/Locale;Lcom/adyen/checkout/core/Environment;Ljava/lang/String;Lcom/adyen/checkout/components/core/AnalyticsConfiguration;Lcom/adyen/checkout/components/core/Amount;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Lcom/adyen/checkout/googlepay/MerchantInfo;Ljava/util/List;Ljava/util/List;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Lcom/adyen/checkout/googlepay/ShippingAddressParameters;Ljava/lang/Boolean;Lcom/adyen/checkout/googlepay/BillingAddressParameters;Lcom/adyen/checkout/googlepay/GooglePayButtonStyling;Lcom/adyen/checkout/action/core/GenericActionConfiguration;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun describeContents ()I
public final fun getAllowedAuthMethods ()Ljava/util/List;
public final fun getAllowedCardNetworks ()Ljava/util/List;
@@ -114,6 +168,7 @@ public final class com/adyen/checkout/googlepay/GooglePayConfiguration : com/ady
public fun getClientKey ()Ljava/lang/String;
public final fun getCountryCode ()Ljava/lang/String;
public fun getEnvironment ()Lcom/adyen/checkout/core/Environment;
+ public final fun getGooglePayButtonStyling ()Lcom/adyen/checkout/googlepay/GooglePayButtonStyling;
public final fun getGooglePayEnvironment ()Ljava/lang/Integer;
public final fun getMerchantAccount ()Ljava/lang/String;
public final fun getMerchantInfo ()Lcom/adyen/checkout/googlepay/MerchantInfo;
@@ -127,10 +182,11 @@ public final class com/adyen/checkout/googlepay/GooglePayConfiguration : com/ady
public final fun isEmailRequired ()Ljava/lang/Boolean;
public final fun isExistingPaymentMethodRequired ()Ljava/lang/Boolean;
public final fun isShippingAddressRequired ()Ljava/lang/Boolean;
+ public fun isSubmitButtonVisible ()Ljava/lang/Boolean;
public fun writeToParcel (Landroid/os/Parcel;I)V
}
-public final class com/adyen/checkout/googlepay/GooglePayConfiguration$Builder : com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder {
+public final class com/adyen/checkout/googlepay/GooglePayConfiguration$Builder : com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder, com/adyen/checkout/components/core/internal/ButtonConfigurationBuilder {
public fun (Landroid/content/Context;Lcom/adyen/checkout/core/Environment;Ljava/lang/String;)V
public fun (Lcom/adyen/checkout/core/Environment;Ljava/lang/String;)V
public fun (Ljava/util/Locale;Lcom/adyen/checkout/core/Environment;Ljava/lang/String;)V
@@ -147,11 +203,14 @@ public final class com/adyen/checkout/googlepay/GooglePayConfiguration$Builder :
public final fun setCountryCode (Ljava/lang/String;)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
public final fun setEmailRequired (Z)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
public final fun setExistingPaymentMethodRequired (Z)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
+ public final fun setGooglePayButtonStyling (Lcom/adyen/checkout/googlepay/GooglePayButtonStyling;)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
public final fun setGooglePayEnvironment (I)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
public final fun setMerchantAccount (Ljava/lang/String;)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
public final fun setMerchantInfo (Lcom/adyen/checkout/googlepay/MerchantInfo;)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
public final fun setShippingAddressParameters (Lcom/adyen/checkout/googlepay/ShippingAddressParameters;)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
public final fun setShippingAddressRequired (Z)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
+ public synthetic fun setSubmitButtonVisible (Z)Lcom/adyen/checkout/components/core/internal/ButtonConfigurationBuilder;
+ public fun setSubmitButtonVisible (Z)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
public final fun setTotalPriceStatus (Ljava/lang/String;)Lcom/adyen/checkout/googlepay/GooglePayConfiguration$Builder;
}
@@ -168,6 +227,12 @@ public final class com/adyen/checkout/googlepay/GooglePayConfigurationKt {
public static synthetic fun googlePay$default (Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/adyen/checkout/components/core/CheckoutConfiguration;
}
+public final class com/adyen/checkout/googlepay/GooglePayUnavailableException : com/adyen/checkout/components/core/PaymentMethodUnavailableException {
+ public fun ()V
+ public fun (Ljava/lang/Throwable;)V
+ public synthetic fun (Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+}
+
public final class com/adyen/checkout/googlepay/MerchantInfo : com/adyen/checkout/core/internal/data/model/ModelObject {
public static final field CREATOR Landroid/os/Parcelable$Creator;
public static final field Companion Lcom/adyen/checkout/googlepay/MerchantInfo$Companion;
diff --git a/googlepay/build.gradle b/googlepay/build.gradle
index ecd8f4e789..60ca3a4987 100644
--- a/googlepay/build.gradle
+++ b/googlepay/build.gradle
@@ -30,6 +30,10 @@ android {
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
consumerProguardFiles "consumer-rules.pro"
}
+
+ buildFeatures {
+ viewBinding true
+ }
}
dependencies {
@@ -40,6 +44,7 @@ dependencies {
api project(':sessions-core')
// Dependencies
+ implementation libs.google.pay.play.services.coroutines
api libs.google.pay.play.services.wallet
//Tests
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayButtonStyling.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayButtonStyling.kt
new file mode 100644
index 0000000000..4d48323a70
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayButtonStyling.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 11/10/2024.
+ */
+
+package com.adyen.checkout.googlepay
+
+import android.os.Parcelable
+import androidx.annotation.Dimension
+import com.google.android.gms.wallet.button.ButtonConstants
+import kotlinx.parcelize.Parcelize
+
+/**
+ * Object to style the Google Pay button. Check [the Google docs](https://developers.google.com/pay/api/android/guides/resources/pay-button-api) for more details.
+ *
+ * @param buttonTheme Affects the color scheme of the button.
+ * @param buttonType Changes the text displayed inside of the button.
+ * @param cornerRadius Sets the corner radius of the button. For example, passing 16 means the radius will be 16 dp.
+ */
+@Suppress("MaxLineLength")
+@Parcelize
+data class GooglePayButtonStyling(
+ val buttonTheme: GooglePayButtonTheme? = null,
+ val buttonType: GooglePayButtonType? = null,
+ @Dimension(Dimension.DP) val cornerRadius: Int? = null,
+) : Parcelable
+
+enum class GooglePayButtonTheme(
+ val value: Int,
+) {
+ LIGHT(ButtonConstants.ButtonTheme.LIGHT),
+ DARK(ButtonConstants.ButtonTheme.DARK),
+}
+
+enum class GooglePayButtonType(
+ val value: Int,
+) {
+ BUY(ButtonConstants.ButtonType.BUY),
+ BOOK(ButtonConstants.ButtonType.BOOK),
+ CHECKOUT(ButtonConstants.ButtonType.CHECKOUT),
+ DONATE(ButtonConstants.ButtonType.DONATE),
+ ORDER(ButtonConstants.ButtonType.ORDER),
+ PAY(ButtonConstants.ButtonType.PAY),
+ SUBSCRIBE(ButtonConstants.ButtonType.SUBSCRIBE),
+ PLAIN(ButtonConstants.ButtonType.PLAIN),
+}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayComponent.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayComponent.kt
index f484ff2b2b..6b65bed094 100644
--- a/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayComponent.kt
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayComponent.kt
@@ -17,6 +17,7 @@ import com.adyen.checkout.action.core.internal.DefaultActionHandlingComponent
import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate
import com.adyen.checkout.components.core.PaymentMethodTypes
import com.adyen.checkout.components.core.internal.ActivityResultHandlingComponent
+import com.adyen.checkout.components.core.internal.ButtonComponent
import com.adyen.checkout.components.core.internal.ComponentEventHandler
import com.adyen.checkout.components.core.internal.PaymentComponent
import com.adyen.checkout.components.core.internal.PaymentComponentEvent
@@ -25,9 +26,12 @@ import com.adyen.checkout.components.core.internal.ui.ComponentDelegate
import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.internal.util.adyenLog
import com.adyen.checkout.googlepay.internal.provider.GooglePayComponentProvider
+import com.adyen.checkout.googlepay.internal.ui.DefaultGooglePayDelegate
import com.adyen.checkout.googlepay.internal.ui.GooglePayDelegate
+import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate
import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
import com.adyen.checkout.ui.core.internal.ui.ViewableComponent
+import com.adyen.checkout.ui.core.internal.util.mergeViewFlows
import kotlinx.coroutines.flow.Flow
/**
@@ -42,12 +46,17 @@ class GooglePayComponent internal constructor(
) : ViewModel(),
PaymentComponent,
ActivityResultHandlingComponent,
+ ButtonComponent,
ViewableComponent,
ActionHandlingComponent by actionHandlingComponent {
override val delegate: ComponentDelegate get() = actionHandlingComponent.activeDelegate
- override val viewFlow: Flow = genericActionDelegate.viewFlow
+ override val viewFlow: Flow = mergeViewFlows(
+ viewModelScope,
+ googlePayDelegate.viewFlow,
+ genericActionDelegate.viewFlow,
+ )
init {
googlePayDelegate.initialize(viewModelScope)
@@ -74,10 +83,27 @@ class GooglePayComponent internal constructor(
* @param activity The activity to start the screen and later receive the result.
* @param requestCode The code that will be returned on the [Activity.onActivityResult]
*/
+ @Deprecated("Deprecated in favor of submit()", ReplaceWith("submit()"))
fun startGooglePayScreen(activity: Activity, requestCode: Int) {
+ @Suppress("DEPRECATION")
googlePayDelegate.startGooglePayScreen(activity, requestCode)
}
+ /**
+ * Start the GooglePay screen.
+ */
+ override fun submit() {
+ (delegate as? ButtonDelegate)?.onSubmit()
+ ?: adyenLog(AdyenLogLevel.ERROR) { "Component is currently not submittable, ignoring." }
+ }
+
+ override fun setInteractionBlocked(isInteractionBlocked: Boolean) {
+ (delegate as? DefaultGooglePayDelegate)?.setInteractionBlocked(isInteractionBlocked)
+ ?: adyenLog(AdyenLogLevel.ERROR) { "Payment component is not interactable, ignoring." }
+ }
+
+ override fun isConfirmationRequired(): Boolean = (delegate as? ButtonDelegate)?.isConfirmationRequired() ?: false
+
/**
* Returns some of the parameters required to initialize the [Google Pay button](https://docs.adyen.com/payment-methods/google-pay/android-component/#2-show-the-google-pay-button).
*/
@@ -92,14 +118,11 @@ class GooglePayComponent internal constructor(
* @param resultCode The result code from the [Activity.onActivityResult]
* @param data The data intent from the [Activity.onActivityResult]
*/
+ @Deprecated("When using submit() this method is no longer needed.", ReplaceWith(""))
override fun handleActivityResult(resultCode: Int, data: Intent?) {
googlePayDelegate.handleActivityResult(resultCode, data)
}
- override fun setInteractionBlocked(isInteractionBlocked: Boolean) {
- adyenLog(AdyenLogLevel.WARN) { "Interaction with GooglePayComponent can't be blocked" }
- }
-
override fun onCleared() {
super.onCleared()
adyenLog(AdyenLogLevel.DEBUG) { "onCleared" }
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayConfiguration.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayConfiguration.kt
index 70a2bb0b4f..09a5e5a4da 100644
--- a/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayConfiguration.kt
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayConfiguration.kt
@@ -16,6 +16,8 @@ import com.adyen.checkout.components.core.Amount
import com.adyen.checkout.components.core.AnalyticsConfiguration
import com.adyen.checkout.components.core.CheckoutConfiguration
import com.adyen.checkout.components.core.PaymentMethod
+import com.adyen.checkout.components.core.internal.ButtonConfiguration
+import com.adyen.checkout.components.core.internal.ButtonConfigurationBuilder
import com.adyen.checkout.components.core.internal.Configuration
import com.adyen.checkout.components.core.internal.util.CheckoutConfigurationMarker
import com.adyen.checkout.core.Environment
@@ -34,9 +36,10 @@ class GooglePayConfiguration private constructor(
override val environment: Environment,
override val clientKey: String,
override val analyticsConfiguration: AnalyticsConfiguration?,
+ override val amount: Amount?,
+ override val isSubmitButtonVisible: Boolean?,
val merchantAccount: String?,
val googlePayEnvironment: Int?,
- override val amount: Amount?,
val totalPriceStatus: String?,
val countryCode: String?,
val merchantInfo: MerchantInfo?,
@@ -51,15 +54,17 @@ class GooglePayConfiguration private constructor(
val shippingAddressParameters: ShippingAddressParameters?,
val isBillingAddressRequired: Boolean?,
val billingAddressParameters: BillingAddressParameters?,
+ val googlePayButtonStyling: GooglePayButtonStyling?,
internal val genericActionConfiguration: GenericActionConfiguration,
-) : Configuration {
+) : Configuration, ButtonConfiguration {
/**
* Builder to create a [GooglePayConfiguration].
*/
@Suppress("TooManyFunctions")
class Builder :
- ActionHandlingPaymentMethodConfigurationBuilder {
+ ActionHandlingPaymentMethodConfigurationBuilder,
+ ButtonConfigurationBuilder {
private var merchantAccount: String? = null
private var googlePayEnvironment: Int? = null
private var merchantInfo: MerchantInfo? = null
@@ -76,6 +81,8 @@ class GooglePayConfiguration private constructor(
private var isBillingAddressRequired: Boolean? = null
private var billingAddressParameters: BillingAddressParameters? = null
private var totalPriceStatus: String? = null
+ private var googlePayButtonStyling: GooglePayButtonStyling? = null
+ private var isSubmitButtonVisible: Boolean? = null
/**
* Initialize a configuration builder with the required fields.
@@ -378,15 +385,38 @@ class GooglePayConfiguration private constructor(
return super.setAmount(amount)
}
+ /**
+ * Set a [GooglePayButtonStyling] object for customization of the Google Pay button.
+ *
+ * @param googlePayButtonStyling The customization object.
+ */
+ fun setGooglePayButtonStyling(googlePayButtonStyling: GooglePayButtonStyling): Builder {
+ this.googlePayButtonStyling = googlePayButtonStyling
+ return this
+ }
+
+ /**
+ * Sets if submit button will be visible or not.
+ *
+ * Default is false.
+ *
+ * @param isSubmitButtonVisible If submit button should be visible or not.
+ */
+ override fun setSubmitButtonVisible(isSubmitButtonVisible: Boolean): Builder {
+ this.isSubmitButtonVisible = isSubmitButtonVisible
+ return this
+ }
+
override fun buildInternal(): GooglePayConfiguration {
return GooglePayConfiguration(
shopperLocale = shopperLocale,
environment = environment,
clientKey = clientKey,
analyticsConfiguration = analyticsConfiguration,
+ amount = amount,
+ isSubmitButtonVisible = isSubmitButtonVisible,
merchantAccount = merchantAccount,
googlePayEnvironment = googlePayEnvironment,
- amount = amount,
totalPriceStatus = totalPriceStatus,
countryCode = countryCode,
merchantInfo = merchantInfo,
@@ -401,6 +431,7 @@ class GooglePayConfiguration private constructor(
shippingAddressParameters = shippingAddressParameters,
isBillingAddressRequired = isBillingAddressRequired,
billingAddressParameters = billingAddressParameters,
+ googlePayButtonStyling = googlePayButtonStyling,
genericActionConfiguration = genericActionConfigurationBuilder.build(),
)
}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayUnavailableException.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayUnavailableException.kt
new file mode 100644
index 0000000000..b78803fe78
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/GooglePayUnavailableException.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 30/10/2024.
+ */
+
+package com.adyen.checkout.googlepay
+
+import com.adyen.checkout.components.core.PaymentMethodUnavailableException
+
+class GooglePayUnavailableException(
+ cause: Throwable? = null,
+) : PaymentMethodUnavailableException(
+ "Google Pay is not available",
+ cause,
+)
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt
index be68f24472..89d484ec28 100644
--- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt
@@ -31,17 +31,16 @@ import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParam
import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams
import com.adyen.checkout.components.core.internal.util.get
import com.adyen.checkout.components.core.internal.util.viewModelFactory
-import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.exception.CheckoutException
import com.adyen.checkout.core.exception.ComponentException
import com.adyen.checkout.core.internal.data.api.HttpClientFactory
import com.adyen.checkout.core.internal.util.LocaleProvider
-import com.adyen.checkout.core.internal.util.adyenLog
import com.adyen.checkout.googlepay.GooglePayComponent
import com.adyen.checkout.googlepay.GooglePayComponentState
import com.adyen.checkout.googlepay.GooglePayConfiguration
import com.adyen.checkout.googlepay.internal.ui.DefaultGooglePayDelegate
import com.adyen.checkout.googlepay.internal.ui.model.GooglePayComponentParamsMapper
+import com.adyen.checkout.googlepay.internal.util.GooglePayAvailabilityCheck
import com.adyen.checkout.googlepay.internal.util.GooglePayUtils
import com.adyen.checkout.googlepay.toCheckoutConfiguration
import com.adyen.checkout.sessions.core.CheckoutSession
@@ -53,10 +52,8 @@ import com.adyen.checkout.sessions.core.internal.data.api.SessionRepository
import com.adyen.checkout.sessions.core.internal.data.api.SessionService
import com.adyen.checkout.sessions.core.internal.provider.SessionPaymentComponentProvider
import com.adyen.checkout.sessions.core.internal.ui.model.SessionParamsFactory
-import com.google.android.gms.common.ConnectionResult
-import com.google.android.gms.common.GoogleApiAvailability
+import com.adyen.checkout.ui.core.internal.ui.SubmitHandler
import com.google.android.gms.wallet.Wallet
-import java.lang.ref.WeakReference
class GooglePayComponentProvider
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -108,12 +105,18 @@ constructor(
sessionId = null,
)
+ val paymentsClient =
+ Wallet.getPaymentsClient(application, GooglePayUtils.createWalletOptions(componentParams))
+
val googlePayDelegate = DefaultGooglePayDelegate(
+ submitHandler = SubmitHandler(savedStateHandle),
observerRepository = PaymentObserverRepository(),
paymentMethod = paymentMethod,
order = order,
componentParams = componentParams,
analyticsManager = analyticsManager,
+ paymentsClient = paymentsClient,
+ googlePayAvailabilityCheck = GooglePayAvailabilityCheck(application),
)
val genericActionDelegate =
@@ -195,12 +198,18 @@ constructor(
sessionId = checkoutSession.sessionSetupResponse.id,
)
+ val paymentsClient =
+ Wallet.getPaymentsClient(application, GooglePayUtils.createWalletOptions(componentParams))
+
val googlePayDelegate = DefaultGooglePayDelegate(
+ submitHandler = SubmitHandler(savedStateHandle),
observerRepository = PaymentObserverRepository(),
paymentMethod = paymentMethod,
order = checkoutSession.order,
componentParams = componentParams,
analyticsManager = analyticsManager,
+ paymentsClient = paymentsClient,
+ googlePayAvailabilityCheck = GooglePayAvailabilityCheck(application),
)
val genericActionDelegate =
@@ -275,14 +284,6 @@ constructor(
checkoutConfiguration: CheckoutConfiguration,
callback: ComponentAvailableCallback
) {
- if (
- GoogleApiAvailability.getInstance()
- .isGooglePlayServicesAvailable(application) != ConnectionResult.SUCCESS
- ) {
- callback.onAvailabilityResult(false, paymentMethod)
- return
- }
- val callbackWeakReference = WeakReference(callback)
val componentParams = GooglePayComponentParamsMapper(CommonComponentParamsMapper()).mapToParams(
checkoutConfiguration = checkoutConfiguration,
deviceLocale = localeProvider.getLocale(application),
@@ -291,20 +292,11 @@ constructor(
paymentMethod = paymentMethod,
)
- val paymentsClient = Wallet.getPaymentsClient(application, GooglePayUtils.createWalletOptions(componentParams))
- val readyToPayRequest = GooglePayUtils.createIsReadyToPayRequest(componentParams)
- val readyToPayTask = paymentsClient.isReadyToPay(readyToPayRequest)
- readyToPayTask.addOnSuccessListener { result ->
- callbackWeakReference.get()?.onAvailabilityResult(result == true, paymentMethod)
- }
- readyToPayTask.addOnCanceledListener {
- adyenLog(AdyenLogLevel.ERROR) { "GooglePay readyToPay task is cancelled." }
- callbackWeakReference.get()?.onAvailabilityResult(false, paymentMethod)
- }
- readyToPayTask.addOnFailureListener {
- adyenLog(AdyenLogLevel.ERROR, it) { "GooglePay readyToPay task is failed." }
- callbackWeakReference.get()?.onAvailabilityResult(false, paymentMethod)
- }
+ GooglePayAvailabilityCheck(application).isAvailable(
+ paymentMethod = paymentMethod,
+ componentParams = componentParams,
+ callback = callback,
+ )
}
override fun isAvailable(
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt
index af2b87eb71..4c01019b2b 100644
--- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt
@@ -28,44 +28,69 @@ import com.adyen.checkout.core.internal.data.model.ModelUtils
import com.adyen.checkout.core.internal.util.adyenLog
import com.adyen.checkout.googlepay.GooglePayButtonParameters
import com.adyen.checkout.googlepay.GooglePayComponentState
+import com.adyen.checkout.googlepay.GooglePayUnavailableException
import com.adyen.checkout.googlepay.internal.data.model.GooglePayPaymentMethodModel
import com.adyen.checkout.googlepay.internal.ui.model.GooglePayComponentParams
+import com.adyen.checkout.googlepay.internal.ui.model.GooglePayOutputData
+import com.adyen.checkout.googlepay.internal.util.GooglePayAvailabilityCheck
import com.adyen.checkout.googlepay.internal.util.GooglePayUtils
+import com.adyen.checkout.googlepay.internal.util.awaitTask
+import com.adyen.checkout.ui.core.internal.ui.ButtonComponentViewType
+import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
+import com.adyen.checkout.ui.core.internal.ui.SubmitHandler
+import com.google.android.gms.common.api.CommonStatusCodes
+import com.google.android.gms.tasks.Task
import com.google.android.gms.wallet.AutoResolveHelper
import com.google.android.gms.wallet.PaymentData
+import com.google.android.gms.wallet.PaymentsClient
import com.google.android.gms.wallet.Wallet
+import com.google.android.gms.wallet.contract.ApiTaskResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
-@Suppress("TooManyFunctions")
+@Suppress("TooManyFunctions", "LongParameterList")
internal class DefaultGooglePayDelegate(
+ private val submitHandler: SubmitHandler,
private val observerRepository: PaymentObserverRepository,
private val paymentMethod: PaymentMethod,
private val order: OrderRequest?,
override val componentParams: GooglePayComponentParams,
private val analyticsManager: AnalyticsManager,
+ private val paymentsClient: PaymentsClient,
+ private val googlePayAvailabilityCheck: GooglePayAvailabilityCheck,
) : GooglePayDelegate {
+ private val _outputDataFlow = MutableStateFlow(createOutputData())
+ override val outputDataFlow: Flow = _outputDataFlow
+ private val outputData: GooglePayOutputData get() = _outputDataFlow.value
+
+ private val _viewFlow = MutableStateFlow(GooglePayComponentViewType)
+ override val viewFlow: Flow = _viewFlow
+
private val _componentStateFlow = MutableStateFlow(createComponentState())
override val componentStateFlow: Flow = _componentStateFlow
private val exceptionChannel: Channel = bufferedChannel()
override val exceptionFlow: Flow = exceptionChannel.receiveAsFlow()
- private val submitChannel: Channel = bufferedChannel()
- override val submitFlow: Flow = submitChannel.receiveAsFlow()
+ override val submitFlow: Flow = submitHandler.submitFlow
+
+ private var _coroutineScope: CoroutineScope? = null
+ private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope)
+
+ private val payEventChannel: Channel> = bufferedChannel()
+ override val payEventFlow: Flow> = payEventChannel.receiveAsFlow()
override fun initialize(coroutineScope: CoroutineScope) {
- initializeAnalytics(coroutineScope)
+ _coroutineScope = coroutineScope
+ submitHandler.initialize(coroutineScope, componentStateFlow)
- componentStateFlow.onEach {
- onState(it)
- }.launchIn(coroutineScope)
+ initializeAnalytics(coroutineScope)
+ checkAvailability()
}
private fun initializeAnalytics(coroutineScope: CoroutineScope) {
@@ -76,12 +101,16 @@ internal class DefaultGooglePayDelegate(
analyticsManager.trackEvent(event)
}
- private fun onState(state: GooglePayComponentState) {
- if (state.isValid) {
- val event = GenericEvents.submit(paymentMethod.type.orEmpty())
- analyticsManager.trackEvent(event)
+ private fun checkAvailability() {
+ googlePayAvailabilityCheck.isAvailable(
+ paymentMethod,
+ componentParams,
+ ) { isAvailable, _ ->
+ updateOutputData(isButtonVisible = isAvailable)
- submitChannel.trySend(state)
+ if (!isAvailable) {
+ exceptionChannel.trySend(GooglePayUnavailableException())
+ }
}
}
@@ -104,14 +133,36 @@ internal class DefaultGooglePayDelegate(
observerRepository.removeObservers()
}
+ private fun updateOutputData(
+ isButtonVisible: Boolean = this.outputData.isButtonVisible,
+ paymentData: PaymentData? = this.outputData.paymentData,
+ ) {
+ val newOutputData = createOutputData(isButtonVisible, paymentData)
+ _outputDataFlow.tryEmit(newOutputData)
+ updateComponentState(newOutputData)
+ }
+
+ private fun createOutputData(
+ isButtonVisible: Boolean = componentParams.isSubmitButtonVisible,
+ paymentData: PaymentData? = null,
+ ): GooglePayOutputData {
+ return GooglePayOutputData(
+ isButtonVisible = isButtonVisible,
+ paymentData = paymentData,
+ )
+ }
+
@VisibleForTesting
- internal fun updateComponentState(paymentData: PaymentData?) {
+ internal fun updateComponentState(outputData: GooglePayOutputData) {
adyenLog(AdyenLogLevel.VERBOSE) { "updateComponentState" }
- val componentState = createComponentState(paymentData)
+ val componentState = createComponentState(outputData)
_componentStateFlow.tryEmit(componentState)
}
- private fun createComponentState(paymentData: PaymentData? = null): GooglePayComponentState {
+ private fun createComponentState(
+ outputData: GooglePayOutputData = this.outputData
+ ): GooglePayComponentState {
+ val paymentData = outputData.paymentData
val isValid = paymentData?.let {
GooglePayUtils.findToken(it).isNotEmpty()
} ?: false
@@ -127,23 +178,71 @@ internal class DefaultGooglePayDelegate(
amount = componentParams.amount,
)
+ val isReady = if (shouldShowSubmitButton()) {
+ outputData.isButtonVisible
+ } else {
+ true
+ }
+
return GooglePayComponentState(
data = paymentComponentData,
isInputValid = isValid,
- isReady = true,
+ isReady = isReady,
paymentData = paymentData,
)
}
+ @Deprecated("Deprecated in favor of onSubmit()", replaceWith = ReplaceWith("onSubmit()"))
override fun startGooglePayScreen(activity: Activity, requestCode: Int) {
adyenLog(AdyenLogLevel.DEBUG) { "startGooglePayScreen" }
val paymentsClient = Wallet.getPaymentsClient(activity, GooglePayUtils.createWalletOptions(componentParams))
val paymentDataRequest = GooglePayUtils.createPaymentDataRequest(componentParams)
- // TODO this forces us to use the deprecated onActivityResult. Look into alternatives when/if Google provides
- // any later.
+ @Suppress("DEPRECATION")
AutoResolveHelper.resolveTask(paymentsClient.loadPaymentData(paymentDataRequest), activity, requestCode)
}
+ override fun onSubmit() {
+ adyenLog(AdyenLogLevel.DEBUG) { "onSubmit" }
+
+ _viewFlow.tryEmit(PaymentInProgressViewType)
+
+ val paymentDataRequest = GooglePayUtils.createPaymentDataRequest(componentParams)
+ val paymentDataTask = paymentsClient.loadPaymentData(paymentDataRequest)
+ coroutineScope.launch {
+ payEventChannel.send(paymentDataTask.awaitTask())
+ }
+ }
+
+ override fun handlePaymentResult(paymentDataTaskResult: ApiTaskResult) {
+ when (val statusCode = paymentDataTaskResult.status.statusCode) {
+ CommonStatusCodes.SUCCESS -> {
+ adyenLog(AdyenLogLevel.INFO) { "GooglePay payment result successful" }
+ initiatePayment(paymentDataTaskResult.result)
+ }
+
+ CommonStatusCodes.CANCELED -> {
+ adyenLog(AdyenLogLevel.INFO) { "GooglePay payment canceled" }
+ exceptionChannel.trySend(ComponentException("GooglePay payment canceled"))
+ }
+
+ AutoResolveHelper.RESULT_ERROR -> {
+ val statusMessage: String = paymentDataTaskResult.status.statusMessage?.let { ": $it" }.orEmpty()
+ adyenLog(AdyenLogLevel.ERROR) { "GooglePay encountered an error$statusMessage" }
+ exceptionChannel.trySend(ComponentException("GooglePay encountered an error$statusMessage"))
+ }
+
+ CommonStatusCodes.INTERNAL_ERROR -> {
+ adyenLog(AdyenLogLevel.ERROR) { "GooglePay encountered an internal error" }
+ exceptionChannel.trySend(ComponentException("GooglePay encountered an internal error"))
+ }
+
+ else -> {
+ adyenLog(AdyenLogLevel.ERROR) { "GooglePay encountered an unexpected error, statusCode: $statusCode" }
+ exceptionChannel.trySend(ComponentException("GooglePay encountered an unexpected error"))
+ }
+ }
+ }
+
override fun handleActivityResult(resultCode: Int, data: Intent?) {
adyenLog(AdyenLogLevel.DEBUG) { "handleActivityResult" }
when (resultCode) {
@@ -152,8 +251,7 @@ internal class DefaultGooglePayDelegate(
exceptionChannel.trySend(ComponentException("Result data is null"))
return
}
- val paymentData = PaymentData.getFromIntent(data)
- updateComponentState(paymentData)
+ initiatePayment(PaymentData.getFromIntent(data))
}
Activity.RESULT_CANCELED -> {
@@ -165,9 +263,22 @@ internal class DefaultGooglePayDelegate(
val statusMessage: String = status?.let { ": ${it.statusMessage}" }.orEmpty()
exceptionChannel.trySend(ComponentException("GooglePay returned an error$statusMessage"))
}
+ }
+ }
- else -> Unit
+ private fun initiatePayment(paymentData: PaymentData?) {
+ if (paymentData == null) {
+ adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" }
+ exceptionChannel.trySend(ComponentException("GooglePay encountered an unexpected error"))
+ return
}
+ adyenLog(AdyenLogLevel.INFO) { "GooglePay payment result successful" }
+
+ val event = GenericEvents.submit(paymentMethod.type.orEmpty())
+ analyticsManager.trackEvent(event)
+
+ updateOutputData(paymentData = paymentData)
+ submitHandler.onSubmit(_componentStateFlow.value)
}
override fun getGooglePayButtonParameters(): GooglePayButtonParameters {
@@ -179,6 +290,14 @@ internal class DefaultGooglePayDelegate(
return GooglePayButtonParameters(allowedPaymentMethods)
}
+ override fun isConfirmationRequired(): Boolean = _viewFlow.value is ButtonComponentViewType
+
+ override fun shouldShowSubmitButton(): Boolean = isConfirmationRequired() && componentParams.isSubmitButtonVisible
+
+ internal fun setInteractionBlocked(isInteractionBlocked: Boolean) {
+ submitHandler.setInteractionBlocked(isInteractionBlocked)
+ }
+
override fun getPaymentMethodType(): String {
return paymentMethod.type ?: PaymentMethodTypes.UNKNOWN
}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayButtonView.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayButtonView.kt
new file mode 100644
index 0000000000..e0fc29e983
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayButtonView.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 26/6/2024.
+ */
+
+package com.adyen.checkout.googlepay.internal.ui
+
+import android.content.Context
+import android.content.res.Resources
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import androidx.core.view.isVisible
+import com.adyen.checkout.googlepay.GooglePayButtonTheme
+import com.adyen.checkout.googlepay.GooglePayButtonType
+import com.adyen.checkout.googlepay.R
+import com.adyen.checkout.googlepay.databinding.ViewGooglePayButtonBinding
+import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate
+import com.adyen.checkout.ui.core.internal.ui.view.PayButton
+import com.google.android.gms.wallet.button.ButtonOptions
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+internal class GooglePayButtonView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : PayButton(context, attrs, defStyleAttr) {
+
+ private val binding = ViewGooglePayButtonBinding.inflate(LayoutInflater.from(context), this)
+
+ private val styledButtonType: GooglePayButtonType?
+ private val styledButtonTheme: GooglePayButtonTheme?
+ private val styledCornerRadius: Int?
+
+ init {
+ val typedArray = context.theme.obtainStyledAttributes(
+ attrs,
+ R.styleable.GooglePayButtonView,
+ defStyleAttr,
+ R.style.AdyenCheckout_GooglePay_Button,
+ )
+ styledButtonType =
+ typedArray.getInt(R.styleable.GooglePayButtonView_adyenGooglePayButtonType, -1).mapStyledButtonType()
+ styledButtonTheme =
+ typedArray.getInt(R.styleable.GooglePayButtonView_adyenGooglePayButtonTheme, -1).mapStyledButtonTheme()
+ styledCornerRadius =
+ typedArray.getDimensionPixelSize(R.styleable.GooglePayButtonView_adyenGooglePayButtonCornerRadius, -1)
+ .mapStyledCornerRadius()
+ typedArray.recycle()
+ }
+
+ override fun initialize(delegate: ButtonDelegate, coroutineScope: CoroutineScope) {
+ check(delegate is GooglePayDelegate)
+
+ observeDelegate(delegate, coroutineScope)
+
+ val buttonStyle = delegate.componentParams.googlePayButtonStyling
+
+ val buttonType = buttonStyle?.buttonType ?: styledButtonType
+ val buttonTheme = buttonStyle?.buttonTheme ?: styledButtonTheme
+ val cornerRadius =
+ buttonStyle?.cornerRadius?.let { (it * Resources.getSystem().displayMetrics.density).toInt() }
+ ?: styledCornerRadius
+
+ binding.payButton.initialize(
+ ButtonOptions.newBuilder().apply {
+ if (buttonType != null) {
+ setButtonType(buttonType.value)
+ }
+
+ if (buttonTheme != null) {
+ setButtonTheme(buttonTheme.value)
+ }
+
+ if (cornerRadius != null) {
+ setCornerRadius(cornerRadius)
+ }
+
+ setAllowedPaymentMethods(delegate.getGooglePayButtonParameters().allowedPaymentMethods)
+ }.build(),
+ )
+ }
+
+ private fun observeDelegate(delegate: GooglePayDelegate, coroutineScope: CoroutineScope) {
+ delegate.outputDataFlow
+ .onEach {
+ binding.payButton.isVisible = it.isButtonVisible
+ }
+ .launchIn(coroutineScope)
+ }
+
+ override fun setEnabled(enabled: Boolean) {
+ binding.payButton.isEnabled = enabled
+ }
+
+ override fun setOnClickListener(listener: OnClickListener?) {
+ binding.payButton.setOnClickListener(listener)
+ }
+
+ override fun setText(text: String?) = Unit
+
+ @Suppress("MagicNumber")
+ private fun Int.mapStyledButtonType(): GooglePayButtonType? = when (this) {
+ 0 -> GooglePayButtonType.BUY
+ 1 -> GooglePayButtonType.BOOK
+ 2 -> GooglePayButtonType.CHECKOUT
+ 3 -> GooglePayButtonType.DONATE
+ 4 -> GooglePayButtonType.ORDER
+ 5 -> GooglePayButtonType.PAY
+ 6 -> GooglePayButtonType.SUBSCRIBE
+ 7 -> GooglePayButtonType.PLAIN
+ else -> null
+ }
+
+ private fun Int.mapStyledButtonTheme(): GooglePayButtonTheme? = when (this) {
+ 0 -> GooglePayButtonTheme.LIGHT
+ 1 -> GooglePayButtonTheme.DARK
+ else -> null
+ }
+
+ private fun Int.mapStyledCornerRadius(): Int? = if (this == -1) {
+ null
+ } else {
+ this
+ }
+}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayDelegate.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayDelegate.kt
index 2d393b6d19..f2f3f70a89 100644
--- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayDelegate.kt
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayDelegate.kt
@@ -14,17 +14,36 @@ import com.adyen.checkout.components.core.internal.ui.PaymentComponentDelegate
import com.adyen.checkout.core.exception.CheckoutException
import com.adyen.checkout.googlepay.GooglePayButtonParameters
import com.adyen.checkout.googlepay.GooglePayComponentState
+import com.adyen.checkout.googlepay.internal.ui.model.GooglePayComponentParams
+import com.adyen.checkout.googlepay.internal.ui.model.GooglePayOutputData
+import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate
+import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate
+import com.google.android.gms.tasks.Task
+import com.google.android.gms.wallet.PaymentData
+import com.google.android.gms.wallet.contract.ApiTaskResult
import kotlinx.coroutines.flow.Flow
-internal interface GooglePayDelegate : PaymentComponentDelegate {
+internal interface GooglePayDelegate :
+ PaymentComponentDelegate,
+ ViewProvidingDelegate,
+ ButtonDelegate {
+
+ override val componentParams: GooglePayComponentParams
+
+ val outputDataFlow: Flow
val componentStateFlow: Flow
val exceptionFlow: Flow
+ val payEventFlow: Flow>
+
+ @Deprecated("Deprecated in favor of onSubmit()", ReplaceWith("onSubmit()"))
fun startGooglePayScreen(activity: Activity, requestCode: Int)
fun handleActivityResult(resultCode: Int, data: Intent?)
fun getGooglePayButtonParameters(): GooglePayButtonParameters
+
+ fun handlePaymentResult(paymentDataTaskResult: ApiTaskResult)
}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayFragment.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayFragment.kt
new file mode 100644
index 0000000000..1f54d1b571
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayFragment.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 15/5/2024.
+ */
+
+package com.adyen.checkout.googlepay.internal.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import com.adyen.checkout.googlepay.databinding.FragmentGooglePayBinding
+import com.google.android.gms.wallet.contract.TaskResultContracts
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+internal class GooglePayFragment : Fragment() {
+
+ private var _binding: FragmentGooglePayBinding? = null
+ private val binding: FragmentGooglePayBinding get() = requireNotNull(_binding)
+
+ private var delegate: GooglePayDelegate? = null
+
+ private val googlePayLauncher = registerForActivityResult(TaskResultContracts.GetPaymentDataResult()) {
+ delegate?.handlePaymentResult(it)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ _binding = FragmentGooglePayBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ fun initialize(delegate: GooglePayDelegate) {
+ this.delegate = delegate
+ delegate.payEventFlow
+ .onEach { googlePayLauncher.launch(it) }
+ .launchIn(viewLifecycleOwner.lifecycleScope)
+ }
+
+ override fun onDestroyView() {
+ delegate = null
+ _binding = null
+ super.onDestroyView()
+ }
+}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayView.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayView.kt
new file mode 100644
index 0000000000..25256e3a32
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayView.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 15/5/2024.
+ */
+
+package com.adyen.checkout.googlepay.internal.ui
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import com.adyen.checkout.components.core.internal.ui.ComponentDelegate
+import com.adyen.checkout.googlepay.databinding.ViewGooglePayBinding
+import com.adyen.checkout.ui.core.internal.ui.ComponentView
+import kotlinx.coroutines.CoroutineScope
+
+internal class GooglePayView
+@JvmOverloads
+internal constructor(
+ layoutInflater: LayoutInflater,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : FrameLayout(layoutInflater.context, attrs, defStyleAttr), ComponentView {
+
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+ ) : this(LayoutInflater.from(context), attrs, defStyleAttr)
+
+ private var binding = ViewGooglePayBinding.inflate(layoutInflater, this)
+
+ private var delegate: GooglePayDelegate? = null
+
+ override fun initView(delegate: ComponentDelegate, coroutineScope: CoroutineScope, localizedContext: Context) {
+ require(delegate is GooglePayDelegate) { "Unsupported delegate type" }
+ this.delegate = delegate
+ initializeFragment(delegate)
+ }
+
+ private fun initializeFragment(delegate: GooglePayDelegate) {
+ binding.fragmentContainer.getFragment()?.initialize(delegate)
+ }
+
+ override fun highlightValidationErrors() = Unit
+
+ override fun getView(): View = this
+}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayViewProvider.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayViewProvider.kt
new file mode 100644
index 0000000000..927e380caf
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/GooglePayViewProvider.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 15/5/2024.
+ */
+
+package com.adyen.checkout.googlepay.internal.ui
+
+import android.content.Context
+import android.view.LayoutInflater
+import com.adyen.checkout.ui.core.internal.ui.ButtonComponentViewType
+import com.adyen.checkout.ui.core.internal.ui.ButtonViewProvider
+import com.adyen.checkout.ui.core.internal.ui.ComponentView
+import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
+import com.adyen.checkout.ui.core.internal.ui.ViewProvider
+import com.adyen.checkout.ui.core.internal.ui.view.PayButton
+import com.adyen.checkout.ui.core.internal.ui.view.ProcessingPaymentView
+
+internal class GooglePayViewProvider : ViewProvider {
+
+ override fun getView(
+ viewType: ComponentViewType,
+ context: Context,
+ ): ComponentView = when (viewType) {
+ GooglePayComponentViewType -> GooglePayView(context)
+ PaymentInProgressViewType -> ProcessingPaymentView(context)
+ else -> throw IllegalArgumentException("Unsupported view type")
+ }
+
+ override fun getView(
+ viewType: ComponentViewType,
+ layoutInflater: LayoutInflater
+ ): ComponentView = when (viewType) {
+ GooglePayComponentViewType -> GooglePayView(layoutInflater)
+ PaymentInProgressViewType -> ProcessingPaymentView(layoutInflater.context)
+ else -> throw IllegalArgumentException("Unsupported view type")
+ }
+}
+
+internal class GooglePayButtonViewProvider : ButtonViewProvider {
+ override fun getButton(context: Context): PayButton = GooglePayButtonView(context)
+}
+
+internal object GooglePayComponentViewType : ButtonComponentViewType {
+ override val buttonViewProvider: ButtonViewProvider get() = GooglePayButtonViewProvider()
+
+ override val viewProvider: ViewProvider get() = GooglePayViewProvider()
+
+ override val buttonTextResId: Int = ButtonComponentViewType.DEFAULT_BUTTON_TEXT_RES_ID
+}
+
+internal object PaymentInProgressViewType : ComponentViewType {
+
+ override val viewProvider: ViewProvider get() = GooglePayViewProvider()
+}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParams.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParams.kt
index e6c7526487..f6d8bed96a 100644
--- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParams.kt
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParams.kt
@@ -9,15 +9,18 @@
package com.adyen.checkout.googlepay.internal.ui.model
import com.adyen.checkout.components.core.Amount
+import com.adyen.checkout.components.core.internal.ui.model.ButtonParams
import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParams
import com.adyen.checkout.components.core.internal.ui.model.ComponentParams
import com.adyen.checkout.googlepay.BillingAddressParameters
+import com.adyen.checkout.googlepay.GooglePayButtonStyling
import com.adyen.checkout.googlepay.MerchantInfo
import com.adyen.checkout.googlepay.ShippingAddressParameters
internal data class GooglePayComponentParams(
private val commonComponentParams: CommonComponentParams,
override val amount: Amount,
+ override val isSubmitButtonVisible: Boolean,
val gatewayMerchantId: String,
val googlePayEnvironment: Int,
val totalPriceStatus: String,
@@ -34,4 +37,5 @@ internal data class GooglePayComponentParams(
val shippingAddressParameters: ShippingAddressParameters?,
val isBillingAddressRequired: Boolean,
val billingAddressParameters: BillingAddressParameters?,
-) : ComponentParams by commonComponentParams
+ val googlePayButtonStyling: GooglePayButtonStyling?,
+) : ComponentParams by commonComponentParams, ButtonParams
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapper.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapper.kt
index ee737a5192..02e974e9dd 100644
--- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapper.kt
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapper.kt
@@ -60,6 +60,7 @@ internal class GooglePayComponentParamsMapper(
return GooglePayComponentParams(
commonComponentParams = commonComponentParams,
amount = commonComponentParams.amount ?: DEFAULT_AMOUNT,
+ isSubmitButtonVisible = shouldShowSubmitButton(commonComponentParams, googlePayConfiguration),
gatewayMerchantId = googlePayConfiguration.getPreferredGatewayMerchantId(paymentMethod),
allowedAuthMethods = googlePayConfiguration.getAvailableAuthMethods(),
allowedCardNetworks = googlePayConfiguration.getAvailableCardNetworks(paymentMethod),
@@ -76,9 +77,22 @@ internal class GooglePayComponentParamsMapper(
shippingAddressParameters = googlePayConfiguration?.shippingAddressParameters,
isBillingAddressRequired = googlePayConfiguration?.isBillingAddressRequired ?: false,
billingAddressParameters = googlePayConfiguration?.billingAddressParameters,
+ googlePayButtonStyling = googlePayConfiguration?.googlePayButtonStyling,
)
}
+ private fun shouldShowSubmitButton(
+ commonComponentParams: CommonComponentParams,
+ googlePayConfiguration: GooglePayConfiguration?,
+ ): Boolean {
+ return if (commonComponentParams.isCreatedByDropIn) {
+ false
+ } else {
+ // TODO return true by default in v6
+ googlePayConfiguration?.isSubmitButtonVisible ?: false
+ }
+ }
+
/**
* Returns the [GooglePayConfiguration.merchantAccount] if set, or falls back to the
* paymentMethod.configuration.gatewayMerchantId field returned by the API.
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayOutputData.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayOutputData.kt
new file mode 100644
index 0000000000..0d8a1fb7d6
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayOutputData.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 30/10/2024.
+ */
+
+package com.adyen.checkout.googlepay.internal.ui.model
+
+import com.google.android.gms.wallet.PaymentData
+
+internal data class GooglePayOutputData(
+ val isButtonVisible: Boolean,
+ val paymentData: PaymentData?,
+)
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/GooglePayAvailabilityCheck.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/GooglePayAvailabilityCheck.kt
new file mode 100644
index 0000000000..80461df070
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/GooglePayAvailabilityCheck.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 30/10/2024.
+ */
+
+package com.adyen.checkout.googlepay.internal.util
+
+import android.app.Application
+import com.adyen.checkout.components.core.ComponentAvailableCallback
+import com.adyen.checkout.components.core.PaymentMethod
+import com.adyen.checkout.core.AdyenLogLevel
+import com.adyen.checkout.core.internal.util.adyenLog
+import com.adyen.checkout.googlepay.internal.ui.model.GooglePayComponentParams
+import com.google.android.gms.common.ConnectionResult
+import com.google.android.gms.common.GoogleApiAvailability
+import com.google.android.gms.wallet.Wallet
+import java.lang.ref.WeakReference
+
+internal class GooglePayAvailabilityCheck(
+ private val application: Application,
+) {
+
+ fun isAvailable(
+ paymentMethod: PaymentMethod,
+ componentParams: GooglePayComponentParams,
+ callback: ComponentAvailableCallback,
+ ) {
+ if (GoogleApiAvailability.getInstance()
+ .isGooglePlayServicesAvailable(application) != ConnectionResult.SUCCESS
+ ) {
+ callback.onAvailabilityResult(false, paymentMethod)
+ return
+ }
+
+ val callbackWeakReference = WeakReference(callback)
+
+ val paymentsClient = Wallet.getPaymentsClient(application, GooglePayUtils.createWalletOptions(componentParams))
+ val readyToPayRequest = GooglePayUtils.createIsReadyToPayRequest(componentParams)
+ val readyToPayTask = paymentsClient.isReadyToPay(readyToPayRequest)
+ readyToPayTask.addOnSuccessListener { result ->
+ callbackWeakReference.get()?.onAvailabilityResult(result == true, paymentMethod)
+ }
+ readyToPayTask.addOnCanceledListener {
+ adyenLog(AdyenLogLevel.ERROR) { "GooglePay readyToPay task is cancelled." }
+ callbackWeakReference.get()?.onAvailabilityResult(false, paymentMethod)
+ }
+ readyToPayTask.addOnFailureListener {
+ adyenLog(AdyenLogLevel.ERROR, it) { "GooglePay readyToPay task is failed." }
+ callbackWeakReference.get()?.onAvailabilityResult(false, paymentMethod)
+ }
+ }
+}
diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/TaskExtensions.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/TaskExtensions.kt
new file mode 100644
index 0000000000..8087675b87
--- /dev/null
+++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/TaskExtensions.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 15/5/2024.
+ */
+
+package com.adyen.checkout.googlepay.internal.util
+
+import com.google.android.gms.tasks.CancellationTokenSource
+import com.google.android.gms.tasks.Task
+import kotlinx.coroutines.suspendCancellableCoroutine
+import java.util.concurrent.Executor
+import kotlin.coroutines.resume
+
+internal suspend fun Task.awaitTask(cancellationTokenSource: CancellationTokenSource? = null): Task {
+ return if (isComplete) {
+ this
+ } else {
+ suspendCancellableCoroutine { cont ->
+ // Run the callback directly to avoid unnecessarily scheduling on the main thread.
+ addOnCompleteListener(DirectExecutor(), cont::resume)
+
+ cancellationTokenSource?.let { cancellationSource ->
+ cont.invokeOnCancellation { cancellationSource.cancel() }
+ }
+ }
+ }
+}
+
+/**
+ * An [Executor] that just directly executes the [Runnable].
+ */
+private class DirectExecutor : Executor {
+ override fun execute(runnable: Runnable) {
+ runnable.run()
+ }
+}
diff --git a/googlepay/src/main/res/layout/fragment_google_pay.xml b/googlepay/src/main/res/layout/fragment_google_pay.xml
new file mode 100644
index 0000000000..6f03758752
--- /dev/null
+++ b/googlepay/src/main/res/layout/fragment_google_pay.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/googlepay/src/main/res/layout/view_google_pay.xml b/googlepay/src/main/res/layout/view_google_pay.xml
new file mode 100644
index 0000000000..ce06d9bc88
--- /dev/null
+++ b/googlepay/src/main/res/layout/view_google_pay.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/googlepay/src/main/res/layout/view_google_pay_button.xml b/googlepay/src/main/res/layout/view_google_pay_button.xml
new file mode 100644
index 0000000000..5adc62558e
--- /dev/null
+++ b/googlepay/src/main/res/layout/view_google_pay_button.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/example-app/src/main/res/values-night/styles.xml b/googlepay/src/main/res/values-night/styles.xml
similarity index 52%
rename from example-app/src/main/res/values-night/styles.xml
rename to googlepay/src/main/res/values-night/styles.xml
index b8704d3158..ad24e59f4c 100644
--- a/example-app/src/main/res/values-night/styles.xml
+++ b/googlepay/src/main/res/values-night/styles.xml
@@ -1,14 +1,15 @@
-
+
diff --git a/googlepay/src/main/res/values/attrs.xml b/googlepay/src/main/res/values/attrs.xml
new file mode 100644
index 0000000000..4239a03f37
--- /dev/null
+++ b/googlepay/src/main/res/values/attrs.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/googlepay/src/main/res/values/styles.xml b/googlepay/src/main/res/values/styles.xml
new file mode 100644
index 0000000000..afba028b53
--- /dev/null
+++ b/googlepay/src/main/res/values/styles.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/GooglePayComponentTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/GooglePayComponentTest.kt
index c4cf36944b..f303433530 100644
--- a/googlepay/src/test/java/com/adyen/checkout/googlepay/GooglePayComponentTest.kt
+++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/GooglePayComponentTest.kt
@@ -12,20 +12,20 @@ import android.app.Activity
import android.content.Intent
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
-import app.cash.turbine.test
import com.adyen.checkout.action.core.internal.DefaultActionHandlingComponent
import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate
import com.adyen.checkout.components.core.internal.ComponentEventHandler
import com.adyen.checkout.components.core.internal.PaymentComponentEvent
-import com.adyen.checkout.googlepay.internal.ui.GooglePayDelegate
+import com.adyen.checkout.googlepay.internal.ui.DefaultGooglePayDelegate
+import com.adyen.checkout.googlepay.internal.ui.GooglePayComponentViewType
import com.adyen.checkout.test.LoggingExtension
import com.adyen.checkout.test.TestDispatcherExtension
import com.adyen.checkout.test.extensions.invokeOnCleared
+import com.adyen.checkout.test.extensions.test
import com.adyen.checkout.ui.core.internal.ui.TestComponentViewType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -35,12 +35,13 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@ExtendWith(MockitoExtension::class, TestDispatcherExtension::class, LoggingExtension::class)
internal class GooglePayComponentTest(
- @Mock private val googlePayDelegate: GooglePayDelegate,
+ @Mock private val googlePayDelegate: DefaultGooglePayDelegate,
@Mock private val genericActionDelegate: GenericActionDelegate,
@Mock private val actionHandlingComponent: DefaultActionHandlingComponent,
@Mock private val componentEventHandler: ComponentEventHandler,
@@ -50,6 +51,7 @@ internal class GooglePayComponentTest(
@BeforeEach
fun before() {
+ whenever(googlePayDelegate.viewFlow) doReturn MutableStateFlow(GooglePayComponentViewType)
whenever(genericActionDelegate.viewFlow) doReturn MutableStateFlow(null)
component = GooglePayComponent(
@@ -96,13 +98,33 @@ internal class GooglePayComponentTest(
}
@Test
- fun `when component is initialized then view flow should bu null`() = runTest {
- component.viewFlow.test {
- assertNull(awaitItem())
- expectNoEvents()
- }
+ fun `when component is initialized, then view flow should match google pay delegate view flow`() = runTest {
+ val viewFlow = component.viewFlow.test(testScheduler)
+
+ assertEquals(GooglePayComponentViewType, viewFlow.latestValue)
}
+ @Test
+ fun `when google pay delegate view flow emits a value then component view flow should match that value`() =
+ runTest {
+ val delegateViewFlow = MutableStateFlow(TestComponentViewType.VIEW_TYPE_1)
+ whenever(googlePayDelegate.viewFlow) doReturn delegateViewFlow
+ component = GooglePayComponent(
+ googlePayDelegate,
+ genericActionDelegate,
+ actionHandlingComponent,
+ componentEventHandler,
+ )
+
+ val testViewFlow = component.viewFlow.test(testScheduler)
+
+ assertEquals(TestComponentViewType.VIEW_TYPE_1, testViewFlow.latestValue)
+
+ delegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2)
+
+ assertEquals(TestComponentViewType.VIEW_TYPE_2, testViewFlow.latestValue)
+ }
+
@Test
fun `when action delegate view flow emits a value then component view flow should match that value`() = runTest {
val actionDelegateViewFlow = MutableStateFlow(TestComponentViewType.VIEW_TYPE_1)
@@ -114,16 +136,18 @@ internal class GooglePayComponentTest(
componentEventHandler = componentEventHandler,
)
- component.viewFlow.test {
- assertEquals(TestComponentViewType.VIEW_TYPE_1, awaitItem())
+ val testViewFlow = component.viewFlow.test(testScheduler)
- actionDelegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2)
- assertEquals(TestComponentViewType.VIEW_TYPE_2, awaitItem())
+ // this value should match the value of the main delegate and not the action delegate
+ // and in practice the initial value of the action delegate view flow is always null so it should be ignored
+ assertEquals(GooglePayComponentViewType, testViewFlow.latestValue)
- expectNoEvents()
- }
+ actionDelegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2)
+
+ assertEquals(TestComponentViewType.VIEW_TYPE_2, testViewFlow.latestValue)
}
+ @Suppress("DEPRECATION")
@Test
fun `when startGooglePayScreen is called then delegate startGooglePayScreen is called`() {
val activity = Activity()
@@ -132,6 +156,7 @@ internal class GooglePayComponentTest(
verify(googlePayDelegate).startGooglePayScreen(activity, requestCode)
}
+ @Suppress("DEPRECATION")
@Test
fun `when handleActivityResult is called then delegate handleActivityResult is called`() {
val requestCode = 1
@@ -139,4 +164,71 @@ internal class GooglePayComponentTest(
component.handleActivityResult(requestCode, intent)
verify(googlePayDelegate).handleActivityResult(requestCode, intent)
}
+
+ @Test
+ fun `when submit is called and active delegate is the payment delegate, then delegate onSubmit is called`() {
+ component = GooglePayComponent(
+ googlePayDelegate,
+ genericActionDelegate,
+ actionHandlingComponent,
+ componentEventHandler,
+ )
+ whenever(component.delegate).thenReturn(googlePayDelegate)
+
+ component.submit()
+
+ verify(googlePayDelegate).onSubmit()
+ }
+
+ @Test
+ fun `when submit is called and active delegate is the action delegate, then delegate onSubmit is not called`() {
+ component = GooglePayComponent(
+ googlePayDelegate,
+ genericActionDelegate,
+ actionHandlingComponent,
+ componentEventHandler,
+ )
+ whenever(component.delegate).thenReturn(genericActionDelegate)
+
+ component.submit()
+
+ verify(googlePayDelegate, never()).onSubmit()
+ }
+
+ @Test
+ fun `when isConfirmationRequired and delegate is default, then delegate is called`() {
+ component = GooglePayComponent(
+ googlePayDelegate,
+ genericActionDelegate,
+ actionHandlingComponent,
+ componentEventHandler,
+ )
+ whenever(component.delegate).thenReturn(googlePayDelegate)
+
+ component.isConfirmationRequired()
+
+ verify(googlePayDelegate).isConfirmationRequired()
+ }
+
+ @Test
+ fun `when setInteractionBlocked is called, then delegate is called`() {
+ component = GooglePayComponent(
+ googlePayDelegate,
+ genericActionDelegate,
+ actionHandlingComponent,
+ componentEventHandler,
+ )
+ whenever(component.delegate).thenReturn(googlePayDelegate)
+
+ component.setInteractionBlocked(true)
+
+ verify(googlePayDelegate).setInteractionBlocked(true)
+ }
+
+ @Test
+ fun `when getGooglePayButtonParameters is called, then delegate is called`() {
+ component.getGooglePayButtonParameters()
+
+ verify(googlePayDelegate).getGooglePayButtonParameters()
+ }
}
diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/GooglePayConfigurationTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/GooglePayConfigurationTest.kt
index e75b35bf10..3160310d55 100644
--- a/googlepay/src/test/java/com/adyen/checkout/googlepay/GooglePayConfigurationTest.kt
+++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/GooglePayConfigurationTest.kt
@@ -22,6 +22,7 @@ internal class GooglePayConfigurationTest {
analyticsConfiguration = AnalyticsConfiguration(AnalyticsLevel.ALL),
) {
googlePay {
+ setSubmitButtonVisible(true)
setMerchantAccount("merchantAccount")
setGooglePayEnvironment(WalletConstants.ENVIRONMENT_PRODUCTION)
setMerchantInfo(MerchantInfo(merchantId = "id"))
@@ -48,6 +49,7 @@ internal class GooglePayConfigurationTest {
environment = Environment.TEST,
clientKey = TEST_CLIENT_KEY,
)
+ .setSubmitButtonVisible(true)
.setAmount(Amount("EUR", 123L))
.setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL))
.setMerchantAccount("merchantAccount")
@@ -99,6 +101,7 @@ internal class GooglePayConfigurationTest {
clientKey = TEST_CLIENT_KEY,
)
.setAmount(Amount("EUR", 123L))
+ .setSubmitButtonVisible(true)
.setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL))
.setMerchantAccount("merchantAccount")
.setGooglePayEnvironment(WalletConstants.ENVIRONMENT_PRODUCTION)
@@ -139,6 +142,7 @@ internal class GooglePayConfigurationTest {
assertEquals(config.environment, actualGooglePayCardConfig?.environment)
assertEquals(config.clientKey, actualGooglePayCardConfig?.clientKey)
assertEquals(config.amount, actualGooglePayCardConfig?.amount)
+ assertEquals(config.isSubmitButtonVisible, actualGooglePayCardConfig?.isSubmitButtonVisible)
assertEquals(config.analyticsConfiguration, actualGooglePayCardConfig?.analyticsConfiguration)
assertEquals(config.merchantAccount, actualGooglePayCardConfig?.merchantAccount)
assertEquals(config.googlePayEnvironment, actualGooglePayCardConfig?.googlePayEnvironment)
diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt
index b44f4b74be..abdb7ac180 100644
--- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt
+++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt
@@ -11,6 +11,7 @@ package com.adyen.checkout.googlepay.internal.ui
import app.cash.turbine.test
import com.adyen.checkout.components.core.Amount
import com.adyen.checkout.components.core.CheckoutConfiguration
+import com.adyen.checkout.components.core.ComponentAvailableCallback
import com.adyen.checkout.components.core.Configuration
import com.adyen.checkout.components.core.OrderRequest
import com.adyen.checkout.components.core.PaymentMethod
@@ -19,41 +20,65 @@ import com.adyen.checkout.components.core.internal.analytics.GenericEvents
import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager
import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper
import com.adyen.checkout.core.Environment
+import com.adyen.checkout.core.exception.CheckoutException
+import com.adyen.checkout.core.exception.ComponentException
+import com.adyen.checkout.googlepay.GooglePayComponentState
import com.adyen.checkout.googlepay.GooglePayConfiguration
+import com.adyen.checkout.googlepay.GooglePayUnavailableException
import com.adyen.checkout.googlepay.googlePay
import com.adyen.checkout.googlepay.internal.ui.model.GooglePayComponentParamsMapper
+import com.adyen.checkout.googlepay.internal.ui.model.GooglePayOutputData
+import com.adyen.checkout.googlepay.internal.util.GooglePayAvailabilityCheck
import com.adyen.checkout.googlepay.internal.util.GooglePayUtils
+import com.adyen.checkout.test.LoggingExtension
+import com.adyen.checkout.test.extensions.test
+import com.adyen.checkout.ui.core.internal.ui.SubmitHandler
+import com.google.android.gms.common.api.Status
+import com.google.android.gms.tasks.Tasks
+import com.google.android.gms.wallet.AutoResolveHelper
import com.google.android.gms.wallet.PaymentData
+import com.google.android.gms.wallet.PaymentsClient
+import com.google.android.gms.wallet.contract.ApiTaskResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertInstanceOf
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments.arguments
import org.junit.jupiter.params.provider.MethodSource
+import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
import java.util.Locale
@OptIn(ExperimentalCoroutinesApi::class)
-@ExtendWith(MockitoExtension::class)
-internal class DefaultGooglePayDelegateTest {
+@ExtendWith(MockitoExtension::class, LoggingExtension::class)
+internal class DefaultGooglePayDelegateTest(
+ @Mock private val submitHandler: SubmitHandler,
+ @Mock private val paymentsClient: PaymentsClient,
+ @Mock private val googlePayAvailabilityCheck: GooglePayAvailabilityCheck,
+) {
private lateinit var analyticsManager: TestAnalyticsManager
private lateinit var delegate: DefaultGooglePayDelegate
- private val paymentData: PaymentData
- get() = PaymentData.fromJson("{\"paymentMethodData\": {\"tokenizationData\": {\"token\": \"test_token\"}}}")
-
@BeforeEach
fun beforeEach() {
+ whenever(paymentsClient.loadPaymentData(any())) doReturn Tasks.forResult(TEST_PAYMENT_DATA)
analyticsManager = TestAnalyticsManager()
delegate = createGooglePayDelegate()
}
@@ -75,7 +100,7 @@ internal class DefaultGooglePayDelegateTest {
@Test
fun `when payment data is null, then state is not valid`() = runTest {
delegate.componentStateFlow.test {
- delegate.updateComponentState(null)
+ delegate.updateComponentState(createOutputData(paymentData = null))
with(awaitItem()) {
assertNull(data.paymentMethod)
@@ -93,9 +118,9 @@ internal class DefaultGooglePayDelegateTest {
delegate.componentStateFlow.test {
skipItems(1)
- val paymentData = paymentData
+ val paymentData = TEST_PAYMENT_DATA
- delegate.updateComponentState(paymentData)
+ delegate.updateComponentState(createOutputData(paymentData = paymentData))
val componentState = awaitItem()
@@ -121,6 +146,18 @@ internal class DefaultGooglePayDelegateTest {
}
}
+ @Test
+ fun `when onSubmit is called, then event is emitted to start Google Pay`() = runTest {
+ val task = Tasks.forResult(TEST_PAYMENT_DATA)
+ whenever(paymentsClient.loadPaymentData(any())) doReturn task
+ val payEventFlow = delegate.payEventFlow.test(testScheduler)
+ delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
+
+ delegate.onSubmit()
+
+ assertEquals(task, payEventFlow.latestValue)
+ }
+
@ParameterizedTest
@MethodSource("amountSource")
fun `when input data is valid then amount is propagated in component state if set`(
@@ -133,11 +170,56 @@ internal class DefaultGooglePayDelegateTest {
}
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
delegate.componentStateFlow.test {
- delegate.updateComponentState(paymentData)
+ delegate.updateComponentState(createOutputData(paymentData = TEST_PAYMENT_DATA))
assertEquals(expectedComponentStateValue, expectMostRecentItem().data.amount)
}
}
+ @Nested
+ @DisplayName("when submit button is configured to be")
+ inner class SubmitButtonVisibilityTest {
+
+ @Test
+ fun `hidden, then it should not show`() {
+ delegate = createGooglePayDelegate(
+ configuration = createCheckoutConfiguration {
+ setSubmitButtonVisible(false)
+ },
+ )
+
+ assertFalse(delegate.shouldShowSubmitButton())
+ }
+
+ @Test
+ fun `visible, then it should show`() {
+ delegate = createGooglePayDelegate(
+ configuration = createCheckoutConfiguration {
+ setSubmitButtonVisible(true)
+ },
+ )
+
+ assertTrue(delegate.shouldShowSubmitButton())
+ }
+ }
+
+ @Nested
+ inner class SubmitHandlerTest {
+
+ @Test
+ fun `when delegate is initialized, then submit handler event is initialized`() = runTest {
+ val coroutineScope = CoroutineScope(UnconfinedTestDispatcher())
+ delegate.initialize(coroutineScope)
+ verify(submitHandler).initialize(coroutineScope, delegate.componentStateFlow)
+ }
+
+ @Test
+ fun `when delegate setInteractionBlocked is called, then submit handler setInteractionBlocked is called`() =
+ runTest {
+ delegate.setInteractionBlocked(true)
+ verify(submitHandler).setInteractionBlocked(true)
+ }
+ }
+
@Nested
inner class AnalyticsTest {
@@ -156,16 +238,6 @@ internal class DefaultGooglePayDelegateTest {
analyticsManager.assertLastEventEquals(expectedEvent)
}
- @Test
- fun `when component state updates amd the data is valid, then submit event is tracked`() {
- delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
-
- delegate.updateComponentState(paymentData)
-
- val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE)
- analyticsManager.assertLastEventEquals(expectedEvent)
- }
-
@Test
fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest {
analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID)
@@ -173,12 +245,24 @@ internal class DefaultGooglePayDelegateTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
delegate.componentStateFlow.test {
- delegate.updateComponentState(paymentData)
+ delegate.updateComponentState(createOutputData(paymentData = TEST_PAYMENT_DATA))
assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId)
}
}
+ @Test
+ fun `when payment is successful and the data is valid, then submit event is tracked`() {
+ delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
+ delegate.updateComponentState(createOutputData(paymentData = TEST_PAYMENT_DATA))
+
+ val result = ApiTaskResult(TEST_PAYMENT_DATA, Status.RESULT_SUCCESS)
+ delegate.handlePaymentResult(result)
+
+ val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE)
+ analyticsManager.assertLastEventEquals(expectedEvent)
+ }
+
@Test
fun `when delegate is cleared then analytics manager is cleared`() {
delegate.onCleared()
@@ -187,6 +271,55 @@ internal class DefaultGooglePayDelegateTest {
}
}
+ @ParameterizedTest
+ @MethodSource("googlePayAvailableSource")
+ fun `when checking Google Pay availability, then expect isReady and-or exception`(
+ isSubmitButtonVisible: Boolean,
+ isAvailable: Boolean,
+ expectedIsReady: Boolean,
+ expectedException: CheckoutException?,
+ ) = runTest {
+ whenever(googlePayAvailabilityCheck.isAvailable(any(), any(), any())) doAnswer { invocation ->
+ (invocation.getArgument(2, ComponentAvailableCallback::class.java))
+ .onAvailabilityResult(isAvailable, PaymentMethod())
+ }
+
+ val config = createCheckoutConfiguration {
+ setSubmitButtonVisible(isSubmitButtonVisible)
+ }
+ delegate = createGooglePayDelegate(config)
+ val componentStateFlow = delegate.componentStateFlow.test(testScheduler)
+ val exceptionFlow = delegate.exceptionFlow.test(testScheduler)
+
+ delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
+
+ assertEquals(expectedIsReady, componentStateFlow.latestValue.isReady)
+
+ if (expectedException != null) {
+ assertEquals(expectedException.message, exceptionFlow.latestValue.message)
+ } else {
+ assertTrue(exceptionFlow.values.isEmpty())
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("paymentResultSource")
+ fun `when handling payment result, then success or error is emitted`(
+ result: ApiTaskResult,
+ isSuccess: Boolean,
+ ) = runTest {
+ val componentStateFlow = delegate.componentStateFlow.test(testScheduler)
+ val exceptionFlow = delegate.exceptionFlow.test(testScheduler)
+
+ delegate.handlePaymentResult(result)
+
+ if (isSuccess) {
+ assertEquals(result.result, componentStateFlow.latestValue.paymentData)
+ } else {
+ assertInstanceOf(ComponentException::class.java, exceptionFlow.latestValue)
+ }
+ }
+
private fun createCheckoutConfiguration(
amount: Amount? = null,
configuration: GooglePayConfiguration.Builder.() -> Unit = {}
@@ -206,19 +339,29 @@ internal class DefaultGooglePayDelegateTest {
),
): DefaultGooglePayDelegate {
return DefaultGooglePayDelegate(
+ submitHandler = submitHandler,
observerRepository = PaymentObserverRepository(),
paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE),
order = TEST_ORDER,
componentParams = GooglePayComponentParamsMapper(CommonComponentParamsMapper())
.mapToParams(configuration, Locale.US, null, null, paymentMethod),
analyticsManager = analyticsManager,
+ paymentsClient = paymentsClient,
+ googlePayAvailabilityCheck = googlePayAvailabilityCheck,
)
}
+ private fun createOutputData(
+ isButtonVisible: Boolean = false,
+ paymentData: PaymentData? = null,
+ ) = GooglePayOutputData(isButtonVisible, paymentData)
+
companion object {
private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA")
private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID"
private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE"
+ private val TEST_PAYMENT_DATA: PaymentData =
+ PaymentData.fromJson("{\"paymentMethodData\": {\"tokenizationData\": {\"token\": \"test_token\"}}}")
@JvmStatic
fun amountSource() = listOf(
@@ -228,5 +371,24 @@ internal class DefaultGooglePayDelegateTest {
arguments(null, Amount("USD", 0)),
arguments(null, Amount("USD", 0)),
)
+
+ @JvmStatic
+ fun paymentResultSource() = listOf(
+ arguments(ApiTaskResult(TEST_PAYMENT_DATA, Status.RESULT_SUCCESS), true),
+ arguments(ApiTaskResult(null, Status.RESULT_SUCCESS), false),
+ arguments(ApiTaskResult(null, Status.RESULT_CANCELED), false),
+ arguments(ApiTaskResult(null, Status.RESULT_INTERNAL_ERROR), false),
+ arguments(ApiTaskResult(null, Status.RESULT_INTERRUPTED), false),
+ arguments(ApiTaskResult(null, Status(AutoResolveHelper.RESULT_ERROR)), false),
+ )
+
+ @JvmStatic
+ fun googlePayAvailableSource() = listOf(
+ // isSubmitButtonVisible, isAvailable, expectedIsReady, expectedException
+ arguments(false, false, true, GooglePayUnavailableException()),
+ arguments(false, true, true, null),
+ arguments(true, false, false, GooglePayUnavailableException()),
+ arguments(true, true, true, null),
+ )
}
}
diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapperTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapperTest.kt
index 244d2b9558..3ec2d394f7 100644
--- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapperTest.kt
+++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapperTest.kt
@@ -26,6 +26,9 @@ import com.adyen.checkout.core.exception.ComponentException
import com.adyen.checkout.googlepay.AllowedAuthMethods
import com.adyen.checkout.googlepay.AllowedCardNetworks
import com.adyen.checkout.googlepay.BillingAddressParameters
+import com.adyen.checkout.googlepay.GooglePayButtonStyling
+import com.adyen.checkout.googlepay.GooglePayButtonTheme
+import com.adyen.checkout.googlepay.GooglePayButtonType
import com.adyen.checkout.googlepay.GooglePayConfiguration
import com.adyen.checkout.googlepay.MerchantInfo
import com.adyen.checkout.googlepay.ShippingAddressParameters
@@ -33,6 +36,9 @@ import com.adyen.checkout.googlepay.googlePay
import com.adyen.checkout.test.LoggingExtension
import com.google.android.gms.wallet.WalletConstants
import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
@@ -71,6 +77,11 @@ internal class GooglePayComponentParamsMapperTest {
val allowedCardNetworks = listOf("CARD1", "CARD2")
val shippingAddressParameters = ShippingAddressParameters(listOf("ZZ", "AA"), true)
val billingAddressParameters = BillingAddressParameters("FORMAT", true)
+ val googlePayButtonStyling = GooglePayButtonStyling(
+ buttonTheme = GooglePayButtonTheme.LIGHT,
+ buttonType = GooglePayButtonType.BOOK,
+ cornerRadius = 16,
+ )
val configuration = CheckoutConfiguration(
shopperLocale = Locale.FRANCE,
@@ -95,6 +106,7 @@ internal class GooglePayComponentParamsMapperTest {
setShippingAddressParameters(shippingAddressParameters)
setShippingAddressRequired(true)
setTotalPriceStatus("STATUS")
+ setGooglePayButtonStyling(googlePayButtonStyling)
}
}
@@ -111,9 +123,10 @@ internal class GooglePayComponentParamsMapperTest {
environment = Environment.APSE,
clientKey = TEST_CLIENT_KEY_2,
analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_2),
+ amount = amount,
+ isSubmitButtonVisible = false,
gatewayMerchantId = "MERCHANT_ACCOUNT",
googlePayEnvironment = WalletConstants.ENVIRONMENT_PRODUCTION,
- amount = amount,
totalPriceStatus = "STATUS",
countryCode = "ZZ",
merchantInfo = merchantInfo,
@@ -128,6 +141,7 @@ internal class GooglePayComponentParamsMapperTest {
shippingAddressParameters = shippingAddressParameters,
isBillingAddressRequired = true,
billingAddressParameters = billingAddressParameters,
+ googlePayButtonStyling = googlePayButtonStyling,
)
assertEquals(expected, params)
@@ -148,6 +162,7 @@ internal class GooglePayComponentParamsMapperTest {
googlePay {
setAmount(Amount("USD", 1L))
setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL))
+ setSubmitButtonVisible(true)
setMerchantAccount(TEST_GATEWAY_MERCHANT_ID)
}
}
@@ -172,6 +187,7 @@ internal class GooglePayComponentParamsMapperTest {
currency = "CAD",
value = 123L,
),
+ isSubmitButtonVisible = false,
)
assertEquals(expected, params)
@@ -480,6 +496,57 @@ internal class GooglePayComponentParamsMapperTest {
assertEquals(expected, params)
}
+ @Nested
+ inner class SubmitButtonVisibilityTest {
+
+ @Test
+ fun `when created by drop-in, then submit button should not be visible`() {
+ val configuration = createCheckoutConfiguration()
+
+ val params = googlePayComponentParamsMapper.mapToParams(
+ checkoutConfiguration = configuration,
+ deviceLocale = DEVICE_LOCALE,
+ dropInOverrideParams = DropInOverrideParams(null, null, true),
+ componentSessionParams = null,
+ paymentMethod = PaymentMethod(),
+ )
+
+ assertFalse(params.isSubmitButtonVisible)
+ }
+
+ @Test
+ fun `when not created by drop-in and set in configuration, then submit button should be visible`() {
+ val configuration = createCheckoutConfiguration {
+ setSubmitButtonVisible(true)
+ }
+
+ val params = googlePayComponentParamsMapper.mapToParams(
+ checkoutConfiguration = configuration,
+ deviceLocale = DEVICE_LOCALE,
+ dropInOverrideParams = null,
+ componentSessionParams = null,
+ paymentMethod = PaymentMethod(),
+ )
+
+ assertTrue(params.isSubmitButtonVisible)
+ }
+
+ @Test
+ fun `when not created by drop-in and not configured, then submit button should not be visible`() {
+ val configuration = createCheckoutConfiguration()
+
+ val params = googlePayComponentParamsMapper.mapToParams(
+ checkoutConfiguration = configuration,
+ deviceLocale = DEVICE_LOCALE,
+ dropInOverrideParams = null,
+ componentSessionParams = null,
+ paymentMethod = PaymentMethod(),
+ )
+
+ assertFalse(params.isSubmitButtonVisible)
+ }
+ }
+
private fun createCheckoutConfiguration(
amount: Amount? = null,
shopperLocale: Locale? = null,
@@ -524,9 +591,10 @@ internal class GooglePayComponentParamsMapperTest {
clientKey: String = TEST_CLIENT_KEY_1,
analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1),
isCreatedByDropIn: Boolean = false,
+ amount: Amount? = null,
+ isSubmitButtonVisible: Boolean = false,
gatewayMerchantId: String = TEST_GATEWAY_MERCHANT_ID,
googlePayEnvironment: Int = WalletConstants.ENVIRONMENT_TEST,
- amount: Amount? = null,
totalPriceStatus: String = "FINAL",
countryCode: String? = null,
merchantInfo: MerchantInfo? = null,
@@ -541,6 +609,7 @@ internal class GooglePayComponentParamsMapperTest {
shippingAddressParameters: ShippingAddressParameters? = null,
isBillingAddressRequired: Boolean = false,
billingAddressParameters: BillingAddressParameters? = null,
+ googlePayButtonStyling: GooglePayButtonStyling? = null,
) = GooglePayComponentParams(
commonComponentParams = CommonComponentParams(
shopperLocale = shopperLocale,
@@ -550,9 +619,10 @@ internal class GooglePayComponentParamsMapperTest {
isCreatedByDropIn = isCreatedByDropIn,
amount = amount,
),
+ amount = amount ?: Amount("USD", 0),
+ isSubmitButtonVisible = isSubmitButtonVisible,
gatewayMerchantId = gatewayMerchantId,
googlePayEnvironment = googlePayEnvironment,
- amount = amount ?: Amount("USD", 0),
totalPriceStatus = totalPriceStatus,
countryCode = countryCode,
merchantInfo = merchantInfo,
@@ -567,6 +637,7 @@ internal class GooglePayComponentParamsMapperTest {
shippingAddressParameters = shippingAddressParameters,
isBillingAddressRequired = isBillingAddressRequired,
billingAddressParameters = billingAddressParameters,
+ googlePayButtonStyling = googlePayButtonStyling,
)
companion object {
diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtilsTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtilsTest.kt
index a5f45707d4..807a2f42c4 100644
--- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtilsTest.kt
+++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtilsTest.kt
@@ -248,6 +248,7 @@ internal class GooglePayUtilsTest {
amount = null,
),
amount = Amount("USD", 0),
+ isSubmitButtonVisible = false,
gatewayMerchantId = "",
googlePayEnvironment = WalletConstants.ENVIRONMENT_TEST,
totalPriceStatus = "NOT_CURRENTLY_KNOWN",
@@ -264,6 +265,7 @@ internal class GooglePayUtilsTest {
shippingAddressParameters = null,
isBillingAddressRequired = false,
billingAddressParameters = null,
+ googlePayButtonStyling = null,
)
}
@@ -278,6 +280,7 @@ internal class GooglePayUtilsTest {
amount = Amount("EUR", 13_37),
),
amount = Amount("EUR", 13_37),
+ isSubmitButtonVisible = true,
gatewayMerchantId = "GATEWAY_MERCHANT_ID",
googlePayEnvironment = WalletConstants.ENVIRONMENT_PRODUCTION,
totalPriceStatus = "TOTAL_PRICE_STATUS",
@@ -303,6 +306,7 @@ internal class GooglePayUtilsTest {
format = "FORMAT",
isPhoneNumberRequired = true,
),
+ googlePayButtonStyling = null,
)
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index af4dad09be..510322fd85 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -108,6 +108,7 @@ compose-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-
# this unused dependency is needed so that renovate can update the compose compiler version. More info in: https://github.com/renovatebot/renovate/issues/18354
compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compose-compiler" }
google-pay-compose-button = { group = "com.google.pay.button", name = "compose-pay-button", version.ref = "google-pay-compose-button" }
+google-pay-play-services-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-play-services", version.ref = "coroutines" }
google-pay-play-services-wallet = { group = "com.google.android.gms", name = "play-services-wallet", version.ref = "play-services-wallet" }
hilt = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index c57a99c478..f294f1de1e 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -15114,6 +15114,14 @@
+
+
+
+
+
+
+
+
diff --git a/ideal/src/main/java/com/adyen/checkout/ideal/IdealComponent.kt b/ideal/src/main/java/com/adyen/checkout/ideal/IdealComponent.kt
index 7a4d9111d7..0e0714abb6 100644
--- a/ideal/src/main/java/com/adyen/checkout/ideal/IdealComponent.kt
+++ b/ideal/src/main/java/com/adyen/checkout/ideal/IdealComponent.kt
@@ -26,6 +26,7 @@ import com.adyen.checkout.ideal.internal.provider.IdealComponentProvider
import com.adyen.checkout.ideal.internal.ui.IdealDelegate
import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
import com.adyen.checkout.ui.core.internal.ui.ViewableComponent
+import com.adyen.checkout.ui.core.internal.util.mergeViewFlows
import kotlinx.coroutines.flow.Flow
/**
@@ -42,12 +43,13 @@ class IdealComponent internal constructor(
ButtonComponent,
ActionHandlingComponent by actionHandlingComponent {
- @Suppress("ForbiddenComment")
- // FIXME: Using actionHandlingComponent.activeDelegate will crash for QR code actions. This is a workaround for the
- // actual issue.
- override val delegate: ComponentDelegate get() = genericActionDelegate.delegate
+ override val delegate: ComponentDelegate get() = actionHandlingComponent.activeDelegate
- override val viewFlow: Flow = genericActionDelegate.viewFlow
+ override val viewFlow: Flow = mergeViewFlows(
+ viewModelScope,
+ idealDelegate.viewFlow,
+ genericActionDelegate.viewFlow,
+ )
init {
idealDelegate.initialize(viewModelScope)
diff --git a/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/DefaultIdealDelegate.kt b/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/DefaultIdealDelegate.kt
index ee91075d9e..f80589cc5a 100644
--- a/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/DefaultIdealDelegate.kt
+++ b/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/DefaultIdealDelegate.kt
@@ -23,6 +23,7 @@ import com.adyen.checkout.components.core.paymentmethod.IdealPaymentMethod
import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.internal.util.adyenLog
import com.adyen.checkout.ideal.IdealComponentState
+import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
@@ -43,6 +44,9 @@ internal class DefaultIdealDelegate(
private val submitChannel: Channel = bufferedChannel()
override val submitFlow: Flow = submitChannel.receiveAsFlow()
+ private val _viewFlow = MutableStateFlow(PaymentInProgressViewType)
+ override val viewFlow: Flow = _viewFlow
+
init {
submitChannel.trySend(componentStateFlow.value)
}
diff --git a/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/IdealDelegate.kt b/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/IdealDelegate.kt
index 472a8dc5d7..be0754166b 100644
--- a/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/IdealDelegate.kt
+++ b/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/IdealDelegate.kt
@@ -10,8 +10,12 @@ package com.adyen.checkout.ideal.internal.ui
import com.adyen.checkout.components.core.internal.ui.PaymentComponentDelegate
import com.adyen.checkout.ideal.IdealComponentState
+import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate
import kotlinx.coroutines.flow.Flow
-internal interface IdealDelegate : PaymentComponentDelegate {
+internal interface IdealDelegate :
+ PaymentComponentDelegate,
+ ViewProvidingDelegate {
+
val componentStateFlow: Flow
}
diff --git a/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/IdealViewProvider.kt b/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/IdealViewProvider.kt
new file mode 100644
index 0000000000..270a55f1a0
--- /dev/null
+++ b/ideal/src/main/java/com/adyen/checkout/ideal/internal/ui/IdealViewProvider.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 18/10/2024.
+ */
+
+package com.adyen.checkout.ideal.internal.ui
+
+import android.content.Context
+import com.adyen.checkout.ui.core.internal.ui.ComponentView
+import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
+import com.adyen.checkout.ui.core.internal.ui.ViewProvider
+import com.adyen.checkout.ui.core.internal.ui.view.ProcessingPaymentView
+
+internal class IdealViewProvider : ViewProvider {
+
+ override fun getView(
+ viewType: ComponentViewType,
+ context: Context
+ ): ComponentView = when (viewType) {
+ PaymentInProgressViewType -> ProcessingPaymentView(context)
+ else -> throw IllegalArgumentException("Unsupported view type")
+ }
+}
+
+internal object PaymentInProgressViewType : ComponentViewType {
+
+ override val viewProvider: ViewProvider get() = IdealViewProvider()
+}
diff --git a/ideal/src/test/java/com/adyen/checkout/ideal/IdealComponentTest.kt b/ideal/src/test/java/com/adyen/checkout/ideal/IdealComponentTest.kt
index 9b35778316..b73985bc0c 100644
--- a/ideal/src/test/java/com/adyen/checkout/ideal/IdealComponentTest.kt
+++ b/ideal/src/test/java/com/adyen/checkout/ideal/IdealComponentTest.kt
@@ -8,6 +8,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate
import com.adyen.checkout.components.core.internal.ComponentEventHandler
import com.adyen.checkout.components.core.internal.PaymentComponentEvent
import com.adyen.checkout.ideal.internal.ui.IdealDelegate
+import com.adyen.checkout.ideal.internal.ui.PaymentInProgressViewType
import com.adyen.checkout.test.LoggingExtension
import com.adyen.checkout.test.TestDispatcherExtension
import com.adyen.checkout.test.extensions.invokeOnCleared
@@ -15,7 +16,6 @@ import com.adyen.checkout.ui.core.internal.ui.TestComponentViewType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -40,6 +40,7 @@ internal class IdealComponentTest(
@BeforeEach
fun before() {
+ whenever(idealDelegate.viewFlow) doReturn MutableStateFlow(PaymentInProgressViewType)
whenever(genericActionDelegate.viewFlow) doReturn MutableStateFlow(null)
component = IdealComponent(
@@ -86,9 +87,25 @@ internal class IdealComponentTest(
}
@Test
- fun `when component is initialized then view flow should bu null`() = runTest {
+ fun `when component is initialized then view flow should match ideal delegate view flow`() = runTest {
component.viewFlow.test {
- assertNull(awaitItem())
+ assertEquals(PaymentInProgressViewType, awaitItem())
+ expectNoEvents()
+ }
+ }
+
+ @Test
+ fun `when ideal delegate view flow emits a value then component view flow should match that value`() = runTest {
+ val idealDelegateViewFlow = MutableStateFlow(TestComponentViewType.VIEW_TYPE_1)
+ whenever(idealDelegate.viewFlow) doReturn idealDelegateViewFlow
+ component = IdealComponent(idealDelegate, genericActionDelegate, actionHandlingComponent, componentEventHandler)
+
+ component.viewFlow.test {
+ assertEquals(TestComponentViewType.VIEW_TYPE_1, awaitItem())
+
+ idealDelegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2)
+ assertEquals(TestComponentViewType.VIEW_TYPE_2, awaitItem())
+
expectNoEvents()
}
}
@@ -105,7 +122,9 @@ internal class IdealComponentTest(
)
component.viewFlow.test {
- assertEquals(TestComponentViewType.VIEW_TYPE_1, awaitItem())
+ // this value should match the value of the main delegate and not the action delegate
+ // and in practice the initial value of the action delegate view flow is always null so it should be ignored
+ assertEquals(PaymentInProgressViewType, awaitItem())
actionDelegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2)
assertEquals(TestComponentViewType.VIEW_TYPE_2, awaitItem())
diff --git a/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentComponent.kt b/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentComponent.kt
index f64efadebd..01c64d1d1b 100644
--- a/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentComponent.kt
+++ b/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentComponent.kt
@@ -17,6 +17,7 @@ import com.adyen.checkout.instant.internal.provider.InstantPaymentComponentProvi
import com.adyen.checkout.instant.internal.ui.InstantPaymentDelegate
import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
import com.adyen.checkout.ui.core.internal.ui.ViewableComponent
+import com.adyen.checkout.ui.core.internal.util.mergeViewFlows
import kotlinx.coroutines.flow.Flow
/**
@@ -32,12 +33,13 @@ class InstantPaymentComponent internal constructor(
ViewableComponent,
ActionHandlingComponent by actionHandlingComponent {
- @Suppress("ForbiddenComment")
- // FIXME: Using actionHandlingComponent.activeDelegate will crash for QR code actions. This is a workaround for the
- // actual issue.
- override val delegate: ComponentDelegate get() = genericActionDelegate.delegate
+ override val delegate: ComponentDelegate get() = actionHandlingComponent.activeDelegate
- override val viewFlow: Flow = genericActionDelegate.viewFlow
+ override val viewFlow: Flow = mergeViewFlows(
+ viewModelScope,
+ instantPaymentDelegate.viewFlow,
+ genericActionDelegate.viewFlow,
+ )
init {
instantPaymentDelegate.initialize(viewModelScope)
diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt
index 3fbea06e94..3c19bfa77c 100644
--- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt
+++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt
@@ -25,6 +25,7 @@ import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.internal.util.adyenLog
import com.adyen.checkout.instant.InstantComponentState
import com.adyen.checkout.instant.internal.ui.model.InstantComponentParams
+import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
@@ -45,6 +46,9 @@ internal class DefaultInstantPaymentDelegate(
private val submitChannel: Channel = bufferedChannel()
override val submitFlow: Flow = submitChannel.receiveAsFlow()
+ private val _viewFlow = MutableStateFlow(PaymentInProgressViewType)
+ override val viewFlow: Flow = _viewFlow
+
init {
submitChannel.trySend(componentStateFlow.value)
}
diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/InstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/InstantPaymentDelegate.kt
index d9b8bc4095..25ff922505 100644
--- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/InstantPaymentDelegate.kt
+++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/InstantPaymentDelegate.kt
@@ -10,8 +10,12 @@ package com.adyen.checkout.instant.internal.ui
import com.adyen.checkout.components.core.internal.ui.PaymentComponentDelegate
import com.adyen.checkout.instant.InstantComponentState
+import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate
import kotlinx.coroutines.flow.Flow
-internal interface InstantPaymentDelegate : PaymentComponentDelegate {
+internal interface InstantPaymentDelegate :
+ PaymentComponentDelegate,
+ ViewProvidingDelegate {
+
val componentStateFlow: Flow
}
diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/InstantPaymentViewProvider.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/InstantPaymentViewProvider.kt
new file mode 100644
index 0000000000..dd7f7d653c
--- /dev/null
+++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/InstantPaymentViewProvider.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 18/10/2024.
+ */
+
+package com.adyen.checkout.instant.internal.ui
+
+import android.content.Context
+import com.adyen.checkout.ui.core.internal.ui.ComponentView
+import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
+import com.adyen.checkout.ui.core.internal.ui.ViewProvider
+import com.adyen.checkout.ui.core.internal.ui.view.ProcessingPaymentView
+
+internal class InstantPaymentViewProvider : ViewProvider {
+
+ override fun getView(
+ viewType: ComponentViewType,
+ context: Context
+ ): ComponentView = when (viewType) {
+ PaymentInProgressViewType -> ProcessingPaymentView(context)
+ else -> throw IllegalArgumentException("Unsupported view type")
+ }
+}
+
+internal object PaymentInProgressViewType : ComponentViewType {
+
+ override val viewProvider: ViewProvider get() = InstantPaymentViewProvider()
+}
diff --git a/instant/src/test/java/com/adyen/checkout/instant/InstantPaymentComponentTest.kt b/instant/src/test/java/com/adyen/checkout/instant/InstantPaymentComponentTest.kt
index 0d4bb574a0..db08736bdf 100644
--- a/instant/src/test/java/com/adyen/checkout/instant/InstantPaymentComponentTest.kt
+++ b/instant/src/test/java/com/adyen/checkout/instant/InstantPaymentComponentTest.kt
@@ -16,6 +16,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate
import com.adyen.checkout.components.core.internal.ComponentEventHandler
import com.adyen.checkout.components.core.internal.PaymentComponentEvent
import com.adyen.checkout.instant.internal.ui.InstantPaymentDelegate
+import com.adyen.checkout.instant.internal.ui.PaymentInProgressViewType
import com.adyen.checkout.test.LoggingExtension
import com.adyen.checkout.test.TestDispatcherExtension
import com.adyen.checkout.test.extensions.invokeOnCleared
@@ -23,7 +24,6 @@ import com.adyen.checkout.ui.core.internal.ui.TestComponentViewType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -48,6 +48,7 @@ internal class InstantPaymentComponentTest(
@BeforeEach
fun before() {
+ whenever(instantPaymentDelegate.viewFlow) doReturn MutableStateFlow(PaymentInProgressViewType)
whenever(genericActionDelegate.viewFlow) doReturn MutableStateFlow(null)
component = InstantPaymentComponent(
@@ -94,9 +95,30 @@ internal class InstantPaymentComponentTest(
}
@Test
- fun `when component is initialized then view flow should bu null`() = runTest {
+ fun `when component is initialized then view flow should match instant delegate view flow`() = runTest {
component.viewFlow.test {
- assertNull(awaitItem())
+ assertEquals(PaymentInProgressViewType, awaitItem())
+ expectNoEvents()
+ }
+ }
+
+ @Test
+ fun `when instant delegate view flow emits a value then component view flow should match that value`() = runTest {
+ val instantDelegateViewFlow = MutableStateFlow(TestComponentViewType.VIEW_TYPE_1)
+ whenever(instantPaymentDelegate.viewFlow) doReturn instantDelegateViewFlow
+ component = InstantPaymentComponent(
+ instantPaymentDelegate = instantPaymentDelegate,
+ genericActionDelegate = genericActionDelegate,
+ actionHandlingComponent = actionHandlingComponent,
+ componentEventHandler = componentEventHandler,
+ )
+
+ component.viewFlow.test {
+ assertEquals(TestComponentViewType.VIEW_TYPE_1, awaitItem())
+
+ instantDelegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2)
+ assertEquals(TestComponentViewType.VIEW_TYPE_2, awaitItem())
+
expectNoEvents()
}
}
@@ -113,7 +135,9 @@ internal class InstantPaymentComponentTest(
)
component.viewFlow.test {
- assertEquals(TestComponentViewType.VIEW_TYPE_1, awaitItem())
+ // this value should match the value of the main delegate and not the action delegate
+ // and in practice the initial value of the action delegate view flow is always null so it should be ignored
+ assertEquals(PaymentInProgressViewType, awaitItem())
actionDelegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2)
assertEquals(TestComponentViewType.VIEW_TYPE_2, awaitItem())
diff --git a/twint-action/src/main/java/com/adyen/checkout/twint/action/internal/ui/TwintActionView.kt b/twint-action/src/main/java/com/adyen/checkout/twint/action/internal/ui/TwintActionView.kt
index ae7b1e483d..405785de68 100644
--- a/twint-action/src/main/java/com/adyen/checkout/twint/action/internal/ui/TwintActionView.kt
+++ b/twint-action/src/main/java/com/adyen/checkout/twint/action/internal/ui/TwintActionView.kt
@@ -18,7 +18,9 @@ import com.adyen.checkout.twint.action.databinding.ViewTwintActionBinding
import com.adyen.checkout.ui.core.internal.ui.ComponentView
import kotlinx.coroutines.CoroutineScope
-internal class TwintActionView internal constructor(
+internal class TwintActionView
+@JvmOverloads
+internal constructor(
layoutInflater: LayoutInflater,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt
index 4fb4c70b86..2b8788813d 100644
--- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt
+++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt
@@ -159,6 +159,8 @@ internal class DefaultTwintDelegate(
val event = GenericEvents.submit(paymentMethod.type.orEmpty())
analyticsManager.trackEvent(event)
+ _viewFlow.tryEmit(PaymentInProgressViewType)
+
val state = _componentStateFlow.value
submitHandler.onSubmit(state = state)
}
diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt
index 15907709d4..8b6f33709d 100644
--- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt
+++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt
@@ -14,11 +14,13 @@ import com.adyen.checkout.ui.core.internal.ui.ButtonComponentViewType
import com.adyen.checkout.ui.core.internal.ui.ComponentView
import com.adyen.checkout.ui.core.internal.ui.ComponentViewType
import com.adyen.checkout.ui.core.internal.ui.ViewProvider
+import com.adyen.checkout.ui.core.internal.ui.view.ProcessingPaymentView
internal class TwintViewProvider : ViewProvider {
override fun getView(viewType: ComponentViewType, context: Context): ComponentView = when (viewType) {
TwintComponentViewType -> TwintView(context)
+ PaymentInProgressViewType -> ProcessingPaymentView(context)
else -> throw IllegalArgumentException("Unsupported view type")
}
}
@@ -29,3 +31,8 @@ internal object TwintComponentViewType : ButtonComponentViewType {
override val buttonTextResId: Int = ButtonComponentViewType.DEFAULT_BUTTON_TEXT_RES_ID
}
+
+internal object PaymentInProgressViewType : ComponentViewType {
+
+ override val viewProvider: ViewProvider get() = TwintViewProvider()
+}
diff --git a/twint/src/main/res/values-ar/strings.xml b/twint/src/main/res/values-ar/strings.xml
index 3c676a010f..1b041cc74f 100644
--- a/twint/src/main/res/values-ar/strings.xml
+++ b/twint/src/main/res/values-ar/strings.xml
@@ -8,4 +8,4 @@
حفظ لمدفوعاتي القادمة
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-bg-rBG/strings.xml b/twint/src/main/res/values-bg-rBG/strings.xml
index b33c81e03f..962a51e8fa 100644
--- a/twint/src/main/res/values-bg-rBG/strings.xml
+++ b/twint/src/main/res/values-bg-rBG/strings.xml
@@ -8,4 +8,4 @@
Запазване за следващото ми плащане
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-ca-rES/strings.xml b/twint/src/main/res/values-ca-rES/strings.xml
index 2eb0bf4599..e3789995b0 100644
--- a/twint/src/main/res/values-ca-rES/strings.xml
+++ b/twint/src/main/res/values-ca-rES/strings.xml
@@ -8,4 +8,4 @@
Desa\'l per al meu proper pagament
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-cs-rCZ/strings.xml b/twint/src/main/res/values-cs-rCZ/strings.xml
index ea36c1927e..d842afd337 100644
--- a/twint/src/main/res/values-cs-rCZ/strings.xml
+++ b/twint/src/main/res/values-cs-rCZ/strings.xml
@@ -8,4 +8,4 @@
Uložit pro příští platby
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-da-rDK/strings.xml b/twint/src/main/res/values-da-rDK/strings.xml
index 83eaefc275..6ead94aac2 100644
--- a/twint/src/main/res/values-da-rDK/strings.xml
+++ b/twint/src/main/res/values-da-rDK/strings.xml
@@ -8,4 +8,4 @@
Gem til min næste betaling
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-de-rDE/strings.xml b/twint/src/main/res/values-de-rDE/strings.xml
index d957332637..a1c7f4ddfa 100644
--- a/twint/src/main/res/values-de-rDE/strings.xml
+++ b/twint/src/main/res/values-de-rDE/strings.xml
@@ -8,4 +8,4 @@
Für zukünftige Zahlvorgänge speichern
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-el-rGR/strings.xml b/twint/src/main/res/values-el-rGR/strings.xml
index f2b61e492a..6631def477 100644
--- a/twint/src/main/res/values-el-rGR/strings.xml
+++ b/twint/src/main/res/values-el-rGR/strings.xml
@@ -8,4 +8,4 @@
Αποθήκευση για την επόμενη πληρωμή μου
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-es-rES/strings.xml b/twint/src/main/res/values-es-rES/strings.xml
index 480120a6c0..a78f6b4999 100644
--- a/twint/src/main/res/values-es-rES/strings.xml
+++ b/twint/src/main/res/values-es-rES/strings.xml
@@ -8,4 +8,4 @@
Recordar para mi próximo pago
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-et-rEE/strings.xml b/twint/src/main/res/values-et-rEE/strings.xml
index 219eb4ee28..c3dc72ac75 100644
--- a/twint/src/main/res/values-et-rEE/strings.xml
+++ b/twint/src/main/res/values-et-rEE/strings.xml
@@ -8,4 +8,4 @@
Salvesta mu järgmise makse jaoks
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-fi-rFI/strings.xml b/twint/src/main/res/values-fi-rFI/strings.xml
index beb439e1af..97c6ca4835 100644
--- a/twint/src/main/res/values-fi-rFI/strings.xml
+++ b/twint/src/main/res/values-fi-rFI/strings.xml
@@ -8,4 +8,4 @@
Tallenna seuraavaa maksuani varten
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-fr-rFR/strings.xml b/twint/src/main/res/values-fr-rFR/strings.xml
index c636922f89..eeaf6ecee0 100644
--- a/twint/src/main/res/values-fr-rFR/strings.xml
+++ b/twint/src/main/res/values-fr-rFR/strings.xml
@@ -8,4 +8,4 @@
Sauvegarder pour mon prochain paiement
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-hr-rHR/strings.xml b/twint/src/main/res/values-hr-rHR/strings.xml
index e0a3103fc5..0c667e965b 100644
--- a/twint/src/main/res/values-hr-rHR/strings.xml
+++ b/twint/src/main/res/values-hr-rHR/strings.xml
@@ -8,4 +8,4 @@
Pohrani za moje sljedeće plaćanje
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-hu-rHU/strings.xml b/twint/src/main/res/values-hu-rHU/strings.xml
index 71aaa91f6e..4ce8ed203e 100644
--- a/twint/src/main/res/values-hu-rHU/strings.xml
+++ b/twint/src/main/res/values-hu-rHU/strings.xml
@@ -8,4 +8,4 @@
Mentés a következő fizetéshez
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-is-rIS/strings.xml b/twint/src/main/res/values-is-rIS/strings.xml
index 49a032ebef..bb8b6de721 100644
--- a/twint/src/main/res/values-is-rIS/strings.xml
+++ b/twint/src/main/res/values-is-rIS/strings.xml
@@ -8,4 +8,4 @@
Spara fyrir næstu greiðslu
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-it-rIT/strings.xml b/twint/src/main/res/values-it-rIT/strings.xml
index 1f450a5576..0547e381fd 100644
--- a/twint/src/main/res/values-it-rIT/strings.xml
+++ b/twint/src/main/res/values-it-rIT/strings.xml
@@ -8,4 +8,4 @@
Salva per il prossimo pagamento
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-ja-rJP/strings.xml b/twint/src/main/res/values-ja-rJP/strings.xml
index f3d599c38d..4661bf0e39 100644
--- a/twint/src/main/res/values-ja-rJP/strings.xml
+++ b/twint/src/main/res/values-ja-rJP/strings.xml
@@ -8,4 +8,4 @@
次回のお支払いのため詳細を保存
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-ko-rKR/strings.xml b/twint/src/main/res/values-ko-rKR/strings.xml
index ef67d17d60..11d6a13add 100644
--- a/twint/src/main/res/values-ko-rKR/strings.xml
+++ b/twint/src/main/res/values-ko-rKR/strings.xml
@@ -8,4 +8,4 @@
다음 결제를 위해 이 수단 저장
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-lt-rLT/strings.xml b/twint/src/main/res/values-lt-rLT/strings.xml
index 5584f3d058..735847f4d8 100644
--- a/twint/src/main/res/values-lt-rLT/strings.xml
+++ b/twint/src/main/res/values-lt-rLT/strings.xml
@@ -8,4 +8,4 @@
Išsaugoti kitam mokėjimui
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-lv-rLV/strings.xml b/twint/src/main/res/values-lv-rLV/strings.xml
index 02801275ac..e0175f1d77 100644
--- a/twint/src/main/res/values-lv-rLV/strings.xml
+++ b/twint/src/main/res/values-lv-rLV/strings.xml
@@ -8,4 +8,4 @@
Saglabāt manam nākamajam maksājumam
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-nb-rNO/strings.xml b/twint/src/main/res/values-nb-rNO/strings.xml
index 3be320881d..766f804875 100644
--- a/twint/src/main/res/values-nb-rNO/strings.xml
+++ b/twint/src/main/res/values-nb-rNO/strings.xml
@@ -8,4 +8,4 @@
Lagre til min neste betaling
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-nl-rNL/strings.xml b/twint/src/main/res/values-nl-rNL/strings.xml
index 47497632a2..ff4c9a96f8 100644
--- a/twint/src/main/res/values-nl-rNL/strings.xml
+++ b/twint/src/main/res/values-nl-rNL/strings.xml
@@ -8,4 +8,4 @@
Bewaar voor mijn volgende betaling
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-pl-rPL/strings.xml b/twint/src/main/res/values-pl-rPL/strings.xml
index c04ebef994..0572c31a14 100644
--- a/twint/src/main/res/values-pl-rPL/strings.xml
+++ b/twint/src/main/res/values-pl-rPL/strings.xml
@@ -8,4 +8,4 @@
Zapisz na potrzeby następnej płatności
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-pt-rBR/strings.xml b/twint/src/main/res/values-pt-rBR/strings.xml
index ed2bbccfe6..a6572eb2cf 100644
--- a/twint/src/main/res/values-pt-rBR/strings.xml
+++ b/twint/src/main/res/values-pt-rBR/strings.xml
@@ -8,4 +8,4 @@
Salvar para meu próximo pagamento
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-pt-rPT/strings.xml b/twint/src/main/res/values-pt-rPT/strings.xml
index b61951548e..2c50ac483e 100644
--- a/twint/src/main/res/values-pt-rPT/strings.xml
+++ b/twint/src/main/res/values-pt-rPT/strings.xml
@@ -8,4 +8,4 @@
Guardar para o meu próximo pagamento
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-ro-rRO/strings.xml b/twint/src/main/res/values-ro-rRO/strings.xml
index 612d0dbc24..102b51e538 100644
--- a/twint/src/main/res/values-ro-rRO/strings.xml
+++ b/twint/src/main/res/values-ro-rRO/strings.xml
@@ -8,4 +8,4 @@
Salvează pentru următoarea mea plată
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-ru-rRU/strings.xml b/twint/src/main/res/values-ru-rRU/strings.xml
index a0d065257e..cb4cefbc3d 100644
--- a/twint/src/main/res/values-ru-rRU/strings.xml
+++ b/twint/src/main/res/values-ru-rRU/strings.xml
@@ -8,4 +8,4 @@
Сохранить для следующего платежа
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-sk-rSK/strings.xml b/twint/src/main/res/values-sk-rSK/strings.xml
index 99b5522b90..41bfac319d 100644
--- a/twint/src/main/res/values-sk-rSK/strings.xml
+++ b/twint/src/main/res/values-sk-rSK/strings.xml
@@ -8,4 +8,4 @@
Uložiť pre moju ďalšiu platbu
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-sl-rSI/strings.xml b/twint/src/main/res/values-sl-rSI/strings.xml
index 36550aca6f..28c2174f42 100644
--- a/twint/src/main/res/values-sl-rSI/strings.xml
+++ b/twint/src/main/res/values-sl-rSI/strings.xml
@@ -8,4 +8,4 @@
Shrani za moje naslednje plačilo
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-sv-rSE/strings.xml b/twint/src/main/res/values-sv-rSE/strings.xml
index efbef0ed6c..6dd5e8ce64 100644
--- a/twint/src/main/res/values-sv-rSE/strings.xml
+++ b/twint/src/main/res/values-sv-rSE/strings.xml
@@ -8,4 +8,4 @@
Spara till min nästa betalning
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-zh-rCN/strings.xml b/twint/src/main/res/values-zh-rCN/strings.xml
index 2dc4996f5d..b7d8c86746 100644
--- a/twint/src/main/res/values-zh-rCN/strings.xml
+++ b/twint/src/main/res/values-zh-rCN/strings.xml
@@ -8,4 +8,4 @@
保存以便下次支付使用
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values-zh-rTW/strings.xml b/twint/src/main/res/values-zh-rTW/strings.xml
index a84f33995d..2961a62b97 100644
--- a/twint/src/main/res/values-zh-rTW/strings.xml
+++ b/twint/src/main/res/values-zh-rTW/strings.xml
@@ -8,4 +8,4 @@
儲存以供下次付款使用
-
\ No newline at end of file
+
diff --git a/twint/src/main/res/values/strings.xml b/twint/src/main/res/values/strings.xml
index e6745cc0fc..358a131969 100644
--- a/twint/src/main/res/values/strings.xml
+++ b/twint/src/main/res/values/strings.xml
@@ -8,4 +8,4 @@
Save for my next payment
-
\ No newline at end of file
+
diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt
index 8988f179d0..26007deda0 100644
--- a/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt
+++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt
@@ -147,6 +147,7 @@ class AdyenComponentView @JvmOverloads constructor(
binding.frameLayoutButtonContainer.isVisible = buttonDelegate.shouldShowSubmitButton()
val buttonView = (viewType as ButtonComponentViewType)
.buttonViewProvider.getButton(context)
+ buttonView.initialize(buttonDelegate, coroutineScope)
buttonView.setText(viewType, componentParams, localizedContext)
buttonView.setOnClickListener {
buttonDelegate.onSubmit()
diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/DefaultPayButton.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/DefaultPayButton.kt
index 96c12a09b2..bf02a0dd19 100644
--- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/DefaultPayButton.kt
+++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/DefaultPayButton.kt
@@ -12,6 +12,8 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import com.adyen.checkout.ui.core.databinding.DefaultPayButtonViewBinding
+import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate
+import kotlinx.coroutines.CoroutineScope
internal class DefaultPayButton @JvmOverloads constructor(
context: Context,
@@ -21,6 +23,8 @@ internal class DefaultPayButton @JvmOverloads constructor(
private val binding = DefaultPayButtonViewBinding.inflate(LayoutInflater.from(context), this)
+ override fun initialize(delegate: ButtonDelegate, coroutineScope: CoroutineScope) = Unit
+
override fun setEnabled(enabled: Boolean) {
binding.payButton.isEnabled = enabled
}
diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/PayButton.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/PayButton.kt
index f818ba1070..47aea907bb 100644
--- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/PayButton.kt
+++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/PayButton.kt
@@ -12,6 +12,8 @@ import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.annotation.RestrictTo
+import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate
+import kotlinx.coroutines.CoroutineScope
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
abstract class PayButton(
@@ -20,6 +22,8 @@ abstract class PayButton(
defStyleAttr: Int,
) : FrameLayout(context, attrs, defStyleAttr) {
+ abstract fun initialize(delegate: ButtonDelegate, coroutineScope: CoroutineScope)
+
abstract override fun setEnabled(enabled: Boolean)
abstract override fun setOnClickListener(listener: OnClickListener?)
diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/view/CashAppPayWaitingView.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/ProcessingPaymentView.kt
similarity index 61%
rename from cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/view/CashAppPayWaitingView.kt
rename to ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/ProcessingPaymentView.kt
index 5bb4d79566..521649ee01 100644
--- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/view/CashAppPayWaitingView.kt
+++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/ProcessingPaymentView.kt
@@ -1,4 +1,12 @@
-package com.adyen.checkout.cashapppay.internal.ui.view
+/*
+ * Copyright (c) 2024 Adyen N.V.
+ *
+ * This file is open source and available under the MIT license. See the LICENSE file for more info.
+ *
+ * Created by oscars on 16/10/2024.
+ */
+
+package com.adyen.checkout.ui.core.internal.ui.view
import android.content.Context
import android.util.AttributeSet
@@ -6,27 +14,28 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
-import com.adyen.checkout.cashapppay.R
-import com.adyen.checkout.cashapppay.databinding.CashAppPayWaitingViewBinding
+import androidx.annotation.RestrictTo
import com.adyen.checkout.components.core.internal.ui.ComponentDelegate
+import com.adyen.checkout.ui.core.R
+import com.adyen.checkout.ui.core.databinding.ProcessingPaymentViewBinding
import com.adyen.checkout.ui.core.internal.ui.ComponentView
import com.adyen.checkout.ui.core.internal.util.setLocalizedTextFromStyle
import kotlinx.coroutines.CoroutineScope
-import com.adyen.checkout.ui.core.R as UICoreR
-internal class CashAppPayWaitingView @JvmOverloads constructor(
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class ProcessingPaymentView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : LinearLayout(context, attrs, defStyleAttr), ComponentView {
- private val binding = CashAppPayWaitingViewBinding.inflate(LayoutInflater.from(context), this)
+ private val binding = ProcessingPaymentViewBinding.inflate(LayoutInflater.from(context), this)
init {
orientation = HORIZONTAL
gravity = Gravity.CENTER
- val padding = resources.getDimension(UICoreR.dimen.standard_margin).toInt()
+ val padding = resources.getDimension(R.dimen.standard_margin).toInt()
setPadding(padding, padding, padding, padding)
}
@@ -36,8 +45,8 @@ internal class CashAppPayWaitingView @JvmOverloads constructor(
private fun initLocalizedStrings(localizedContext: Context) {
binding.textViewPaymentInProgressDescription.setLocalizedTextFromStyle(
- R.style.AdyenCheckout_CashAppPay_WaitingDescriptionTextView,
- localizedContext
+ R.style.AdyenCheckout_ProcessingPaymentView_WaitingDescriptionTextView,
+ localizedContext,
)
}
diff --git a/cashapppay/src/main/res/layout/cash_app_pay_waiting_view.xml b/ui-core/src/main/res/layout/processing_payment_view.xml
similarity index 80%
rename from cashapppay/src/main/res/layout/cash_app_pay_waiting_view.xml
rename to ui-core/src/main/res/layout/processing_payment_view.xml
index d002b61f8b..ece00fae24 100644
--- a/cashapppay/src/main/res/layout/cash_app_pay_waiting_view.xml
+++ b/ui-core/src/main/res/layout/processing_payment_view.xml
@@ -9,12 +9,12 @@
diff --git a/ui-core/src/main/res/template/values/strings.xml.tt b/ui-core/src/main/res/template/values/strings.xml.tt
index 518685147b..676d8239d5 100644
--- a/ui-core/src/main/res/template/values/strings.xml.tt
+++ b/ui-core/src/main/res/template/values/strings.xml.tt
@@ -55,4 +55,6 @@
%%address.enterManually%%
%%address.lookup.submit%%
%%address.lookup.item.validationFailureMessage.empty%%
+
+ %%paypal.processingPayment%%
diff --git a/ui-core/src/main/res/values-ar/strings.xml b/ui-core/src/main/res/values-ar/strings.xml
index 0c600a31fe..27e07bc79c 100644
--- a/ui-core/src/main/res/values-ar/strings.xml
+++ b/ui-core/src/main/res/values-ar/strings.xml
@@ -55,4 +55,6 @@
أدخل العنوان يدويًا
استخدم هذا العنوان
العنوان مطلوب
+
+ جارِ معالجة المدفوعات…
diff --git a/ui-core/src/main/res/values-bg-rBG/strings.xml b/ui-core/src/main/res/values-bg-rBG/strings.xml
index 60b9caff52..cbfe431112 100644
--- a/ui-core/src/main/res/values-bg-rBG/strings.xml
+++ b/ui-core/src/main/res/values-bg-rBG/strings.xml
@@ -55,4 +55,6 @@
Въведете адреса ръчно
Използвайте този адрес
Изисква се адрес
+
+ Обработка на плащането…
diff --git a/ui-core/src/main/res/values-ca-rES/strings.xml b/ui-core/src/main/res/values-ca-rES/strings.xml
index 0dcd892455..bb82408927 100644
--- a/ui-core/src/main/res/values-ca-rES/strings.xml
+++ b/ui-core/src/main/res/values-ca-rES/strings.xml
@@ -55,4 +55,6 @@
Introduïu l\'adreça manualment
Utilitza aquesta adreça
Adreça obligatòria
+
+ S\'esta processant el pagament…
diff --git a/ui-core/src/main/res/values-cs-rCZ/strings.xml b/ui-core/src/main/res/values-cs-rCZ/strings.xml
index 08a6d31a05..bf5cdd623f 100644
--- a/ui-core/src/main/res/values-cs-rCZ/strings.xml
+++ b/ui-core/src/main/res/values-cs-rCZ/strings.xml
@@ -55,4 +55,6 @@
Zadejte adresu ručně
Použijte tuto adresu
Požadovaná adresa
+
+ Zpracování platby…
diff --git a/ui-core/src/main/res/values-da-rDK/strings.xml b/ui-core/src/main/res/values-da-rDK/strings.xml
index 16a357ebe7..6d7800ca30 100644
--- a/ui-core/src/main/res/values-da-rDK/strings.xml
+++ b/ui-core/src/main/res/values-da-rDK/strings.xml
@@ -55,4 +55,6 @@
Indtast adresse manuelt
Brug denne adresse
Adresse er påkrævet
+
+ Behandler betaling…
diff --git a/ui-core/src/main/res/values-de-rDE/strings.xml b/ui-core/src/main/res/values-de-rDE/strings.xml
index c0f7f3fb4a..9375a2d105 100644
--- a/ui-core/src/main/res/values-de-rDE/strings.xml
+++ b/ui-core/src/main/res/values-de-rDE/strings.xml
@@ -55,4 +55,6 @@
Geben Sie die Adresse manuell ein
Diese Adresse verwenden
Adresse erforderlich
+
+ Zahlung wird verarbeitet…
diff --git a/ui-core/src/main/res/values-el-rGR/strings.xml b/ui-core/src/main/res/values-el-rGR/strings.xml
index 9df7e3b258..6b72311c35 100644
--- a/ui-core/src/main/res/values-el-rGR/strings.xml
+++ b/ui-core/src/main/res/values-el-rGR/strings.xml
@@ -55,4 +55,6 @@
Εισαγάγετε τη διεύθυνση μη αυτόματα
Χρησιμοποιήστε αυτήν τη διεύθυνση
Απαιτείται διεύθυνση
+
+ Επεξεργασία πληρωμής…
diff --git a/ui-core/src/main/res/values-es-rES/strings.xml b/ui-core/src/main/res/values-es-rES/strings.xml
index 56db8cd164..f04df842c8 100644
--- a/ui-core/src/main/res/values-es-rES/strings.xml
+++ b/ui-core/src/main/res/values-es-rES/strings.xml
@@ -55,4 +55,6 @@
Introduzca la dirección manualmente
Usar esta dirección
Se necesita la dirección
+
+ Procesando pago…
diff --git a/ui-core/src/main/res/values-et-rEE/strings.xml b/ui-core/src/main/res/values-et-rEE/strings.xml
index be7feabe52..5102dad98c 100644
--- a/ui-core/src/main/res/values-et-rEE/strings.xml
+++ b/ui-core/src/main/res/values-et-rEE/strings.xml
@@ -55,4 +55,6 @@
Sisestage aadress käsitsi
Kasuta seda aadressi
Aadress on kohustuslik
+
+ Makse töötlemine …
diff --git a/ui-core/src/main/res/values-fi-rFI/strings.xml b/ui-core/src/main/res/values-fi-rFI/strings.xml
index 40768d332f..2bed3074a4 100644
--- a/ui-core/src/main/res/values-fi-rFI/strings.xml
+++ b/ui-core/src/main/res/values-fi-rFI/strings.xml
@@ -55,4 +55,6 @@
Syötä osoite manuaalisesti
Käytä tätä osoitetta
Osoite vaaditaan
+
+ Maksua käsitellään…
diff --git a/ui-core/src/main/res/values-fr-rFR/strings.xml b/ui-core/src/main/res/values-fr-rFR/strings.xml
index 7bfef50a70..0e190448d8 100644
--- a/ui-core/src/main/res/values-fr-rFR/strings.xml
+++ b/ui-core/src/main/res/values-fr-rFR/strings.xml
@@ -55,4 +55,6 @@
Saisissez l\'adresse manuellement
Utiliser cette adresse
Adresse requise
+
+ Traitement du paiement en cours…
diff --git a/ui-core/src/main/res/values-hr-rHR/strings.xml b/ui-core/src/main/res/values-hr-rHR/strings.xml
index 8756b3c7f0..d78008307e 100644
--- a/ui-core/src/main/res/values-hr-rHR/strings.xml
+++ b/ui-core/src/main/res/values-hr-rHR/strings.xml
@@ -55,4 +55,6 @@
Ručno unesite adresu
Koristi ovu adresu
Potrebna je adresa
+
+ Obrada plaćanja u tijeku…
diff --git a/ui-core/src/main/res/values-hu-rHU/strings.xml b/ui-core/src/main/res/values-hu-rHU/strings.xml
index 3fe5f079e7..b148728aca 100644
--- a/ui-core/src/main/res/values-hu-rHU/strings.xml
+++ b/ui-core/src/main/res/values-hu-rHU/strings.xml
@@ -55,4 +55,6 @@
Manuálisan írjon be egy címet
Használja ezt a címet
A cím megadása kötelező
+
+ Fizetés feldolgozása…
diff --git a/ui-core/src/main/res/values-is-rIS/strings.xml b/ui-core/src/main/res/values-is-rIS/strings.xml
index 5357859c35..b41822122d 100644
--- a/ui-core/src/main/res/values-is-rIS/strings.xml
+++ b/ui-core/src/main/res/values-is-rIS/strings.xml
@@ -55,4 +55,6 @@
Slá inn heimilisfang handvirkt
Nota þetta heimilisfang
Heimilisfang er áskilið
+
+ Unnið úr greiðslu…
diff --git a/ui-core/src/main/res/values-it-rIT/strings.xml b/ui-core/src/main/res/values-it-rIT/strings.xml
index 362587fe11..bf4ec27201 100644
--- a/ui-core/src/main/res/values-it-rIT/strings.xml
+++ b/ui-core/src/main/res/values-it-rIT/strings.xml
@@ -55,4 +55,6 @@
Inserisci l\'indirizzo manualmente
Usa questo indirizzo
Indirizzo richiesto
+
+ Elaborazione del pagamento in corso…
diff --git a/ui-core/src/main/res/values-ja-rJP/strings.xml b/ui-core/src/main/res/values-ja-rJP/strings.xml
index f4779958bd..ac9b224306 100644
--- a/ui-core/src/main/res/values-ja-rJP/strings.xml
+++ b/ui-core/src/main/res/values-ja-rJP/strings.xml
@@ -55,4 +55,6 @@
住所を手動で入力してください
この住所を使用する
住所が必要です
+
+ 支払いを処理しています…
diff --git a/ui-core/src/main/res/values-ko-rKR/strings.xml b/ui-core/src/main/res/values-ko-rKR/strings.xml
index 0ceaad2bd9..41a35e7022 100644
--- a/ui-core/src/main/res/values-ko-rKR/strings.xml
+++ b/ui-core/src/main/res/values-ko-rKR/strings.xml
@@ -55,4 +55,6 @@
수동으로 주소 입력
이 주소 사용
주소 필수
+
+ 결제 처리 중…
diff --git a/ui-core/src/main/res/values-lt-rLT/strings.xml b/ui-core/src/main/res/values-lt-rLT/strings.xml
index 63643d7a4a..0d61c38d3f 100644
--- a/ui-core/src/main/res/values-lt-rLT/strings.xml
+++ b/ui-core/src/main/res/values-lt-rLT/strings.xml
@@ -55,4 +55,6 @@
Įveskite adresą rankiniu būdu
Naudokite šį adresą
Adresas privalomas
+
+ Mokėjimas apdorojamas…
diff --git a/ui-core/src/main/res/values-lv-rLV/strings.xml b/ui-core/src/main/res/values-lv-rLV/strings.xml
index 75ad230807..de3ae5ef24 100644
--- a/ui-core/src/main/res/values-lv-rLV/strings.xml
+++ b/ui-core/src/main/res/values-lv-rLV/strings.xml
@@ -55,4 +55,6 @@
Ievadiet adresi manuāli
Izmantot šo adresi
Nepieciešama adrese
+
+ Notiek maksājuma apstrāde…
diff --git a/ui-core/src/main/res/values-nb-rNO/strings.xml b/ui-core/src/main/res/values-nb-rNO/strings.xml
index 60b237c954..43b34dd4a1 100644
--- a/ui-core/src/main/res/values-nb-rNO/strings.xml
+++ b/ui-core/src/main/res/values-nb-rNO/strings.xml
@@ -55,4 +55,6 @@
Skriv inn adressen manuelt
Bruk denne adressen
Adresse er nødvendig
+
+ Behandler betaling…
diff --git a/ui-core/src/main/res/values-nl-rNL/strings.xml b/ui-core/src/main/res/values-nl-rNL/strings.xml
index ddab9d7a2a..49d65bba1f 100644
--- a/ui-core/src/main/res/values-nl-rNL/strings.xml
+++ b/ui-core/src/main/res/values-nl-rNL/strings.xml
@@ -55,4 +55,6 @@
Voer het adres handmatig in
Dit adres gebruiken
Adres verplicht
+
+ Betaling wordt verwerkt…
diff --git a/ui-core/src/main/res/values-pl-rPL/strings.xml b/ui-core/src/main/res/values-pl-rPL/strings.xml
index 0b1549741a..f7ba1e3212 100644
--- a/ui-core/src/main/res/values-pl-rPL/strings.xml
+++ b/ui-core/src/main/res/values-pl-rPL/strings.xml
@@ -55,4 +55,6 @@
Wprowadź adres ręcznie
Użyj tego adresu
Wymagane jest podanie adresu
+
+ Przetwarzanie płatności…
diff --git a/ui-core/src/main/res/values-pt-rBR/strings.xml b/ui-core/src/main/res/values-pt-rBR/strings.xml
index f9c6125cd8..7fb23e3234 100644
--- a/ui-core/src/main/res/values-pt-rBR/strings.xml
+++ b/ui-core/src/main/res/values-pt-rBR/strings.xml
@@ -55,4 +55,6 @@
Inserir endereço manualmente
Usar este endereço
O endereço é obrigatório
+
+ Processando pagamento…
diff --git a/ui-core/src/main/res/values-pt-rPT/strings.xml b/ui-core/src/main/res/values-pt-rPT/strings.xml
index 85748bf8ab..e53023132a 100644
--- a/ui-core/src/main/res/values-pt-rPT/strings.xml
+++ b/ui-core/src/main/res/values-pt-rPT/strings.xml
@@ -55,4 +55,6 @@
Introduza o endereço manualmente
Utilize este endereço
Endereço necessário
+
+ A processar pagamento…
diff --git a/ui-core/src/main/res/values-ro-rRO/strings.xml b/ui-core/src/main/res/values-ro-rRO/strings.xml
index d4e2fba271..5622e9aeb0 100644
--- a/ui-core/src/main/res/values-ro-rRO/strings.xml
+++ b/ui-core/src/main/res/values-ro-rRO/strings.xml
@@ -55,4 +55,6 @@
Introduceți adresa manual
Folosiți această adresă
Adresa este necesară
+
+ Se prelucrează plata…
diff --git a/ui-core/src/main/res/values-ru-rRU/strings.xml b/ui-core/src/main/res/values-ru-rRU/strings.xml
index 403d293a36..ddaf975f5b 100644
--- a/ui-core/src/main/res/values-ru-rRU/strings.xml
+++ b/ui-core/src/main/res/values-ru-rRU/strings.xml
@@ -55,4 +55,6 @@
Ввести адрес вручную
Используйте этот адрес
Требуется адрес
+
+ Платеж обрабатывается…
diff --git a/ui-core/src/main/res/values-sk-rSK/strings.xml b/ui-core/src/main/res/values-sk-rSK/strings.xml
index e70c9c52cf..e1907ba92d 100644
--- a/ui-core/src/main/res/values-sk-rSK/strings.xml
+++ b/ui-core/src/main/res/values-sk-rSK/strings.xml
@@ -55,4 +55,6 @@
Manuálne zadajte adresu
Použite túto adresu
Adresa sa požaduje
+
+ Platba sa spracúva.
diff --git a/ui-core/src/main/res/values-sl-rSI/strings.xml b/ui-core/src/main/res/values-sl-rSI/strings.xml
index e9ab25d9f0..313d5c7001 100644
--- a/ui-core/src/main/res/values-sl-rSI/strings.xml
+++ b/ui-core/src/main/res/values-sl-rSI/strings.xml
@@ -55,4 +55,6 @@
Naslov vnesite ročno
Uporabite ta naslov
Naslov je obvezen
+
+ Obdelava plačila…
diff --git a/ui-core/src/main/res/values-sv-rSE/strings.xml b/ui-core/src/main/res/values-sv-rSE/strings.xml
index 549b056e98..a8853b8a33 100644
--- a/ui-core/src/main/res/values-sv-rSE/strings.xml
+++ b/ui-core/src/main/res/values-sv-rSE/strings.xml
@@ -55,4 +55,6 @@
Ange adress manuellt
Använd denna adress
Adress krävs
+
+ Behandlar betalning…
diff --git a/ui-core/src/main/res/values-zh-rCN/strings.xml b/ui-core/src/main/res/values-zh-rCN/strings.xml
index d0aecfc55d..c2dbe72405 100644
--- a/ui-core/src/main/res/values-zh-rCN/strings.xml
+++ b/ui-core/src/main/res/values-zh-rCN/strings.xml
@@ -55,4 +55,6 @@
手动输入地址
使用此地址
地址为必填项
+
+ 正在处理付款…
diff --git a/ui-core/src/main/res/values-zh-rTW/strings.xml b/ui-core/src/main/res/values-zh-rTW/strings.xml
index ac39b50ea0..02e9e7ab85 100644
--- a/ui-core/src/main/res/values-zh-rTW/strings.xml
+++ b/ui-core/src/main/res/values-zh-rTW/strings.xml
@@ -55,4 +55,6 @@
手動輸入地址
使用此地址
必須填寫地址
+
+ 正在處理付款……
diff --git a/ui-core/src/main/res/values/strings.xml b/ui-core/src/main/res/values/strings.xml
index f4d78d9e87..6214b33801 100644
--- a/ui-core/src/main/res/values/strings.xml
+++ b/ui-core/src/main/res/values/strings.xml
@@ -55,4 +55,6 @@
Enter address manually
Use this address
Address required
+
+ Processing payment…
diff --git a/ui-core/src/main/res/values/styles.xml b/ui-core/src/main/res/values/styles.xml
index 9596fdc49a..af11d3136b 100644
--- a/ui-core/src/main/res/values/styles.xml
+++ b/ui-core/src/main/res/values/styles.xml
@@ -407,4 +407,19 @@
- someColor3
-->
+
+
+
+
+
+