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

Recurring PaymentsและSubscriptions

ใช้งาน subscription billing, recurring paymentsและ scheduled chargesอัตโนมัติด้วยบัตรที่บันทึกไว้โดยใช้ Customers APIและSchedulesของ Omise

ภาพรวม

Recurring paymentsช่วยให้คุณเรียกเก็บเงินจากลูกค้าโดยอัตโนมัติตามกำหนดเวลาปกติสำหรับ subscriptions, memberships, ผลิตภัณฑ์ SaaSหรือบริการที่มีการเรียกเก็บเงินซ้ำ Omiseมีสองวิธีการ: recurring chargesแบบ manualโดยใช้ Customers APIหรือ recurring chargesแบบอัตโนมัติโดยใช้ Schedules

คุณสมบัติหลัก:

  • บัตรที่บันทึกไว้ - เก็บวิธีการชำระเงินของลูกค้าอย่างปลอดภัย
  • Schedulesที่ยืดหยุ่น - รายวัน รายสัปดาห์ รายเดือน รายปี
  • การเรียกเก็บเงินอัตโนมัติ - การจัดการ subscriptionแบบตั้งค่าแล้วทิ้งไว้
  • การควบคุมแบบ manual - เรียกเก็บเงินลูกค้าด้วยโปรแกรม
  • ตรรกะการลองใหม่ - ลองชำระเงินที่ล้มเหลวอีกครั้งโดยอัตโนมัติ
  • การเรียกเก็บเงินแบบ prorated - การเปลี่ยน subscriptionกลางรอบ
  • หลายบัตร - ลูกค้าสามารถบันทึกวิธีการชำระเงินหลายวิธี

การเปรียบเทียบวิธีการ

คุณสมบัติCustomers API (Manual)Schedules (อัตโนมัติ)
การควบคุมควบคุมเต็มรูปแบบอัตโนมัติ
ความยืดหยุ่นยืดหยุ่นมากSchedulesคงที่
ตรรกะการลองใหม่คุณต้องใช้งานมีในตัว
ความซับซ้อนโค้ดมากขึ้นโค้ดน้อยลง
กรณีการใช้งานการเรียกเก็บเงินแบบกำหนดเองSubscriptionsมาตรฐาน
การคำนวณ Prorationคุณคำนวณคุณต้องใช้งาน
เหมาะที่สุดสำหรับกฎการเรียกเก็บเงินที่ซับซ้อนRecurring chargesแบบง่าย

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

วิธี Customers API

วิธี Schedules

Customers API (Manual Recurring)

ขั้นตอนที่ 1: สร้าง Customerพร้อมบัตร

const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});

// สร้าง customerพร้อมบัตรเริ่มต้น
const customer = await omise.customers.create({
email: 'user@example.com',
description: 'John Doe - Premium Plan',
card: tokenId, // จาก Omise.jsหรือ mobile SDK
metadata: {
plan: 'premium',
billing_cycle: 'monthly',
signup_date: new Date().toISOString()
}
});

console.log('ลูกค้า ID:', customer.id);
console.log('Default card:', customer.default_card);

ขั้นตอนที่ 2: บันทึก ลูกค้า ID

// บันทึกในฐานข้อมูลของคุณ
await db.users.update({
user_id: userId,
omise_customer_id: customer.id,
subscription_status: 'active',
subscription_plan: 'premium',
next_billing_date: calculateNextBillingDate(),
amount: 29900 // ฿299.00
});

ขั้นตอนที่ 3: เรียกเก็บเงิน Customerแบบ Recurring

// งานที่กำหนดเวลา (ทำงานทุกวัน)
async function processRecurringBilling() {
const today = new Date().toDateString();

// ค้นหาลูกค้าที่ครบกำหนดชำระเงิน
const dueCustomers = await db.users.find({
subscription_status: 'active',
next_billing_date: today
});

for (const user of dueCustomers) {
try {
// เรียกเก็บเงินจากบัตรที่บันทึกไว้ของลูกค้า
const charge = await omise.charges.create({
amount: user.amount,
currency: 'THB',
customer: user.omise_customer_id,
description: `Subscription renewal - ${user.subscription_plan}`,
metadata: {
user_id: user.user_id,
plan: user.subscription_plan,
billing_period: today
}
});

if (charge.status === 'successful') {
// อัปเดต subscription
await extendSubscription(user.user_id);
await sendReceiptEmail(user.email, charge);

console.log(`✓ Charged ${user.email}: ${charge.amount / 100}`);
} else {
// จัดการความล้มเหลว
await handleFailedPayment(user, charge);
}

} catch (error) {
console.error(`✗ Failed to charge ${user.email}:`, error.message);
await handlePaymentError(user, error);
}
}
}

