Autentica con una Confirmación de pago seguro

Los comercios pueden usar la Confirmación de pago seguro (SPC) como parte de un proceso de autenticación reforzada de clientes (SCA) para una tarjeta de crédito o cuenta bancaria determinada. WebAuthn realiza la autenticación (a menudo a través de datos biométricos). WebAuthn se debe registrar con anticipación. Para obtener más información, consulta Cómo registrar una confirmación de pago segura.

Cómo funciona una implementación típica

El uso más común de la 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 requiere 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 pregunta a la entidad emisora o al banco correspondiente de la credencial de pago (entidad de confianza o RP) si el pagador necesita una autenticación independiente. Este intercambio puede ocurrir, por ejemplo, con EMV® 3-D Secure.
    • Si el RP desea que el comercio use SPC y 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 seguir completando la transacción.
  3. Si se necesita una autenticación, el comercio determina si el navegador admite SPC.
    • Si el navegador no admite SPC, continúa con el flujo de autenticación existente.
  4. El comercio invoca SPC. El navegador muestra un diálogo de confirmación.
    • Si no se pasan IDs de credenciales desde el RP, recurre al flujo de autenticación existente. Después de una autenticación correcta, considera usar el registro de SPC para optimizar las autenticaciones futuras.
  5. El usuario desbloquea el dispositivo para confirmar y autenticar el importe y el destino del pago.
  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 muestra al usuario un mensaje para indicar si el pago se realizó correctamente o no.

Detección de atributos

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 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 los 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) Es el nombre del beneficiario.
payeeOrigin Es el origen del beneficiario. En el caso mencionado anteriormente, es el origen del comercio.
instrument Una cadena para displayName y una URL para icon que apunte a un recurso de imagen. Es un valor booleano opcional (el valor predeterminado es true) para iconMustBeShown que especifica que se debe recuperar y mostrar un ícono correctamente 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 "payment".

Consulta 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 que los verifique el RP.

La credencial resultante se debe transferir entre orígenes al RP y verificar.

Cómo verifica la transacción el RP

La verificación de los datos de la transacción en el servidor de 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 la aserción de autenticación de WebAuthn. Además, deben verificar el payment.

Ejemplo de carga útil 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 debería haber mostrado al usuario.
  • El 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.';
}

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

Próximos pasos