DANA
インドネシアの主要デジタルウォレットDANAから支払いを受け付けます。1億5000万人以上のユーザーを持ち、群島全体で広く受け入れられています。
概要
DANAはインドネシアで最大かつ最も人気のあるデジタルウォレットの1つで、Ant Group(Alipayの親会社)の支援を受けています。交通機関やフードデリバリーからeコマースや請求書の支払いまで、日常的な取引 に広く使用されています。
主な特徴:
- ✅ 大規模なリーチ - インドネシア全土で1億5000万人以上のユーザー
- ✅ 迅速な確認 - ほぼリアルタイムの支払い確認(通常数秒以内)
- ✅ 高い受入率 - 1000万以上の加盟店で受入
- ✅ モバイルファースト - シームレスなスマートフォン体験
- ✅ 低摩擦 - PINまたは生体認証による迅速な認証
- ✅ 幅広いユースケース - eコマース、サービス、交通機関
サポート地域
| 地域 | 通貨 | 最小金額 | 最大金額 | APIバージョン |
|---|---|---|---|---|
| Indonesia | IDR | Rp1,000 | Rp10,000,000 | 2017-11-02 |
確認レベル別のアカウント制限
| 確認レベル | 残高制限 | 1取引あたり | 月次制限 |
|---|---|---|---|
| Basic (電話のみ) | Rp 2,000,000 | Rp 2,000,000 | Rp 10,000,000 |
| Premium (ID確認済み) | Rp 10,000,000 | Rp 5,000,000 | Rp 30,000,000 |
| Priority (Premium + アップグレー ド) | Rp 100,000,000 | Rp 10,000,000 | 無制限 |
仕組み
顧客体験:
- 顧客がチェックアウトでDANAを選択
- DANA認証ページにリダイレクト
- DANAアプリが自動的に開く(ディープリンク)
- 顧客が支払い詳細を確認
- PINを入力するか生体認証を使用
- 加盟店サイトに戻る
- 即座に確認を受信
一般的な完了時間: 30〜90秒
支払いフローの例
モバイル支払いフロー:

シームレスなモバイルアプリ体験:
- ❶ DANAを選択 - 顧客がチェックアウトでDANAウォレットを選択
- ❷ DANAにリダイレクト - ディープリンクが自動的にDANAアプリを開く
- ❸ 支払いを確認 - 取引詳細が表示される(加盟店、金額、注文ID)
- ❹ 認証 - DANA PINを入力するか生体認証を使用
- ❺ 支払いを確認 - 確認ボタンをタップして承認
- ❻ 支払い完了 - DANAウォレッ トから資金が引き落とされ、加盟店に戻る
デスクトップ支払いフロー:

