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

Refunds

ออก refund เต็มจำนวนหรือบางส่วนให้กับลูกค้าได้อย่างรวดเร็วและง่ายดายผ่าน Omise API หรือ dashboard

ภาพรวม

Omise อนุญาตให้คุณ refund การชำระเงินที่ทำผ่านวิธีการชำระเงินส่วนใหญ่ Refunds สามารถเป็นแบบเต็มจำนวน (ทั้งหมด) หรือบางส่วน (ส่วนหนึ่งของจำนวน) ขึ้นอยู่กับวิธีการชำระเงินที่ใช้

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

  • การประมวลผล refund แบบทันที
  • รองรับ refund เต็มจำนวนและบางส่วน (สำหรับวิธีที่รองรับ)
  • ช่วงเวลา refund สูงสุด 365 วัน
  • หักจากยอดโอนอัตโนมัติ
  • การแจ้งเตือน Webhook

วิธีการชำระเงินที่รองรับ

รองรับ Refund เต็มจำนวนและบางส่วน

  • Credit/Debit Cards - เต็มจำนวนและบางส่วน (สูงสุด 15 ครั้งต่อ charge)
  • DuitNow - เต็มจำนวนและบางส่วนภายใน 180 วัน
  • PayNow - เต็มจำนวนและบางส่วนภายใน 6 เดือน
  • GrabPay - เต็มจำนวนและบางส่วนภายใน 3 เดือน
  • ShopeePay - เต็มจำนวนและบางส่วนภายใน 180 วัน

Refund เต็มจำนวนเท่านั้น

  • ⚠️ Installments - refund เต็มจำนวนเท่านั้น (ช่วงเวลาแตกต่างกันตามธนาคาร: 30 วันถึง 1 ปี)
  • ⚠️ TrueMoney - refund เต็มจำนวนเท่านั้นภายใน 30 วัน

ไม่รองรับ Refund

  • PromptPay - ไม่สามารถ refund ได้
  • Mobile Banking (ไทย) - ไม่สามารถ refund ได้
  • Internet Banking (ไทย) - ไม่สามารถ refund ได้
  • Konbini/Pay-easy (ญี่ปุ่น) - ไม่สามารถ refund ได้
  • Online Direct Debit - ไม่สามารถ refund ได้
ตรวจสอบเอกสารวิธีการชำระเงิน

ตรวจสอบนโยบาย refund ของวิธีการชำระเงินเฉพาะก่อนการใช้งานเสมอ การรองรับ refund และช่วงเวลาแตกต่างกันอย่างมาก

Refunds ทำงานอย่างไร

การสร้าง Refunds

Refund เต็มจำนวน

curl https://api.omise.co/charges/chrg_test_5rt6s9vah5lkvi1rh9c/refunds \
-u skey_test_YOUR_SECRET_KEY: \
-d "amount=10025"

Refund บางส่วน

// Refund ส่วนหนึ่งของ charge (เช่น THB 50 จาก THB 100.25)
const partialRefund = await omise.charges.refund('chrg_test_...', {
amount: 5000 // THB 50.00 (ในหน่วยที่เล็กที่สุด)
});

// ลูกค้าได้รับเงินคืน THB 50
// ผู้ขายเก็บ THB 50.25

Refunds บางส่วนหลายครั้ง

// Refund บางส่วนครั้งแรก (THB 30)
await omise.charges.refund(chargeId, { amount: 3000 });

// Refund บางส่วนครั้งที่สอง (THB 20)
await omise.charges.refund(chargeId, { amount: 2000 });

// Refund บางส่วนครั้งที่สาม (THB 50)
await omise.charges.refund(chargeId, { amount: 5000 });

// รวม refund: THB 100
// สูงสุด: 15 ครั้งต่อ charge

ข้อจำกัดของ Refund

ช่วงเวลา

วิธีการชำระเงินช่วงเวลา Refund
Credit/Debit Cards365 วัน
DuitNow180 วัน
ShopeePay180 วัน
PayNow6 เดือน
GrabPay3 เดือน
Atome60 วัน
TrueMoney30 วัน (voids เท่านั้น)
WeChat Pay90 วัน
PayPay1 ปี

ข้อจำกัด Refund บางส่วน

  • สูงสุดต่อ charge: 15 ครั้ง
  • จำนวนขั้นต่ำ: แตกต่างกันตามวิธีการชำระเงิน
  • รวมไม่สามารถเกิน: จำนวน charge เดิม

