ข้ามไปยังเนื้อหาหลัก

บัตรที่บันทึกไว้และการชำระเงินซ้ำ

จัดเก็บบัตรลูกค้าอย่างปลอดภัยสำหรับการเรียกเก็บเงินในอนาคตโดยใช้ Customers API เหมาะสำหรับการสมัครสมาชิก การเรียกเก็บเงินซ้ำๆ และลูกค้าที่กลับมาใช้บริการอีก

ภาพรวม

Omise Customers API ช่วยให้คุณสามารถบันทึกวิธีการชำระเงินเพื่อใช้ในอนาคต แทนที่จะขอให้ลูกค้ากรอกรายละเอียดบัตรใหม่ทุกครั้งที่ซื้อ คุณสามารถบันทึกบัตรของพวกเขาอย่างปลอดภัยและเรียกเก็บเงินด้วยการเรียก API เพียงครั้งเดียว

กรณีการใช้งาน:

  • การสมัครสมาชิกรายเดือน (โมเดล Netflix, Spotify)
  • การเรียกเก็บเงินซ้ำๆ (สาธารณูปโภค, ค่าเช่า)
  • การเช็คเอาต์คลิกเดียวสำหรับลูกค้าที่กลับมาใช้บริการอีก
  • การต่ออายุอัตโนมัติ
  • การชำระเงินแบบผ่อนชำระ

วิธีการทำงาน

คู่มือการใช้งาน

ขั้นตอนที่ 1: สร้าง Customer ด้วยบัตร

เมื่อลูกค้าทำการซื้อครั้งแรก ให้สร้างวัตถุ Customer:

curl https://api.omise.co/customers \
-u skey_test_YOUR_SECRET_KEY: \
-d "email=john@example.com" \
-d "description=John Doe - สมาชิกพรีเมียม" \
-d "card=tokn_test_5rt6s9vah5lkvi1rh9c"

ขั้นตอนที่ 2: จัดเก็บ Customer ID

บันทึก customer ID ในฐานข้อมูลของคุณพร้อมกับข้อมูลผู้ใช้:

-- ตัวอย่างสคีมาฐานข้อมูล
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255),
omise_customer_id VARCHAR(50), -- จัดเก็บสิ่งนี้!
subscription_status VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- แทรกผู้ใช้พร้อม customer ID
INSERT INTO users (email, name, omise_customer_id, subscription_status)
VALUES ('john@example.com', 'John Doe', 'cust_test_...', 'active');

ขั้นตอนที่ 3: เรียกเก็บเงินจากบัตรที่บันทึกไว้

สำหรับการชำระเงินในอนาคต ให้เรียกเก็บเงินโดยใช้ customer ID แทน token:

curl https://api.omise.co/charges \
-u skey_test_YOUR_SECRET_KEY: \
-d "amount=29900" \
-d "currency=thb" \
-d "customer=cust_test_5rt6s9vah5lkvi1rh9c" \
-d "description=การสมัครสมาชิกรายเดือน - กุมภาพันธ์ 2024"
ไม่ต้องการ Token

เมื่อเรียกเก็บเงิน customer ID คุณไม่จำเป็นต้องสร้าง token Omise จะใช้บัตรเริ่มต้นของลูกค้าโดยอัตโนมัติ

การจัดการบัตรหลายใบ

เพิ่มบัตรเพิ่มเติม

// สร้าง token สำหรับบัตรใหม่ผ่าน Omise.js ก่อน
const token = 'tokn_test_...';

// เพิ่มบัตรให้กับ customer ที่มีอยู่
const card = await omise.customers.addCard(customerId, {
card: token
});

แสดงรายการบัตรของลูกค้า

curl https://api.omise.co/customers/cust_test_.../cards \
-u skey_test_YOUR_SECRET_KEY:

ตั้งบัตรเริ่มต้น

await omise.customers.update(customerId, {
default_card: 'card_test_...'
});

ลบบัตร

curl https://api.omise.co/customers/cust_test_.../cards/card_test_... \
-X DELETE \
-u skey_test_YOUR_SECRET_KEY:

การใช้งานการชำระเงินซ้ำ

ตัวอย่าง: การสมัครสมาชิกรายเดือน

// ฟังก์ชันการเรียกเก็บเงินการสมัครสมาชิก (รันทุกวันผ่าน cron)
async function processSubscriptionBilling() {
// ค้นหาการสมัครสมาชิกที่ครบกำหนดต่ออายุวันนี้
const dueSubscriptions = await db.query(`
SELECT user_id, omise_customer_id, subscription_plan, amount
FROM users
WHERE subscription_status = 'active'
AND next_billing_date = CURRENT_DATE
`);

for (const sub of dueSubscriptions) {
try {
// เรียกเก็บเงินจากลูกค้า
const charge = await omise.charges.create({
amount: sub.amount,
currency: 'thb',
customer: sub.omise_customer_id,
description: `${sub.subscription_plan} - ${new Date().toISOString().slice(0, 7)}`,
metadata: {
user_id: sub.user_id,
subscription_type: sub.subscription_plan
}
});

if (charge.status === 'successful') {
// อัปเดตวันที่เรียกเก็บเงินครั้งถัดไป
await db.query(`
UPDATE users
SET next_billing_date = DATE_ADD(CURRENT_DATE, INTERVAL 1 MONTH),
last_charge_id = ?
WHERE user_id = ?
`, [charge.id, sub.user_id]);

// ส่งอีเมลใบเสร็จ
await sendReceiptEmail(sub.user_id, charge);

} else {
// จัดการการชำระเงินที่ล้มเหลว
await handleFailedPayment(sub.user_id, charge.failure_message);
}

} catch (error) {
console.error(`ไม่สามารถเรียกเก็บเงินผู้ใช้ ${sub.user_id}:`, error);
await handleBillingError(sub.user_id, error);
}
}
}

