Saltar al contenido principal

Aceptar pagos en un WebView de una app móvil

Tu app es nativa (iOS, Android, Flutter, React Native) y quieres cobrar con la página de pago alojada. Tu backend crea el pago y tu app muestra su redirectUrl. Esta página explica cómo mostrar esa página dentro de la app, cómo devolver al cliente a la app y qué cambia en un WebView.

Recomendado: abre el checkout en el navegador integrado​

Abre la redirectUrl del pago en el navegador integrado del sistema, no en un WebView embebido:

  • iOS: SFSafariViewController. Apple Pay funciona en él igual que en Safari.
  • Android: Custom Tabs. Todas las funciones web del navegador están disponibles, incluidos los métodos de pago.
  • Flutter: url_launcher con LaunchMode.inAppBrowserView, que abre uno de los dos anteriores.
  • React Native (Expo): WebBrowser.openBrowserAsync, que abre uno de los dos anteriores.

No tienes que configurar nada para 3D Secure, Bizum, PayPal, Apple Pay ni Google Pay. El checkout funciona igual que en el navegador del móvil.

import SafariServices

let safari = SFSafariViewController(url: redirectUrl)
safari.delegate = self
present(safari, animated: true)

// SFSafariViewControllerDelegate
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
// Pide a tu backend el estado del pago.
}

Devuelve al cliente a tu app​

Cuando el pago termina, MONEI envía al cliente a la completeUrl del pago. Un pago fallido, cancelado o caducado va a la failUrl, si la configuras. MONEI añade a la URL el id y el status del pago. Puedes devolver al cliente de dos formas:

  • Una página de retorno con un botón. Configura completeUrl y failUrl con una página de tu web. La página muestra el resultado y un botón que abre tu app con un universal link (iOS), un app link (Android) o un esquema propio. Usa un botón, no una redirección automática: un navegador abre otra app de forma fiable solo cuando el cliente toca un enlace.
  • Una sesión de autenticación que se cierra sola. Configura completeUrl y failUrl con una URL con el esquema propio de tu app, por ejemplo myapp://checkout/complete. MONEI acepta URLs con esquema propio y redirige a ellas como a cualquier otra URL. Abre el checkout con una API que espere ese esquema. Cuando llega la redirección, la sesión se cierra y entrega la URL a tu app.
import AuthenticationServices

let session = ASWebAuthenticationSession(url: redirectUrl, callbackURLScheme: "myapp") { callbackURL, error in
// callbackURL es tu completeUrl o failUrl con el id y el estado del pago,
// o nil si el cliente cerró la ventana.
// Pide a tu backend el estado del pago.
}
session.presentationContextProvider = self
// Sin esto, iOS pide al cliente que permita a la app "iniciar sesión" con el sitio de pago.
session.prefersEphemeralWebBrowserSession = true
session.start()

Obtén el resultado de tu backend, no de la URL de retorno. El status de la URL le dice a tu app qué pantalla mostrar, pero no prueba que el pago se haya completado. Tu backend conoce el estado por el webhook a tu callbackUrl o por Get Payment.

Para la mejor experiencia con monederos en una app nativa, también puedes usar los SDK nativos: Apple Pay en apps iOS y Google Pay en apps Android.

Si usas un WebView embebido​

Un WebView embebido (WKWebView, WebView de Android, webview_flutter, react-native-webview) no es un navegador completo. Carga en él la redirectUrl. El checkout funciona, con estas diferencias.

Detecta el resultado​

La página de pago alojada no envía ningún mensaje a tu app. Cuando el pago termina, navega el WebView a tu completeUrl o failUrl. Intercepta esa navegación, cierra el WebView y pide a tu backend el estado del pago. No necesitas JavaScript en la página, así que Apple Pay sigue funcionando.

// WKNavigationDelegate
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url,
url.absoluteString.hasPrefix(returnUrlPrefix) {
decisionHandler(.cancel)
// Cierra el WebView y pide a tu backend el estado del pago.
return
}
decisionHandler(.allow)
}

returnUrlPrefix es el inicio de tu completeUrl y tu failUrl. Puede ser una página de tu web o una URL con esquema propio.

3D Secure​

Cuando la tarjeta necesita 3D Secure, la página de pago alojada lleva al cliente a la página de verificación de su banco en el mismo WebView. Cuando el cliente termina, el banco lo devuelve a la página de pago. No se abre nada en una ventana nueva.

  • Permite JavaScript en el WebView.
  • Permite la navegación a otros dominios HTTPS. Tu gestor de navegación solo debe detener tu URL de retorno, porque la página del banco está en el dominio del banco.
  • No ocultes ni recargues el WebView mientras se verifica la tarjeta. Si la página se oculta durante el primer paso de verificación, por ejemplo porque el cliente cambia de app, el intento de pago se cancela y el cliente debe volver a intentarlo.

