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

ข้อจำกัดและข้อห้ามของ Refund

การเข้าใจข้อจำกัดและข้อห้ามของ refund เป็นสิ่งสำคัญสำหรับการจัดการความคาดหวังของลูกค้าและการรับประกันการดำเนินงาน refund ที่ราบรื่น คู่มือนี้ครอบคลุมกฎ ข้อจำกัด และแนวปฏิบัติที่ดีที่สุดของ refund ทั้งหมด

ภาพรวม

Refunds ใน Omise อยู่ภายใต้ข้อจำกัดต่างๆ ขึ้นอยู่กับ:

  • สถานะ Charge: เฉพาะ charges ที่สำเร็จเท่านั้นที่สามารถ refund ได้
  • ข้อจำกัดเวลา: วิธีการชำระเงินบางอย่างมีข้อจำกัดเวลา
  • ข้อจำกัดจำนวน: ไม่สามารถเกินจำนวน charge เดิม
  • ความต้องการยอดคงเหลือ: ต้องมียอดคงเหลือในบัญชีเพียงพอ
  • วิธีการชำระเงิน: วิธีต่างๆ มีกฎที่แตกต่างกัน
  • สกุลเงิน: Refunds ต้องใช้สกุลเงินเดิม
  • กฎของเครือข่าย: นโยบายของเครือข่ายบัตรและธนาคารมีผล

ข้อจำกัดทั่วไป

ความต้องการสถานะ Charge

สถานะ Chargeสามารถ Refund?หมายเหตุ
successfulได้สถานการณ์ refund มาตรฐาน
pendingไม่ได้รอให้ charge เสร็จสมบูรณ์
failedไม่ได้ไม่มีเงินที่ถูกจับ
expiredไม่ได้Charge ไม่เคยเสร็จสมบูรณ์
reversedไม่ได้กลับรายการแล้ว
async function checkRefundEligibility(chargeId) {
const charge = await omise.charges.retrieve(chargeId);

if (charge.status !== 'successful') {
return {
eligible: false,
reason: `Charge status is ${charge.status}, must be successful`
};
}

if (charge.refunded) {
return {
eligible: false,
reason: 'Charge has already been fully refunded'
};
}

const remaining = charge.amount - charge.refunded_amount;
if (remaining === 0) {
return {
eligible: false,
reason: 'No remaining balance to refund'
};
}

return {
eligible: true,
remaining: remaining,
currency: charge.currency
};
}

ข้อจำกัดจำนวนเงิน

จำนวน Refund สูงสุด

  • ไม่สามารถเกินจำนวน charge เดิม
  • ผลรวมของ refunds ทั้งหมดไม่สามารถเกินจำนวน charge
  • ต้องคำนึงถึง refunds ที่มีอยู่

จำนวน Refund ขั้นต่ำ

  • ขึ้นอยู่กับสกุลเงิน
  • THB: 20 สตางค์ (0.20 THB) ขั้นต่ำ
  • USD: 1 เซนต์ขั้นต่ำ
  • JPY: 1 เยนขั้นต่ำ
def validate_refund_amount(charge_id, refund_amount):
"""ตรวจสอบจำนวน refund กับข้อจำกัด"""

charge = omise.Charge.retrieve(charge_id)

# ตรวจสอบจำนวนขั้นต่ำ (เฉพาะสกุลเงิน)
minimums = {
'thb': 20, # 0.20 THB
'usd': 1, # 0.01 USD
'jpy': 1, # 1 JPY
'sgd': 1, # 0.01 SGD
'eur': 1 # 0.01 EUR
}

min_amount = minimums.get(charge.currency.lower(), 1)
if refund_amount < min_amount:
raise ValueError(f"Refund amount below minimum of {min_amount} {charge.currency}")

# ตรวจสอบจำนวนสูงสุด
remaining = charge.amount - charge.refunded_amount
if refund_amount > remaining:
raise ValueError(
f"Refund amount {refund_amount} exceeds remaining balance {remaining}"
)

return True

ข้อจำกัดเวลา

ไทม์ไลน์ทั่วไป

  • ไม่มีข้อจำกัดเวลาที่เข้มงวดสำหรับ refunds ส่วนใหญ่
  • Refunds เก่ากว่าอาจมีอัตราการปฏิเสธที่สูงขึ้น
  • แนะนำ: Refund ภายใน 180 วัน

