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

多通貨決済

世界中の顧客から現地通貨での支払いを受け入れながら、希望の通貨で決済を受け取ります。

概要

多通貨対応により、顧客の現地通貨(表示通貨)で請求しながら、あなたの決済通貨で資金を受け取ることができます。これにより、国際的な顧客のコンバージョン率が向上し、透明性が提供されます。

主な利点:

  • 💰 コンバージョン率の向上 - 顧客は見慣れた通貨で価格を確認できます
  • 🌍 グローバルリーチ - 世界中から支払いを受け入れます
  • 💱 自動変換 - リアルタイムの為替レートが適用されます
  • 📊 透明な価格設定 - 顧客に予期しない為替レートはありません
  • 🏦 単一決済 - すべての資金を1つの通貨で受け取ります

サポートされている通貨

表示通貨(顧客に請求)

  • THB - タイバーツ
  • SGD - シンガポール ドル
  • MYR - マレーシア リンギット
  • JPY - 日本円
  • USD - 米ドル
  • EUR - ユーロ
  • GBP - 英ポンド
  • AUD - オーストラリア ドル
  • HKD - 香港ドル
  • KRW - 韓国ウォン
  • IDR - インドネシア ルピア
  • PHP - フィリピン ペソ
  • CNY - 中国人民元

およびカード決済用の100以上の通貨。

決済通貨(資金を受け取る)

あなたの決済通貨は、マーチャント契約によって異なります:

  • THB (タイのマーチャント)
  • SGD (シンガポールのマーチャント)
  • MYR (マレーシアのマーチャント)
  • JPY (日本のマーチャント)
  • USD (国際マーチャント)

動作方法

フロー例:

  1. マーチャントは米国の顧客に USD での価格を表示します
  2. 顧客は $100 USD を支払う
  3. Omise は現在の為替レートで変換します(例:1 USD = 35 THB)
  4. マーチャントは決済で ฿3,500 THB を受け取ります

実装

基本的な多通貨請求

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

// 顧客の現地通貨で請求
const charge = await omise.charges.create({
amount: 10000, // $100.00 USD
currency: 'USD', // 表示通貨
card: cardToken,
description: 'Order #12345'
});

// あなたは決済通貨で決済を受け取ります(例:THB)
console.log('顧客が請求された:', charge.amount, charge.currency);
// 決済は為替レートに基づいて THB になります

動的通貨選択

// 顧客の場所を検出し、適切な通貨を表示
const customerCurrency = detectCustomerCurrency(req.headers);

const prices = {
USD: { amount: 10000, symbol: '$', display: '$100.00' },
EUR: { amount: 9000, symbol: '€', display: '€90.00' },
GBP: { amount: 8000, symbol: '£', display: '£80.00' },
THB: { amount: 350000, symbol: '฿', display: '฿3,500' }
};

const price = prices[customerCurrency] || prices.USD;

// 顧客に表示
showPrice(price.display);

// 顧客の通貨で請求
const charge = await omise.charges.create({
amount: price.amount,
currency: customerCurrency,
card: cardToken
});

通貨検出

IP ジオロケーション別

const geoip = require('geoip-lite');

function detectCustomerCurrency(ipAddress) {
const geo = geoip.lookup(ipAddress);

if (!geo) return 'USD'; // デフォルト

const currencyMap = {
'TH': 'THB',
'SG': 'SGD',
'MY': 'MYR',
'JP': 'JPY',
'US': 'USD',
'GB': 'GBP',
'AU': 'AUD'
// さらに国を追加
};

return currencyMap[geo.country] || 'USD';
}

// 使用法
const currency = detectCustomerCurrency(req.ip);

ブラウザ言語別

function detectCurrencyByLanguage(acceptLanguage) {
const langCurrencyMap = {
'th': 'THB',
'en-SG': 'SGD',
'ms': 'MYR',
'ja': 'JPY',
'en-US': 'USD',
'en-GB': 'GBP'
};

// Accept-Language ヘッダーを解析
const primaryLang = acceptLanguage.split(',')[0].split(';')[0];
return langCurrencyMap[primaryLang] || 'USD';
}