// กำหนดเวลาให้ทำงานทุกวัน
// ใช้ node-cronหรือ schedulerของคุณ
cron.schedule('0 0 * * *', processRecurringBilling);

ขั้นตอนที่ 4: จัดการการชำระเงินที่ล้มเหลว

async function handleFailedPayment(user, charge) {
// เพิ่มจำนวนการลองใหม่
const retryCount = (user.payment_retry_count || 0) + 1;

await db.users.update({
user_id: user.user_id,
payment_retry_count: retryCount,
last_payment_attempt: new Date(),
last_payment_error: charge.failure_message
});

// ตรรกะการลองใหม่
if (retryCount <= 3) {
// ลองใหม่หลังจาก 3, 5, 7 วัน
const retryDays = [3, 5, 7][retryCount - 1];
const retryDate = new Date();
retryDate.setDate(retryDate.getDate() + retryDays);

await db.users.update({
user_id: user.user_id,
next_billing_date: retryDate.toDateString()
});

// ส่งอีเมลให้ลูกค้า
await sendPaymentFailedEmail(user.email, {
reason: charge.failure_message,
retryDate: retryDate,
updateCardUrl: `https://yoursite.com/billing/update-card`
});

} else {
// ระงับ subscriptionหลังจากลองใหม่ 3 ครั้ง
await db.users.update({
user_id: user.user_id,
subscription_status: 'suspended',
suspended_at: new Date()
});

await sendSubscriptionSuspendedEmail(user.email);
}
}

ขั้นตอนที่ 5: อัปเดตบัตร

// ลูกค้าอัปเดตบัตรของพวกเขา
app.post('/billing/update-card', async (req, res) => {
const { userId, newTokenId } = req.body;

try {
const user = await db.users.findOne({ user_id: userId });

// อัปเดตบัตรเริ่มต้นของลูกค้า
const customer = await omise.customers.update(user.omise_customer_id, {
card: newTokenId
});

// รีเซ็ตจำนวนการลองใหม่
await db.users.update({
user_id: userId,
payment_retry_count: 0,
subscription_status: 'active'
});

res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});

Schedules (อัตโนมัติ Recurring)

ขั้นตอนที่ 1: สร้าง ลูกค้า

// สร้าง customerก่อน
const customer = await omise.customers.create({
email: 'user@example.com',
description: 'John Doe',
card: tokenId
});

ขั้นตอนที่ 2: สร้าง Schedule

// สร้าง recurring scheduleรายเดือน
const schedule = await omise.schedules.create({
every: 1,
period: 'month',
start_date: '2025-02-15', // วันเรียกเก็บเงินครั้งแรก
end_date: '2026-02-15', // ตัวเลือก: วันสิ้นสุด schedule
charge: {
customer: customer.id,
amount: 29900,
currency: 'THB',
description: 'Monthly subscription - Premium Plan'
}
});

console.log('Schedule ID:', schedule.id);
console.log('Next occurrence:', schedule.next_occurrence_dates[0]);

ตัวเลือกระยะเวลา

// รายวัน
const dailySchedule = await omise.schedules.create({
every: 1,
period: 'day',
start_date: '2025-02-10',
charge: { /* พารามิเตอร์ charge */ }
});

// รายสัปดาห์
const weeklySchedule = await omise.schedules.create({
every: 1,
period: 'week',
on: { weekdays: ['monday'] }, // เรียกเก็บเงินทุกวันจันทร์
start_date: '2025-02-10',
charge: { /* พารามิเตอร์ charge */ }
});

// รายเดือน (วันที่เฉพาะ)
const monthlySchedule = await omise.schedules.create({
every: 1,
period: 'month',
on: { days_of_month: [1] }, // วันที่ 1 ของทุกเดือน
start_date: '2025-02-01',
charge: { /* พารามิเตอร์ charge */ }
});

// รายปี
const yearlySchedule = await omise.schedules.create({
every: 1,
period: 'year',
start_date: '2025-02-15',
charge: { /* พารามิเตอร์ charge */ }
});

ขั้นตอนที่ 3: จัดการ Schedule Webhooks

app.post('/webhooks/omise', (req, res) => {
const event = req.body;

switch (event.key) {
case 'charge.complete':
handleScheduledCharge(event.data);
break;

case 'schedule.suspend':
// Scheduleถูกระงับเนื่องจาก chargesล้มเหลว
handleScheduleSuspended(event.data);
break;

case 'schedule.expiring':
// Scheduleกำลังจะหมดอายุ
handleScheduleExpiring(event.data);
break;
}

res.sendStatus(200);
});

async function handleScheduledCharge(charge) {
if (charge.schedule) {
console.log('Schedule charge:', charge.schedule);

if (charge.status === 'successful') {
// ขยาย subscriptionของผู้ใช้
await extendSubscription(charge.customer);
await sendReceiptEmail(charge.customer);
} else {
// Omiseลองใหม่โดยอัตโนมัติ แต่แจ้งผู้ใช้
await sendPaymentIssueEmail(charge.customer);
}
}
}

ขั้นตอนที่ 4: จัดการ Schedules

// หยุด schedule
await omise.schedules.update('schd_test_...', {
status: 'suspended'
});

// กลับมาใช้ schedule
await omise.schedules.update('schd_test_...', {
status: 'active'
});

// ยกเลิก schedule
await omise.schedules.destroy('schd_test_...');

ตัวอย่างระบบ Subscriptionที่สมบูรณ์

const express = require('express');
const cron = require('node-cron');
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});

