URL-адрес проверки Mollie не отображается в Android Webview

Вопрос или проблема

В моем Android приложении, созданном с использованием Jetpack Compose и Kotlin, я использую Android Webview для загрузки URL-адреса проверки Mollie для платежей. URL (https://www.mollie.com/checkout/select-method/SOMECODESPECIFICTOPAYMENT) отображает содержимое, похожее на это
введите описание изображения здесь

Сначала все работало, и Android Webview правильно загружал URL-адреса Mollie, но теперь пользователи на живом приложении сталкиваются с проблемой отображения пустой страницы, т.е. Webview загружает URL, но содержимое не отображается.

Ниже приведен BookingPaymentScreen, который загружает URL

@RequiresApi(Build.VERSION_CODES.O)
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun BookingPaymentScreen(
    navController: NavController,
    sharedViewModel: SharedViewModel,
    addBookingViewModel: AddBookingViewModel,
) {
    val context = LocalContext.current
    val scope = rememberCoroutineScope()
    val result = navController.previousBackStackEntry?.savedStateHandle?.get<Boolean>("isFromRetry")
    val resultUrl = navController.previousBackStackEntry?.savedStateHandle?.get<String>("url")
    val errorToastState = remember { mutableStateOf(false) }
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult(),
        onResult = { activityResult ->
            addBookingViewModel.retryCallMade = false
            val bookingDetail = BookingDetail(
                bookingId = addBookingViewModel.bookingFinalUiState.bookingId,
                ownerToken = addBookingViewModel.bookingFinalUiState.ownerToken,
                url = addBookingViewModel.bookingFinalUiState.paymentUrl
            )
            navController.currentBackStackEntry?.savedStateHandle?.set(
                key = "bookingDetail", value = bookingDetail
            )
            navController.navigate(BOOKING_DETAIL_ROUTE)
        })


    BackHandler {
        goBackBookingPaymentScreen(addBookingViewModel, navController)
    }
    // Убедитесь, что логика повторной попытки выполняется только один раз и после того, как компонент отображен
    LaunchedEffect(result) {
        if (result == true && !addBookingViewModel.retryCallMade) {
            addBookingViewModel.onEvent(AddBookingUiEvent.onRetryPayment)
            addBookingViewModel.retryCallMade = true
            sharedViewModel.logAnalyticsEvent(AnalyticsEvents.mlBookingsRetryPaymentBtnClicked)
        }
    }
    Surface(
        modifier = Modifier
            .background(
                color = PallaAppTheme.colors.primary
            )
            .fillMaxHeight()
            .statusBarsPadding()
    ) {
        if (errorToastState.value) {
            Toast.makeText(context, "Приложение не установлено или не настроено", Toast.LENGTH_LONG).show()
            errorToastState.value = false
        }

        if (addBookingViewModel.checkoutLoading) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(AppVariables.AppPading)
                    .background(PallaAppTheme.AppBackground), contentAlignment = Alignment.Center
            ) {
                CircularProgressIndicator(
                    modifier = Modifier.size(20.dp),
                    color = PallaAppTheme.colors.secondary,
                    strokeWidth = 2.dp
                )
            }
        } else {
            AndroidView(modifier = Modifier
                .fillMaxSize()
                .statusBarsPadding()
                .navigationBarsPadding(),
                factory = { context ->
                    WebView(context).apply {
                        settings.javaScriptEnabled = true
                        settings.domStorageEnabled = true
                        webViewClient = object : WebViewClient() {
                            override fun onPageCommitVisible(view: WebView?, url: String?) {
                                super.onPageCommitVisible(view, url)
                            }

                            override fun onPageStarted(
                                view: WebView?, url: String?, favicon: Bitmap?
                            ) {
                                super.onPageStarted(view, url, favicon)
                            }

                            override fun onPageFinished(view: WebView?, url: String?) {
                                super.onPageFinished(view, url)
                            }

                            override fun shouldOverrideUrlLoading(
                                view: WebView?, request: WebResourceRequest?
                            ): Boolean {
                                Log.d("shouldOverrideUrlLoading", request?.url.toString())
                                return handleOverrideUrlLoading(webView = view,
                                    url = request?.url.toString(),
                                    addBookingViewModel,
                                    navController,
                                    context,
                                    launcher,
                                    onBankingAppNotFound = {
                                        errorToastState.value = true
                                    })
                            }

                            @Deprecated("Устарело в Java")
                            override fun shouldOverrideUrlLoading(
                                view: WebView?, url: String?
                            ): Boolean {
                                Log.d("shouldOverrideUrlLoading", "$url")
                                return handleOverrideUrlLoading(webView = view,
                                    url = url,
                                    addBookingViewModel,
                                    navController,
                                    context,
                                    launcher,
                                    onBankingAppNotFound = {
                                        errorToastState.value = true
                                    })
                            }

                            override fun onReceivedError(
                                view: WebView?,
                                request: WebResourceRequest?,
                                error: WebResourceError?
                            ) {
                                Log.d("onReceivedError", error.toString())
                                val error1 = error
                                val abc = "fddg"
                                super.onReceivedError(view, request, error)
                            }

                            override fun onReceivedSslError(
                                view: WebView?,
                                handler: SslErrorHandler?,
                                error: SslError?
                            ) {
                                Log.d("onReceivedSslError", error.toString())

                                // игнорировать ssl ошибку
                                if (handler != null) {
                                    handler.proceed()
                                } else {
                                    super.onReceivedSslError(view, null, error)
                                }
                            }
                        }
                        // WebChromeClient для логов консоли JavaScript
                        webChromeClient = object : WebChromeClient() {
                            override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
                                Log.d("WebView Console", "${consoleMessage.message()} -- из строки " +
                                        "${consoleMessage.lineNumber()} из ${consoleMessage.sourceId()}")
                                return super.onConsoleMessage(consoleMessage)
                            }
                        }
                        var url = addBookingViewModel.bookingFinalUiState.paymentUrl
                        if (url.isEmpty()) {
                            url = resultUrl ?: ""
                        }
                        loadUrl(url)
                    }
                },
                update = { webView ->
                    // Обновите WebView, если это необходимо
                    //CookieManager.getInstance()?.setAcceptThirdPartyCookies(webView, true)
                })
        }

    }
}

