Refunds บางส่วน
Refunds บางส่วนช่วยให้คุณคืนส่วนหนึ่งของจำนวน charge ให้กับลูกค้าของคุณ สิ่งนี้มีประโยชน์สำหรับสถานการณ์เช่นการคืนสินค้าบางส่วน การปรับราคา refunds ค่าจัดส่ง หรือสถานการณ์ใดๆ ที่ไม่จำเป็นต้อง refund เต็มจำนวน
ภาพรวม
Refunds บางส่วนให้ความยืดหยุ่นในวิธี ที่คุณจัดการการคืนสินค้าและการปรับเปลี่ยน:
- จำนวนที่ยืดหยุ่น: Refund จำนวนใดก็ได้จนถึงจำนวน charge เดิม
- Refunds หลายครั้ง: สร้าง refunds บางส่วนหลายครั้งจนกว่าจะ refund เต็มจำนวน
- ยอดคงเหลือที่เหลืออยู่: ติดตามว่ายังสามารถ refund ได้เท่าไหร่
- กระบวนการเดียวกัน: ใช้ API เดียวกันกับ refunds เต็มจำนวน เพียงระบุจำนวน
- รอยบันทึกการตรวจสอบ: Refunds บางส่วนทั้งหมดถูกติดตามบน charge
คุณสมบัติหลัก
- การควบคุมจำนวนที่แม่นยำ: ระบุจำนวน refund ที่แน่นอน
- รับรู้สกุลเงิน: ใช้สกุลเงินของ charge โดยอัตโนมัติ
- การติดตามยอดคงเหลือ: ดูจำนวนที่ refund ได้ที่เหลือ
- รองรับ Metadata: แท็กแต่ละ refund ด้วยบริบท
- ไม่มีข้อจำกัดจำนวน: สร้าง refunds บางส่วนได้มากเท่าที่ต้องการ
- การตรวจสอบอัตโนมัติ: ระบบป้องกันการ refund เกิน
เมื่อใดควรใช้ Refunds บางส่วน
สถานการณ์ทั่วไปสำหรับ refunds บางส่วน:
- การคืนสินค้าบางส่วน: ลูกค้าคืนบางรายการจากคำสั่งซื้อ
- การปรับราคา: แก้ไขข้อผิดพลาดในการเรียกเก็บเงินหรือใช้ส่วนลด
- Refunds ค่าจัดส่ง: Refund ค่าส่งเท่านั้น
- สินค้าที่เสียหาย: การชดเชยบางส่วนสำหรับสินค้าที่เสียหาย
- เครดิตบริการ: เสนอ refunds บางส่วนสำหรับปัญหาบริการ
- การปรับโปรโมชั่น: ใช้คูปองหลังการซื้อ
- การแก้ไขจำนวน: Refund สำหรับจำนวนที่ไม่ถูกต้อง
- การปรับชุด: Refund รายการเฉพาะจากชุด
การสร้าง Refunds บางส่วนผ่าน API
Refund บางส่วนพื้นฐาน
ระบุพารามิเตอร์ amount เพื่อสร้าง refund บางส่วน:
- Node.js
- Python
- PHP
const omise = require('omise')({
secretKey: 'skey_test_123456789',
});
// สร้าง refund บางส่วน
async function createPartialRefund(chargeId, amount) {
try {
const refund = await omise.charges.createRefund(chargeId, {
amount: amount, // จำนวนในหน่วยสกุลเงินที่เล็กที่สุด
metadata: {
reason: 'Partial product return',
items_returned: '2 out of 5 items'
}
});
console.log('Partial refund created:', refund.id);
console.log('Amount refunded:', refund.amount);
console.log('Remaining refundable:', calculateRemaining(refund));
return refund;
} catch (error) {
console.error('Partial refund failed:', error.message);
throw error;
}
}
// คำนวณจำนวนที่ refund ได้ที่เหลือ
function calculateRemaining(refund) {
return omise.charges.retrieve(refund.charge).then(charge => {
const totalRefunded = charge.refunded_amount || 0;
const remaining = charge.amount - totalRefunded;
return remaining;
});
}
// Refund ค่าส่งเท่านั้น
async function refundShippingCost(chargeId, shippingAmount) {
const refund = await omise.charges.createRefund(chargeId, {
amount: shippingAmount,
metadata: {
reason: 'shipping_refund',
type: 'delivery_fee'
}
});
console.log(`Refunded shipping: ${shippingAmount / 100} THB`);
return refund;
}
// ตัวอย่างการใช้งาน
createPartialRefund('chrg_test_123456789', 50000); // Refund 500 THB
refundShippingCost('chrg_test_123456789', 10000); // Refund ค่าส่ง 100 THB
import omise
omise.api_secret = 'skey_test_123456789'
def create_partial_refund(charge_id, amount):
"""สร้าง refund บางส่วนสำหรับ charge"""
try:
refund = omise.Charge.retrieve(charge_id).refund(
amount=amount, # จำนวนในหน่วยสกุลเงินที่เล็กที่สุด
metadata={
'reason': 'Partial product return',
'items_returned': '2 out of 5 items'
}
)
print(f"Partial refund created: {refund.id}")
print(f"Amount refunded: {refund.amount}")
# คำนวณที่เหลือ
charge = omise.Charge.retrieve(charge_id)
remaining = charge.amount - charge.refunded_amount
print(f"Remaining refundable: {remaining}")
return refund
except omise.errors.BaseError as e:
print(f"Partial refund failed: {str(e)}")
raise
def refund_shipping_cost(charge_id, shipping_amount):
"""Refund ค่าส่งเท่านั้น"""
refund = omise.Charge.retrieve(charge_id).refund(
amount=shipping_amount,
metadata={
'reason': 'shipping_refund',
'type': 'delivery_fee'
}
)
print(f"Refunded shipping: {shipping_amount / 100} THB")
return refund
def refund_line_items(charge_id, items):
"""Refund รายการบรรทัดเฉพาะจากคำสั่งซื้อ"""
total_refund = sum(item['price'] * item['quantity'] for item in items)
refund = omise.Charge.retrieve(charge_id).refund(
amount=total_refund,
metadata={
'reason': 'partial_line_item_refund',
'items': str(items),
'item_count': len(items)
}
)
return refund
# ตัวอย่างการใช้งาน
create_partial_refund('chrg_test_123456789', 50000) # Refund 500 THB
refund_shipping_cost('chrg_test_123456789', 10000) # Refund 100 THB
# Refund รายการเฉพาะ
items_to_refund = [
{'name': 'T-Shirt', 'price': 29900, 'quantity': 1},
{'name': 'Socks', 'price': 9900, 'quantity': 2}
]
refund_line_items('chrg_test_123456789', items_to_refund)
<?php
require_once 'vendor/autoload.php';
define('OMISE_SECRET_KEY', 'skey_test_123456789');
// สร้าง refund บางส่วน
function createPartialRefund($chargeId, $amount) {
try {
$charge = OmiseCharge::retrieve($chargeId);
$refund = $charge->refund([
'amount' => $amount, // จำนวนในหน่วยสกุลเงินที่เล็กที่สุด
'metadata' => [
'reason' => 'Partial product return',
'items_returned' => '2 out of 5 items'
]
]);
echo "Partial refund created: {$refund['id']}\n";
echo "Amount refunded: {$refund['amount']}\n";
// คำนวณที่เหลือ
$charge->reload();
$remaining = $charge['amount'] - $charge['refunded_amount'];
echo "Remaining refundable: {$remaining}\n";
return $refund;
} catch (Exception $e) {
echo "Partial refund failed: {$e->getMessage()}\n";
throw $e;
}
}
// Refund ค่าส่งเท่านั้น
function refundShippingCost($chargeId, $shippingAmount) {
$charge = OmiseCharge::retrieve($chargeId);
$refund = $charge->refund([
'amount' => $shippingAmount,
'metadata' => [
'reason' => 'shipping_refund',
'type' => 'delivery_fee'
]
]);
echo "Refunded shipping: " . ($shippingAmount / 100) . " THB\n";
return $refund;
}
// รับจำนวนที่ refund ได้ที่เหลือ
function getRemainingRefundable($chargeId) {
$charge = OmiseCharge::retrieve($chargeId);
$remaining = $charge['amount'] - $charge['refunded_amount'];
return [
'charge_amount' => $charge['amount'],
'refunded_amount' => $charge['refunded_amount'],
'remaining' => $remaining,
'currency' => $charge['currency']
];
}
// ตัวอย่างการใช้งาน
createPartialRefund('chrg_test_123456789', 50000);
refundShippingCost('chrg_test_123456789', 10000);
$remaining = getRemainingRefundable('chrg_test_123456789');
print_r($remaining);
?>
API Response
{
"object": "refund",
"id": "rfnd_test_5xyz789abc",
"location": "/charges/chrg_test_123456789/refunds/rfnd_test_5xyz789abc",
"amount": 50000,
"currency": "thb",
"charge": "chrg_test_123456789",
"transaction": "trxn_test_5xyz789abc",
"created": "2024-01-15T10:30:00Z",
"status": "pending",
"metadata": {
"reason": "Partial product return",
"items_returned": "2 out of 5 items"
}
}
Refunds บางส่วนหลายครั้ง
คุณสามารถสร้าง refunds บางส่วนหลายครั้งสำหรับ charge เดียว:
async function handleMultiplePartialRefunds(chargeId) {
// Charge เดิม: 100,000 (1,000 THB)
// Refund บางส่วนครั้งแรก - ค่าส่ง
const refund1 = await omise.charges.createRefund(chargeId, {
amount: 10000, // 100 THB
metadata: { reason: 'shipping_refund' }
});
// Refund บางส่วนครั้งที่สอง - รายการหนึ่ง
const refund2 = await omise.charges.createRefund(chargeId, {
amount: 30000, // 300 THB
metadata: { reason: 'item_return', item: 'Product A' }
});
// Refund บางส่วนครั้งที่สาม - รายการอื่น
const refund3 = await omise.charges.createRefund(chargeId, {
amount: 25000, // 250 THB
metadata: { reason: 'item_return', item: 'Product B' }
});
// ตรวจสอบที่เหลือ: 100,000 - 10,000 - 30,000 - 25,000 = 35,000 (350 THB)
const charge = await omise.charges.retrieve(chargeId);
console.log('Total refunded:', charge.refunded_amount);
console.log('Remaining:', charge.amount - charge.refunded_amount);
return [refund1, refund2, refund3];
}
กรณีการใช้งานทั่วไป
1. Refunds รายการบรรทัด
Refund รายการเฉพาะจากคำสั่งซื้อ:
class OrderRefundManager:
def __init__(self, charge_id):
self.charge_id = charge_id
self.charge = omise.Charge.retrieve(charge_id)
def refund_items(self, item_ids):
"""Refund รายการเฉพาะตาม IDs"""
order = self.get_order_details()
# คำนวณจำนวน refund
refund_amount = 0
refunded_items = []
for item_id in item_ids:
item = next((i for i in order['items'] if i['id'] == item_id), None)
if item:
refund_amount += item['price'] * item['quantity']
refunded_items.append(item)
# สร้าง refund
refund = self.charge.refund(
amount=refund_amount,
metadata={
'reason': 'line_item_refund',
'refunded_items': str(refunded_items),
'item_count': len(refunded_items)
}
)
# อัปเดตรายการคำสั่งซื้อ
self.update_order_items(item_ids, 'refunded')
return refund
def refund_by_percentage(self, percentage):
"""Refund เปอร์เซ็นต์ของ charge ทั้งหมด"""
if not 0 < percentage <= 100:
raise ValueError("Percentage must be between 0 and 100")
refund_amount = int(self.charge.amount * percentage / 100)
refund = self.charge.refund(
amount=refund_amount,
metadata={
'reason': 'percentage_refund',
'percentage': percentage
}
)
return refund
# ตัวอย่างการใช้งาน
manager = OrderRefundManager('chrg_test_123456789')
manager.refund_items(['item_1', 'item_3'])
manager.refund_by_percentage(10) # ส่วนลด 10%
2. การคิดตามสัดส่วนสำหรับ Subscription
Refunds ตามสัดส่วนสำหรับการยกเลิก subscription:
class SubscriptionRefund {
constructor(chargeId, subscriptionStart, subscriptionEnd) {
this.chargeId = chargeId;
this.subscriptionStart = new Date(subscriptionStart);
this.subscriptionEnd = new Date(subscriptionEnd);
}
async calculateProratedRefund(cancellationDate) {
const charge = await omise.charges.retrieve(this.chargeId);
// คำนวณวัน
const totalDays = this.daysBetween(this.subscriptionStart, this.subscriptionEnd);
const usedDays = this.daysBetween(this.subscriptionStart, cancellationDate);
const remainingDays = totalDays - usedDays;
// คำนวณจำนวน refund
const dailyRate = charge.amount / totalDays;
const refundAmount = Math.floor(dailyRate * remainingDays);
return {
totalAmount: charge.amount,
totalDays: totalDays,
usedDays: usedDays,
remainingDays: remainingDays,
refundAmount: refundAmount,
effectiveRate: dailyRate
};
}
async processProratedRefund(cancellationDate, reason) {
const calculation = await this.calculateProratedRefund(cancellationDate);
const refund = await omise.charges.createRefund(this.chargeId, {
amount: calculation.refundAmount,
metadata: {
reason: 'subscription_cancellation_prorated',
cancellation_date: cancellationDate.toISOString(),
subscription_start: this.subscriptionStart.toISOString(),
subscription_end: this.subscriptionEnd.toISOString(),
total_days: calculation.totalDays,
used_days: calculation.usedDays,
remaining_days: calculation.remainingDays,
cancellation_reason: reason
}
});
return {
refund: refund,
calculation: calculation
};
}
daysBetween(date1, date2) {
const oneDay = 24 * 60 * 60 * 1000;
return Math.round(Math.abs((date2 - date1) / oneDay));
}
}
// ตัวอย่างการใช้งาน
const subsRefund = new SubscriptionRefund(
'chrg_test_123456789',
'2024-01-01',
'2024-01-31'
);
const result = await subsRefund.processProratedRefund(
new Date('2024-01-15'),
'Customer requested cancellation'
);
console.log(`Refunding ${result.calculation.refundAmount} for ${result.calculation.remainingDays} unused days`);
การติดตามยอด Refund
ติดตามว่าได้ refund เท่าไหร่และเหลือเท่าไหร่:
class RefundTracker:
def __init__(self, charge_id):
self.charge_id = charge_id
self.refresh()
def refresh(self):
"""รีเฟรชข้อมูล charge"""
self.charge = omise.Charge.retrieve(self.charge_id)
def get_refund_summary(self):
"""รับสรุป refund ที่ครอบคลุม"""
self.refresh()
refunds = self.charge.refunds.data if hasattr(self.charge, 'refunds') else []
return {
'charge_id': self.charge_id,
'original_amount': self.charge.amount,
'refunded_amount': self.charge.refunded_amount,
'remaining_amount': self.charge.amount - self.charge.refunded_amount,
'refund_count': len(refunds),
'fully_refunded': self.charge.refunded,
'currency': self.charge.currency,
'refunds': [
{
'id': r.id,
'amount': r.amount,
'status': r.status,
'created': r.created,
'reason': r.metadata.get('reason', 'N/A') if r.metadata else 'N/A'
}
for r in refunds
]
}
def can_refund(self, amount):
"""ตรวจสอบว่าสามารถ refund จำนวนได้หรือไม่"""
self.refresh()
remaining = self.charge.amount - self.charge.refunded_amount
return amount <= remaining
def get_refund_percentage(self):
"""รับเปอร์เซ็นต์ของ charge ที่ถูก refund"""
self.refresh()
if self.charge.amount == 0:
return 0
return (self.charge.refunded_amount / self.charge.amount) * 100
# ตัวอย่างการใช้งาน
tracker = RefundTracker('chrg_test_123456789')
summary = tracker.get_refund_summary()
if tracker.can_refund(50000):
print("สามารถ refund 500 THB ได้")
else:
print("ยอดคงเหลือที่เหลือไม่เพียงพอ")
แนวปฏิบัติที่ดีที่สุด
1. ตรวจสอบจำนวน Refund
ตรวจสอบเสมอก่อนสร้าง refund:
async function safePartialRefund(chargeId, amount, reason) {
// ดึง charge
const charge = await omise.charges.retrieve(chargeId);
// ตรวจสอบจำนวน
if (amount <= 0) {
throw new Error('Refund amount must be positive');
}
const remaining = charge.amount - charge.refunded_amount;
if (amount > remaining) {
throw new Error(`Amount exceeds remaining refundable balance. Remaining: ${remaining}`);
}
// ตรวจสอบสกุลเงิน (ตรวจสอบว่าจำนวนอยู่ในหน่วยที่เล็กที่สุด)
if (amount % 1 !== 0) {
throw new Error('Amount must be an integer (smallest currency unit)');
}
// สร้าง refund
const refund = await omise.charges.createRefund(chargeId, {
amount: amount,
metadata: {
reason: reason,
validated_at: new Date().toISOString()
}
});
return refund;
}
2. รักษาบันทึกที่ละเอียด
class RefundAuditLog
def self.log_refund(charge_id, refund, context = {})
log_entry = {
timestamp: Time.now.iso8601,
charge_id: charge_id,
refund_id: refund.id,
amount: refund.amount,
currency: refund.currency,
status: refund.status,
reason: refund.metadata['reason'],
user_id: context[:user_id],
ip_address: context[:ip_address],
user_agent: context[:user_agent],
notes: context[:notes]
}
# บันทึกลงฐานข้อมูลหรือระบบบันทึก
save_audit_log(log_entry)
# บันทึกลงไฟล์ด้วยเพื่อการปฏิบัติตามกฎ
File.open('refund_audit.log', 'a') do |f|
f.puts JSON.generate(log_entry)
end
end
end
3. จัดการ Edge Cases
function createRefundWithValidation($chargeId, $amount, $metadata = []) {
try {
$charge = OmiseCharge::retrieve($chargeId);
// ตรวจสอบว่า charge สามารถ refund ได้หรือไม่
if (!$charge['paid']) {
throw new Exception('Charge ยังไม่ได้ชำระ');
}
if ($charge['refunded']) {
throw new Exception('Charge ถูก refund เต็มจำนวนแล้ว');
}
// ตรวจสอบยอดคงเหลือที่เหลือ
$remaining = $charge['amount'] - $charge['refunded_amount'];
if ($amount > $remaining) {
throw new Exception("จำนวนเกินยอดคงเหลือที่เหลือ {$remaining}");
}
// สร้าง refund
$refund = $charge->refund([
'amount' => $amount,
'metadata' => $metadata
]);
return [
'success' => true,
'refund' => $refund
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
FAQ
จำนวน refund ขั้นต่ำคือเท่าไหร่?
จำนวน refund ขั้นต่ำขึ้นอยู่กับสกุลเงิน สำหรับบาทไทย (THB) ขั้นต่ำคือ 1 สตางค์ (0.01 THB) ระบุจำนวนในหน่วยสกุลเงินที่เล็กที่สุดเสมอ (สตางค์สำหรับ THB, เซนต์สำหรับ USD ฯลฯ)
ฉันสามารถ refund มากกว่าจำนวน charge เดิมได้หรือไม่?
ไม่ได้ ผลรวมของ refunds ทั้งหมด (เต็มจำนวนและบางส่วน) ไม่สา มารถเกินจำนวน charge เดิม API จะคืนข้อผิดพลาดถ้าคุณพยายาม refund เกิน
ฉันสามารถสร้าง refunds บางส่วนกี่ครั้งสำหรับ charge หนึ่ง?
ไม่มีข้อจำกัดจำนวน refunds บางส่วนที่คุณสามารถสร้างได้ ตราบใดที่ผลรวมไม่เกินจำนวน charge เดิม คุณสามารถสร้าง refunds บางส่วนได้มากเท่าที่ต้องการจนกว่า charge จะ refund เต็มจำนวน
จะเกิดอะไรขึ้นถ้าฉันพยายาม refund มากกว่ายอดคงเหลือที่เหลือ?
API จะคืนข้อผิดพลาดด้วยโค้ด invalid_request ที่ระบุว่าจำนวน refund เกินยอดคงเหลือที่มี ตรวจสอบจำนวนที่ refund ได้ที่เหลือเสมอก่อนสร้าง refund
แหล่งข้อมูลที่เกี่ยวข้อง
- การสร้าง Refunds - เรียนรู้เกี่ยวกับ refunds เต็มจำนวน
- ข้อจำกัดของ Refund - เข้าใจข้อจำกัดของ refund
- ประวัติธุรกรรม - ดูประวัติ refund
- คู่มือ Webhooks - จัดการ refund events
- API Reference - เอกสาร refund API ที่สมบูรณ์
ขั้นตอนถัดไป
- เข้าใ จ ข้อจำกัดของ refund และข้อห้าม
- ตั้งค่า webhook handlers สำหรับ refund events
- เรียนรู้เกี่ยวกับ การติดตามธุรกรรม
- ใช้งาน การกระทบยอด สำหรับการจับคู่ refund
- ตรวจสอบแนวปฏิบัติที่ดีที่สุดของ การจัดการข้อผิดพลาด