const app = express();
app.use(express.json());

// แผน Subscription
const PLANS = {
basic: { amount: 9900, name: 'Basic', features: ['Feature A'] },
premium: { amount: 29900, name: 'Premium', features: ['Feature A', 'Feature B'] },
enterprise: { amount: 99900, name: 'Enterprise', features: ['All features'] }
};

// สร้าง subscription
app.post('/subscribe', async (req, res) => {
try {
const { userId, plan, tokenId, email } = req.body;

// ตรวจสอบแผน
if (!PLANS[plan]) {
return res.status(400).json({ error: 'Invalid plan' });
}

// สร้าง customer
const customer = await omise.customers.create({
email: email,
description: `User ${userId} - ${PLANS[plan].name}`,
card: tokenId,
metadata: {
user_id: userId,
plan: plan
}
});

// การเรียกเก็บเงินครั้งแรก
const charge = await omise.charges.create({
amount: PLANS[plan].amount,
currency: 'THB',
customer: customer.id,
description: `${PLANS[plan].name} - First payment`
});

if (charge.status === 'successful') {
// บันทึก subscription
await db.subscriptions.create({
user_id: userId,
customer_id: customer.id,
plan: plan,
status: 'active',
current_period_start: new Date(),
current_period_end: addMonths(new Date(), 1),
amount: PLANS[plan].amount,
next_billing_date: addMonths(new Date(), 1)
});

res.json({
success: true,
subscription: {
plan: PLANS[plan].name,
amount: PLANS[plan].amount / 100,
next_billing: addMonths(new Date(), 1)
}
});
} else {
res.status(400).json({ error: 'Payment failed' });
}

} catch (error) {
res.status(500).json({ error: error.message });
}
});

// ประมวลผล recurring billing (ทำงานทุกวันเวลาเที่ยงคืน)
cron.schedule('0 0 * * *', async () => {
console.log('Processing recurring billing...');

const today = new Date().toDateString();
const dueSubscriptions = await db.subscriptions.find({
status: 'active',
next_billing_date: today
});

for (const sub of dueSubscriptions) {
try {
const charge = await omise.charges.create({
amount: sub.amount,
currency: 'THB',
customer: sub.customer_id,
description: `${sub.plan} - Monthly subscription`
});

if (charge.status === 'successful') {
// อัปเดต subscription
await db.subscriptions.update({
subscription_id: sub.id,
current_period_start: new Date(),
current_period_end: addMonths(new Date(), 1),
next_billing_date: addMonths(new Date(), 1),
retry_count: 0
});

await sendReceiptEmail(sub.user_id, charge);
console.log(`✓ Billed user ${sub.user_id}`);
} else {
await handleFailedBilling(sub, charge);
}

} catch (error) {
console.error(`✗ Error billing user ${sub.user_id}:`, error);
await handleBillingError(sub, error);
}
}
});