เฉพาะวิธีการชำระเงิน

  • บัตรเครดิต: ไม่มีข้อจำกัดเวลา
  • บัตรเดบิต: ไม่มีข้อจำกัดเวลา
  • Internet banking: โดยทั่วไป 90 วัน
  • Mobile banking: โดยทั่วไป 90 วัน
  • E-wallets: แตกต่างกันตามผู้ให้บริการ
def check_refund_timing(charge_id)
charge = Omise::Charge.retrieve(charge_id)
charge_age_days = (Time.now - Time.at(charge.created)) / 86400

warnings = []

# ตรวจสอบคำเตือนตามอายุ
if charge_age_days > 180
warnings << "Charge มีอายุมากกว่า 180 วัน - refund อาจมีอัตราการปฏิเสธที่สูงขึ้น"
end

if charge_age_days > 365
warnings << "Charge มีอายุมากกว่า 1 ปี - พิจารณาการชดเชยทางเลือก"
end

# การตรวจสอบเฉพาะวิธีการชำระเงิน
case charge.source.type
when 'internet_banking'
if charge_age_days > 90
warnings << "Internet banking refunds ดีที่สุดภายใน 90 วัน"
end
when 'mobile_banking'
if charge_age_days > 90
warnings << "Mobile banking refunds ดีที่สุดภายใน 90 วัน"
end
end

{
charge_age_days: charge_age_days.round(1),
warnings: warnings,
recommended: charge_age_days <= 180
}
end

ความต้องการยอดคงเหลือ

ยอดคงเหลือไม่เพียงพอ

Refunds ต้องการยอดคงเหลือเพียงพอในบัญชี Omise ของคุณ:

async function checkRefundBalance(chargeId, refundAmount) {
try {
// รับยอดคงเหลือบัญชี
const balance = await omise.balance.retrieve();

// รับรายละเอียด charge
const charge = await omise.charges.retrieve(chargeId);

// ตรวจสอบว่าเรามียอดคงเหลือเพียงพอหรือไม่
if (balance.available < refundAmount) {
return {
canRefund: false,
reason: 'insufficient_balance',
available: balance.available,
required: refundAmount,
shortfall: refundAmount - balance.available,
nextSettlement: await getNextSettlementDate()
};
}

return {
canRefund: true,
available: balance.available,
afterRefund: balance.available - refundAmount
};

} catch (error) {
return {
canRefund: false,
reason: 'error',
error: error.message
};
}
}

ข้อจำกัดวิธีการชำระเงิน

บัตรเครดิตและเดบิต

กฎทั่วไป

  • ต้อง refund ไปยังบัตรเดิม
  • ไม่สามารถ refund ไปยังบัตรอื่นได้
  • การหมดอายุของบัตรไม่ป้องกัน refunds
  • บัญชีที่ปิดอาจทำให้เกิดปัญหา
function validateCardRefund($charge) {
$restrictions = [
'can_refund_to_different_card' => false,
'requires_active_card' => false,
'time_limit_days' => null, // ไม่มีข้อจำกัดเวลา
'min_amount' => 1, // 1 cent/satang
'max_refund_count' => null // ไม่จำกัด
];

// ตรวจสอบว่าบัตรหมดอายุหรือไม่
if (isset($charge['card'])) {
$currentYear = intval(date('Y'));
$currentMonth = intval(date('m'));

$cardYear = intval($charge['card']['expiration_year']);
$cardMonth = intval($charge['card']['expiration_month']);

if ($cardYear < $currentYear ||
($cardYear == $currentYear && $cardMonth < $currentMonth)) {
$restrictions['warnings'][] = 'บัตรหมดอายุแล้ว - refund อาจยังใช้งานได้';
}
}

return $restrictions;
}

Internet Banking

ข้อจำกัด

  • ช่วงเวลาแนะนำ 90 วัน
  • กฎเฉพาะธนาคารอาจมีผล
  • ธนาคารบางแห่งไม่รองรับ refunds
  • อาจต้องการการยืนยันตัวตนของลูกค้าอีกครั้ง
def validate_internet_banking_refund(charge)
restrictions = {
recommended_time_limit: 90, # วัน
requires_active_account: true,
bank_specific_rules: true
}