デスクトップユーザー用のQRコードスキャン:
- ❶ DANA支払いを開始 - 顧客がデスクトップでDANAを選択
- ❷ QRコードを生成 - システムが一意のDANA支払いQRを作成
- ❸ QRを表示 - QRコードが画面に目立つように表示される
- ❹ DANAアプリを開く - 顧客がモバイルでDANAを起動
- ❺ QRをスキャン - アプリ内QRスキャナーを使用してコードをキャプチャ
- ❻ 確認と承認 - 支払い詳細が表示され、PINで認証
- ❼ 成功 - 支払いが処理され、デスクトップに確認が表示される
実装
ステップ1: DANAソースを作成
- cURL
- Node.js
- PHP
- Python
- Ruby
- Go
curl https://api.omise.co/sources \
-u skey_test_YOUR_SECRET_KEY: \
-d "type=dana" \
-d "amount=100000" \
-d "currency=IDR"
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
const source = await omise.sources.create({
type: 'dana',
amount: 100000, // Rp 1,000
currency: 'IDR'
});
console.log('DANA source:', source.id);
<?php
$source = OmiseSource::create(array(
'type' => 'dana',
'amount' => 100000,
'currency' => 'IDR'
));
?>
import omise
omise.api_secret = 'skey_test_YOUR_SECRET_KEY'
source = omise.Source.create(
type='dana',
amount=100000,
currency='IDR'
)
require 'omise'
Omise.api_key = 'skey_test_YOUR_SECRET_KEY'
source = Omise::Source.create({
type: 'dana',
amount: 100000,
currency: 'IDR'
})
source, err := client.Sources().Create(&operations.CreateSource{
Type: "dana",
Amount: 100000,
Currency: "IDR",
})
レスポンス:
{
"object": "source",
"id": "src_test_5rt6s9vah5lkvi1rh9c",
"type": "dana",
"flow": "redirect",
"amount": 100000,
"currency": "IDR"
}
ステップ2: チャージを作成してリダイレクト
app.post('/checkout/dana', async (req, res) => {
try {
const { amount, order_id, customer_email } = req.body;
// 金額を検証
if (amount < 100000) { // Rp 1,000最小
return res.status(400).json({
error: 'Minimum amount is Rp 1,000'
});
}
if (amount > 1000000000) { // Rp 10,000,000最大
return res.status(400).json({
error: 'Maximum amount is Rp 10,000,000'
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'dana',
amount: amount,
currency: 'IDR'
});
// 課金を作成
const charge = await omise.charges.create({
amount: amount,
currency: 'IDR',
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id,
customer_email: customer_email,
payment_method: 'dana'
}
});
// DANAにリダイレクト
res.redirect(charge.authorize_uri);
} catch (error) {
console.error('DANA error:', error);
res.status(500).json({ error: error.message });
}
});
ステップ3: リターンコールバックを処理
app.get('/payment/callback', async (req, res) => {
try {
const chargeId = req.query.charge_id;
const charge = await omise.charges.retrieve(chargeId);
if (charge.status === 'successful') {
await processOrder(charge.metadata.order_id);
res.redirect(`/order-success?order=${charge.metadata.order_id}`);
} else if (charge.status === 'failed') {
res.redirect(`/payment-failed?reason=${charge.failure_message}`);
} else {
res.redirect('/payment-pending');
}
} catch (error) {
res.redirect('/payment-error');
}
});
ステップ4: Webhookを処理
app.post('/webhooks/omise', async (req, res) => {
const event = req.body;
if (event.key === 'charge.complete') {
const charge = event.data;
if (charge.source.type === 'dana') {
if (charge.status === 'successful') {
await fulfillOrder(charge.metadata.order_id);
await sendConfirmationEmail(charge.metadata.customer_email);
} else if (charge.status === 'failed') {
await handleFailedPayment(charge.metadata.order_id);
}
}
}
res.sendStatus(200);
});
完全な実装例
const express = require('express');
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});
const app = express();
app.use(express.json());
// DANA設定
const DANA_CONFIG = {
currency: 'IDR',
minAmount: 100000, // Rp 1,000
maxAmount: 1000000000, // Rp 10,000,000
displayName: 'DANA',
timeout: 15 * 60 * 1000 // 15分
};
// DANA支払いを作成
app.post('/checkout/dana', async (req, res) => {
try {
const { amount, order_id, customer_email, customer_name } = req.body;
// 金額を検証
if (amount < DANA_CONFIG.minAmount) {
return res.status(400).json({
error: `Minimum amount is Rp ${DANA_CONFIG.minAmount / 100}`
});
}
if (amount > DANA_CONFIG.maxAmount) {
return res.status(400).json({
error: `Maximum amount is Rp ${DANA_CONFIG.maxAmount / 100}`
});
}
// ソースを作成
const source = await omise.sources.create({
type: 'dana',
amount: amount,
currency: DANA_CONFIG.currency
});
// 課金を作成
const charge = await omise.charges.create({
amount: amount,
currency: DANA_CONFIG.currency,
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/callback`,
metadata: {
order_id: order_id,
customer_email: customer_email,
customer_name: customer_name,
payment_method: 'dana'
}
});
// トラッキングのためにログ記録
console.log(`DANA payment initiated: ${charge.id} for order ${order_id}`);
// 認証URLを返す
res.json({
authorize_uri: charge.authorize_uri,
charge_id: charge.id
});
} catch (error) {
console.error('DANA error:', error);
res.status(500).json({ error: error.message });
}
});
// 支払いコールバック
app.get('/payment/callback', async (req, res) => {
try {
const charge = await omise.charges.retrieve(req.query.charge_id);
if (charge.status === 'successful') {
res.redirect(`/order-confirmation?order=${charge.metadata.order_id}`);
} else {
res.redirect(`/payment-failed?charge=${req.query.charge_id}`);
}
} catch (error) {
res.redirect('/payment-error');
}
});
// Webhookハンドラ
app.post('/webhooks/omise', async (req, res) => {
const event = req.body;
if (event.key === 'charge.complete' && event.data.source.type === 'dana') {
const charge = event.data;
if (charge.status === 'successful') {
await fulfillOrder(charge.metadata.order_id);
await sendReceipt(charge.metadata.customer_email, charge);
console.log(`DANA payment successful: ${charge.id}`);
} else {
await cancelOrder(charge.metadata.order_id);
console.log(`DANA payment failed: ${charge.id}`);
}
}
res.sendStatus(200);
});
app.listen(3000);