Autentica con una Confirmación de pago seguro

Eiji Kitamura
Eiji Kitamura

Los comercios pueden utilizar la Confirmación de pago segura (SPC) como parte de un proceso sólido de autenticación de clientes (SCA) para una tarjeta de crédito o cuenta bancaria determinadas. WebAuthn realiza la autenticación (con frecuencia, mediante datos biométricos). WebAuthn se debe registrar con anticipación. Obtén más información en Registra una confirmación de pago seguro.

Cómo funciona una implementación típica

El uso más común de las SPC es cuando un cliente realiza una compra en el sitio de un comercio y el banco o la entidad emisora de la tarjeta de crédito requieren la autenticación del pagador.

Flujo de trabajo de autenticación.

Veamos el flujo de autenticación:

  1. Un cliente proporciona sus credenciales de pago (como la información de la tarjeta de crédito) al comercio.
  2. El comercio le solicita al banco o al emisor correspondiente de la credencial de pago (el remitente o el RP) si el pagador necesita otra autenticación. Este intercambio puede ocurrir, por ejemplo, con EMV® 3-D Secure.
    • Si el RP desea que el comercio use SPC, y si el usuario ya se registró, el RP responde con una lista de IDs de credenciales registrados por el pagador y un desafío.
    • Si no se necesita una autenticación, el comercio puede continuar completando la transacción.
  3. Si se necesita autenticación, el comercio determina si el navegador admite SPC.
    • Si el navegador no es compatible con SPC, continúa con el flujo de autenticación existente.
  4. El comercio invoca a SPC. El navegador muestra un diálogo de confirmación.
    • Si no se pasan IDs de credenciales del RP, recurre al flujo de autenticación existente. Después de una autenticación exitosa, considera usar el registro de SPC para optimizar las autenticaciones futuras.
  5. El usuario desbloquea y autentica el importe y el destino del pago desbloqueando el dispositivo.
  6. El comercio recibe una credencial de la autenticación.
  7. El RP recibe la credencial del comercio y verifica su autenticidad.
  8. El RP envía los resultados de la verificación al comercio.
  9. El comercio le muestra al usuario un mensaje para indicar si el pago se realizó correctamente o no.

Detección de atributos

Para detectar si el navegador es compatible con SPC, puedes enviar una llamada falsa a canMakePayment().

Copia y pega el siguiente código para detectar las SPC en el sitio web de un comercio.

const isSecurePaymentConfirmationSupported = async () => {
  if (!'PaymentRequest' in window) {
    return [false, 'Payment Request API is not supported'];
  }

  try {
    // The data below is the minimum required to create the request and
    // check if a payment can be made.
    const supportedInstruments = [
      {
        supportedMethods: "secure-payment-confirmation",
        data: {
          // RP's hostname as its ID
          rpId: 'rp.example',
          // A dummy credential ID
          credentialIds: [new Uint8Array(1)],
          // A dummy challenge
          challenge: new Uint8Array(1),
          instrument: {
            // Non-empty display name string
            displayName: ' ',
            // Transparent-black pixel.
            icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==',
          },
          // A dummy merchant origin
          payeeOrigin: 'https://non-existent.example',
        }
      }
    ];

    const details = {
      // Dummy shopping details
      total: {label: 'Total', amount: {currency: 'USD', value: '0'}},
    };

    const request = new PaymentRequest(supportedInstruments, details);
    const canMakePayment = await request.canMakePayment();
    return [canMakePayment, canMakePayment ? '' : 'SPC is not available'];
  } catch (error) {
    console.error(error);
    return [false, error.message];
  }
};

isSecurePaymentConfirmationSupported().then(result => {
  const [isSecurePaymentConfirmationSupported, reason] = result;
  if (isSecurePaymentConfirmationSupported) {
    // Display the payment button that invokes SPC.
  } else {
    // Fallback to the legacy authentication method.
  }
});

Autentica al usuario

Para autenticar al usuario, invoca el método PaymentRequest.show() con los parámetros secure-payment-confirmation y WebAuthn:

Estos son los parámetros que debes proporcionar a la propiedad data de la forma de pago, SecurePaymentConfirmationRequest.