fun goBackBookingPaymentScreen(
    addBookingViewModel: AddBookingViewModel, navController: NavController
) {
    addBookingViewModel.retryCallMade = false
    val bookingDetail = BookingDetail(
        bookingId = addBookingViewModel.bookingFinalUiState.bookingId,
        ownerToken = addBookingViewModel.bookingFinalUiState.ownerToken
    )
    navController.currentBackStackEntry?.savedStateHandle?.set(
        key = "bookingDetail", value = bookingDetail
    )
    navController.navigate(BOOKING_DETAIL_ROUTE)
}

fun isDeepLink(url: String?): Boolean {
    if ((url?.startsWith("nl") == true && url.contains("payloadUri"))) {
        return true
    }
    if (url?.startsWith("http") == false && !url.startsWith("https") && url.contains("://")) {
        return true
    }
    return false
}

//СТАРАЯ ФУНКЦИЯ С ГЛУБОКИМ СССЫЛКИ

private fun handleOverrideUrlLoading(
    webView: WebView?,
    url: String?,
    addBookingViewModel: AddBookingViewModel,
    navController: NavController,
    context: Context,
    launcher: ManagedActivityResultLauncher<Intent, ActivityResult>,
    onBankingAppNotFound: () -> Unit
): Boolean {
    if (url != null && url.startsWith(AppDeepLinks.COURT_BOOKING)) {
        addBookingViewModel.retryCallMade = false
        val bookingDetail = BookingDetail(
            bookingId = addBookingViewModel.bookingFinalUiState.bookingId,
            ownerToken = addBookingViewModel.bookingFinalUiState.ownerToken,
            url = addBookingViewModel.bookingFinalUiState.paymentUrl
        )
        navController.currentBackStackEntry?.savedStateHandle?.set(
            key = "bookingDetail", value = bookingDetail
        )
        navController.navigate(BOOKING_DETAIL_ROUTE)
        return true
    }
    if (isIntentLink(url)) {
        handleBankingAppIntent(url ?: "", context, launcher, onBankingAppNotFound = {
            onBankingAppNotFound()
        })
        return true
    }
    if (isDeepLink(url)) {
        try {
            val intent = Intent(ACTION_VIEW, Uri.parse(url))
            //context.startActivity(intent)
            launcher.launch(intent)
        } catch (e: ActivityNotFoundException) {
            // Доступны только приложения браузеров или браузер является значением по умолчанию.
            // Поэтому вы можете открыть URL-адрес напрямую в своем приложении, например,
            e.printStackTrace()
            onBankingAppNotFound()
        }
        return true
    }
    url?.let {
        webView?.loadUrl(it)
        return true
    }
    return true
}

fun isIntentLink(url: String?): Boolean {
    if ((url?.startsWith("intent://") == true)) {
        return true
    }
    return false
}


/*// В некоторых случаях Mollie создает неправильный Intent Android Chrome (с маленькой буквы)
// https://developer.chrome.com/multidevice/android/intents
private fun String.fixedIntentName(): String = replace("#intent;", "#Intent;")*/
private fun handleBankingAppIntent(
    url: String, context: Context,
    launcher: ManagedActivityResultLauncher<Intent, ActivityResult>,
    onBankingAppNotFound: () -> Unit
) {
    try {
        val intent = Intent.parseUri(
            url, Intent.URI_INTENT_SCHEME
        )
        try {
            // context.startActivity(intent)
            launcher.launch(intent)
        } catch (e: ActivityNotFoundException) {
            // Доступны только приложения браузеров или браузер является значением по умолчанию.
            // Поэтому вы можете открыть URL-адрес напрямую в своем приложении, например,
            e.printStackTrace()
            onBankingAppNotFound()
        }
    } catch (e: URISyntaxException) {
        e.printStackTrace()
        onBankingAppNotFound()
    }
}

