מוכרים יכולים להשתמש באישור תשלום מאובטח (SPC) כחלק מתהליך אימות חזק של לקוח (SCA) עבור כרטיס אשראי או חשבון בנק נתון. WebAuthn מבצע את האימות (לרוב באמצעות נתונים ביומטריים). צריך להירשם מראש ל-WebAuthn. מידע נוסף זמין במאמר רישום אישור תשלום מאובטח.
איך פועלת הטמעה אופיינית
השימוש הנפוץ ביותר ב-SPC הוא כשלקוח מבצע רכישה באתר של מוֹכר, ומנפיק כרטיס האשראי או הבנק דורשים אימות של המשלם.
נלמד איך מתבצע תהליך האימות:
- הלקוח מספק למוכרים את פרטי התשלום שלו (כמו פרטי כרטיס האשראי).
- המוכר שואל את המנפיק או את הבנק התואם של פרטי הכניסה לתשלום (הצד הנסמך או RP) אם לשולח התשלום נדרש אימות נפרד. ההחלפה הזו עשויה להתרחש, לדוגמה, עם EMV® 3-D Secure.
- אם ה-RP רוצה שהמוכר ישתמש ב-SPC, ואם המשתמש נרשם בעבר, ה-RP משיב עם רשימה של מזהי פרטי הכניסה שנרשמו על ידי המשלם ואתגר.
- אם לא נדרש אימות, המוכר יכול להמשיך להשלים את העסקה.
- אם נדרש אימות, המוכר קובע אם הדפדפן תומך ב-SPC.
- אם הדפדפן לא תומך ב-SPC, ממשיכים בתהליך האימות הקיים.
- המוכר מפעיל את SPC. בדפדפן תוצג תיבת דו-שיח לאישור.
- אם לא מועברים מזהי פרטי כניסה מה-RP, חוזרים לתהליך האימות הקיים. אחרי שהאימות בוצע בהצלחה, כדאי להשתמש ברישום SPC כדי לייעל את האימותים העתידיים.
- המשתמש מאשר ומאמת את הסכום ואת יעד התשלום על ידי ביטול הנעילה של המכשיר.
- המוכר מקבל פרטי כניסה מהאימות.
- ה-RP מקבל את פרטי הכניסה מהמוכר ומאמת את האותנטיות שלהם.
- ה-RP שולח את תוצאות האימות אל המוכר.
- המוכר מציג למשתמש הודעה כדי לציין אם התשלום בוצע בהצלחה או לא.
זיהוי תכונות
כדי לבדוק אם יש תמיכה ב-SPC בדפדפן, אפשר לשלוח קריאה מזויפת למספר canMakePayment()
.
מעתיקים ומדביקים את הקוד הבא כדי לזהות מודעות SPC באתר של מוכר.
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.
}
});
אימות המשתמש
כדי לאמת את המשתמש, מפעילים את השיטה PaymentRequest.show()
עם הפרמטרים secure-payment-confirmation
ו-WebAuthn:
PublicKeyCredentialRequestOptions
- פרמטרים ספציפיים לתשלום אחרים בפלטפורמה של המוכר.
אלה הפרמטרים שצריך לספק למאפיין data
של אמצעי התשלום, SecurePaymentConfirmationRequest
.
קוד לדוגמה:
// 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);
}
הפונקציה .show()
יוצרת אובייקט PaymentResponse
, מלבד העובדה ש-details
מכיל פרטי כניסה למפתח ציבורי עם clientDataJSON
שמכיל את נתוני העסקה (payment
) לאימות על ידי RP.
צריך להעביר את פרטי הכניסה שהתקבלו בין מקורות ל-RP ולאמת אותם.
איך ה-RP מאמת את העסקה
השלב החשוב ביותר בתהליך התשלום הוא אימות נתוני העסקאות בשרת של RP.
כדי לאמת את נתוני העסקאות, ה-RP יכול לפעול לפי תהליך האימות של טענת נכוֹנוּת (assertion) של WebAuthn.
בנוסף, הם צריכים לאמת את payment
.
דוגמה למטען ייעודי (payload) של 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"
}
}
}
- השדה
rp
תואם למקור של RP. - הערך
topOrigin
תואם למקור ברמה העליונה שהגורם המוגבל מצפה לקבל (מקור המוכר בדוגמה שלמעלה). - הערך
payeeOrigin
תואם למקור של מקבל התשלום שצריך היה להציג למשתמש. - הערך
total
תואם לסכום העסקה שצריך היה להציג למשתמש. - הערך
instrument
תואם לפרטים של אמצעי התשלום שצריכים להיות מוצגים למשתמש.
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.';
}
אחרי שכל קריטריוני האימות יעברו את כל הקריטריונים, הגורם המוגבל (RP) יוכל להודיע למוכר שהעסקה הושלמה.
השלבים הבאים
- קריאת הסקירה הכללית על אישור תשלום מאובטח
- מידע נוסף על רישום באמצעות אישור תשלום מאובטח