カード情報の収集
Omise.jsを使用して、機密データをサーバーから遠ざけながら、ウェブサイトから直接カード情報を安全に収集します。
概要
Omise.jsは、ウェブサイトから直接セキュアなカードトークン化を可能にするクライアント側JavaScriptライブラリです。このアプローチにより、機密性の高い決済データがサーバーに触れることがないため、PCI準拠の負担が大幅に削減されます。
トークン化の仕組み
主な利点:
- カードデータはOmiseのPCI認証サーバーに直接送信されます
- サーバーは1回限りのトークンのみを受信します
- PCI準拠要件の削減
- 顧客のセキュリティの向上
実装手順
ステップ1: Omise.jsライブラリを含める
</body>タグを閉じる前にOmise.jsスクリプトを追加します:
<script src="https://cdn.omise.co/omise.js"></script>
Omise.jsはチェックアウトページでHTTPSを必要とします。最適なセキュリティのためにTLS 1.2以上を使用してください。
ステップ2: 決済フォームを作成
カード入力フィールドを含むHTMLフォームを構築します:
<form id="payment-form">
<div class="form-group">
<label for="card-name">カード名義人</label>
<input type="text" id="card-name" placeholder="山田太郎" required />
</div>
<div class="form-group">
<label for="card-number">カード番号</label>
<input type="text" id="card-number" placeholder="4242 4242 4242 4242" required />
</div>
<div class="form-row">
<div class="form-group">
<label for="expiry-month">有効期限 月</label>
<input type="text" id="expiry-month" placeholder="12" maxlength="2" required />
</div>
<div class="form-group">
<label for="expiry-year">有効期限 年</label>
<input type="text" id="expiry-year" placeholder="2027" maxlength="4" required />
</div>
<div class="form-group">
<label for="cvv">CVV</label>
<input type="text" id="cvv" placeholder="123" maxlength="4" required />
</div>
</div>
<!-- オプションの請求先住所 (承認率を向上) -->
<div class="form-group">
<label for="postal-code">郵便番号</label>
<input type="text" id="postal-code" placeholder="100-0001" />
</div>
<button type="submit" id="pay-button">今すぐ支払う</button>
<div id="error-message" style="color: red;"></div>
</form>
ステップ3: 公開鍵を設定
公開鍵でOmise.jsを初期化します:
Omise.setPublicKey("pkey_test_YOUR_PUBLIC_KEY");
開発中はテストキー(pkey_test_...)を使用し、本番環境ではライブキー(pkey_...)を使用してください。テスト環境でライブキーを使用しないでください!
ステップ4: フォーム送信を処理
フォーム送信時にトークンを作成します:
document.getElementById('payment-form').addEventListener('submit', function(e) {
e.preventDefault();
// 二重送信を防ぐために送信ボタンを無効化
const payButton = document.getElementById('pay-button');
payButton.disabled = true;
payButton.textContent = '処理中...';
// カードトー クンを作成
Omise.createToken("card", {
name: document.getElementById('card-name').value,
number: document.getElementById('card-number').value,
expiration_month: parseInt(document.getElementById('expiry-month').value),
expiration_year: parseInt(document.getElementById('expiry-year').value),
security_code: document.getElementById('cvv').value,
postal_code: document.getElementById('postal-code').value
}, function(statusCode, response) {
if (statusCode === 200) {
// 成功! トークンが作成されました
handleToken(response.id);
} else {
// エラーが発生しました
handleError(response.message);
payButton.disabled = false;
payButton.textContent = '今すぐ支払う';
}
});
});
function handleToken(token) {
// トークンをサーバーに送信
fetch('/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// セキュリティのためにCSRFトークンを含める (フレームワーク依存)
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.content
},
body: JSON.stringify({
token: token,
amount: 10025, // 最小単位での金額 (例: セント)
currency: 'thb'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 成功ページにリダイレクト
window.location.href = '/payment-success';
} else {
handleError(data.error);
}
})
.catch(error => {
handleError('ネットワークエラーが発生しました');
});
}
function handleError(message) {
document.getElementById('error-message').textContent = message;
const payButton = document.getElementById('pay-button');
payButton.disabled = false;
payButton.textContent = '今すぐ支払う';
}
ステップ5: サーバーで決済を処理
サーバーでトークンを受け取り、課金を作成します:
- Node.js
- PHP
- Python
const express = require('express');
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
app.post('/checkout', async (req, res) => {
try {
const { token, amount, currency } = req.body;
const charge = await omise.charges.create({
amount: amount,
currency: currency,
card: token
});
if (charge.status === 'successful') {
res.json({ success: true, chargeId: charge.id });
} else {
res.json({ success: false, error: charge.failure_message });
}
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
<?php
require_once 'vendor/autoload.php';
define('OMISE_SECRET_KEY', 'skey_test_YOUR_SECRET_KEY');
$token = $_POST['token'];
$amount = $_POST['amount'];
$currency = $_POST['currency'];
try {
$charge = OmiseCharge::create(array(
'amount' => $amount,
'currency' => $currency,
'card' => $token
));
if ($charge['status'] == 'successful') {
echo json_encode(['success' => true, 'chargeId' => $charge['id']]);
} else {
echo json_encode(['success' => false, 'error' => $charge['failure_message']]);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
?>
from flask import Flask, request, jsonify
import omise
omise.api_secret = 'skey_test_YOUR_SECRET_KEY'
@app.route('/checkout', methods=['POST'])
def checkout():
try:
data = request.get_json()
token = data['token']
amount = data['amount']
currency = data['currency']
charge = omise.Charge.create(
amount=amount,
currency=currency,
card=token
)
if charge.status == 'successful':
return jsonify({'success': True, 'chargeId': charge.id})
else:
return jsonify({'success': False, 'error': charge.failure_message})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
セキュリティのベストプラクティス
1. サーバーにカードデータを保存しない
生のカードデータをサーバーを通じてログに記録、保存、または送信しないでください。常にトークンを使用してください。
// ❌ 悪い例: カードデータをサーバーに送信
fetch('/checkout', {
body: JSON.stringify({
cardNumber: '4242424242424242', // これはしないでください!
cvv: '123' // これはしないでください!
})
});
// ✅ 良い例: トークンのみを送信
fetch('/checkout', {
body: JSON.stringify({
token: 'tokn_test_...' // これは安全です!
})
});
2. 常にHTTPSを使用
- 最低TLS 1.2が必要
- チェックアウトだけでなくサイト全体に適用
- 無料証明書にはLet's Encryptを使用
3. CSPヘッダーを実装
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://cdn.omise.co;">
4. チェックアウトで分析を無効化
偶発的なカードデータのキャプチャを防ぎます:
// チェックアウトページでGoogle Analyticsを無効化
window['ga-disable-UA-XXXXXX-Y'] = true;
5. CSRF保護を実装
クロスサイトリクエストフォージェリ攻撃を防ぐため、決済リクエストに常にCSRFトークンを含めます:
// クライアント側: リクエストにCSRFトークンを含める
fetch('/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ token: tokenId })
});
6. 送信後にフォームをクリア
function clearForm() {
document.getElementById('payment-form').reset();
// またはプログラム的に値を削除
document.getElementById('card-number').value = '';
document.getElementById('cvv').value = '';
}
承認率の向上
請求先住所を収集
Omise.createToken("card", {
// 必須フィールド
name: "山田太郎",
number: "4242424242424242",
expiration_month: 12,
expiration_year: 2027,
security_code: "123",
// オプションだが承認向上のため推奨
city: "東京",
postal_code: "100-0001",
country: "JP",
street1: "千代田区千代田1-1",
phone_number: "+81312345678"
}, callback);
完全な請求情報の利点:
- 承認率が5-15%向上
- Address Verification Service (AVS)を有効化
- 不正リスクの削減
- 一部の国際カードに必要
一般的な問題とトラブルシューティング
問題: "公開鍵が必要です"
原因: 公開鍵の設定を忘れた
解決策:
Omise.setPublicKey("pkey_test_YOUR_KEY");
問題: "カード番号が無効です"
原因:
- 間違ったカード番号形式
- ライブモードでテストカードを使用
- サポートされていないカードブランド
解決策:
- 送信前にカード形式を検証
- クライアント側の検証にLuhnアルゴリズムを使用
- サポートされているカードブランドを確認
問題: トークン作成が400エラーを返す
一般的な原因:
- 必須フィールドの欠落 (名前、番号、有効期限、CVV)
- 無効な有効期限
- 間違ったデータ型 (文字列 vs 整数)
解決策:
// 正しいデータ型を確保
Omise.createToken("card", {
name: cardName, // 文字列
number: cardNumber.replace(/\s/g, ''), // 文字列、スペースを削除
expiration_month: parseInt(expiryMonth), // 整数!
expiration_year: parseInt(expiryYear), // 整数!
security_code: cvv // 文字列
}, callback);
FAQ
決済フォームの外観をカスタマイズできますか?
はい! HTMLとCSSを完全にコントロールできます。Omise.jsはトー クン化のみを処理します - フォームはあなたがデザインします。ブランドに完全に一致するようにスタイルを設定してください。
トークンは再利用できますか?
いいえ、トークンは1回限りの使用のみです。各決済には新しいトークンが必要です。将来の使用のためにカードを保存するには、カスタマーを作成してカードを添付します。
トークン化が失敗した場合はどうなりますか?
Omise.jsはステータスコードとエラーメッセージを返します。一般的なエラー:
- 無効なカード番号
- 期限切れのカード
- 間違ったCVV
- ネットワーク接続の問題
常にエラーを適切に処理し、ユーザーフレンドリーなメッセージを表示してください。
PCI準拠が必要ですか?
Omise.jsを使用すると、PCI要件が大幅に削減されます。カードデータがサーバーに触れないため、通常はSAQ Aコンプライアンス(最もシンプルな形式)のみが必要です。詳細についてはセキュリティチームにご相談ください。
関連リソース
- Omise.jsドキュメント - 完全なAPIリファレンス
- クレジットカード決済 - 実装ガイド
- セキュリティのベストプラクティス - 統合を保護
- テストガイド - テストカードとシナリオ
- 3D Secure - 強化されたセキュリティ