Я пробовал настройки Webview, такие как начальный масштаб, масштабирование, прокрутка, статическая высота/ширина и разные строки пользовательского агента, но ничего не работает. Такие же URL-адреса платежей Mollie работают в приложении iOS.

settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
CookieManager.getInstance().setAcceptThirdPartyCookies(this, true)
settings.userAgentString="Mozilla/5.0 (Linux; Android 6.0.1; SM-J500M Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36"

Любая помощь будет оценена.

Ответ или решение

Проблема с отображением URL-адреса Mollie в Android WebView

Введение

В данном руководстве мы рассматриваем проблему, с которой столкнулись разработчики приложений на Android, использующих WebView для загрузки страниц Молли (Mollie) для обработки платежей. Хотя первоначально всё работало корректно, пользователи вашей живой версии приложения сейчас сталкиваются с ситуацией, когда веб-страница просто не загружается. Это может быть вызвано несколькими факторами, включая настройки WebView, ошибки на стороне сервера или изменения в URL-адресе. В этой статье мы детально разберём вашу ситуацию и предложим возможные решения.

Описание проблемы

В вашей реализации используется Android WebView для загрузки URL адреса платежной системы Mollie, который формируется следующим образом:

https://www.mollie.com/checkout/select-method/SOMECODESPECIFICTOPAYMENT

Несмотря на то, что URL корректно загружается, пользователи видят пустую страницу. Важно отметить, что этот URL успешно работает в приложении iOS, что указывает на потенциальные проблемы, специфичные для Android WebView.

Возможные причины проблемы

  1. Блокировка JavaScript: Если JavaScript не будет включен, страницы, требующие выполнения скриптов, могут не загружаться должным образом.
  2. Настройки безопасности: Некоторые настройки, такие как mixedContentMode и setAcceptThirdPartyCookies, могут быть неправильно настроены, что приведёт к блокировке контента.
  3. Проблемы с кэшированием: Временные файлы и кэш WebView могут содержать старые данные, которые могут создавать конфликты.
  4. Проблемы на сервере: Это может быть вызвано изменениями на стороне Mollie в API или политике загрузки контента.
  5. Разные версии WebView: Разные устройства могут использовать разные версии WebView, что может сказаться на совместимости.

Рекомендации и решения

  1. Проверьте настройки WebView:

    • Убедитесь, что JavaScript включен:

      settings.javaScriptEnabled = true
    • Убедитесь, что включено принятие сторонних файлов cookie:

      CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
    • Проверьте настройки для Mixed Content:

      settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
  2. Сбросьте кэш WebView:
    Очищайте кэш WebView перед загрузкой нового URL, чтобы избежать загрузки устаревших данных:

    webView.clearCache(true)
  3. Логирование ошибок:
    Добавьте более обширное логирование ошибок, чтобы выяснить, какая именно ошибка возникает при получении страниц:

    override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
        Log.e("WebView Error", "Error loading page: ${error?.description}")
    }
  4. Тестирование User-Agent:
    Попробуйте изменить строку User-Agent, чтобы убедиться, что сервер Mollie не блокирует ваш WebView:

    settings.userAgentString = "Mozilla/5.0 (Linux; Android 6.0.1; SM-J500M Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36"
  5. Обновление WebView:
    Убедитесь, что у вас установлена последняя версия WebView. Иногда проблемы могут устраняться через обновления приложения.

  6. Документация от Mollie:
    Обратитесь к официальной документации Mollie и службы поддержки, чтобы убедиться, что нет изменений на стороне сервера, о которых вы не знаете.

Заключение

Проблема с отображением URL-адреса Mollie в Android WebView может быть вызвана множеством факторов. Применение вышеуказанных решений должно помочь в диагностике и исправлении ситуации. Если проблема сохраняется, вы можете рассмотреть возможность использования внешнего браузера для обработки платежей, вдобавок к вашему текущему решению. Обязательно продолжайте отслеживать производительность вашего приложения и собирайте отзывы пользователей для дальнейших улучшений.

SEO Оптимизация

  • Ключевые слова: Android WebView, Mollie, платежная система, Kotlin, JavaScript, проблемы с отображением.
  • Заголовки и подзаголовки используют простые и понятные формулировки.
  • Приведение примеров кода для лучшего понимания тем.
Оцените материал
Добавить комментарий

Капча загружается...