# ธนาคารที่มีข้อจำกัดที่ทราบ
limited_banks = {
'bay' => { max_days: 90, notes: 'ข้อจำกัด 90 วันที่เข้มงวด' },
'bbl' => { max_days: 180, notes: 'ช่วงเวลานานขึ้นที่มีให้' }
}

if charge.source.type == 'internet_banking'
bank_code = charge.source.bank_code

if limited_banks.key?(bank_code)
restrictions[:bank_limits] = limited_banks[bank_code]
end
end

restrictions
end

Mobile Banking และ E-Wallets

ข้อจำกัดทั่วไป

  • ข้อจำกัดเฉพาะผู้ให้บริการ
  • บัญชีต้องเป็นแบบ active
  • อาจต้องการการยืนยันของลูกค้า
  • ช่วงเวลาที่แตกต่างกัน
const PAYMENT_METHOD_LIMITS = {
'promptpay': {
timeLimit: 180,
canRefund: true,
requiresActiveAccount: true,
notes: 'PromptPay refunds ต้องการการลงทะเบียนที่ active'
},
'truemoney': {
timeLimit: 90,
canRefund: true,
requiresActiveAccount: true,
notes: 'TrueMoney wallet ต้อง active'
},
'alipay': {
timeLimit: 180,
canRefund: true,
requiresActiveAccount: true,
notes: 'บัญชี Alipay ต้องเข้าถึงได้'
}
};

function getPaymentMethodLimits(paymentMethod) {
return PAYMENT_METHOD_LIMITS[paymentMethod] || {
timeLimit: 180,
canRefund: true,
requiresActiveAccount: false,
notes: 'กฎ refund มาตรฐานมีผล'
};
}

ข้อจำกัดสกุลเงิน

ความต้องการสกุลเงินเดียวกัน

Refunds ต้องใช้สกุลเงิน charge เดิม:

def validate_refund_currency(charge_id, refund_amount, refund_currency=None):
"""ตรวจสอบสกุลเงิน refund ตรงกับสกุลเงิน charge"""

charge = omise.Charge.retrieve(charge_id)

# สกุลเงินได้รับการสืบทอดโดยอัตโนมัติ แต่ตรวจสอบถ้าระบุ
if refund_currency and refund_currency.lower() != charge.currency.lower():
raise ValueError(
f"Refund currency {refund_currency} must match charge currency {charge.currency}"
)

# การตรวจสอบเฉพาะสกุลเงิน
currency_rules = {
'thb': {
'decimal_places': 2,
'min_amount': 20, # 0.20 THB
'unit_name': 'satang'
},
'usd': {
'decimal_places': 2,
'min_amount': 1, # 0.01 USD
'unit_name': 'cent'
},
'jpy': {
'decimal_places': 0,
'min_amount': 1, # 1 JPY (ไม่มีทศนิยม)
'unit_name': 'yen'
}
}

rules = currency_rules.get(charge.currency.lower(), {
'decimal_places': 2,
'min_amount': 1,
'unit_name': 'unit'
})

return {
'currency': charge.currency,
'amount_in_smallest_unit': refund_amount,
'rules': rules,
'valid': True
}

แนวปฏิบัติที่ดีที่สุดสำหรับการทำงานกับข้อจำกัด

1. การตรวจสอบก่อน Refund

class RefundValidator {
public static function validateRefund($chargeId, $amount, $metadata = []) {
$errors = [];
$warnings = [];

try {
$charge = OmiseCharge::retrieve($chargeId);

// การตรวจสอบสถานะ
if ($charge['status'] !== 'successful') {
$errors[] = "สถานะ Charge คือ {$charge['status']} ต้องเป็น successful";
}

// การตรวจสอบจำนวน
$remaining = $charge['amount'] - $charge['refunded_amount'];
if ($amount > $remaining) {
$errors[] = "จำนวน {$amount} เกินยอดคงเหลือ {$remaining}";
}

// การตรวจสอบเวลา
$ageInDays = (time() - $charge['created']) / 86400;
if ($ageInDays > 180) {
$warnings[] = "Charge มีอายุ {$ageInDays} วัน - อาจมีอัตราการปฏิเสธที่สูงขึ้น";
}

return [
'valid' => empty($errors),
'errors' => $errors,
'warnings' => $warnings,
'charge' => $charge,
'remaining' => $remaining
];

} catch (Exception $e) {
return [
'valid' => false,
'errors' => [$e->getMessage()],
'warnings' => []
];
}
}
}