// ยกเลิก subscription
app.post('/cancel-subscription', async (req, res) => {
const { userId } = req.body;

try {
const sub = await db.subscriptions.findOne({
user_id: userId,
status: 'active'
});

// ไม่เรียกเก็บเงินอีก แต่ให้ระยะเวลาปัจจุบันเสร็จสิ้น
await db.subscriptions.update({
subscription_id: sub.id,
status: 'canceled',
canceled_at: new Date(),
access_until: sub.current_period_end
});

res.json({
success: true,
message: 'Subscription canceled',
access_until: sub.current_period_end
});

} catch (error) {
res.status(500).json({ error: error.message });
}
});

// ฟังก์ชันยูทิลิตี้
function addMonths(date, months) {
const result = new Date(date);
result.setMonth(result.getMonth() + months);
return result;
}

app.listen(3000);

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

1. การจัดการ Dunning

const RETRY_SCHEDULE = [
{ day: 3, message: 'first_reminder' },
{ day: 7, message: 'second_reminder' },
{ day: 14, message: 'final_notice' }
];

async function handleFailedBilling(subscription, charge) {
const retryCount = subscription.retry_count || 0;

if (retryCount < RETRY_SCHEDULE.length) {
const retry = RETRY_SCHEDULE[retryCount];

// กำหนดเวลาการลองใหม่ครั้งถัดไป
const nextRetry = new Date();
nextRetry.setDate(nextRetry.getDate() + retry.day);

await db.subscriptions.update({
subscription_id: subscription.id,
retry_count: retryCount + 1,
next_billing_date: nextRetry
});

// ส่งอีเมล dunning
await sendDunningEmail(subscription.user_id, retry.message, {
retryDate: nextRetry,
failureReason: charge.failure_message
});

} else {
// ยกเลิกหลังจากลองใหม่ทั้งหมด
await cancelSubscription(subscription.id);
}
}

2. การคำนวณแบบ Proration

async function upgradeSubscription(userId, newPlan) {
const sub = await db.subscriptions.findOne({ user_id: userId });
const oldPlanAmount = PLANS[sub.plan].amount;
const newPlanAmount = PLANS[newPlan].amount;

// คำนวณ proration
const daysInMonth = 30;
const daysRemaining = Math.ceil(
(sub.current_period_end - new Date()) / (1000 * 60 * 60 * 24)
);
const proratedRefund = (oldPlanAmount / daysInMonth) * daysRemaining;
const proratedCharge = (newPlanAmount / daysInMonth) * daysRemaining;
const amountDue = proratedCharge - proratedRefund;

// เรียกเก็บเงินส่วนต่าง
if (amountDue > 0) {
const charge = await omise.charges.create({
amount: Math.round(amountDue),
currency: 'THB',
customer: sub.customer_id,
description: `Upgrade to ${newPlan} (prorated)`
});
}

// อัปเดต subscription
await db.subscriptions.update({
subscription_id: sub.id,
plan: newPlan,
amount: newPlanAmount
});
}

3. ช่วงเวลาผ่อนผัน

// ให้ช่วงเวลาผ่อนผัน 3 วันหลังจากการชำระเงินล้มเหลว
async function checkGracePeriod(subscription) {
const daysSinceFailure = Math.floor(
(new Date() - subscription.last_payment_attempt) / (1000 * 60 * 60 * 24)
);

if (daysSinceFailure > 3 && subscription.status === 'past_due') {
// ระงับการเข้าถึง
await db.subscriptions.update({
subscription_id: subscription.id,
status: 'suspended'
});

await notifyUserSuspension(subscription.user_id);
}
}

4. ช่วงทดลองใช้

async function startTrial(userId, tokenId) {
// สร้าง customerแต่ไม่เรียกเก็บเงิน
const customer = await omise.customers.create({
email: user.email,
card: tokenId
});

// ตั้งวันสิ้นสุดการทดลองใช้
const trialEnd = new Date();
trialEnd.setDate(trialEnd.getDate() + 14); // ทดลองใช้ 14 วัน

await db.subscriptions.create({
user_id: userId,
customer_id: customer.id,
status: 'trialing',
trial_end: trialEnd,
next_billing_date: trialEnd
});
}

