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

カード情報の収集

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>
HTTPS必須

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: サーバーで決済を処理

サーバーでトークンを受け取り、課金を作成します:

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

セキュリティのベストプラクティス

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コンプライアンス(最もシンプルな形式)のみが必要です。詳細についてはセキュリティチームにご相談ください。

関連リソース

次のステップ

  1. HTMLフォームを設定する
  2. JavaScriptトークン化を実装する
  3. サーバー側の課金エンドポイントを作成する
  4. テストカードでテストする
  5. 高額取引に3D Secureを追加する
  6. 本番環境に移行する