ตรรกะการลองใหม่สำหรับการชำระเงินที่ล้มเหลว

async function handleFailedPayment(userId, failureMessage) {
const user = await db.getUserById(userId);
const retryCount = user.payment_retry_count || 0;

if (retryCount < 3) {
// กำหนดการลองใหม่
await db.query(`
UPDATE users
SET payment_retry_count = ?,
next_retry_date = DATE_ADD(CURRENT_DATE, INTERVAL ? DAY)
WHERE user_id = ?
`, [retryCount + 1, retryCount + 1, userId]);

// แจ้งเตือนลูกค้า
await sendPaymentFailedEmail(userId, {
reason: failureMessage,
retryDate: new Date(Date.now() + (retryCount + 1) * 24 * 60 * 60 * 1000)
});

} else {
// ถึงจำนวนการลองใหม่สูงสุดแล้ว - ระงับการสมัครสมาชิก
await db.query(`
UPDATE users
SET subscription_status = 'suspended',
suspension_reason = 'payment_failure'
WHERE user_id = ?
`, [userId]);

await sendSubscriptionSuspendedEmail(userId);
}
}

อัปเดตข้อมูลบัตร

อัปเดตวันหมดอายุ

ลูกค้าสามารถอัปเดตวันหมดอายุของบัตรโดยไม่ต้องกรอกหมายเลขบัตรใหม่:

await omise.customers.updateCard(customerId, cardId, {
expiration_month: 12,
expiration_year: 2028,
name: 'John Doe',
postal_code: '10110'
});

เปลี่ยนบัตรทั้งหมด

ด้วยเหตุผลด้านความปลอดภัย หมายเลขบัตรไม่สามารถอัปเดตได้ ในการเปลี่ยนบัตร:

  1. สร้าง token ใหม่ด้วย Omise.js
  2. เพิ่มบัตรใหม่ให้กับ customer
  3. ตั้งเป็นบัตรเริ่มต้น
  4. ลบบัตรเก่า
// 1. สร้าง token ผ่าน Omise.js (ฝั่งไคลเอนต์)
const newToken = 'tokn_test_...';

// 2. เพิ่มบัตรใหม่
const newCard = await omise.customers.addCard(customerId, {
card: newToken
});

// 3. ตั้งเป็นค่าเริ่มต้น
await omise.customers.update(customerId, {
default_card: newCard.id
});

// 4. ลบบัตรเก่า
await omise.customers.destroyCard(customerId, oldCardId);

ความปลอดภัยและการปฏิบัติตาม

การปฏิบัติตาม PCI

Omise จัดการการจัดเก็บบัตรใน vault ที่ปฏิบัติตาม PCI คุณจัดเก็บเฉพาะ:

  • Customer ID (ปลอดภัยในการจัดเก็บ)
  • Metadata ของบัตร (4 หลักสุดท้าย, แบรนด์, วันหมดอายุ - ปลอดภัยในการแสดง)

อย่าจัดเก็บ:

  • หมายเลขบัตรเต็ม
  • CVV/รหัสความปลอดภัย
  • ข้อมูลบัตรดิบ

การป้องกันข้อมูลลูกค้า

// ✅ ดี: จัดเก็บเฉพาะ ID และ metadata
const user = {
id: 12345,
email: 'john@example.com',
omise_customer_id: 'cust_test_...',
card_last_digits: '4242', // ปลอดภัยในการแสดง
card_brand: 'Visa', // ปลอดภัยในการแสดง
card_expiry: '12/2027' // ปลอดภัยในการแสดง
};

// ❌ แย่: อย่าจัดเก็บสิ่งเหล่านี้
const badExample = {
card_number: '4242424242424242', // อย่าจัดเก็บสิ่งนี้!
cvv: '123' // อย่าจัดเก็บสิ่งนี้!
};

ความยินยอมของลูกค้า

ขอความยินยอมอย่างชัดเจนเสมอก่อนบันทึกบัตร:

<form id="payment-form">
<!-- ฟิลด์ป้อนข้อมูลบัตร -->

<label>
<input type="checkbox" id="save-card" name="save_card" />
บันทึกบัตรนี้สำหรับการซื้อในอนาคต
</label>

<button type="submit">ทำการชำระเงินให้เสร็จสิ้น</button>
</form>

