אימות באמצעות 'אישור תשלום מאובטח'

מוכרים יכולים להשתמש באישור תשלום מאובטח (SPC) כחלק מתהליך אימות חזק ללקוח (SCA) בכרטיס אשראי או בחשבון בנק מסוימים. WebAuthn מבצע את האימות (לרוב באמצעות נתונים ביומטריים). יש להירשם מראש ל-WebAuthn. מידע נוסף על כך זמין במאמר רישום אישור תשלום מאובטח.

איך פועל הטמעה טיפוסית

השימוש הנפוץ ביותר ב-SPC הוא כאשר לקוח מבצע רכישה באתר של מוֹכר, והבנק או מנפיק כרטיס האשראי דורשים אימות של המשלם.

תהליך עבודה של אימות.

בואו נעבור על תהליך האימות:

  1. לקוח מספק למוכר את פרטי הכניסה שלו (למשל, פרטי כרטיס אשראי).
  2. המוכר שואל את המנפיק או הבנק המתאימים של פרטי הכניסה (הצד הנסמך או הגורם המוגבל) אם המשלם צריך אימות נפרד. ההחלפה הזו עשויה להתרחש, לדוגמה, עם EMV® 3-D Secure.
    • אם ה-RP רוצה שהמוכר ישתמש ב-SPC, ואם המשתמש נרשם בעבר, ה-RP יענה עם רשימה של מזהי פרטי הכניסה שרשמת על ידי המשלם ובקשה לאתגר.
    • אם לא נדרש אימות, המוכר יכול להמשיך להשלים את העסקה.
  3. אם נדרש אימות, המוכר קובע אם הדפדפן תומך ב-SPC.
    • אם הדפדפן לא תומך ב-SPC, ממשיכים בתהליך האימות הקיים.
  4. המוכר מפעיל SPC. בדפדפן תוצג תיבת דו-שיח לאישור.
    • אם לא הועברו מזהי פרטי כניסה מה-RP, צריך לחזור לתהליך האימות הקיים. אחרי שהאימות בוצע בהצלחה, כדאי להשתמש ברישום SPC כדי לייעל את האימותים העתידיים.
  5. המשתמש מאשר ומאמת את הסכום ואת יעד התשלום על ידי ביטול הנעילה של המכשיר.
  6. המוכר מקבל פרטי כניסה מהאימות.
  7. ה-RP מקבל את פרטי הכניסה מהמוכר ומאמת את האותנטיות שלהם.
  8. הגורם המוגבל שולח את תוצאות האימות למוכר.
  9. המוכר מציג למשתמש הודעה שמציינת אם התשלום בוצע בהצלחה או נכשל.

זיהוי תכונות

כדי לזהות אם 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.
  }
});

אימות המשתמש

כדי לאמת את המשתמש, צריך להפעיל את ה-method PaymentRequest.show() עם הפרמטרים secure-payment-confirmation ו-WebAuthn:

אלה הפרמטרים שצריך לספק לנכס data של אמצעי התשלום, SecurePaymentConfirmationRequest.

פרמטר תיאור
rpId שם המארח של מקור הגורם המוגבל (RP) כמזהה הגורם המוגבל.
challenge אתגר אקראי שמונע התקפות שליחה מחדש
credentialIds מערך של מזהים של פרטי כניסה. באימות של WebAuthn, המאפיין allowCredentials מקבל מערך של אובייקטים של PublicKeyCredentialDescriptor, אבל ב-SPC מעבירים רק רשימה של מזהים של פרטי כניסה.
ֶpayeeName (אופציונלי) שם מקבל התשלום.
payeeOrigin המקור של מקבל התשלום. בתרחיש שלמעלה, המקור הוא של המוכר.
instrument מחרוזת של displayName וכתובת URL של icon שמפנה למשאב תמונה. ערך בוליאני אופציונלי (ברירת המחדל היא true) עבור iconMustBeShown, שמציין כי הסמל חייב להיות מאוחזר ולהציג אותו כדי שהבקשה תכשל.
timeout הזמן הקצוב לחתימה על העסקה באלפיות השנייה
extensions התוספים נוספו לשיחת WebAuthn. אתם לא חייבים לציין בעצמכם את תוסף ה"תשלום".

מומלץ לעיין בקוד לדוגמה:

// 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 תואם למקור של הגורם המוגבל.
  • הערך 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) יוכל להודיע למוכר שהעסקה הושלמה.

השלבים הבאים