5. การแจ้งเตือนทางอีเมล

const EMAIL_TEMPLATES = {
receipt: 'การชำระเงินของคุณสำเร็จแล้ว',
failed: 'การชำระเงินล้มเหลว - กรุณาอัปเดตบัตรของคุณ',
upcoming: 'การสมัครสมาชิกของคุณจะต่ออายุในอีก 3 วัน',
canceled: 'การสมัครสมาชิกของคุณถูกยกเลิกแล้ว',
suspended: 'การสมัครสมาชิกของคุณถูกระงับ'
};

async function sendSubscriptionEmail(userId, type, data) {
const user = await db.users.findOne({ user_id: userId });

await sendEmail({
to: user.email,
subject: EMAIL_TEMPLATES[type],
template: type,
data: data
});
}

การทดสอบ

Recurring Paymentsในโหมดทดสอบ

ทดสอบการใช้งาน subscriptionและ recurring paymentของคุณโดยใช้บัตรทดสอบ:

const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});

// สร้าง customerทดสอบพร้อมบัตรที่บันทึกไว้
const customer = await omise.customers.create({
email: 'test.customer@example.com',
description: 'Test Subscriber',
card: 'tokn_test_5rt6s9vah5lkvi1rh9c' // Token บัตรทดสอบ
});

console.log('Test customer ID:', customer.id);

สถานการณ์การทดสอบ

1. Recurring Chargeที่สำเร็จ

// สร้าง customer
const customer = await omise.customers.create({
email: 'test@example.com',
card: 'tokn_test_4242' // บัตรสำเร็จ
});

// Chargeแรก (สมัคร)
const charge1 = await omise.charges.create({
amount: 29900,
currency: 'THB',
customer: customer.id,
description: 'First payment'
});

// Recurring charge (เดือนถัดไป)
const charge2 = await omise.charges.create({
amount: 29900,
currency: 'THB',
customer: customer.id,
description: 'Monthly renewal'
});

console.log('Both charges successful:',
charge1.status === 'successful' &&
charge2.status === 'successful'
);

2. Recurring Paymentที่ล้มเหลว (บัตรถูกปฏิเสธ)

// สร้าง customerด้วยบัตรปฏิเสธ
const customer = await omise.customers.create({
email: 'test@example.com',
card: 'tokn_test_0002' // บัตรปฏิเสธ
});

// พยายาม recurring charge
try {
const charge = await omise.charges.create({
amount: 29900,
currency: 'THB',
customer: customer.id
});
} catch (error) {
console.log('Payment failed:', error.code); // 'payment_rejected'
console.log('Implement retry logic here');
}

3. การทดสอบการอัปเดตบัตร

// ลูกค้าอัปเดตบัตรหมดอายุ
const customer = await omise.customers.create({
email: 'test@example.com',
card: 'tokn_test_4242'
});

// อัปเดตเป็นบัตรใหม่
const updated = await omise.customers.update(customer.id, {
card: 'tokn_test_new_card'
});

// ตรวจสอบว่าบัตรใหม่เป็นค่าเริ่มต้น
console.log('Card updated:', updated.default_card !== customer.default_card);

4. ทดสอบ Schedule (อัตโนมัติ Recurring)

// สร้าง customer
const customer = await omise.customers.create({
email: 'test@example.com',
card: 'tokn_test_4242'
});

// สร้าง scheduleรายเดือน
const schedule = await omise.schedules.create({
every: 1,
period: 'month',
start_date: '2025-02-15',
charge: {
customer: customer.id,
amount: 29900,
currency: 'THB',
description: 'Test subscription'
}
});

console.log('Schedule created:', schedule.id);
console.log('Next charge:', schedule.next_occurrence_dates[0]);

// จำลอง chargeแรกใน dashboard
// Dashboard → Schedules → Run Schedule

บัตรทดสอบสำหรับ Subscriptions

หมายเลขบัตรChargeแรกRecurring Chargeกรณีการใช้งาน
4242 4242 4242 4242สำเร็จสำเร็จเส้นทางที่ดี
4000 0000 0000 0002ถูกปฏิเสธN/Aการสมัครล้มเหลว
4000 0000 0000 0010สำเร็จถูกปฏิเสธการต่ออายุล้มเหลว (เงินไม่พอ)
4242 4242 4242 4242 → อัปเดตสำเร็จสำเร็จฟลอว์การอัปเดตบัตร

