Autentica con una Confirmación de pago seguro

Los comercios pueden utilizar la confirmación de pago segura (SPC) como parte de un proceso de autenticación sólida de clientes (SCA) para una tarjeta de crédito o cuenta bancaria determinadas. WebAuthn realiza la autenticación (frecuentemente mediante datos biométricos). WebAuthn debe registrarse con anticipación. Puedes obtener más información en Registra una confirmación de pago segura.

Cómo funciona una implementación típica

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

Flujo de trabajo de autenticación

Analicemos 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 pregunta al banco o la entidad emisora de la credencial de pago correspondiente (parte de confianza o RP) si el pagador necesita una autenticación diferente. Este intercambio puede ocurrir, por ejemplo, con EMV® 3-D Secure.
    • Si el RP quiere que el comercio use SPC, y si el usuario se registró con anterioridad, el RP responde con una lista de IDs de credenciales registradas 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 una autenticación, el comercio determina si el navegador es compatible con 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 cuadro de diálogo de confirmación.
    • Si no hay IDs de credenciales pasados desde la 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 confirma 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. La parte restringida envía los resultados de la verificación al comercio.
  9. El comercio muestra al usuario un mensaje para indicar si el pago se realizó correctamente o no.

Detección de funciones

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

Copia y pega el siguiente código para detectar la 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 del RP como ID de RP.
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 se pasa una lista de IDs de credenciales.
payeeName (opcional) Nombre del beneficiario.
payeeOrigin Indica el origen del beneficiario. En el caso anterior, se trata del origen del comercio.
instrument 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 un ícono que se debe recuperar y mostrar correctamente para que la solicitud se realice correctamente.
timeout Tiempo de espera para firmar la transacción en milisegundos
extensions Se agregaron las extensiones a la llamada de WebAuthn. No es necesario que especifiques 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() da como resultado 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 del RP.

La credencial resultante debe transferirse de origen cruzado al RP y verificarse.

Cómo la parte restringida verifica la transacción

El paso más importante del proceso de pago es verificar los datos de la transacción en el servidor de RP.

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 le 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 elemento instrument coincide con los detalles del instrumento de pago que se le deberían 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.';
}

Una vez que se aprueban todos los criterios de verificación, el RP puede indicarle al comercio que la transacción se realizó correctamente.

Próximos pasos