保存されたカード & 繰り返しの決済
Customers APIを使用して、将来の課金のために顧客カードを安全に保存します。サブスクリプション、繰り返し請求、リピーター顧客に最適です。
概要
Omise Customers APIを使用すると、将来の使用のために決済方法を保存できます。顧客が購入ごとにカード情報を再入力する代わりに、カードを安全に保存し、1回のAPI呼び出しで課金できます。
ユースケース:
- 月額サブスクリプション (Netflix、Spotifyモデル)
- 繰り返し請求 (公共料金、家賃)
- リピーター顧客のワンクリックチェックアウト
- 自動更新
- 分割払い
仕組み
実装ガイド
ステップ1: カードでカスタマーを作成
顧客が最初の購入を行うときに、Customerオブジェクトを作成します:
- cURL
- Node.js
- PHP
- Python
curl https://api.omise.co/customers \
-u skey_test_YOUR_SECRET_KEY: \
-d "email=john@example.com" \
-d "description=John Doe - プレミアムメンバー" \
-d "card=tokn_test_5rt6s9vah5lkvi1rh9c"
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
const customer = await omise.customers.create({
email: 'john@example.com',
description: 'John Doe - プレミアムメンバー',
card: tokenId // Omise.jsからのトークン
});
// データベースにカスタマーIDを保存
console.log(customer.id); // cust_test_...
<?php
$customer = OmiseCustomer::create(array(
'email' => 'john@example.com',
'description' => 'John Doe - プレミアムメンバー',
'card' => $_POST['omiseToken']
));
// データベースにカスタマーIDを保存
$customerId = $customer['id'];
?>
import omise
omise.api_secret = 'skey_test_YOUR_SECRET_KEY'
customer = omise.Customer.create(
email='john@example.com',
description='John Doe - プレミアムメンバー',
card='tokn_test_5rt6s9vah5lkvi1rh9c'
)
# データベースにカスタマーIDを保存
customer_id = customer.id
ステップ2: カスタマーIDを保存
ユーザー情報と一緒にデータベースにカスタマーIDを保存します:
-- データベーススキーマの例
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255),
omise_customer_id VARCHAR(50), -- これを保存!
subscription_status VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- カスタマーIDでユーザーを挿入
INSERT INTO users (email, name, omise_customer_id, subscription_status)
VALUES ('john@example.com', 'John Doe', 'cust_test_...', 'active');
ステップ3: 保存されたカードに課金
将来の決済には、トークンの代わりにカスタマーIDを使用して課金します:
- cURL
- Node.js
- PHP
curl https://api.omise.co/charges \
-u skey_test_YOUR_SECRET_KEY: \
-d "amount=29900" \
-d "currency=thb" \
-d "customer=cust_test_5rt6s9vah5lkvi1rh9c" \
-d "description=月額サブスクリプション - 2024年2月"
// データベースからカスタマーIDを取得
const customerId = await getUserOmiseCustomerId(userId);
// 課金を作成
const charge = await omise.charges.create({
amount: 29900, // THB 299.00
currency: 'thb',
customer: customerId,
description: '月額サブスクリプション - 2024年2月'
});
<?php
// データベースからカスタマーIDを取得
$customerId = getUserOmiseCustomerId($userId);
$charge = OmiseCharge::create(array(
'amount' => 29900,
'currency' => 'thb',
'customer' => $customerId,
'description' => '月額サブスクリプション - 2024年2月'
));
?>
トークン不要
カスタマーIDを課金する場合、トークンを作成する必要はありません。Omiseは自動的に顧客のデフォルトカードを使用します。
複数のカードの管理
追加カードの追加
// 最初にOmise.js経由で新しいカードのトークンを作成
const token = 'tokn_test_...';
// 既存のカスタマーにカードを追加
const card = await omise.customers.addCard(customerId, {
card: token
});
カスタマーのカードをリスト
curl https://api.omise.co/customers/cust_test_.../cards \
-u skey_test_YOUR_SECRET_KEY:
デフォルトカードを設定
await omise.customers.update(customerId, {
default_card: 'card_test_...'
});
カードを削除
curl https://api.omise.co/customers/cust_test_.../cards/card_test_... \
-X DELETE \
-u skey_test_YOUR_SECRET_KEY:
繰り返しの決済の実装
例: 月額サブスクリプション
// サブスクリプション請求機能 (cronで毎日実行)
async function processSubscriptionBilling() {
// 今日更新が必要なサブスクリプションを検索
const dueSubscriptions = await db.query(`
SELECT user_id, omise_customer_id, subscription_plan, amount
FROM users
WHERE subscription_status = 'active'
AND next_billing_date = CURRENT_DATE
`);
for (const sub of dueSubscriptions) {
try {
// カスタマーに課金
const charge = await omise.charges.create({
amount: sub.amount,
currency: 'thb',
customer: sub.omise_customer_id,
description: `${sub.subscription_plan} - ${new Date().toISOString().slice(0, 7)}`,
metadata: {
user_id: sub.user_id,
subscription_type: sub.subscription_plan
}
});
if (charge.status === 'successful') {
// 次回請求日を更新
await db.query(`
UPDATE users
SET next_billing_date = DATE_ADD(CURRENT_DATE, INTERVAL 1 MONTH),
last_charge_id = ?
WHERE user_id = ?
`, [charge.id, sub.user_id]);
// 領収書メールを送信
await sendReceiptEmail(sub.user_id, charge);
} else {
// 失敗した決済を処理
await handleFailedPayment(sub.user_id, charge.failure_message);
}
} catch (error) {
console.error(`ユーザー ${sub.user_id} の課金に失敗しました:`, error);
await handleBillingError(sub.user_id, error);
}
}
}
失敗した決済の再試行ロジック
async function handleFailedPayment(userId, failureMessage) {
const user = await db.getUserById(userId);
const retryCount = user.payment_retry_count || 0;
if (retryCount < 3) {
// 再試行をスケジュール
await db.query(`
UPDATE users
SET payment_retry_count = ?,
next_retry_date = DATE_ADD(CURRENT_DATE, INTERVAL ? DAY)
WHERE user_id = ?
`, [retryCount + 1, retryCount + 1, userId]);
// 顧客に通知
await sendPaymentFailedEmail(userId, {
reason: failureMessage,
retryDate: new Date(Date.now() + (retryCount + 1) * 24 * 60 * 60 * 1000)
});
} else {
// 最大再試行回数に達した - サブスクリプションを停止
await db.query(`
UPDATE users
SET subscription_status = 'suspended',
suspension_reason = 'payment_failure'
WHERE user_id = ?
`, [userId]);
await sendSubscriptionSuspendedEmail(userId);
}
}
カード情報の更新
有効期限を更新
顧客はカード番号を再入力せずにカードの有効期限を更新できます:
await omise.customers.updateCard(customerId, cardId, {
expiration_month: 12,
expiration_year: 2028,
name: 'John Doe',
postal_code: '10110'
});
カードを完全に交換
セキュリティ上の理由から、カード番号は更新できません。カードを交換するには:
- Omise.jsで新しいトークンを作成
- カスタマーに新しいカードを追加
- デフォルトカードとして設定
- 古いカードを削除
// 1. Omise.js経由でトークンを作成 (クライアント側)
const newToken = 'tokn_test_...';
// 2. 新しいカードを追加
const newCard = await omise.customers.addCard(customerId, {
card: newToken
});
// 3. デフォルトとして設定
await omise.customers.update(customerId, {
default_card: newCard.id
});
// 4. 古いカードを削除
await omise.customers.destroyCard(customerId, oldCardId);
セキュリティ & コンプライアンス
PCI準拠
OmiseはPCI準拠のボールトでカードストレージを処理します。保存するのは以下のみです:
- カスタマーID (保存しても安全)
- カードメタデータ (下4桁、ブランド、有効期限 - 表示しても安全)
決して保存しないでください:
- 完全なカード番号
- CVV/セキュリティコード
- 生のカードデータ
顧客データ保護
// ✅ 良い例: IDとメタデータのみを保存
const user = {
id: 12345,
email: 'john@example.com',
omise_customer_id: 'cust_test_...',
card_last_digits: '4242', // 表示しても安全
card_brand: 'Visa', // 表示しても安全
card_expiry: '12/2027' // 表示しても安全
};
// ❌ 悪い例: これらを保存しないでください
const badExample = {
card_number: '4242424242424242', // これを保存しないでください!
cvv: '123' // これを保存しないでください!
};
顧客の同意
カードを保存する前に常に明示的な同意を得てください:
<form id="payment-form">
<!-- カード入力フィールド -->
<label>
<input type="checkbox" id="save-card" name="save_card" />
将来の購入のためにこのカードを保存する
</label>
<button type="submit">決済を完了</button>
</form>
<script>
document.getElementById('payment-form').addEventListener('submit', function(e) {
e.preventDefault();
const saveCard = document.getElementById('save-card').checked;
// カードを保存する場合、カスタマーを作成
// そうでない場合、トークンで課金を作成するだけ
});
</script>
一般的な問題とトラブルシューティング
問題: カスタマーが既に存在します
エラー: customer_already_exists
解決策: 既存のカスタマーを取得または更新します:
try {
const customer = await omise.customers.create({
email: email,
card: token
});
} catch (error) {
if (error.code === 'customer_already_exists') {
// 代わりに取得して更新
const existingCustomer = await omise.customers.list({
limit: 1,
// カスタマーIDを見つけるためにデータベースを使用
});
}
}
問題: デフォルトカードが見つかりません
エラー: default_card_not_found
原因: カスタマーにカードがないか、デフォルトカードが削除されました
解決策:
// カスタマーにカードがあるか確認
const customer = await omise.customers.retrieve(customerId);
if (customer.cards.total === 0) {
// ユーザーにカードを追加するよう促す
throw new Error('決済方法を追加してください');
}
if (!customer.default_card) {
// 最初のカードをデフォルトに設定
await omise.customers.update(customerId, {
default_card: customer.cards.data[0].id
});
}
ベストプラクティス
-
決済の失敗を適切に処理する
- 再試行ロジックを実装
- 顧客に速やかに通知
- 簡単なカード更新フローを提供
-