3D Secure
ใช้การยืนยันตัวตน 3D Secure 2.0 เพื่อลดการฉ้อโกง เปลี่ยนความรับผิด และปฏิบัติตามข้อกำหนด Strong ลูกค้า Authentication (SCA) ในยุโรปและอื่นๆ
ภาพรวม
3D Secure (3DS) เป็นโปรโตคอลการยืนยันตัวตนที่เพิ่มชั้นความปลอดภัยเพิ่มเติมให้กับธุรกรรมบัตรออนไลน์ เมื่อเปิดใช้งาน ลูกค้าจะยืนยันตัวตนกับผู้ออกบัตรก่อนทำการชำระเงินให้เสร็จสิ้น
ประโยชน์หลัก:
- 🔒 ลดการฉ้อโกง - ขั้นตอนการยืนยันตัวตนเพิ่มเติม
- 🛡️ เปลี่ยนความรับผิด - ความรับผิดจากการฉ้อโกงเปลี่ยนไปที่ผู้ออกบัตร
- ✅ การปฏิบัติตาม SCA - จำเป็นสำหรับการชำระเงินในยุโรป
- 📈 อัตราการอนุมัติที่สูงขึ้น - ผู้ออกบัตรเชื่อถือธุรกรรมที่ผ่านการยืนยันตัวตน
- 💳 รองรับบัตรทั้งหมด - Visa, Mastercard, JCB
แบรนด์ 3D Secure:
- Visa: Visa Secure (เดิมคือ Verified by Visa)
- Mastercard: Mastercard Identity Check (เดิมคือ Mastercard SecureCode)
- JCB: J/Secure
3D Secure 1 เทียบกับ 3D Secure 2
| คุณสมบัติ | 3DS 1.0 (เก่า) | 3DS 2.0 (ปัจจุบัน) |
|---|---|---|
| ประสบการณ์ผู้ใช้ | ป๊อปอัป, ยุ่งยาก | ในแอป, ราบรื่น |
| รองรับมือถือ | แย่ | ยอดเยี่ยม |
| ข้อมูลที่แชร์ | จำกัด | บริบทที่สมบูรณ์ |
| ความยุ่งยาก | สูง | ต่ำ (มักไร้รอยต่อ) |
| การยืนยันตัวตน | รหัสผ่าน | ไบโอเมตริก, PIN, OTP |
| อัตราความสำเร็จ | ต่ำกว่า | สูงกว่า |
3D Secure 1.0 ถูกยกเลิกในเดือนตุลาคม 2022 ธุรกรรมบัตรทั้งหมดใช้ 3D Secure 2.0 แล้ว
Omise รองรับ 3D Secure 2.0 โดยค่าเริ่มต้น ให้ประสบการณ์ผู้ใช้ที่ดีที่สุดและอัตราการอนุมัติสูงสุด
วิธีการทำงานของ 3D Secure
ประสบการณ์ของลูกค้า:
- ลูกค้ากรอกรายละเอียดบัตรที่เช็คเอาต์
- หาก 3DS จำเป็น เปลี่ยนเส้นทางไปยังหน้ายืนยันตัวตนของผู้ออกบัตร
- ลูกค้ายืนยันตัวตนด้วย:
- ไบโอเมตริก (ลายนิ้วมือ, Face ID)
- PIN หรือ รหัสผ่าน
- รหัสผ่านแบบใช้ครั้งเดียว (SMS/แอป)
- กลับไปยังไซต์ผู้ค้า
- การชำระเงินเสร็จสมบูรณ์หรือล้มเหลวตามการยืนยันตัวตน
เวลาที่ใช้: 10-30 วินาที (ราบรื่นด้วยไบโอเมตริก)
การใช้งาน
3DS อัตโนมัติ (แนะนำ)
Omise กระตุ้น 3DS โดยอัตโนมัติเมื่อจำเป็น:
- Node.js
- PHP
- Python
const omise = require('omise')({
publicKey: 'pkey_test_YOUR_PUBLIC_KEY',
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
// สร้างการเรียกเก็บเงินด้วย card token
const charge = await omise.charges.create({
amount: 100000,
currency: 'THB',
card: cardToken, // จาก Omise.js
return_uri: 'https://yourdomain.com/payment/callback'
});
// ตรวจสอบว่า 3DS จำเป็นหรือไม่
if (charge.authorize_uri) {
// เปลี่ยนเส้นทางลูกค้าไปยังการยืนยันตัวตน 3DS
res.redirect(charge.authorize_uri);
} else if (charge.status === 'successful') {
// การชำระเงินสำเร็จโดยไม่มี 3DS
res.redirect('/success');
}
<?php
$charge = OmiseCharge::create(array(
'amount' => 100000,
'currency' => 'THB',
'card' => $cardToken,
'return_uri' => 'https://yourdomain.com/payment/callback'
));
if ($charge['authorize_uri']) {
// เปลี่ยนเส้นทางไปยัง 3DS
header('Location: ' . $charge['authorize_uri']);
} else if ($charge['status'] === 'successful') {
// สำเร็จโดยไม่มี 3DS
header('Location: /success');
}
?>
import omise
charge = omise.Charge.create(
amount=100000,
currency='THB',
card=card_token,
return_uri='https://yourdomain.com/payment/callback'
)
if charge.authorize_uri:
# เปลี่ยนเส้นทางไปยัง 3DS
return redirect(charge.authorize_uri)
elif charge.status == 'successful':
# สำเร็จโดยไม่มี 3DS
return redirect('/success')
จัดการการกลับมาจาก 3DS
app.get('/payment/callback', async (req, res) => {
try {
const chargeId = req.query.charge_id;
const charge = await omise.charges.retrieve(chargeId);
if (charge.status === 'successful') {
// การยืนยันตัวตน 3DS สำเร็จ
await processOrder(charge.metadata.order_id);
res.redirect('/payment-success');
} else if (charge.status === 'failed') {
// การยืนยันตัวตนหรือการชำระเงินล้มเหลว
res.redirect(`/payment-failed?reason=${charge.failure_message}`);
} else {
// รอดำเนินการ (หายาก)
res.redirect('/payment-pending');
}
} catch (error) {
res.redirect('/payment-error');
}
});
เมื่อไหร่ที่ต้องใช้ 3DS
บังคับ (จำเป็นเสมอ)
เขตเศรษฐกิจยุโรป (EEA):
- การชำระเงินของผู้บริโภคทั้งหมดต้องใช้ Strong ลูกค้า Authentication (SCA)
- ครอบคลุม EU + ไอซ์แลนด์, ลิกเตนสไตน์, นอร์เวย์
- ผู้ค้าต้องใช้ 3DS หรือเผชิญกับการปฏิเสธบัตร
บางธนาคาร/ภูมิภาค:
- อินเดีย (ข้อบังคับ RBI)
- บางส่วนของเอเชีย-แปซิฟิก
- แตกต่างกันตามนโยบายผู้ออกบัตร
ทางเลือก (ตามความเสี่ยง)
ผู้ออกบัตรอาจต้องการ 3DS สำหรับ:
- ธุรกรรมมูลค่าสูง
- ผู้ค้าความเสี่ยงสูง
- รูปแบบที่น่าสงสัย
- ธุรกรรมแรกของลูกค้ากับผู้ค้า
- การชำระเงินข้ามพรมแดน
ไร้รอยต่อ vs ท้าทาย:
- ไร้รอยต่อ: ผู้ออกบัตรอนุมัติโดยไม่ต้องให้ลูกค้าทำอะไร (80%+ ของ 3DS 2.0)
- ท้าทาย: ลูกค้าต้องยืนยันตัวตน (20%)
ตัวอย่างการใช้งานที่สมบูรณ์
const express = require('express');
const omise = require('omise')({
publicKey: process.env.OMISE_PUBLIC_KEY,
secretKey: process.env.OMISE_SECRET_KEY
});
const app = express();
app.use(express.json());
// ขั้นตอนที่ 1: สร้างการเรียกเก็บเงิน
app.post('/checkout', async (req, res) => {
try {
const { token, amount, currency, order_id } = req.body;
const charge = await omise.charges.create({
amount: amount,
currency: currency,
card: token, // จาก Omise.js
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id
}
});
if (charge.authorize_uri) {
// ต้องใช้ 3DS - คืนค่า URL เปลี่ยนเส้นทาง
res.json({
requires_authentication: true,
authorize_uri: charge.authorize_uri,
charge_id: charge.id
});
} else if (charge.status === 'successful') {
// สำเร็จโดยไม่มี 3DS
await processOrder(order_id);
res.json({
requires_authentication: false,
status: 'successful',
charge_id: charge.id
});
} else {
// ล้มเหลว
res.status(400).json({
error: charge.failure_message
});
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// ขั้นตอนที่ 2: จัดการการกลับมาจาก 3DS
app.get('/payment/callback', async (req, res) => {
try {
const charge = await omise.charges.retrieve(req.query.charge_id);
if (charge.status === 'successful') {
await processOrder(charge.metadata.order_id);
res.redirect(`/order-confirmation?order=${charge.metadata.order_id}`);
} else {
res.redirect(`/payment-failed?error=${charge.failure_code}`);
}
} catch (error) {
res.redirect('/payment-error');
}
});
// ขั้นตอนที่ 3: Webhook (แนะนำ)
app.post('/webhooks/omise', async (req, res) => {
const event = req.body;
if (event.key === 'charge.complete') {
const charge = event.data;
if (charge.status === 'successful') {
await processOrder(charge.metadata.order_id);
}
}
res.sendStatus(200);
});
app.listen(3000);
การทดสอบ 3D Secure
บัตรทดสอบ
| หมายเลขบัตร | พฤติกรรม 3DS | ผลลัพธ์ |
|---|---|---|
| 4242424242424242 | ไม่มี 3DS | สำเร็จ |
| 4000000000003220 | ต้องใช้ 3DS, สำเร็จ | สำเร็จ |
| 4000000000009235 | ต้องใช้ 3DS, ล้มเหลว | ปฏิเสธ |
| 4000008260003178 | 3DS, เงินไม่เพียงพอ | ปฏิเสธ |
ขั้นตอนการทดสอบ
- ใช้บัตรทดสอบ 3DS
- เปลี่ยนเส้นทางไปยังหน้าทดสอบ 3DS
- คลิกปุ่ม "Success" หรือ "Fail"
- กลับไปยัง return_uri พร้อมผลลัพธ์
ดูรายการทั้งหมดได้ที่คู่มือการทดสอบ
แนวทางปฏิบัติที่ดีที่สุด
1. ให้ return_uri เสมอ
// จำเป็นสำหรับการเปลี่ยนเส้นทาง 3DS
const charge = await omise.charges.create({
// ... พารามิเตอร์อื่นๆ
return_uri: 'https://yourdomain.com/payment/callback' // ต้องเป ็น HTTPS
});
2. จัดการสถานะการเรียกเก็บเงินทั้งหมด
switch (charge.status) {
case 'successful':
// การชำระเงินเสร็จสมบูรณ์
break;
case 'failed':
// แสดงข้อผิดพลาด อนุญาตให้ลองใหม่
break;
case 'pending':
// รอ webhook (หายาก)
break;
case 'expired':
// ลูกค้าไม่ได้ทำ 3DS ให้เสร็จ
break;
}