メインコンテンツへスキップ

Refunds

Omise APIまたはダッシュボードを通じて、お客様への全額または一部返金を迅速かつ簡単に発行できます。

概要

Omiseでは、ほとんどの決済方法で行われた支払いを返金することができます。返金は、使用した決済方法に応じて、全額(全体の金額)または一部(金額の一部)が可能です。

主な機能:

  • 即時返金処理
  • 全額および一部返金のサポート(サポートされている方法の場合)
  • 最大365日間の返金ウィンドウ
  • 自動決済控除
  • Webhook通知

サポートされている決済方法

全額および一部返金のサポート

  • Credit/Debit Cards - 全額および一部(charge当たり最大15回の一部返金)
  • DuitNow - 180日以内の全額および一部
  • PayNow - 6ヶ月以内の全額および一部
  • GrabPay - 3ヶ月以内の全額および一部
  • ShopeePay - 180日以内の全額および一部

全額返金のみ

  • ⚠️ Installments - 全額返金のみ(ウィンドウは銀行により異なる: 30日から1年)
  • ⚠️ TrueMoney - 30日以内の全額返金のみ

返金サポートなし

  • PromptPay - 返金不可
  • Mobile Banking(タイ) - 返金不可
  • Internet Banking(タイ) - 返金不可
  • Konbini/Pay-easy(日本) - 返金不可
  • Online Direct Debit - 返金不可
決済方法のドキュメントを確認

実装前に、必ず特定の決済方法の返金ポリシーを確認してください。返金サポートとウィンドウは大きく異なります。

返金の仕組み

返金の作成

全額返金

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

一部返金

// chargeの一部を返金(例: THB 100.25のうちTHB 50)
const partialRefund = await omise.charges.refund('chrg_test_...', {
amount: 5000 // THB 50.00(最小単位)
});

// お客様はTHB 50を受け取る
// 加盟店はTHB 50.25を保持

複数の一部返金

// 最初の一部返金(THB 30)
await omise.charges.refund(chargeId, { amount: 3000 });

// 2番目の一部返金(THB 20)
await omise.charges.refund(chargeId, { amount: 2000 });

// 3番目の一部返金(THB 50)
await omise.charges.refund(chargeId, { amount: 5000 });

// 合計返金額: THB 100
// 最大: charge当たり15回の一部返金

返金の制限

期間ウィンドウ

決済方法返金ウィンドウ
Credit/Debit Cards365日
DuitNow180日
ShopeePay180日
PayNow6ヶ月
GrabPay3ヶ月
Atome60日
TrueMoney30日(voidのみ)
WeChat Pay90日
PayPay1年

一部返金の制限

  • charge当たりの最大回数: 15回の一部返金
  • 最小金額: 決済方法により異なる
  • 合計が超えてはならない: 元のcharge金額

VoidingとRefunding

Voidingは、決済前に返金が処理される場合に発生します:

  • まだ実際の譲渡が行われていない
  • chargeがキャンセルされる
  • より迅速な処理
  • レスポンスでvoidフラグがtrueに設定される

Refundingは決済後に発生します:

  • すでに加盟店に送金されている
  • 実際の返金トランザクションが作成される
  • 加盟店残高から控除される
  • voidフラグがfalseに設定される
const refund = await omise.charges.refund(chargeId, { amount: 10000 });

if (refund.voided) {
console.log('Chargeがvoidされた(決済前)');
} else {
console.log('返金が処理された(決済後)');
}

返金ステータスとタイムライン

返金ステータス

  • pending - 返金作成、処理中
  • successful - 返金完了
  • failed - 返金失敗(まれ)

処理タイムライン

決済方法お客様が返金を受け取る
Credit Cards5-10営業日(銀行による)
Debit Cards1-7営業日
QR Payments即時から1営業日
E-Wallets即時から3営業日
Bank Transfers1-3営業日
銀行の処理時間

返金のタイミングはお客様の銀行によって異なります。Omiseは即座に返金を処理しますが、銀行がお客様のアカウントに入金するまでに数日かかる場合があります。