Voiding เทียบกับ Refunding

Voiding เกิดขึ้นเมื่อ refund ถูกประมวลผลก่อนการโอน:

  • ยังไม่มีการโอนจริง
  • Charge ถูกยกเลิก
  • ประมวลผลเร็วขึ้น
  • ตั้งค่า void flag เป็น true ใน response

Refunding เกิดขึ้นหลังการโอน:

  • เงินโอนไปยังผู้ขายแล้ว
  • สร้างธุรกรรม refund จริง
  • หักจากยอดเงินผู้ขาย
  • ตั้งค่า void flag เป็น false
const refund = await omise.charges.refund(chargeId, { amount: 10000 });

if (refund.voided) {
console.log('Charge ถูก void (ก่อนการโอน)');
} else {
console.log('Refund ถูกประมวลผล (หลังการโอน)');
}

สถานะและไทม์ไลน์ของ Refund

สถานะ Refund

  • pending - สร้าง Refund แล้ว กำลังประมวลผล
  • successful - Refund เสร็จสมบูรณ์
  • failed - Refund ล้มเหลว (หายาก)

ไทม์ไลน์การประมวลผล

วิธีการชำระเงินลูกค้าได้รับ Refund
Credit Cards5-10 วันทำการ (ขึ้นอยู่กับธนาคาร)
Debit Cards1-7 วันทำการ
QR Paymentsทันทีถึง 1 วันทำการ
E-Walletsทันทีถึง 3 วันทำการ
ธนาคาร Transfers1-3 วันทำการ
เวลาประมวลผลของธนาคาร

เวลา refund ขึ้นอยู่กับธนาคารของลูกค้า Omise ประมวลผล refunds ทันที แต่ธนาคารอาจใช้เวลาหลายวันในการเครดิตบัญชีลูกค้า

ตรวจสอบสถานะ Refund

ดึง Refund เฉพาะ

const refund = await omise.charges.retrieveRefund(
'chrg_test_...',
'rfnd_test_...'
);

console.log(refund.status);
console.log(refund.amount);
console.log(refund.created_at);

แสดงรายการ Refunds ทั้งหมดสำหรับ Charge

const refunds = await omise.charges.listRefunds('chrg_test_...');

refunds.data.forEach(refund => {
console.log(`Refund ${refund.id}: ${refund.amount/100} ${refund.currency}`);
});

ตรวจสอบสถานะ Refund ของ Charge

const charge = await omise.charges.retrieve('chrg_test_...');

console.log('Refundable:', charge.refundable);
console.log('Refunded amount:', charge.refunded_amount);
console.log('Net amount:', charge.net - charge.refunded_amount);

การจัดการ Webhooks ของ Refund

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

if (event.key === 'refund.create') {
const refund = event.data;
const chargeId = refund.charge;

// อัพเดทสถานะคำสั่งซื้อ
updateOrderStatus(chargeId, 'refunded');

// แจ้งเตือนลูกค้า
sendRefundConfirmation(refund);

// อัพเดทสินค้าคงคลังถ้าจำเป็น
restockItems(chargeId);
}

res.sendStatus(200);
});

กรณีการใช้งานทั่วไป

1. การยกเลิกคำสั่งซื้อ

async function cancelOrder(orderId) {
const order = await getOrder(orderId);

if (order.omise_charge_id && order.status === 'paid') {
// ออก refund เต็มจำนวน
const refund = await omise.charges.refund(order.omise_charge_id, {
amount: order.amount,
metadata: {
order_id: orderId,
reason: 'customer_request'
}
});

// อัพเดทสถานะคำสั่งซื้อ
await updateOrder(orderId, {
status: 'refunded',
refund_id: refund.id
});

// แจ้งเตือนลูกค้า
await sendCancellationEmail(order.customer_email, refund);
}
}

2. Refund บางส่วนสำหรับการคืนสินค้า

async function processPartialReturn(orderId, returnedItems) {
const order = await getOrder(orderId);
let refundAmount = 0;

// คำนวณจำนวน refund
returnedItems.forEach(item => {
refundAmount += item.price * item.quantity;
});

// ออก refund บางส่วน
const refund = await omise.charges.refund(order.omise_charge_id, {
amount: refundAmount,
metadata: {
order_id: orderId,
reason: 'partial_return',
items: JSON.stringify(returnedItems)
}
});

return refund;
}