Apple Pay​

  • WKWebView admite Apple Pay. Apple lo desactiva en las páginas donde tu app inyecta JavaScript (WKUserScript, evaluateJavaScript). Si tu app inyecta scripts en la página del checkout, el cliente no ve el botón de Apple Pay.
  • MONEI muestra el botón de Apple Pay solo cuando el navegador indica que Apple Pay está disponible (ApplePaySession.canMakePayments()). Si el WebView no lo admite, el botón no aparece y los demás métodos siguen disponibles.

Google Pay​

  • iOS: Google Pay no está disponible en los WebView de iOS. MONEI no muestra el botón.
  • Android: el WebView de Android admite Google Pay mediante la Payment Request API. Debes activarla:
// implementation("androidx.webkit:webkit:1.14.0") o posterior
if (WebViewFeature.isFeatureSupported(WebViewFeature.PAYMENT_REQUEST)) {
WebSettingsCompat.setPaymentRequestEnabled(webView.settings, true)
}
AndroidManifest.xml
<queries>
<intent><action android:name="org.chromium.intent.action.PAY" /></intent>
<intent><action android:name="org.chromium.intent.action.IS_READY_TO_PAY" /></intent>
<intent><action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS" /></intent>
</queries>

El dispositivo del cliente necesita Google Play services 25.18.30 o posterior y Android System WebView 137 o posterior. Consulta la guía de Android WebView de Google. En Flutter, webview_flutter_android ofrece el mismo ajuste (setPaymentRequestEnabled) y necesita las mismas entradas <queries>.

Bizum​

El cliente confirma el pago con Bizum en su aplicación bancaria. La página del checkout le pide que complete el pago en su aplicación bancaria y consulta el estado del pago hasta que cambia. No abre la aplicación bancaria por el cliente.

Mantén el WebView abierto y no lo recargues mientras el cliente está en su aplicación bancaria. Cuando vuelva a tu app, la página muestra el resultado.

Click to Pay​

Click to Pay de Visa no se ofrece en los WebView embebidos de iOS. Click to Pay de Mastercard sí se ofrece.

PayPal y ventanas nuevas​

El botón de PayPal usa el SDK de JavaScript de PayPal, que puede abrir una ventana nueva. Un WebView no abre ventanas nuevas salvo que tu app las gestione:

  • iOS: implementa webView(_:createWebViewWith:for:windowFeatures:) en tu WKUIDelegate.
  • Android: llama a settings.setSupportMultipleWindows(true) e implementa WebChromeClient.onCreateWindow.
  • Flutter: webview_flutter carga la ventana nueva en el mismo WebView. Si PayPal no se completa, usa el navegador integrado.

Un WebView no abre enlaces con un esquema que no sea HTTP (por ejemplo intent:// o el esquema de una app bancaria). Si una página de pago envía al cliente a otra app, intercepta el enlace y ábrelo con el sistema:

  • iOS: webView(_:decidePolicyFor:decisionHandler:) en tu WKNavigationDelegate.
  • Android: WebViewClient.shouldOverrideUrlLoading. Usa Intent.parseUri(url, Intent.URI_INTENT_SCHEME) para los enlaces intent://.
  • Flutter: NavigationDelegate.onNavigationRequest, devolviendo NavigationDecision.prevent después de abrir el enlace.
  • React Native: onShouldStartLoadWithRequest, devolviendo false después de abrir el enlace con Linking.openURL.

Preguntas frecuentes​

¿Puedo usar la página de pago alojada en un WebView embebido?​

Sí. Carga la redirectUrl en el WebView, intercepta la navegación a tu URL de retorno y revisa las demás diferencias del WebView embebido. El navegador integrado requiere menos trabajo y admite más métodos de pago.

¿La página de pago envía un mensaje a mi app?​

No. Cuando cargas la página de pago alojada directamente en un WebView, no envía ningún postMessage a tu app. Navega a tu completeUrl o failUrl. Intercepta esa navegación.

¿Puede completeUrl usar un esquema propio como myapp://?​

Sí. completeUrl, failUrl y cancelUrl aceptan URLs con esquema propio, y MONEI redirige a ellas añadiendo el id y el status del pago. Úsalas con una sesión de autenticación o con un WebView que intercepte la navegación.

¿Por qué no aparece el botón de Apple Pay o Google Pay en mi app?​

MONEI muestra un botón de monedero solo cuando el navegador indica que el monedero está disponible. En un WebView, revisa las condiciones de Apple Pay y Google Pay, o abre el checkout en el navegador integrado.