Signatur
Beschreibung
SecurePaymentConfirmationRequest ist ein Wörterbuch (Dictionary), keine instanziierbare Klasse. Man übergibt es im Feld data eines Eintrags in methodData an den Konstruktor new PaymentRequest(), wenn supportedMethods den Wert "secure-payment-confirmation" hat. Damit lässt sich die Payment Request API zur starken Kundenauthentifizierung (z. B. nach PSD2/3-D Secure) während eines Bezahlvorgangs nutzen.
Secure Payment Confirmation (SPC) baut auf WebAuthn auf: Der Nutzer hat zuvor bei seiner Bank bzw. beim Zahlungsdienstleister (der Relying Party) ein Passkey-/FIDO-Credential mit der Erweiterung payment registriert. Beim Bezahlen zeigt der Browser einen eigenen, vertrauenswürdigen Dialog mit Händler, Betrag und Zahlungsmittel (Name und Symbol der Karte) an. Bestätigt der Nutzer per Biometrie oder PIN, wird eine WebAuthn-Assertion erzeugt, die diese Transaktionsdaten kryptografisch signiert enthält.
Das Ergebnis erhält man über PaymentRequest.show() als PaymentResponse. Dessen Eigenschaft details ist ein PublicKeyCredential, das an den Server geschickt und dort geprüft wird (Challenge, Signatur, clientDataJSON mit Typ "payment.get" und den Transaktionsdaten).
Einsatzgebiet: Banken, Kartennetzwerke und Zahlungsdienstleister, die auch in fremden Händler-Kontexten (z. B. in einem Iframe mit allow="payment") eine reibungsarme, phishing-resistente Authentifizierung anbieten wollen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $challenge Pflicht | BufferSource | Zufällige, serverseitig erzeugte Challenge. Wird in die signierten Client-Daten aufgenommen und schützt vor Replay-Angriffen. | |
| $rpId Pflicht | string | Relying-Party-ID (Domain), bei der die Credentials registriert wurden, z. B. "bank.example". |
|
| $credentialIds Pflicht | Array<BufferSource> | Liste der zulässigen Credential-IDs. Keine leere Liste; mindestens eine muss auf dem Gerät vorhanden sein. | |
| $instrument Pflicht | PaymentCredentialInstrument | Anzeige des Zahlungsmittels: displayName (string), icon (URL, string) und optional iconMustBeShown (boolean, Standard true). |
|
| $payeeName | string | Anzeigename des Zahlungsempfängers. Mindestens payeeName oder payeeOrigin muss gesetzt sein. |
|
| $payeeOrigin | string | HTTPS-Origin des Zahlungsempfängers, z. B. "https://shop.example". |
|
| $timeout | number | Zeitlimit in Millisekunden für die Authentifizierung (Hinweis an den Browser). | |
| $extensions | AuthenticationExtensionsClientInputs | Zusätzliche WebAuthn-Erweiterungen, die an die Assertion weitergereicht werden. | |
| $locale | Array<string> | BCP-47-Sprachtags für die Darstellung von displayName und payeeName. |
|
| $showOptOut | boolean | false | Zeigt im Dialog eine Opt-out-Option an, mit der der Nutzer die Löschung seiner SPC-Daten anfordern kann (führt zu OptOutError). |
Rückgabewert
PaymentRequest.show() ist ein Promise<PaymentResponse>, dessen details ein PublicKeyCredential enthält.Beispiele
Zahlung per SPC bestätigen lassen
// Werte kommen in der Praxis vom Server der Bank
const toBuffer = (b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
const confirmPayment = async ({ challengeB64, credentialIdB64 }) => {
/** @type {SecurePaymentConfirmationRequest} */
const spcData = {
challenge: toBuffer(challengeB64),
rpId: 'bank.example',
credentialIds: [toBuffer(credentialIdB64)],
instrument: {
displayName: 'Visa •••• 4242',
icon: 'https://bank.example/icons/visa.png',
},
payeeName: 'Beispiel-Shop GmbH',
payeeOrigin: 'https://shop.example',
timeout: 60000,
};
const request = new PaymentRequest(
[{ supportedMethods: 'secure-payment-confirmation', data: spcData }],
{ total: { label: 'Gesamt', amount: { currency: 'EUR', value: '49.90' } } }
);
const response = await request.show(); // benötigt Nutzeraktivierung (Klick)
await response.complete('success');
return response.details; // PublicKeyCredential
};
Verfügbarkeit prüfen und Assertion an den Server senden
const payButton = document.querySelector('#pay');
payButton.addEventListener('click', async () => {
if (!window.PaymentRequest) {
console.log('Payment Request API nicht verfügbar – Fallback nutzen');
return;
}
const request = new PaymentRequest(
[{ supportedMethods: 'secure-payment-confirmation', data: spcData }],
{ total: { label: 'Gesamt', amount: { currency: 'EUR', value: '49.90' } } }
);
if (!(await request.canMakePayment())) {
console.log('SPC nicht möglich – klassische 3-D-Secure-Challenge');
return;
}
try {
const response = await request.show();
const cred = response.details;
const b64 = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)));
const res = await fetch('/api/spc/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: cred.id,
clientDataJSON: b64(cred.response.clientDataJSON),
authenticatorData: b64(cred.response.authenticatorData),
signature: b64(cred.response.signature),
}),
});
await response.complete(res.ok ? 'success' : 'fail');
} catch (err) {
// NotAllowedError: abgebrochen oder kein passendes Credential
console.log(err.name);
}
});
Voraussetzung: SPC-fähiges Credential registrieren
// Einmalig bei der Bank (Relying Party) ausführen
const registerPaymentCredential = async (challenge, userId) => {
const credential = await navigator.credentials.create({
publicKey: {
challenge,
rp: { id: 'bank.example', name: 'Beispielbank' },
user: { id: userId, name: 'max@example.com', displayName: 'Max Muster' },
pubKeyCredParams: [{ type: 'public-key', alg: -7 }, { type: 'public-key', alg: -257 }],
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
residentKey: 'required',
},
extensions: { payment: { isPayment: true } }, // macht es SPC-fähig
},
});
console.log('Credential-ID:', credential.id);
return credential;
};