<script>
document.getElementById('payment-form').addEventListener('submit', function(e) {
e.preventDefault();
const saveCard = document.getElementById('save-card').checked;

// ถ้าบันทึกบัตร สร้าง customer
// ถ้าไม่ เพียงสร้างการเรียกเก็บเงินด้วย token
});
</script>

ปัญหาทั่วไปและการแก้ไขปัญหา

ปัญหา: Customer มีอยู่แล้ว

ข้อผิดพลาด: customer_already_exists

วิธีแก้: ดึง customer ที่มีอยู่หรืออัปเดต:

try {
const customer = await omise.customers.create({
email: email,
card: token
});
} catch (error) {
if (error.code === 'customer_already_exists') {
// ดึงและอัปเดตแทน
const existingCustomer = await omise.customers.list({
limit: 1,
// ใช้ฐานข้อมูลของคุณเพื่อค้นหา customer ID
});
}
}

ปัญหา: ไม่พบบัตรเริ่มต้น

ข้อผิดพลาด: default_card_not_found

สาเหตุ: Customer ไม่มีบัตรหรือบัตรเริ่มต้นถูกลบ

วิธีแก้:

// ตรวจสอบว่า customer มีบัตรหรือไม่
const customer = await omise.customers.retrieve(customerId);

if (customer.cards.total === 0) {
// แจ้งผู้ใช้ให้เพิ่มบัตร
throw new Error('กรุณาเพิ่มวิธีการชำระเงิน');
}

if (!customer.default_card) {
// ตั้งบัตรแรกเป็นค่าเริ่มต้น
await omise.customers.update(customerId, {
default_card: customer.cards.data[0].id
});
}

แนวทางปฏิบัติที่ดีที่สุด

  1. จัดการความล้มเหลวในการชำระเงินอย่างเหมาะสม

    • ใช้ตรรกะการลองใหม่
    • แจ้งเตือนลูกค้าทันที
    • ให้ขั้นตอนการอัปเดตบัตรที่ง่าย
  2. ตรวจสอบวันหมดอายุของบัตร

    • ส่งการแจ้งเตือน 30 วันก่อนหมดอายุ
    • ให้การอัปเดตบัตรแบบคลิกเดียว
    • หยุดการสมัครสมาชิกชั่วคราวหากการอัปเดตบัตรล้มเหลว
  3. ให้การจัดการการสมัครสมาชิก

    • อนุญาตให้ลูกค้าดูบัตรที่บันทึกไว้
    • ลบบัตรได้ง่าย
    • ตัวเลือกการยกเลิกการสมัครสมาชิก
    • การเข้าถึงประวัติการเรียกเก็บเงิน
  4. ใช้ Metadata อย่างมีประสิทธิภาพ

    await omise.customers.create({
    email: email,
    card: token,
    metadata: {
    user_id: '12345',
    signup_source: 'mobile_app',
    subscription_tier: 'premium',
    referral_code: 'FRIEND2024'
    }
    });
  5. ใช้ Webhooks ฟังเหตุการณ์ charge.complete และ charge.failed:

    // ใน webhook handler ของคุณ
    if (event.key === 'charge.complete') {
    await updateSubscriptionStatus(charge.customer, 'active');
    } else if (event.key === 'charge.failed') {
    await handleFailedPayment(charge.customer, charge.failure_message);
    }

คำถามที่พบบ่อย

ลูกค้าสามารถมีบัตรหลายใบได้หรือไม่?

ได้! ลูกค้าสามารถแนบบัตรหลายใบได้ ใช้ฟิลด์ default_card เพื่อระบุบัตรที่จะเรียกเก็บเงินโดยอัตโนมัติ ลูกค้าสามารถเปลี่ยนบัตรเริ่มต้นได้ทุกเมื่อ

ฉันจะเรียกเก็บเงินจากบัตรเฉพาะแทนค่าเริ่มต้นได้อย่างไร?

ระบุพารามิเตอร์ card เมื่อสร้างการเรียกเก็บเงิน:

await omise.charges.create({
amount: 10000,
currency: 'thb',
customer: customerId,
card: 'card_test_specific_card_id'
});
ฉันสามารถใช้บัตรที่บันทึกไว้กับ 3D Secure ได้หรือไม่?

การเรียกเก็บเงินครั้งแรกโดยทั่วไปต้องใช้การยืนยันตัวตน 3D Secure การเรียกเก็บเงินครั้งต่อๆ ไปอาจไม่ต้องการการยืนยันตัวตน ขึ้นอยู่กับธนาคารผู้ออกบัตร นี่เรียกว่า "frictionless flow" และจัดการโดยอัตโนมัติโดยธนาคารตามการประเมินความเสี่ยง

แหล่งข้อมูลที่เกี่ยวข้อง

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

  1. สร้าง customer แรกของคุณ
  2. ใช้การเรียกเก็บเงินซ้ำ
  3. ตั้งค่าการจัดการความล้มเหลว
  4. กำหนดค่า webhooks
  5. ทดสอบอย่างละเอียด
  6. เข้าสู่การผลิต