手動通貨セレクター

<div class="currency-selector">
<label>通貨:</label>
<select id="currency" onchange="updatePrices()">
<option value="USD" selected>USD ($)</option>
<option value="EUR">EUR (€)</option>
<option value="GBP">GBP (£)</option>
<option value="THB">THB (฿)</option>
<option value="SGD">SGD ($)</option>
<option value="MYR">MYR (RM)</option>
<option value="JPY">JPY (¥)</option>
</select>
</div>

<script>
function updatePrices() {
const currency = document.getElementById('currency').value;
const basePrice = 100; // USD

// 現在の為替レートを取得
fetch(`/api/convert?from=USD&to=${currency}&amount=${basePrice}`)
.then(r => r.json())
.then(data => {
document.querySelectorAll('.price').forEach(el => {
el.textContent = formatCurrency(data.amount, currency);
});
});
}
</script>

為替レート

現在のレート

// 現在の為替レートを取得
app.get('/api/exchange-rate', async (req, res) => {
const { from, to } = req.query;

// Omise は API を通じて為替レートを提供します
const rate = await omise.forexRates.retrieve({
from: from,
to: to
});

res.json({
from: rate.from,
to: rate.to,
rate: rate.rate,
updated_at: rate.updated_at
});
});

価格変換

function convertPrice(amount, fromCurrency, toCurrency, fxRate) {
if (fromCurrency === toCurrency) {
return amount;
}

const converted = Math.round(amount * fxRate);
return converted;
}

// 使用法
const usdAmount = 10000; // $100.00
const thbAmount = convertPrice(usdAmount, 'USD', 'THB', 35);
// ฿3,500 を返す

完全な例

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

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

// USD での基本価格
const PRODUCTS = {
'product-1': { name: 'Product 1', price_usd: 10000 },
'product-2': { name: 'Product 2', price_usd: 25000 }
};

// 為替レート(これらをキャッシュし、1時間ごとに更新)
let fxRates = {
EUR: 0.92,
GBP: 0.79,
THB: 35.00,
SGD: 1.35,
JPY: 149.50
};

// 顧客の通貨での製品価格を取得
app.get('/api/products/:id/price', (req, res) => {
const product = PRODUCTS[req.params.id];
const currency = req.query.currency || 'USD';

if (!product) {
return res.status(404).json({ error: 'Product not found' });
}

let amount = product.price_usd;

if (currency !== 'USD') {
const rate = fxRates[currency];
if (rate) {
amount = Math.round(amount * rate);
}
}

res.json({
product_id: req.params.id,
name: product.name,
amount: amount,
currency: currency,
formatted: formatCurrency(amount, currency)
});
});

