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

オンラインダイレクト・デビット(自動引き落とし)

顧客の銀行口座から直接的に自動で引き落とし、定期的に請求が可能です。タイ国内の事前承認された引き落とし権限により、定期購読や自動請求が実現します。

概要

オンラインダイレクト・デビット(自動引き落とし)を利用することで、顧客の銀行口座から定期的に自動で引き落としできます。顧客が一度承認すれば、その後は定期購読、分割払い、又は定期的なサービス料金に対して自動的に口座から引き落とすことができます。

主な機能:

  • 定期支払い - スケジュール通りに自動請求
  • 事前承認 - 顧客が一度だけ承認
  • 高い成功率 - 銀行口座から直接引き落とし
  • 高額制限 - 1取引につき฿5,000,000以上も対応
  • 返金対応 - 全額及び部分的な返金に対応
  • 複数銀行対応 - タイ主要銀行全て対応

ユースケース

おすすめの利用場面:

  • 定期購読 - 月額制SaaS、ストリーミングサービス、会員制
  • 分割払い - ローン返済、購入プラン
  • 公共料金 - 定期的なサービス料金
  • 保険料 - 定期的な保険料支払い
  • 寄附 - 定期的な寄附金

不適切な利用場面:

  • 一度だけの支払い(モバイルまたはインターネットバンキングをご利用ください)
  • ゲストチェックアウト(承認設定が必要)
  • 即座の支払い(初期設定に時間がかかります)

対応地域

地域通貨最小金額最大金額返金可能
タイTHB฿20.00฿5,000,000+✅ はい

仕組み

初期設定フロー

定期請求フロー

実装

ステップ1:顧客を作成

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

// 顧客を作成
const customer = await omise.customers.create({
email: 'customer@example.com',
description: '定期購読用顧客',
metadata: {
user_id: '12345'
}
});

console.log('顧客を作成:', customer.id);

ステップ2:ダイレクト・デビットソースを作成

// ダイレクト・デビットソースを作成(顧客認証が必要)
const source = await omise.sources.create({
type: 'pay_with_bill_payment',
amount: 50000, // 初期承認金額
currency: 'THB',
customer: customer.id
});

// 顧客を承認ページへリダイレクト
res.redirect(source.authorize_uri);

ステップ3:認可コールバックを処理

app.get('/debit/callback', async (req, res) => {
const sourceId = req.query.source_id;
const source = await omise.sources.retrieve(sourceId);

if (source.flow_status === 'successful') {
// ソースが認可され、請求可能
// 顧客にアタッチ(未設定の場合)
await omise.customers.update(customer.id, {
default_source: source.id
});

res.redirect('/subscription-success');
} else {
res.redirect('/authorization-failed');
}
});

ステップ4:定期請求を作成

// 定期的に顧客に請求
async function chargeSubscription(customerId, amount) {
try {
const charge = await omise.charges.create({
customer: customerId,
amount: amount,
currency: 'THB',
description: '月額定期購読',
metadata: {
billing_period: '2024-02',
subscription_id: 'sub_12345'
}
});

return charge;
} catch (error) {
console.error('請求失敗:', error);
// 失敗を処理(再試行、顧客通知など)
}
}

// 定期請求のスケジュール設定
setInterval(async () => {
const subscriptions = await getActiveSubscriptions();

for (const sub of subscriptions) {
if (sub.next_billing_date === today()) {
await chargeSubscription(sub.customer_id, sub.amount);
}
}
}, 24 * 60 * 60 * 1000); // 毎日確認

完全な実装例

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

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