2. การลดระดับอย่างเหมาะสม

class RefundWithFallback
def self.attempt_refund(charge_id, amount, options = {})
max_retries = options[:max_retries] || 3
retry_count = 0

begin
# ตรวจสอบก่อน
validation = validate_refund(charge_id, amount)
unless validation[:valid]
return {
success: false,
reason: 'validation_failed',
errors: validation[:errors]
}
end

# พยายาม refund
charge = Omise::Charge.retrieve(charge_id)
refund = charge.refund(amount: amount, metadata: options[:metadata])

{
success: true,
refund: refund,
warnings: validation[:warnings]
}

rescue Omise::InvalidRequestError => e
if e.message.include?('insufficient') && retry_count < max_retries
# จัดคิวสำหรับภายหลัง
queue_refund(charge_id, amount, options)
{
success: false,
reason: 'insufficient_balance',
queued: true,
message: 'Refund จัดคิวเมื่อมียอดคงเหลือ'
}
else
{
success: false,
reason: 'invalid_request',
error: e.message
}
end
end
end
end

3. ข้อความแสดงข้อผิดพลาดที่เป็นมิตรกับผู้ใช้

def get_user_friendly_error(error_code, context={}):
"""แปลงข้อผิดพลาดทางเทคนิคเป็นข้อความที่เป็นมิตรกับผู้ใช้"""

messages = {
'charge_already_refunded': {
'user': 'การชำระเงินนี้ถูก refund แล้ว',
'action': 'ตรวจสอบประวัติ refund ของคุณสำหรับรายละเอียด'
},
'insufficient_fund': {
'user': 'ไม่สามารถประมวลผล refund ในขณะนี้เนื่องจากยอดคงเหลือบัญชี',
'action': f"Refund ของคุณจะถูกประมวลผลอัตโนมัติภายใน {context.get('wait_days', 2)} วันทำการ"
},
'invalid_charge': {
'user': 'ไม่พบการชำระเงินนี้หรือไม่ถูกต้อง',
'action': 'โปรดตรวจสอบ payment ID และลองอีกครั้ง'
},
'charge_not_paid': {
'user': 'การชำระเงินนี้ยังไม่เสร็จสมบูรณ์',
'action': 'Refunds สามารถสร้างได้เฉพาะสำหรับการชำระเงินที่สำเร็จเท่านั้น'
},
'amount_exceeds_refundable': {
'user': f"จำนวน refund เกินยอดคงเหลือที่มีอยู่ {context.get('remaining', 'unknown')}",
'action': 'โปรดป้อนจำนวนที่ต่ำกว่าหรือติดต่อฝ่ายสนับสนุน'
}
}

return messages.get(error_code, {
'user': 'เกิดข้อผิดพลาดขณะประมวลผล refund ของคุณ',
'action': 'โปรดลองอีกครั้งหรือติดต่อฝ่ายสนับสนุนหากปัญหายังคงอยู่'
})

FAQ

ทำไมฉันไม่สามารถ refund charge ที่ pending ได้?

Charges ที่ pending ยังไม่ได้รับการประมวลผลอย่างเต็มที่ คุณต้องรอให้ charge ถึงสถานะ successful ก่อนที่จะสร้าง refund ถ้าคุณต้องการยกเลิก charge ที่ pending ให้ใช้ endpoint การยกเลิก charge แทน

จะเกิดอะไรขึ้นถ้าฉันพยายาม refund มากกว่าจำนวน charge?

API จะคืนข้อผิดพลาดที่ระบุว่าจำนวน refund เกินยอดคงเหลือที่ refund ได้ คุณไม่สามารถ refund มากกว่าจำนวน charge เดิม รวมถึง refunds ก่อนหน้าทั้งหมด

ฉันสามารถ refund charge ได้หรือไม่ถ้าบัตรของลูกค้าหมดอายุ?

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

จะเกิดอะไรขึ้นถ้าบัญชีธนาคารของลูกค้าปิด?

ถ้า refund ไปยังบัญชีที่ปิด refund มักจะล้มเหลวหรือถูกปฏิเสธโดยธนาคาร คุณจะต้องจัดการชดเชยทางเลือก เช่น การโอนเงินธนาคารด้วยตนเองไปยังบัญชีใหม่หรือ store credit

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

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