返金ステータスの確認

特定の返金を取得

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

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

chargeのすべての返金をリスト

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

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

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);

返金Webhookの処理

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') {
// 全額返金を発行
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. 返品のための一部返金

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

// 返金額を計算
returnedItems.forEach(item => {
refundAmount += item.price * item.quantity;
});

// 一部返金を発行
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) {
// 差額を返金
const refund = await omise.charges.refund(chargeId, {
amount: overpayment,
metadata: {
reason: 'overpayment_correction'
}
});

return refund;
}
}

ベストプラクティス

1. 常に返金可能性を確認

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');
}

// 残りの返金可能金額を確認
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: 'Customer received damaged item'
}
});

3. 冪等性を実装

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') {
// 返金はすでに存在
return await findExistingRefund(chargeId, amount);
}
throw error;
}
}

4. お客様に通知

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

// 返金を作成
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}
You should see the credit in your account within 5-10 business days.
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"

原因:

  • 決済方法が返金をサポートしていない(例: PromptPay、Mobile Banking)
  • 返金ウィンドウが期限切れ(カードの場合365日超)
  • chargeが失敗した
  • すでに全額返金済み

解決策:

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');
// 代替案を提供: 手動銀行振込、ストアクレジットなど
}

問題: "Insufficient balance"

原因: 加盟店アカウントに十分な資金がない

解決策:

  • 今後の決済を待つ
  • アカウントに手動で資金を追加
  • 十分な残高が利用可能になった後に返金を処理

問題: 一部返金が失敗する

原因:

  • 決済方法が一部返金をサポートしていない
  • 15回の一部返金制限を超えた
  • 金額が残りの返金可能金額を超えている

解決策:

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

お客様のアカウントに返金が表示されるまでどのくらいかかりますか?

返金の処理時間は決済方法によって異なります:

  • クレジットカード: 5-10営業日(お客様の銀行による)
  • デビットカード: 1-7営業日
  • E-wallets: 即時から3営業日
  • QR決済: 即時から1営業日

Omiseは即座に返金を処理しますが、銀行がお客様にクレジットをいつ表示するかを管理します。

元のcharge金額より多く返金できますか?

いいえ、元のcharge金額より多く返金することはできません。すべての返金の合計は元のchargeを超えることはできません。

返金時に手数料はどうなりますか?

トランザクション手数料は返金されません。返金を発行すると:

  • お客様は全額返金額を受け取る
  • 加盟店はトランザクション手数料を負担する
  • 返金は譲渡可能残高から控除される

例: 3.65%の手数料でTHB 100のcharge = THB 3.65の手数料

  • 加盟店は最初にTHB 96.35を受け取る
  • 全額返金が発行される: 残高からTHB 100が控除される
  • 加盟店の純損失: THB 103.65(返金 + 元の手数料)
テストモードからchargeを返金できますか?

はい、テスト目的でテストchargeを返金できます。テスト返金は実際のお金を含まず、返金ワークフローのテストに役立ちます。

返金とdisputeの違いは何ですか?
  • Refund: お客様に自発的にお金を返す

    • プロセスを管理する
    • 返金を開始する
    • より低コスト(元のトランザクション手数料のみ)
  • Dispute/Chargeback: お客様が銀行に苦情を申し立てる

    • 銀行がプロセスを管理する
    • 追加手数料が発生する可能性がある($15-30)
    • dispute率にカウントされる
    • アカウントペナルティにつながる可能性がある

ベストプラクティス: disputeを避けるために積極的に返金を発行する。

disputeの詳細 →

返金をテストするにはどうすればよいですか?

テストモードで:

  1. テストchargeを作成
  2. APIまたはダッシュボード経由で返金を発行
  3. 返金ステータスを確認
  4. webhook処理をテスト

すべてのテスト返金はテスト目的で即座に処理されます。

関連リソース

次のステップ

  1. 返金エンドポイントを実装
  2. 返金webhook処理を設定
  3. 返金ワークフローをテスト
  4. お客様通知を追加
  5. 返金分析を監視