// 顧客の通貨で請求を作成
app.post('/checkout', async (req, res) => {
try {
const { product_id, currency, token } = req.body;
const product = PRODUCTS[product_id];

// 顧客の通貨での金額を計算
let amount = product.price_usd;
if (currency !== 'USD' && fxRates[currency]) {
amount = Math.round(amount * fxRates[currency]);
}

// 請求を作成
const charge = await omise.charges.create({
amount: amount,
currency: currency || 'USD',
card: token,
description: `${product.name} - ${product_id}`,
metadata: {
product_id: product_id,
original_currency: 'USD',
original_amount: product.price_usd
}
});

res.json({
charge_id: charge.id,
status: charge.status,
amount: charge.amount,
currency: charge.currency
});

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

// 為替レートを定期的に更新
setInterval(async () => {
try {
// Omise または外部プロバイダーから最新のレートを取得
const rates = await fetchLatestRates();
fxRates = rates;
} catch (error) {
console.error('Failed to update FX rates:', error);
}
}, 60 * 60 * 1000); // 1時間ごと

app.listen(3000);

通貨フォーマット

function formatCurrency(amount, currency) {
// 金額は最小通貨単位です
const divisors = {
JPY: 1, // 円は小数がない
KRW: 1, // ウォンは小数がない
THB: 100, // 2小数点
USD: 100, // 2小数点
EUR: 100, // 2小数点
GBP: 100 // 2小数点
};

const divisor = divisors[currency] || 100;
const value = amount / divisor;

return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
minimumFractionDigits: divisor === 1 ? 0 : 2,
maximumFractionDigits: divisor === 1 ? 0 : 2
}).format(value);
}

// 使用法
formatCurrency(10000, 'USD'); // "$100.00"
formatCurrency(350000, 'THB'); // "฿3,500.00"
formatCurrency(10000, 'JPY'); // "¥10,000"

ベストプラクティス

1. 総価格を明確に表示

<div class="price-breakdown">
<div class="item-price">
<span>商品価格:</span>
<span id="item-price">$100.00 USD</span>
</div>
<div class="total-price">
<span>合計:</span>
<strong id="total-price">$100.00 USD</strong>
</div>
<p class="settlement-note">
<small>USD で請求されます。購入時に為替レートが適用されます。</small>
</p>
</div>

2. 為替レートをキャッシュ

// すべてのリクエストでレートを取得しないでください
// 1時間キャッシュして、定期的に更新
const cache = new Map();
const CACHE_TTL = 60 * 60 * 1000; // 1時間

async function getCachedRate(from, to) {
const key = `${from}-${to}`;
const cached = cache.get(key);

if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.rate;
}

const rate = await fetchRateFromAPI(from, to);
cache.set(key, { rate, timestamp: Date.now() });
return rate;
}

3. ゼロ小数点通貨を処理

// JPY、KRW は小数点がありません
const zeroDecimalCurrencies = ['JPY', 'KRW', 'VND', 'CLP'];

function getAmountForCurrency(baseAmount, currency) {
if (zeroDecimalCurrencies.includes(currency)) {
return baseAmount; // 100 を乗算しない
}
return baseAmount * 100; // 小数点を持つ通貨の場合
}

4. 通貨シンボルを表示

const currencySymbols = {
USD: '$',
EUR: '€',
GBP: '£',
THB: '฿',
SGD: 'S$',
MYR: 'RM',
JPY: '¥',
KRW: '₩'
};

function formatPrice(amount, currency) {
const symbol = currencySymbols[currency] || currency;
return `${symbol}${amount}`;
}

5. 複数の通貨でテスト

チェックアウトフロー をテストする:

  • USD (標準)
  • EUR (一般的な国際通貨)
  • JPY (ゼロ小数点)
  • THB (異なる決済通貨の場合)

FAQ

どの為替レートが使用されますか?

Omise は主要金融機関からのリアルタイム市場為替レートを使用します。レートは1日を通じて頻繁に更新されます。

誰が為替リスクを負いますか?

マーチャントが為替リスクを負います。請求から決済までの間にレートが変わった場合、決済額がわずかに異なる場合があります。

独自の為替レートを設定できますか?

いいえ。Omise は市場レートを自動的に適用します。必要に応じて価格に利益率を追加することはできますが、為替レート自体には設定できません。

どの通貨で資金を受け取りますか?

あなたの決済通貨(通常、マーチャント国に基づいています - タイの場合は THB、シンガポールの場合は SGD など)で資金を受け取ります。

顧客は通貨を選択できますか?

はい、通貨セレクターを実装できます。ただし、顧客の場所に基づいて自動的に顧客の現地通貨で価格を提示することは、通常、コンバージョンが増加します。

関連リソース

次のステップ

  1. サポートする通貨を決定
  2. 通貨検出を実装
  3. 通貨セレクターを追加(オプション)
  4. 複数の通貨でテスト
  5. 為替レートと決済を監視
  6. 本番環境に進む