Parámetro Descripción
rpId El nombre de host del origen de la parte restringida como ID de la parte restringida.
challenge Un desafío aleatorio que evita ataques de repetición.
credentialIds Un array de IDs de credenciales. En la autenticación de WebAuthn, la propiedad allowCredentials acepta un array de objetos PublicKeyCredentialDescriptor, pero en SPC, solo pasas una lista de IDs de credenciales.
payeeName (opcional) Nombre del beneficiario.
payeeOrigin El origen del beneficiario. En el caso anterior, se trata del origen del comercio.
instrument Es una cadena para displayName y una URL para icon que apunta a un recurso de imagen. Un valor booleano opcional (el valor predeterminado es true) para iconMustBeShown que especifica que un ícono se debe recuperar correctamente y mostrar para que la solicitud se realice correctamente.
timeout Tiempo de espera para firmar la transacción en milisegundos
extensions Se agregaron extensiones a la llamada de WebAuthn. No tienes que especificar la extensión de pago por tu cuenta.

Mira este código de ejemplo:

// After confirming SPC is available on this browser via a feature detection,
// fetch the request options cross-origin from the RP server.
const options = fetchFromServer('https://rp.example/spc-auth-request');
const { credentialIds, challenge } = options;

const request = new PaymentRequest([{
  // Specify `secure-payment-confirmation` as payment method.
  supportedMethods: "secure-payment-confirmation",
  data: {
    // The RP ID
    rpId: 'rp.example',

    // List of credential IDs obtained from the RP server.
    credentialIds,

    // The challenge is also obtained from the RP server.
    challenge,

    // A display name and an icon that represent the payment instrument.
    instrument: {
      displayName: "Fancy Card ****1234",
      icon: "https://rp.example/card-art.png",
      iconMustBeShown: false
    },

    // The origin of the payee (merchant)
    payeeOrigin: "https://merchant.example",

    // The number of milliseconds to timeout.
    timeout: 360000,  // 6 minutes
  }
}], {
  // Payment details.
  total: {
    label: "Total",
    amount: {
      currency: "USD",
      value: "5.00",
    },
  },
});

try {
  const response = await request.show();

  // response.details is a PublicKeyCredential, with a clientDataJSON that
  // contains the transaction data for verification by the issuing bank.
  // Make sure to serialize the binary part of the credential before
  // transferring to the server.
  const result = fetchFromServer('https://rp.example/spc-auth-response', response.details);
  if (result.success) {
    await response.complete('success');
  } else {
    await response.complete('fail');
  }
} catch (err) {
  // SPC cannot be used; merchant should fallback to traditional flows
  console.error(err);
}

La función .show() genera un objeto PaymentResponse, excepto que details contiene una credencial de clave pública con un clientDataJSON que contiene los datos de la transacción (payment) para la verificación por parte del RP.

La credencial resultante se debe transferir de origen cruzado al RP y verificar.

Cómo verifica la transacción el RP

Verificar los datos de las transacciones en el servidor del RP es el paso más importante del proceso de pago.

Para verificar los datos de la transacción, el RP puede seguir el proceso de verificación de aserción de autenticación de WebAuthn. Además, debe verificar el payment.

Una carga útil de ejemplo de clientDataJSON:

{
  "type":"payment.get",
  "challenge":"SAxYy64IvwWpoqpr8JV1CVLHDNLKXlxbtPv4Xg3cnoc",
  "origin":"https://spc-merchant.glitch.me",
  "crossOrigin":false,
  "payment":{
    "rp":"spc-rp.glitch.me",
    "topOrigin":"https://spc-merchant.glitch.me",
    "payeeOrigin":"https://spc-merchant.glitch.me",
    "total":{
      "value":"15.00",
      "currency":"USD"
    },
    "instrument":{
      "icon":"https://cdn.glitch.me/94838ffe-241b-4a67-a9e0-290bfe34c351%2Fbank.png?v=1639111444422",
      "displayName":"Fancy Card 825809751248"
    }
  }
}
  • El rp coincide con el origen del RP.
  • El topOrigin coincide con el origen de nivel superior que espera el RP (el origen del comercio en el ejemplo anterior).
  • El payeeOrigin coincide con el origen del beneficiario que se debería haber mostrado al usuario.
  • El total coincide con el importe de la transacción que se le debería haber mostrado al usuario.
  • El instrument coincide con los detalles del instrumento de pago que se le debería haber mostrado al usuario.
const clientData = base64url.decode(response.clientDataJSON);
const clientDataJSON = JSON.parse(clientData);

if (!clientDataJSON.payment) {
  throw 'The credential does not contain payment payload.';
}

const payment = clientDataJSON.payment;
if (payment.rp !== expectedRPID ||
    payment.topOrigin !== expectedOrigin ||
    payment.payeeOrigin !== expectedOrigin ||
    payment.total.value !== '15.00' ||
    payment.total.currency !== 'USD') {
  throw 'Malformed payment information.';
}

Después de pasar todos los criterios de verificación, el RP puede indicarle al comercio que la transacción se realizó correctamente.

Próximos pasos