การทดสอบบัตรที่บันทึกไว้

// ทดสอบหลายบัตร
const customer = await omise.customers.create({
email: 'test@example.com'
});

// เพิ่มบัตรแรก
const card1 = await omise.customers.update(customer.id, {
card: 'tokn_test_visa'
});

// เพิ่มบัตรที่สอง
const card2 = await omise.customers.update(customer.id, {
card: 'tokn_test_mastercard'
});

// รายการบัตรของลูกค้า
const customerData = await omise.customers.retrieve(customer.id);
console.log('Number of cards:', customerData.cards.total);

// เรียกเก็บเงินบัตรเฉพาะ
const charge = await omise.charges.create({
amount: 29900,
currency: 'THB',
customer: customer.id,
card: card1.default_card // ใช้บัตรเฉพาะ
});

การทดสอบผ่าน Dashboard

Recurring Chargesแบบ Manual

  1. ไปที่ Test Dashboard → Customers
  2. ค้นหา customerทดสอบของคุณ
  3. ดูบัตรที่บันทึกไว้
  4. คลิก "Create Charge" เพื่อจำลอง recurring payment

การทดสอบ Schedules

  1. ไปที่ Test Dashboard → Schedules
  2. ค้นหา scheduleของคุณ
  3. คลิก "Run Schedule" เพื่อทริกเกอร์ chargeทันที
  4. ติดตามผลลัพธ์ในหน้า Charges

ทดสอบ Webhooksสำหรับ Subscriptions

app.post('/webhooks/omise', (req, res) => {
const event = req.body;

switch (event.key) {
case 'charge.complete':
// Recurring paymentสำเร็จ
if (event.data.schedule) {
console.log('Schedule charge:', event.data.schedule);
}
handleSuccessfulRenewal(event.data);
break;

case 'charge.failed':
// Recurring paymentล้มเหลว
console.log('Failure reason:', event.data.failure_message);
handleFailedRenewal(event.data);
break;

case 'customer.update.card':
// ลูกค้าอัปเดตวิธีการชำระเงิน
console.log('Card updated:', event.data.default_card);
break;

case 'schedule.suspend':
// Scheduleถูกระงับเนื่องจาก chargesล้มเหลว
console.log('Schedule suspended:', event.data.id);
notifyCustomerSuspension(event.data);
break;
}

res.sendStatus(200);
});

การทดสอบตรรกะการลองใหม่

// จำลองการจัดการ dunning
async function testRetryFlow() {
const customer = await omise.customers.create({
email: 'test@example.com',
card: 'tokn_test_0010' // เงินไม่พอ
});

// ความพยายามที่ 1: Chargeเริ่มต้นล้มเหลว
try {
await omise.charges.create({
amount: 29900,
currency: 'THB',
customer: customer.id
});
} catch (error) {
console.log('Attempt 1 failed:', error.code);
}

// อัปเดตเป็นบัตรที่ถูกต้อง
await omise.customers.update(customer.id, {
card: 'tokn_test_4242' // บัตรสำเร็จ
});

// ความพยายามที่ 2: ควรสำเร็จ
const retry = await omise.charges.create({
amount: 29900,
currency: 'THB',
customer: customer.id
});

console.log('Retry successful:', retry.status === 'successful');
}

ทดสอบตรรกะ Proration

// ทดสอบการอัปเกรด subscriptionด้วย proration
async function testProration() {
const subscription = {
plan: 'basic',
amount: 9900,
started: new Date('2025-02-01'),
next_billing: new Date('2025-03-01')
};

// คำนวณการอัปเกรดแบบ prorated
const daysInMonth = 30;
const daysUsed = 10; // 10 วันในรอบการเรียกเก็บเงิน
const daysRemaining = daysInMonth - daysUsed;

const oldPlanDaily = 9900 / daysInMonth;
const newPlanDaily = 29900 / daysInMonth;

const refund = oldPlanDaily * daysRemaining;
const charge = newPlanDaily * daysRemaining;
const amountDue = Math.round(charge - refund);

console.log('Prorated amount:', amountDue / 100);

// เรียกเก็บเงินจำนวน prorated
const proratedCharge = await omise.charges.create({
amount: amountDue,
currency: 'THB',
customer: 'cust_test_...',
description: 'Upgrade to premium (prorated)'
});

console.log('Proration charged:', proratedCharge.status === 'successful');
}

