ตรวจสอบสิทธิ์ด้วยการยืนยันการชำระเงินที่ปลอดภัย

ผู้ขายสามารถใช้การยืนยันการชำระเงินที่ปลอดภัย (SPC) เป็นส่วนหนึ่งของกระบวนการตรวจสอบสิทธิ์ลูกค้าแบบเข้มงวด (Strong Customer Authentication หรือ SCA) สำหรับบัตรเครดิตหรือบัญชีธนาคารหนึ่งๆ WebAuthn จะตรวจสอบสิทธิ์ (ผ่านข้อมูลไบโอเมตริกบ่อยๆ) ต้องลงทะเบียน WebAuthn ล่วงหน้า ซึ่งคุณจะดูข้อมูลได้ในการลงทะเบียนการยืนยันการชำระเงินที่ปลอดภัย

วิธีการทำงานของการติดตั้งใช้งานทั่วไป

การใช้งาน SPC ที่พบบ่อยที่สุดคือเมื่อลูกค้าทำการซื้อบนเว็บไซต์ของผู้ขาย และผู้ออกบัตรเครดิตหรือธนาคารกำหนดให้มีการตรวจสอบสิทธิ์ผู้ชำระเงิน

เวิร์กโฟลว์การตรวจสอบสิทธิ์

ลองดูขั้นตอนการตรวจสอบสิทธิ์

  1. ลูกค้าให้ข้อมูลเข้าสู่ระบบการชำระเงิน (เช่น ข้อมูลบัตรเครดิต) กับผู้ขาย
  2. ผู้ขายจะขอให้ผู้ออกบัตรหรือธนาคารที่เกี่ยวข้องของข้อมูลเข้าสู่ระบบการชำระเงิน (ฝ่ายที่เกี่ยวข้องหรือ RP) หากผู้ชำระเงินต้องมีการตรวจสอบสิทธิ์แยกต่างหาก ซึ่งการแลกเปลี่ยนนี้อาจเกิดขึ้น เช่น เมื่อใช้ EMV® 3-D Secure
    • หาก RP ต้องการให้ผู้ขายใช้ SPC และหากผู้ใช้ได้ลงทะเบียนไว้ก่อนหน้านี้ RP จะตอบกลับด้วยรายการรหัสข้อมูลเข้าสู่ระบบที่ผู้ชำระเงินลงทะเบียนพร้อมการยืนยันตัวตน
    • หากไม่จำเป็นต้องมีการตรวจสอบสิทธิ์ ผู้ขายสามารถทำธุรกรรมให้เสร็จสมบูรณ์ต่อไปได้
  3. หากต้องมีการตรวจสอบสิทธิ์ ผู้ขายจะตรวจสอบว่าเบราว์เซอร์รองรับ SPC หรือไม่
    • หากเบราว์เซอร์ไม่รองรับ SPC ให้ดำเนินการตามขั้นตอนการตรวจสอบสิทธิ์ที่มีอยู่
  4. ผู้ขายขอรับ SPC เบราว์เซอร์จะแสดงกล่องโต้ตอบการยืนยัน
    • หากไม่มีรหัสข้อมูลเข้าสู่ระบบที่ส่งผ่านจาก RP ให้กลับไปใช้ขั้นตอนการตรวจสอบสิทธิ์ที่มีอยู่ หลังจากการตรวจสอบสิทธิ์สำเร็จแล้ว ให้พิจารณาใช้การลงทะเบียน SPC เพื่อปรับปรุงการตรวจสอบสิทธิ์ในอนาคต
  5. ผู้ใช้ยืนยันและตรวจสอบสิทธิ์จำนวนเงินและปลายทางของการชำระเงินโดยการปลดล็อกอุปกรณ์
  6. ผู้ขายจะได้รับข้อมูลเข้าสู่ระบบจากการตรวจสอบสิทธิ์
  7. โดย RP จะได้รับเอกสารรับรองจากผู้ขายและยืนยันความน่าเชื่อถือ
  8. โดย RP จะส่งผลการยืนยันไปยังผู้ขาย
  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.
  }
});

ตรวจสอบสิทธิ์ผู้ใช้

หากต้องการตรวจสอบสิทธิ์ผู้ใช้ ให้เรียกใช้เมธอด PaymentRequest.show() ที่มีพารามิเตอร์ secure-payment-confirmation และ WebAuthn ดังนี้

นี่คือพารามิเตอร์ที่คุณควรระบุในพร็อพเพอร์ตี้ data ของวิธีการชำระเงิน SecurePaymentConfirmationRequest

พารามิเตอร์ คำอธิบาย
rpId ชื่อโฮสต์ของต้นทาง RP เป็นรหัส 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 อาจปฏิบัติตามขั้นตอนการยืนยันการยืนยันการตรวจสอบสิทธิ์ของ WebAuthn เพื่อยืนยันข้อมูลธุรกรรม นอกจากนี้ยังต้องยืนยัน payment ด้วย

ตัวอย่างเพย์โหลดของ 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 ตรงกับต้นทางระดับบนสุดที่ 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 จะบอกผู้ขายว่าการทำธุรกรรมเสร็จสมบูรณ์

ขั้นตอนถัดไป