Autenticar com confirmação de pagamento seguro

Eiji Kitamura
Eiji Kitamura

Os comerciantes podem usar a confirmação de pagamento seguro (SPC, na sigla em inglês) como parte do processo de autenticação forte do cliente (SCA, na sigla em inglês) em um cartão de crédito ou conta bancária. O WebAuthn realiza a autenticação (geralmente usando biometria). O WebAuthn precisa ser registrado com antecedência. Saiba mais em Registrar uma confirmação de pagamento seguro.

Como funciona uma implementação típica

O uso mais comum do SPC é quando um cliente faz uma compra no site de um comerciante e o banco ou emissor do cartão de crédito exige autenticação do pagador.

Fluxo de trabalho de autenticação.

Vamos analisar o fluxo de autenticação:

  1. Um cliente fornece as credenciais de pagamento (como informações de cartão de crédito) ao comerciante.
  2. O comerciante pergunta ao emissor ou banco correspondente da credencial de pagamento (parte confiável ou RP) se o pagador precisa de uma autenticação separada. Essa troca pode acontecer, por exemplo, com o EMV® 3-D Secure.
    • Se a RP quiser que o comerciante use o SPC e se o usuário tiver feito o registro, a RP responderá com uma lista de IDs de credencial registrados pelo pagador e um desafio.
    • Se a autenticação não for necessária, o comerciante poderá continuar concluindo a transação.
  3. Se a autenticação for necessária, o comerciante determina se o navegador é compatível com SPC.
    • Se o navegador não for compatível com SPC, continue com o fluxo de autenticação atual.
  4. O comerciante invoca o SPC. O navegador exibe uma caixa de diálogo de confirmação.
    • Se nenhum ID de credencial for transmitido do RP, use o fluxo de autenticação atual. Após a autenticação, considere usar o registro de SPF para simplificar autenticações futuras.
  5. O usuário confirma e autentica o valor e o destino do pagamento desbloqueando o dispositivo.
  6. O comerciante recebe uma credencial da autenticação.
  7. A RP recebe a credencial do comerciante e verifica a autenticidade dele.
  8. A RP envia os resultados da verificação ao comerciante.
  9. O comerciante mostra ao usuário uma mensagem para indicar se o pagamento foi concluído ou não.

Detecção de recursos

Para detectar se o SPC oferece suporte ao navegador, envie uma chamada falsa para canMakePayment().

Copie e cole o código a seguir para usar o SPC de detecção no site de um comerciante.

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.
  }
});

Autenticar o usuário

Para autenticar o usuário, invoque o método PaymentRequest.show() com os parâmetros secure-payment-confirmation e WebAuthn:

Estes são os parâmetros que você precisa informar à propriedade data da forma de pagamento, SecurePaymentConfirmationRequest.

Parâmetro Descrição
rpId O nome do host da origem da RP como ID da RP.
challenge Um desafio aleatório que impede ataques repetidos.
credentialIds Uma matriz de IDs de credenciais. Na autenticação da WebAuthn, a propriedade allowCredentials aceita uma matriz de objetos PublicKeyCredentialDescriptor, mas na SPC você transmite apenas uma lista de IDs de credenciais.
payeeName (opcional) Nome do beneficiário.
payeeOrigin A origem do beneficiário. No cenário mencionado acima, é a origem do comerciante.
instrument Uma string para displayName e um URL para icon que aponta para um recurso de imagem. Um booleano opcional (o padrão é true) para iconMustBeShown que especifica que um ícone precisa ser buscado e exibido para que a solicitação seja atendida.
timeout Tempo limite para assinar a transação em milissegundos
extensions Extensões adicionadas à chamada do WebAuthn. Não é necessário especificar a extensão "payment".

Confira este código de exemplo:

// 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);
}

A função .show() resulta em um objeto PaymentResponse, exceto que details contém uma credencial de chave pública com um clientDataJSON que contém os dados da transação (payment) para verificação pela RP.

A credencial resultante precisa ser transferida de origem cruzada para a RP e verificada.

Como a parte restrita verifica a transação

A verificação dos dados da transação no servidor RP é a etapa mais importante do processo de pagamento.

Para verificar os dados da transação, a RP pode seguir o processo de verificação de declaração de autenticação do WebAuthn. Além disso, é necessário verificar o payment.

Um payload de exemplo do 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"
    }
  }
}
  • O rp corresponde à origem da RP.
  • O topOrigin corresponde à origem de nível superior esperada pela RP (origem do comerciante no exemplo acima).
  • O payeeOrigin corresponde à origem do beneficiário que deveria ter sido exibida para o usuário.
  • O total corresponde ao valor da transação que precisa ser mostrado ao usuário.
  • O instrument corresponde aos detalhes do instrumento de pagamento que precisam ser exibidos ao usuário.
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.';
}

Depois que todos os critérios de verificação forem aprovados, a RP poderá informar ao comerciante que a transação foi concluída.

Próximas etapas