Payment Request API: consentimiento y alternativas seguras
Usa PaymentRequest con activación explícita, contexto seguro, cancelaciones visibles y una alternativa funcional sin recopilar credenciales.
BotBrowser Team
Quieres la documentación estructurada de Plataforma?
Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.
Payment Request puede abrir una hoja de pago controlada por el navegador, pero que show() termine no demuestra autorización ni que el comercio haya recibido fondos. BotBrowser puede reproducir fixtures propios y comprobar estados visibles; no proporciona instrumentos de pago reales, no aprueba operaciones bancarias y no procesa credenciales del usuario. Mantén límites claros: inicia la solicitud desde una acción explícita en un contexto seguro, no confundas una comprobación de capacidad con consentimiento, trata por separado cancelaciones y tiempos de espera, usa complete() solo para el estado final de la hoja y conserva un checkout normal cuando la API o los datos de pago no estén disponibles.
Resumen
new PaymentRequest(methodData, details, options)describe los métodos y detalles de la operación; no solicita credenciales ni garantiza que exista un método concreto.canMakePayment()es solo una pista de capacidad.trueno significa consentimiento, autorización, fondos disponibles ni queshow()vaya a funcionar.- Llama a
show()desde un clic visible o una acción de teclado en un contexto seguro. TrataAbortError,NotAllowedError, los tiempos de espera y los métodos no compatibles como estados normales del producto. - Tras validar la respuesta, cierra la hoja con
complete('success'),complete('fail')ocomplete('unknown').complete()informa del resultado de la interfaz, no de la liquidación. - Nunca registres credenciales, la respuesta de pago, datos de tarjeta ni excepciones completas. Conserva un formulario manual, una solicitud de factura u otra ruta sin PaymentRequest.
Contenido
Separar capacidad y consentimiento
Payment Request API permite describir métodos de pago y pedir al navegador que intermedie la elección del usuario. No sustituye al procesador ni sirve para leer una tarjeta o una cuenta de cartera. El navegador presenta la hoja y la persona decide si continúa; el comercio aún necesita autorización en servidor, idempotencia, controles antifraude y el resultado de liquidación del proveedor.
canMakePayment() puede decidir si muestras una entrada de pago rápido. El resultado puede ser false, true o un rechazo según políticas del navegador, métodos registrados, protección de privacidad y la implementación, y puede cambiar entre visitas. No lo uses para ocultar el checkout completo, inferir la cartera del usuario ni crear un perfil estable; solicítalo cuando la persona elija la ruta y dale una vida corta.
La detección de funciones también debe tener límites:
const paymentSupported = window.isSecureContext && 'PaymentRequest' in window;
if (!paymentSupported) showCheckoutFallback('unsupported');
Que exista el constructor no significa que todos los métodos sean compatibles. El documento puede no tener el foco, estar en un frame con políticas restrictivas o haber perdido la activación transitoria. Una actualización del navegador puede cambiar los métodos disponibles sin cambiar tu código. Trata cada intento como una observación nueva y no como un atributo permanente del usuario.
Para un límite de consentimiento cercano, Web Share y sus alternativas explica que completar una transferencia no demuestra que el receptor haya aceptado los datos; los límites de permisos de Web Share añaden que contexto y activación reciente también condicionan una llamada válida. Payment Request puede exponer una respuesta al comercio, pero el servidor y el procesador siguen a cargo de la liquidación. La guía de contextos seguros cubre la base HTTPS común a las funciones potentes del navegador.
Construir un PaymentRequest estrecho
Mantén mínimos los datos del método y los detalles de la operación. El identificador indica qué clase de método puede procesar el comercio. details ofrece un total visible y líneas opcionales, pero no es una fuente fiable de importes. Cuando llegue la respuesta, el servidor debe recalcular el importe final: precios, impuestos, inventario y moneda no pueden confiarse a la página.
const methodData = [
{
supportedMethods: 'https://example.test/pay',
data: { version: 1 },
},
];
const details = {
id: 'order-2026-1042',
total: { label: 'Example order', amount: { currency: 'USD', value: '19.00' } },
displayItems: [{ label: 'Item', amount: { currency: 'USD', value: '19.00' } }],
};
const options = { requestPayerName: false, requestPayerEmail: false };
const request = new PaymentRequest(methodData, details, options);
No pongas números de tarjeta, tokens de cartera, secretos ni identificadores personales en methodData o en la URL. Opciones como requestPayerEmail solo solicitan datos y no validan lo que vuelve. Pide únicamente los campos que necesita el pedido, explica el motivo y deja de conservarlos cuando el procesador reciba un token autorizado.
Haz que cada objeto request pertenezca a un solo intento. Si cambia el carrito, crea otro request con un details.id y una versión de pedido nuevos. No reutilices el objeto después de un timeout o una navegación. Un token de operación de corta duración permite que la interfaz rechace respuestas tardías de un carrito anterior.
Iniciar show con activación
request.show() abre la hoja de pago y devuelve una promesa con PaymentResponse. Inícialo directamente desde un botón visible o una activación de teclado. No lo llames durante la carga, desde un temporizador o un callback de analítica, ni esperes antes trabajo que pueda consumir la activación transitoria. El contexto seguro, las políticas, los límites del iframe o las reglas de la plataforma también pueden bloquear la hoja.
let activeAttempt = 0;
async function beginPayment() {
const attempt = ++activeAttempt;
if (!window.isSecureContext || !('PaymentRequest' in window)) {
showCheckoutFallback('unsupported');
return;
}
const request = buildRequest();
showPaymentState('opening');
try {
const response = await request.show();
if (attempt !== activeAttempt) return;
await handlePaymentResponse(response, request, attempt);
} catch (error) {
if (attempt !== activeAttempt) return;
showPaymentState(error?.name === 'AbortError' ? 'cancelled' : 'rejected');
showCheckoutFallback(error?.name === 'AbortError' ? 'cancelled' : 'rejected');
}
}
payButton.addEventListener('click', beginPayment);
Puedes desactivar activaciones duplicadas mientras la hoja está abierta, pero conserva una alternativa visible. Devuelve el foco al botón de origen tras una cancelación o rechazo. Anuncia opening, cancelled y fallback mediante una live region sin exponer la respuesta, para que la persona entienda el siguiente paso sin ver un error del navegador.
El checkout embebido requiere una decisión de despliegue explícita. Las políticas superiores o los atributos del iframe pueden decidir si el navegador permite pagos, pero delegar no elude permisos ni concede consentimiento. Prueba por separado el origen superior y el flujo embebido; no supongas que lo disponible en una pestaña superior funciona en un frame de otro origen.
Validar la respuesta y completar la hoja
PaymentResponse contiene datos propios del método, los campos payer solicitados y complete(). Tras validar su forma, envía al servidor únicamente el token aprobado por el procesador a través de un canal autenticado. Nunca trates la respuesta como un recibo: el procesador puede rechazar, retrasar o revocar la autorización.
async function handlePaymentResponse(response, request, attempt) {
showPaymentState('validating');
let outcome = 'unknown';
try {
const result = await postPaymentToken({
methodName: response.methodName,
details: response.details,
orderId: request.id,
attempt,
});
outcome = result.authorized === true ? 'success' : 'fail';
} catch {
outcome = 'unknown';
}
try {
await response.complete(outcome);
} catch {
// The payment sheet may already have closed; keep the server outcome.
}
showPaymentState(outcome);
}
Llama a complete() una sola vez por respuesta. success pide al navegador mostrar un estado completado, fail representa un intento fallido y unknown se usa cuando no hay resultado del servidor. Ninguna cadena demuestra un cargo, reembolso o entrega; el estado autoritativo procede de la respuesta del procesador y del registro idempotente del pedido.
response.complete() puede rechazar si la respuesta ya se cerró, el navegador terminó la hoja o el documento perdió relevancia. No conviertas ese rechazo en un segundo cargo. Conserva el resultado del servidor, muestra un reintento que cree un nuevo intento de pedido y evita enviar el mismo token dos veces.
Clasificar cancelación, rechazo y timeout
Una cancelación no es un fallo de tarjeta. La persona puede cerrar la hoja, cambiar de idea o rechazar un método. AbortError suele indicar cancelación, pero no explica el motivo. Separa cancelled de rejected, unsupported y processor-failed; conserva el carrito y ofrece un formulario manual sin pedir credenciales en los mensajes de diagnóstico.
Un timeout necesita un responsable claro. La página puede fijar un límite para la llamada al servidor, pero un temporizador no demuestra qué hizo la hoja. Si la llamada del servidor vence después de recibir una respuesta, marca unknown, detén los reenvíos y consulta el pedido al procesador antes de reintentar. Un callback tardío no debe sobrescribir un intento nuevo.
Los errores del método difieren de los errores del navegador. Un método ausente puede hacer que show() rechace antes de mostrar la hoja, mientras que una política o activación bloqueada puede producir NotAllowedError. Una respuesta mal formada pertenece a la fase de validación y el rechazo del procesador a la fase de servidor. Conserva estas fases en la interfaz y en soporte para no pedir otra vez la tarjeta por un problema de política del iframe.
Usa una secuencia acotada: idle, opening, shown, response-received, authorizing, success, fail, unknown, cancelled, rejected, unsupported y fallback. Registra solo la primera fase fallida de cada intento. No reintentes show() automáticamente tras cerrar la hoja ni llames a abort() desde una tarea de limpieza en segundo plano salvo que el producto sea explícitamente dueño de esa cancelación.
Datos y alternativa
La respuesta de pago puede contener un token sensible del método, datos payer, referencias de facturación o valores opacos que se convierten en credenciales al reproducirse. No pongas response.details, el correo payer, fragmentos de tarjeta ni excepciones completas en registros, URL, capturas, replay de sesión, informes de fallos o dimensiones analíticas. La desidentificación también debe cubrir integraciones fuera del punto de llamada de PaymentRequest.
{
"api": "PaymentRequest",
"attempt": "pay-7f31",
"capability": "available",
"outcome": "cancelled",
"methodNamePresent": true,
"paymentDetailsRetained": false,
"settlement": "not-asserted"
}
Este registro basta para saber si se abrió la hoja y qué resultado mostró la página, pero no si la persona tiene una cuenta ni si el procesador hizo un cargo. Los tokens deben ser aleatorios y breves; las exportaciones de diagnóstico deben caducar, y cualquier captura de soporte debe ser explícita y revisable. Hashear un token no lo vuelve inocuo: un valor estable aún puede vincular varias sesiones.
Diseñar una alternativa y separar liquidación.
Conserva la alternativa desde la primera pantalla. Si no existe PaymentRequest, canMakePayment() rechaza, show() se cancela, el método no es compatible o la respuesta del procesador es desconocida, deja disponible un formulario normal, una solicitud de factura, un enlace de pago o un contacto. La alternativa debe conservar el carrito, el acceso por teclado, las etiquetas, la validación y las instrucciones siguientes.
No desactives la alternativa porque canMakePayment() devuelva true. Sin un resultado autoritativo del procesador en el servidor, no muestres «pago completado» después de complete('success'). No confundas la hoja del navegador con Web Share, Credential Management o la interfaz de una cartera. Payment Request es una interfaz de checkout intermediada por el navegador, no un almacén de credenciales ni una forma de eludir al procesador.
En fixtures controlados usa identificadores de método sintéticos y totales de pedido ficticios. Comprueba que no se registran campos de credenciales, que el foco vuelve tras cancelar, que un resultado de servidor desconocido llama a complete('unknown') y que la ruta manual sigue disponible. Las credenciales reales y los endpoints del proveedor no deben entrar en fixtures de automatización.
Tabla de decisiones.
| Fase observada | Qué demuestra | Siguiente paso seguro |
|---|---|---|
| API ausente o contexto inseguro | No se puede iniciar Payment Request | Conserva checkout manual o factura |
canMakePayment() es false o rechaza | No hay una pista útil esta vez | Conserva el checkout principal; no infieras la cartera |
show() rechaza con AbortError | La hoja se canceló o cerró | Conserva el carrito, registra cancelled y espera una nueva acción |
| Política o activación bloqueada | El navegador rechazó el intento | Revisa contexto seguro, foco, política del frame y responsable del gesto |
| Se recibe una respuesta | El navegador devolvió datos del método | Valida estrictamente y envía al servidor solo datos de token aprobados |
| El servidor rechaza la autorización | El procesador no autorizó el intento | Llama a complete('fail'), explica la alternativa y evita reintentos duplicados |
| Timeout del resultado del servidor | El estado de liquidación es desconocido | Si procede, llama a complete('unknown') y consulta antes de reintentar |
complete('success') termina | La hoja aceptó la solicitud de UI | Muestra solo el estado autoritativo del pedido |
Capacidad y límite de BotBrowser.
Un contexto controlado de BotBrowser puede reproducir un fixture de PaymentRequest del mismo origen con métodos sintéticos, ejecutar ramas de capacidad y activación, simular cancelaciones o timeouts y comprobar resultados visibles desidentificados y la alternativa disponible. La documentación de capacidades avanzadas describe los límites propios de la página. BotBrowser no proporciona instrumentos reales, no aprueba operaciones bancarias o de cartera, no demuestra liquidación ni recopila credenciales para las pruebas.
Conserva un registro determinista del fixture:
{
"api": "PaymentRequest",
"secureContext": true,
"userActivation": true,
"canMakePayment": "hint-only",
"show": "cancelled",
"complete": "unknown",
"paymentDetailsRetained": false,
"fallback": "manual-checkout"
}
Repite la prueba cambiando una sola variable cada vez: versión del navegador, origen superior, política del iframe, momento de activación, método sintético, timeout del servidor o rama de cancelación. Compara la primera diferencia visible. Un fixture solo demuestra el manejo del estado de la página; no demuestra autorización ni movimiento de fondos.
Fuentes
- W3C Payment Request API
- MDN:Payment Request API
- MDN:PaymentRequest.show()
- MDN:PaymentRequest.canMakePayment()
- MDN:PaymentResponse.complete()
- MDN: contextos seguros
- BotBrowser: capacidades avanzadas
Conclusión práctica
BotBrowser puede reproducir un fixture sintético de PaymentRequest y comprobar activación, indicios de capacidad, cancelación y alternativa; no puede proporcionar un instrumento real ni demostrar una liquidación.
Trata PaymentRequest únicamente como una interfaz de consentimiento intermediada por el navegador. Construye una solicitud mínima, llama a show() desde una acción de usuario reciente en un contexto seguro, valida la respuesta en el servidor y deja que complete() gestione solo el estado de la hoja, sin afirmar liquidación. Distingue cancelación, rechazo, timeout y método no compatible, registra resultados desidentificados y conserva un checkout completo sin PaymentRequest. Un fixture controlado de BotBrowser demuestra ramas visibles de la página, no un cargo real.
Este artículo forma parte de la guía: Superficies de la plataforma web: mapa de revisión
Artículos Relacionados
Web Share API: límites de permisos y respaldos seguros
PlataformaWeb Share API y uso compartido iniciado por la persona
PlataformaPresentation API: límites de pantalla y sesión
PlataformaWeb MIDI API: permisos y privacidad del dispositivo
PlataformaEyeDropper API: activación y privacidad del color
PlataformaIdle Detection API: control, permisos y privacidad
Lleva BotBrowser de la investigación a producción
Usa estas guías para entender el modelo y después avanzar hacia validación multiplataforma, contextos aislados y despliegue de navegador preparado para escalar.