Authentification avec confirmation de paiement sécurisé

Les marchands peuvent utiliser la confirmation de paiement sécurisée (SPC) dans le cadre d'un processus d'authentification forte du client (SCA) pour une carte de crédit ou un compte bancaire donnés. WebAuthn effectue l'authentification (souvent via la biométrie). WebAuthn doit être enregistré au préalable. Pour en savoir plus, consultez l'article Enregistrer une confirmation de paiement sécurisé.

Fonctionnement d'une implémentation type

Le cas d'utilisation le plus courant de la SPC est lorsqu'un client effectue un achat sur le site d'un marchand et que l'émetteur de la carte de crédit ou la banque exige l'authentification du payeur.

Flux d'authentification.

Voyons le flux d'authentification :

  1. Un client fournit ses identifiants de paiement (par exemple, les informations de sa carte de crédit) au marchand.
  2. Le marchand demande à l'émetteur ou à la banque correspondants des identifiants de paiement (partie de confiance ou tiers assujetti à des restrictions) si le payeur a besoin d'une authentification distincte. Cet échange peut se produire, par exemple, avec EMV® 3-D Secure.
    • Si le RP souhaite que le marchand utilise SPC et si l'utilisateur s'est déjà inscrit, le RP répond avec une liste d'ID d'identifiants enregistrés par le payeur et un défi.
    • Si aucune authentification n'est nécessaire, le marchand peut continuer à effectuer la transaction.
  3. Si une authentification est nécessaire, le marchand détermine si le navigateur est compatible avec le SPC.
    • Si le navigateur n'est pas compatible avec SPC, poursuivez le flux d'authentification existant.
  4. Le marchand appelle SPC. Le navigateur affiche une boîte de dialogue de confirmation.
    • Si aucun ID d'identifiants n'est transmis par le RP, utilisez le flux d'authentification existant. Une fois l'authentification réussie, envisagez d'utiliser l'enregistrement SPC pour simplifier les futures authentifications.
  5. L'utilisateur confirme et authentifie le montant et la destination du paiement en déverrouillant l'appareil.
  6. Le marchand reçoit un identifiant de l'authentification.
  7. Le RP reçoit les identifiants du marchand et vérifie leur authenticité.
  8. Le RP envoie les résultats de la validation au marchand.
  9. Le marchand présente à l'utilisateur un message indiquant si le paiement a abouti ou échoué.

Détection de fonctionnalités

Pour savoir si la fonctionnalité SPC est compatible avec le navigateur, vous pouvez envoyer un faux appel à canMakePayment().

Copiez et collez le code suivant pour détecter les produits soumis à une promotion sur le site Web d'un marchand.

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

Authentifier l'utilisateur

Pour authentifier l'utilisateur, appelez la méthode PaymentRequest.show() avec les paramètres secure-payment-confirmation et WebAuthn :

Voici les paramètres que vous devez fournir à la propriété data du mode de paiement, SecurePaymentConfirmationRequest.

Paramètre Description
rpId Nom d'hôte de l'origine de RP en tant qu'ID de RP.
challenge Défi aléatoire qui empêche les attaques par rejeu.
credentialIds Tableau d'ID d'identifiants. Dans l'authentification de WebAuthn, la propriété allowCredentials accepte un tableau d'objets PublicKeyCredentialDescriptor, mais dans SPC, vous ne transmettez qu'une liste d'ID d'identifiants.
payeeName (facultatif) Nom du bénéficiaire.
payeeOrigin Origine du bénéficiaire. Dans le scénario ci-dessus, il s'agit de l'origine du marchand.
instrument Chaîne pour displayName et URL pour icon pointant vers une ressource d'image. Valeur booléenne facultative (par défaut, true) pour iconMustBeShown qui spécifie qu'une icône doit être récupérée et affichée pour que la requête aboutisse.
timeout Délai avant expiration de la signature de la transaction en millisecondes
extensions Extensions ajoutées à l'appel WebAuthn. Vous n'avez pas besoin de spécifier vous-même l'extension "payment".

Consultez cet exemple de code :

// 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 fonction .show() génère un objet PaymentResponse, sauf que details contient des identifiants de clé publique avec un clientDataJSON contenant les données de transaction (payment) pour vérification par la RP.

Les identifiants obtenus doivent être transférés entre les origines vers le RP et validés.

Comment le RP valide-t-il la transaction ?

La vérification des données de transaction sur le serveur RP est l'étape la plus importante du processus de paiement.

Pour vérifier les données de transaction, la tiers assujetti à des restrictions peut suivre le processus de validation des assertions d'authentification de WebAuthn. De plus, il doit valider le payment.

Exemple de charge utile de l'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"
    }
  }
}
  • Le rp correspond à l'origine de la RP.
  • Le topOrigin correspond à l'origine de premier niveau attendue par le tiers assujetti à des restrictions (le point de départ du marchand dans l'exemple ci-dessus).
  • payeeOrigin correspond à l'origine du bénéficiaire qui aurait dû être affichée à l'utilisateur.
  • total correspond au montant de la transaction qui aurait dû être affiché à l'utilisateur.
  • L'instrument correspond aux détails du mode de paiement qui auraient dû être présentés à l'utilisateur.
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.';
}

Une fois que tous les critères de validation ont été remplis, le RP peut indiquer au marchand que la transaction a réussi.

Étapes suivantes