การทดสอบการจัดการข้อผิดพลาด

// ทดสอบ: Chargeด้วย customerที่ถูกลบ
try {
await omise.charges.create({
amount: 29900,
currency: 'THB',
customer: 'cust_test_deleted'
});
} catch (error) {
console.log('Expected error:', error.code); // 'not_found'
}

// ทดสอบ: Charge customerที่ไม่มีบัตร
try {
const customer = await omise.customers.create({
email: 'test@example.com'
// ไม่มีบัตรแนบ
});

await omise.charges.create({
amount: 29900,
currency: 'THB',
customer: customer.id
});
} catch (error) {
console.log('Expected error:', error.code); // 'invalid_card'
}

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

ควรใช้ Customers APIหรือ Schedules?
  • ใช้ Customers API หากคุณต้องการตรรกะการเรียกเก็บเงินแบบกำหนดเอง การคำนวณ proration การเรียกเก็บเงินตามการใช้งาน หรือราคาที่ซับซ้อน
  • ใช้ Schedules สำหรับ recurring chargesจำนวนเงินคงที่แบบง่ายๆ ที่มีช่วงเวลามาตรฐาน
ฉันจะจัดการกับ subscription paymentsที่ล้มเหลวอย่างไร?

ใช้งานการจัดการ dunning:

  1. ลองชำระเงินที่ล้มเหลวอีกครั้งโดยอัตโนมัติ (3, 7, 14 วัน)
  2. ส่งอีเมลเตือนให้อัปเดตบัตร
  3. ให้ช่วงเวลาผ่อนผัน (3-7 วัน)
  4. ระงับ subscriptionหลังจากลองครั้งสุดท้าย
  5. อนุญาตให้เปิดใช้งานใหม่ได้ง่าย
ลูกค้าสามารถมีหลาย subscriptionsได้หรือไม่?

ได้ สร้างระเบียน subscriptionแยกต่างหากในฐานข้อมูลของคุณ โดยแต่ละรายการเชื่อมโยงกับ customer_id เดียวกันใน Omise เรียกเก็บเงินแต่ละ subscriptionแยกกัน

ฉันจะใช้งานการเรียกเก็บเงินรายปีอย่างไร?
// เรียกเก็บเงินปีละครั้ง
const nextYear = new Date();
nextYear.setFullYear(nextYear.getFullYear() + 1);

await db.subscriptions.create({
user_id: userId,
billing_period: 'yearly',
next_billing_date: nextYear,
amount: 299900 // ฿2,999 ต่อปี
});
แล้ว metered billing (ตามการใช้งาน) ล่ะ?

ติดตามการใช้งานตลอดช่วงเวลาการเรียกเก็บเงิน จากนั้นเรียกเก็บเงินในตอนท้าย:

// ติดตามการใช้งาน
await db.usage.increment({
user_id: userId,
api_calls: 1000
});

// ในตอนท้ายของช่วงเวลา
const usage = await db.usage.get(userId);
const amount = usage.api_calls * 10; // ฿0.10 ต่อการเรียก

await omise.charges.create({
amount: amount,
customer: customerId
});
ฉันจะจัดการกับบัตรหมดอายุอย่างไร?

ส่งอีเมลเชิงรุก:

// ตรวจสอบบัตรที่หมดอายุภายใน 30 วัน
const expiringCards = await db.subscriptions.find({
card_expiry_month: nextMonth.getMonth() + 1,
card_expiry_year: nextMonth.getFullYear()
});

// ส่งอีเมลให้ลูกค้า
for (const sub of expiringCards) {
await sendCardExpiringEmail(sub.user_id);
}

ทรัพยากรที่เกี่ยวข้อง

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

  1. สร้าง customerพร้อมบัตร
  2. ใช้งานตรรกะการเรียกเก็บเงิน
  3. ตั้งค่ากลไกการลองใหม่
  4. กำหนดค่า webhooks
  5. ทดสอบฟลอว์ subscription
  6. เปิดใช้งานจริง