3. แก้ไขการชำระเงินเกิน

async function correctOverpayment(chargeId, correctAmount) {
const charge = await omise.charges.retrieve(chargeId);
const overpayment = charge.amount - correctAmount;

if (overpayment > 0) {
// Refund ส่วนต่าง
const refund = await omise.charges.refund(chargeId, {
amount: overpayment,
metadata: {
reason: 'overpayment_correction'
}
});

return refund;
}
}

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

1. ตรวจสอบความสามารถใน Refund เสมอ

const charge = await omise.charges.retrieve(chargeId);

if (!charge.refundable) {
throw new Error('This charge cannot be refunded');
}

if (charge.refunded_amount >= charge.amount) {
throw new Error('Charge already fully refunded');
}

// ตรวจสอบจำนวนที่ refund ได้ที่เหลืออยู่
const remainingAmount = charge.amount - charge.refunded_amount;
console.log(`Can refund up to: ${remainingAmount/100}`);

2. ใช้ Metadata สำหรับการติดตาม

const refund = await omise.charges.refund(chargeId, {
amount: 5000,
metadata: {
order_id: 'ORD-12345',
reason: 'defective_product',
initiated_by: 'customer_service',
ticket_id: 'TICKET-789',
notes: 'ลูกค้า received damaged item'
}
});

3. ใช้งาน Idempotency

async function safeRefund(chargeId, amount, idempotencyKey) {
try {
const refund = await omise.charges.refund(chargeId, {
amount: amount
}, {
headers: {
'Idempotency-Key': idempotencyKey
}
});

return refund;
} catch (error) {
if (error.code === 'already_refunded') {
// Refund มีอยู่แล้ว
return await findExistingRefund(chargeId, amount);
}
throw error;
}
}

4. แจ้งเตือนลูกค้า

async function issueRefundWithNotification(chargeId, amount, reason) {
const charge = await omise.charges.retrieve(chargeId);

// สร้าง refund
const refund = await omise.charges.refund(chargeId, { amount });

// ส่งการแจ้งเตือนทางอีเมล
await sendEmail({
to: charge.metadata.customer_email,
subject: 'Refund Processed',
body: `
Your refund of ${amount/100} ${charge.currency} has been processed.
Reason: ${reason}
คุณควรเห็นเครดิตในบัญชีของคุณภายใน 5-10 วันทำการ
Refund ID: ${refund.id}
`
});

return refund;
}

5. จัดการข้อผิดพลาดอย่างเหมาะสม

async function handleRefund(chargeId, amount) {
try {
const refund = await omise.charges.refund(chargeId, { amount });
return { success: true, refund };

} catch (error) {
console.error('Refund error:', error);

if (error.code === 'invalid_charge') {
return { success: false, error: 'Charge not found' };
}

if (error.code === 'charge_not_refundable') {
return { success: false, error: 'This payment method does not support refunds' };
}

if (error.code === 'insufficient_balance') {
return { success: false, error: 'Insufficient balance for refund' };
}

return { success: false, error: 'Refund failed. Please try again.' };
}
}

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

ปัญหา: "Charge not refundable"

สาเหตุ:

  • วิธีการชำระเงินไม่รองรับ refunds (เช่น PromptPay, Mobile Banking)
  • ช่วงเวลา refund หมดอายุ (> 365 วันสำหรับบัตร)
  • Charge ไม่สำเร็จ
  • Refund เต็มจำนวนแล้ว

วิธีแก้:

const charge = await omise.charges.retrieve(chargeId);

if (!charge.paid) {
console.log('Charge was not successful');
}

if (charge.refunded_amount === charge.amount) {
console.log('Already fully refunded');
}

if (!charge.refundable) {
console.log('Payment method does not support refunds');
// เสนอทางเลือกอื่น: โอนเงินธนาคารด้วยตนเอง, store credit ฯลฯ
}

ปัญหา: "Insufficient balance"

สาเหตุ: เงินในบัญชีผู้ขายไม่เพียงพอ

วิธีแก้:

  • รอการโอนเงินที่กำลังมา
  • เพิ่มเงินเข้าบัญชีด้วยตนเอง
  • ประมวลผล refund หลังจากมียอดคงเหลือเพียงพอ

ปัญหา: Refund บางส่วนล้มเหลว

