מוכרים יכולים להשתמש באישור תשלום מאובטח (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 יכול לפעול לפי תהליך האימות של טענת האימות של 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
תואם למקור של הגורם המוגבל. - השדה
topOrigin
תואם למקור ברמה העליונה שה-RP מצפה לו (המקור של המוכר בדוגמה שלמעלה). - הערך
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) יכול להודיע למוכר שהעסקה בוצעה בהצלחה.