// ステップ1:顧客を登録
app.post('/subscribe', async (req, res) => {
try {
const { email, plan_id, amount } = req.body;

// 顧客を作成
const customer = await omise.customers.create({
email: email,
description: `定期購読: ${plan_id}`
});

// ダイレクト・デビットソースを作成
const source = await omise.sources.create({
type: 'pay_with_bill_payment',
amount: amount,
currency: 'THB',
customer: customer.id
});

// データベースに定期購読を保存
await saveSubscription({
customer_id: customer.id,
source_id: source.id,
plan_id: plan_id,
amount: amount,
status: 'pending_authorization'
});

// 銀行認可ページへリダイレクト
res.json({
authorize_uri: source.authorize_uri,
customer_id: customer.id
});

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

// ステップ2:認可コールバックを処理
app.get('/debit/callback', async (req, res) => {
try {
const sourceId = req.query.source_id;
const source = await omise.sources.retrieve(sourceId);

if (source.flow_status === 'successful') {
// 定期購読ステータスを更新
await updateSubscription(source.id, {
status: 'active',
authorized_at: new Date()
});

// 最初の請求を作成
const charge = await omise.charges.create({
customer: source.customer,
amount: source.amount,
currency: 'THB',
description: '初期定期購読支払い'
});

res.redirect('/subscription-active');
} else {
await updateSubscription(source.id, {
status: 'authorization_failed'
});
res.redirect('/authorization-failed');
}
} catch (error) {
res.redirect('/error');
}
});

// ステップ3:定期請求を処理(cronジョブ)
app.post('/cron/process-subscriptions', async (req, res) => {
try {
const dueSubscriptions = await getDueSubscriptions();

for (const subscription of dueSubscriptions) {
try {
const charge = await omise.charges.create({
customer: subscription.customer_id,
amount: subscription.amount,
currency: 'THB',
description: `定期: ${subscription.plan_id}`,
metadata: {
subscription_id: subscription.id,
billing_period: getCurrentPeriod()
}
});

await recordPayment(subscription.id, charge.id);
} catch (error) {
await handleFailedPayment(subscription.id, error);
}
}

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

// Webhookハンドラー
app.post('/webhooks/omise', async (req, res) => {
const event = req.body;

if (event.key === 'charge.complete') {
const charge = event.data;

if (charge.status === 'successful') {
await updateSubscriptionPayment(charge.metadata.subscription_id, 'paid');
await sendReceiptEmail(charge);
} else {
await handleFailedPayment(charge.metadata.subscription_id);
await notifyCustomer(charge);
}
}

res.sendStatus(200);
});

app.listen(3000);

返金対応

// 全額または部分的な返金
const refund = await omise.charges.refund('chrg_test_...', {
amount: 50000 // 全額または部分的
});

ベストプラクティス

1. 明確な承認フロー

<div class="direct-debit-setup">
<h3>ตั้งค่าการหักบัญชีอัตโนมัติ</h3>
<div class="benefits">
<p>✅ ชำระอัตโนมัติทุกเดือน</p>
<p>✅ ไม่ต้องกังวลเรื่องลืมจ่าย</p>
<p>✅ ยกเลิกได้ทุกเมื่อ</p>
</div>
<p class="amount">จำนวนเงิน: <strong>฿500/เดือน</strong></p>
<button>อนุมัติการหักบัญชี</button>
</div>

2. 失敗した支払いを処理

async function handleFailedPayment(subscriptionId, error) {
// 失敗をログ
await logPaymentFailure(subscriptionId, error);

// 再試行ロジック
const subscription = await getSubscription(subscriptionId);
subscription.retry_count++;

if (subscription.retry_count < 3) {
// 24時間後に再試行
await scheduleRetry(subscriptionId, 24);
} else {
// 定期購読を一時停止
await suspendSubscription(subscriptionId);
await notifyCustomerSuspension(subscription.customer_id);
}
}

3. 顧客への通知

// 請求前に通知
async function sendUpcomingChargeNotification(customerId, amount, dueDate) {
await sendEmail(customerId, {
subject: 'การแจ้งเตือนการหักบัญชี',
body: `บัญชีของคุณจะถูกหัก ฿${amount} ในวันที่ ${dueDate}`
});
}

// 請求成功後に通知
async function sendChargeConfirmation(customerId, charge) {
await sendEmail(customerId, {
subject: 'ยืนยันการหักบัญชี',
body: `การหักบัญชีสำเร็จ ฿${charge.amount / 100}`
});
}

4. キャンセルを許可

app.post('/subscription/cancel', async (req, res) => {
const { subscription_id } = req.body;

// キャンセル済みにマーク(次回請求をしない)
await updateSubscription(subscription_id, {
status: 'canceled',
canceled_at: new Date()
});

// オプション:ソースを削除
// await omise.sources.destroy(source_id);

res.json({ message: 'Subscription canceled' });
});

FAQ

オンラインダイレクト・デビットとは何ですか?

オンラインダイレクト・デビットを使用すると、顧客の銀行口座から定期的に自動で引き落としできます。顧客が一度承認すれば、その後、事業者が請求を開始できます。定期購読と自動請求に最適です。

モバイル/インターネットバンキングとの違いは何ですか?
  • モバイル/インターネットバンキング: 一度だけの支払い、顧客が毎回支払いを開始
  • ダイレクト・デビット: 定期支払い、加盟店が承認後に請求を開始
顧客は承認をキャンセルできますか?

はい、顧客は銀行を通じて、またはお客様のプラットフォームを通じてダイレクト・デビット権限をキャンセルできます。常に簡単にキャンセルできるオプションを提供してください。

請求に失敗した場合、どうなりますか?

一般的な原因:残高不足、口座閉鎖、権限キャンセル。再試行ロジック(2~3回)を実装し、顧客に支払い方法を更新するよう通知してください。

返金は対応していますか?

はい、ダイレクト・デビットは全額及び部分的な返金に対応しています。

認可にはどのくらい時間がかかりますか?

初期認可には2~5分かかります。認可後、定期請求は1~2営業日以内に処理されます。

関連リソース

次のステップ

  1. 顧客オブジェクトを作成
  2. ダイレクト・デビットソースを作成
  3. 認可コールバックを処理
  4. 定期請求ロジックを実装
  5. 再試行と失敗処理を追加
  6. 全定期購読フローをテスト
  7. 本番運用を開始