สาเหตุ:

  • วิธีการชำระเงินไม่รองรับ refund บางส่วน
  • เกินขั้ดจำกัด 15 ครั้ง
  • จำนวนเงินเกินจำนวนที่ refund ได้ที่เหลืออยู่

วิธีแก้:

const charge = await omise.charges.retrieve(chargeId);
const refunds = await omise.charges.listRefunds(chargeId);

if (refunds.total >= 15) {
console.log('Maximum 15 partial refunds reached');
}

const remainingAmount = charge.amount - charge.refunded_amount;
if (requestedAmount > remainingAmount) {
console.log(`Can only refund up to ${remainingAmount/100}`);
}

FAQ

Refunds ใช้เวลานานแค่ไหนจึงจะปรากฏในบัญชีลูกค้า?

เวลาประมวลผล refund แตกต่างกันตามวิธีการชำระเงิน:

  • บัตรเครดิต: 5-10 วันทำการ (ขึ้นอยู่กับธนาคารของลูกค้า)
  • บัตรเดบิต: 1-7 วันทำการ
  • E-wallets: ทันทีถึง 3 วันทำการ
  • การชำระเงิน QR: ทันทีถึง 1 วันทำการ

Omise ประมวลผล refunds ทันที แต่ธนาคารควบคุมว่าเมื่อใดที่ลูกค้าจะเห็นเครดิต

ฉันสามารถ refund มากกว่าจำนวน charge เดิมได้หรือไม่?

ไม่ได้ คุณไม่สามารถ refund มากกว่าจำนวน charge เดิม ผลรวมของ refunds ทั้งหมดไม่สามารถเกิน charge เดิม

เกิดอะไรขึ้นกับค่าธรรมเนียมเมื่อฉัน refund?

ค่าธรรมเนียมธุรกรรมจะไม่ถูก refund เมื่อคุณออก refund:

  • ลูกค้าได้รับจำนวน refund เต็ม
  • ผู้ขายรับภาระค่าธรรมเนียมธุรกรรม
  • Refund ถูกหักจากยอดคงเหลือที่โอนได้

ตัวอย่าง: Charge THB 100 พร้อมค่าธรรมเนียม 3.65% = ค่าธรรมเนียม THB 3.65

  • ผู้ขายได้รับในตอนแรก: THB 96.35
  • ออก refund เต็มจำนวน: THB 100 ถูกหักจากยอดคงเหลือ
  • ขาดทุนสุทธิของผู้ขาย: THB 103.65 (refund + ค่าธรรมเนียมเดิม)
ฉันสามารถ refund charges จาก test mode ได้หรือไม่?

ได้ คุณสามารถ refund test charges เพื่อวัตถุประสงค์ในการทดสอบ Test refunds ไม่เกี่ยวข้องกับเงินจริงและช่วยให้คุณทดสอบขั้นตอน refund ของคุณ

ความแตกต่างระหว่าง refunds และ disputes คืออะไร?
  • Refund: คุณคืนเงินให้ลูกค้าโดยสมัครใจ

    • คุณควบคุมกระบวนการ
    • คุณเริ่มต้น refund
    • ต้นทุนต่ำกว่า (ค่าธรรมเนียมธุรกรรมเดิมเท่านั้น)
  • Dispute/Chargeback: ลูกค้ายื่นเรื่องร้องเรียนกับธนาคาร

    • ธนาคารควบคุมกระบวนการ
    • อาจส่งผลให้เกิดค่าธรรมเนียมเพิ่มเติม ($15-30)
    • นับรวมในอัตรา dispute ของคุณ
    • อาจนำไปสู่บทลงโทษบัญชี

แนวปฏิบัติที่ดีที่สุด: ออก refunds เชิงรุกเพื่อหลีกเลี่ยง disputes

เรียนรู้เพิ่มเติมเกี่ยวกับ disputes →

ฉันจะทดสอบ refunds ได้อย่างไร?

ใน test mode:

  1. สร้าง test charge
  2. ออก refund ผ่าน API หรือ dashboard
  3. ตรวจสอบสถานะ refund
  4. ทดสอบการจัดการ webhook

Refunds ทดสอบทั้งหมดจะประมวลผลทันทีเพื่อวัตถุประสงค์ในการทดสอบ

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

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

  1. ใช้งาน refund endpoint
  2. ตั้งค่าการจัดการ refund webhook
  3. ทดสอบขั้นตอน refund
  4. เพิ่มการแจ้งเตือนลูกค้า
  5. ติดตามการวิเคราะห์ refund