Integración con aplicación móvil
Recomendación general
⚠️ No se recomienda el uso de WebView para el proceso de pago. Aunque técnicamente es posible integrar el checkout dentro de un
WebView, no es la opción más recomendada.
Se recomienda utilizar browsers reales del sistema, ya que usan el motor nativo, comparten cookies con el browser del dispositivo y mantienen correctamente el contexto del usuario durante redirecciones (3DS, autenticaciones, wallets).
¿Por qué no WebView?
- Restricciones en acciones del usuario (autoplay, popups, wallets).
- Limitaciones en redirecciones entre dominios.
- Comportamientos inconsistentes entre versiones del sistema operativo.
- Mayor superficie de mantenimiento.
Integración con Expo / React Native
Una vez hayas obtenido una sesión de pago de tu servicio de backend, puedes iniciar el proceso de pago en la aplicación móvil. A diferencia de Android nativo, en Expo tienes tres opciones para abrir la processUrl, cada una con distintos niveles de compatibilidad y experiencia de usuario.
Browser in-app (recomendada)
Usa expo-web-browser para abrir Chrome Custom Tabs (Android) o SFSafariViewController (iOS) sin salir de la app. Soporta 3DS y comparte las cookies del sistema.
npx expo install expo-web-browser
import * as WebBrowser from 'expo-web-browser'
const result = await WebBrowser.openBrowserAsync(processUrl)
Browser externo
Abre la URL en el browser por defecto del dispositivo. Usa deep links con tu returnUrl y cancelUrl para que la app sea notificada al finalizar.
import { Linking } from 'react-native'
await Linking.openURL(processUrl)
WebView (no recomendada)
npx expo install react-native-webview
import { WebView } from 'react-native-webview'
;<WebView
source={{ uri: processUrl }}
javaScriptEnabled={true} // Requerido para ejecutar los scripts del checkout.
domStorageEnabled={true} // Mantiene el estado de la sesión entre redirecciones.
thirdPartyCookiesEnabled={true} // Necesario para 3DS y otras redirecciones de autenticación.
mediaPlaybackRequiresUserAction={false} //Solo si requieres reproducir video. Permite autoplay de video sin interacción del usuario. En iOS también requiere el atributo `muted` en el video.
allowsInlineMediaPlayback={true} // Usar junto con `mediaPlaybackRequiresUserAction={false}`.
/>
Ejemplo de integración
Antes de integrar en tu app, puedes validar el comportamiento del checkout con las siguientes herramientas. Todas permiten ingresar la URL de la sesión y abrirla en distintos modos de navegación (WebView, browser externo y browser in-app).
El punto de entrada es App.js. La lógica del browser in-app (opción recomendada) está en el modo inapp, que usa WebBrowser.openBrowserAsync.
También está disponible un Snack en Expo para ejecución rápida sin configuración local.
Integración con Android (Kotlin)
Una vez hayas obtenido una sesión de pago de tu servicio de backend, puedes iniciar el proceso de pago en la aplicación móvil. Consulta la recomendación general para elegir el modo de navegación más adecuado.
Si decides usar WebView, puedes cargar la processUrl con el método loadUrl de la clase WebView. Es importante que no olvides permitir tanto el JavaScript como las Cookies para el correcto funcionamiento de la sesión de pago, de lo contrario esta no permitirá avanzar en el proceso.
Configurar la WebView
Estas configuraciones ayudan a optimizar y personalizar la experiencia de navegación dentro del WebView.
Es importante que puedas identificar cuál es la URL de retorno y la URL de cancelación para poder cerrar el WebView una vez finalice el proceso de pago.
import android.annotation.SuppressLint
import android.view.ViewGroup
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.runtime.Composable
import androidx.compose.ui.viewinterop.AndroidView
import com.placetopay.p2pr.utilities.Constants
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun CheckoutWebView(processUrl: String, returnUrl: String, cancelUrl: String, refreshWebView: Boolean, onFinished: () -> Unit) {
AndroidView(factory = {
WebView(it).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
)
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
clearCache(true)
CookieManager.getInstance().setAcceptCookie(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().setAcceptThirdPartyCookies(this, true)
}
webChromeClient = WebChromeClient()
webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?, request: WebResourceRequest?
): Boolean {
if (request?.url.toString() == returnUrl || request?.url.toString() == cancelUrl)
onFinished()
return super.shouldOverrideUrlLoading(view, request)
}
}
loadUrl(processUrl)
}
}, update = {
it.loadUrl(processUrl)
if (refreshWebView) it.reload()
})
}