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

Eiji Kitamura
Eiji Kitamura

ผู้ขายสามารถใช้การยืนยันการชำระเงินที่ปลอดภัย (SPC) เป็นส่วนหนึ่งของกระบวนการยืนยันตัวตนลูกค้าแบบขั้นสูง (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 สามารถแจ้งให้ผู้ขายทราบว่าธุรกรรมเสร็จสมบูรณ์

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