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

顧客管理

定期支払い、保存されたカード、サブスクリプション請求のためにOmiseで顧客を作成および管理する方法を学びます。顧客オブジェクトは将来のトランザクションのために支払い情報と顧客詳細を格納します。

概要

顧客APIは、将来の使用のために顧客情報と支払い方法を保存することを可能にします:

  • 定期請求とサブスクリプション
  • ワンクリックチェックアウト体験
  • 複数の保存支払い方法
  • 顧客ライフサイクル管理
  • 支払い履歴追跡

主な利点

  • 簡潔な定期支払い - 毎回カード詳細を収集することなく顧客に請求
  • 削減されたPCIスコープ - トークン化された支払い方法を安全に保存
  • 優れた顧客体験 - ワンクリック購入を有効化
  • 柔軟な支払いオプション - 顧客ごとに複数のカードをサポート
  • 包括的な追跡 - 顧客支払い履歴を保持

顧客を使用する時期

次の場合に顧客オブジェクトを使用します:

  • 定期的またはサブスクリプション支払いを受け入れる
  • 保存支払い方法を提供する
  • スケジュール済み請求を作成する
  • サブスクリプションサービスを実装する
  • メンバーシッププラットフォームを構築する
  • 自動更新機能を有効化する

顧客の作成

基本的な顧客作成

curl https://api.omise.co/customers \
-u skey_test_123: \
-d "email=john@example.com" \
-d "description=John Doe - Premium Member"
const omise = require('omise')({
secretKey: 'skey_test_123'
});

const customer = await omise.customers.create({
email: 'john@example.com',
description: 'John Doe - Premium Member',
metadata: {
user_id: '12345',
plan: 'premium'
}
});

console.log('Customer ID:', customer.id);
import omise

omise.api_secret = 'skey_test_123'

customer = omise.Customer.create(
email='john@example.com',
description='John Doe - Premium Member',
metadata={
'user_id': '12345',
'plan': 'premium'
}
)

print(f'Customer ID: {customer.id}')
<?php
$omise = new Omise([
'secretKey' => 'skey_test_123'
]);

$customer = $omise['customers']->create([
'email' => 'john@example.com',
'description' => 'John Doe - Premium Member',
'metadata' => [
'user_id' => '12345',
'plan' => 'premium'
]
]);

echo "Customer ID: " . $customer['id'];
require 'omise'

Omise.api_key = 'skey_test_123'

customer = Omise::Customer.create(
email: 'john@example.com',
description: 'John Doe - Premium Member',
metadata: {
user_id: '12345',
plan: 'premium'
}
)

puts "Customer ID: #{customer.id}"

カード付き顧客の作成

// オプション1: トークンで作成
const customer = await omise.customers.create({
email: 'john@example.com',
description: 'John Doe',
card: 'tokn_test_123456'
});

// オプション2: 2段階プロセス
const customer = await omise.customers.create({
email: 'john@example.com'
});

await omise.customers.update(customer.id, {
card: 'tokn_test_123456'
});
# オプション1: トークンで作成
customer = omise.Customer.create(
email='john@example.com',
description='John Doe',
card='tokn_test_123456'
)

# オプション2: 2段階プロセス
customer = omise.Customer.create(
email='john@example.com'
)

customer.update(card='tokn_test_123456')

顧客レスポンス

{
"object": "customer",
"id": "cust_test_123456",
"livemode": false,
"location": "/customers/cust_test_123456",
"default_card": "card_test_123456",
"email": "john@example.com",
"description": "John Doe - Premium Member",
"created_at": "2024-01-15T10:30:00Z",
"cards": {
"object": "list",
"data": [
{
"object": "card",
"id": "card_test_123456",
"livemode": false,
"location": "/customers/cust_test_123456/cards/card_test_123456",
"country": "TH",
"city": "Bangkok",
"postal_code": "10200",
"financing": "",
"bank": "",
"brand": "Visa",
"fingerprint": "XjOdjbZr6KPdljhG0fGAT2E=",
"first_digits": null,
"last_digits": "4242",
"name": "JOHN DOE",
"expiration_month": 12,
"expiration_year": 2025,
"security_code_check": true,
"tokenization_method": null,
"created_at": "2024-01-15T10:30:00Z"
}
],
"limit": 20,
"offset": 0,
"total": 1,
"order": null,
"from": "1970-01-01T00:00:00Z",
"to": "2024-01-15T10:30:00Z"
},
"metadata": {
"user_id": "12345",
"plan": "premium"
}
}

顧客の取得

単一の顧客を取得

const customer = await omise.customers.retrieve('cust_test_123456');

console.log('Customer:', customer.email);
console.log('Cards:', customer.cards.total);
console.log('Default Card:', customer.default_card);
customer = omise.Customer.retrieve('cust_test_123456')

print(f'Customer: {customer.email}')
print(f'Cards: {customer.cards.total}')
print(f'Default Card: {customer.default_card}')
<?php
$customer = $omise['customers']->retrieve('cust_test_123456');

echo "Customer: " . $customer['email'] . "\n";
echo "Cards: " . $customer['cards']['total'] . "\n";
echo "Default Card: " . $customer['default_card'];

すべての顧客を一覧表示

// ページネーション付きで一覧表示
const customers = await omise.customers.list({
limit: 20,
offset: 0,
order: 'reverse_chronological'
});

customers.data.forEach(customer => {
console.log(`${customer.email} - ${customer.id}`);
});

// フィルター付き
const premiumCustomers = await omise.customers.list({
limit: 100,
from: '2024-01-01T00:00:00Z',
to: '2024-12-31T23:59:59Z'
});
# ページネーション付きで一覧表示
customers = omise.Customer.list(
limit=20,
offset=0,
order='reverse_chronological'
)

for customer in customers.data:
print(f'{customer.email} - {customer.id}')

# フィルター付き
premium_customers = omise.Customer.list(
limit=100,
from_date='2024-01-01T00:00:00Z',
to_date='2024-12-31T23:59:59Z'
)

顧客を検索

// メールアドレスで検索
async function findCustomerByEmail(email) {
let offset = 0;
const limit = 100;

while (true) {
const customers = await omise.customers.list({ limit, offset });

const found = customers.data.find(c => c.email === email);
if (found) return found;

if (customers.data.length < limit) break;
offset += limit;
}

return null;
}

const customer = await findCustomerByEmail('john@example.com');
def find_customer_by_email(email):
offset = 0
limit = 100

while True:
customers = omise.Customer.list(limit=limit, offset=offset)

for customer in customers.data:
if customer.email == email:
return customer

if len(customers.data) < limit:
break
offset += limit

return None

customer = find_customer_by_email('john@example.com')

顧客の更新

顧客情報を更新

const customer = await omise.customers.update('cust_test_123456', {
email: 'newemail@example.com',
description: 'John Doe - Enterprise Member',
metadata: {
user_id: '12345',
plan: 'enterprise',
upgraded_at: new Date().toISOString()
}
});
customer = omise.Customer.retrieve('cust_test_123456')
customer.update(
email='newemail@example.com',
description='John Doe - Enterprise Member',
metadata={
'user_id': '12345',
'plan': 'enterprise',
'upgraded_at': datetime.now().isoformat()
}
)
<?php
$customer = $omise['customers']->update('cust_test_123456', [
'email' => 'newemail@example.com',
'description' => 'John Doe - Enterprise Member',
'metadata' => [
'user_id' => '12345',
'plan' => 'enterprise',
'upgraded_at' => date('c')
]
]);

デフォルトカードを変更

// デフォルトカードを設定
await omise.customers.update('cust_test_123456', {
default_card: 'card_test_789012'
});

// 変更を確認
const customer = await omise.customers.retrieve('cust_test_123456');
console.log('New default card:', customer.default_card);
# デフォルトカードを設定
customer = omise.Customer.retrieve('cust_test_123456')
customer.update(default_card='card_test_789012')

# 変更を確認
customer.reload()
print(f'New default card: {customer.default_card}')

顧客の削除

顧客を削除

// 顧客と関連するすべてのカードを削除
await omise.customers.destroy('cust_test_123456');

// 削除を確認
try {
await omise.customers.retrieve('cust_test_123456');
} catch (error) {
console.log('Customer successfully deleted');
}
# 顧客を削除
customer = omise.Customer.retrieve('cust_test_123456')
customer.destroy()

# 削除を確認
try:
omise.Customer.retrieve('cust_test_123456')
except omise.errors.NotFoundError:
print('Customer successfully deleted')
curl https://api.omise.co/customers/cust_test_123456 \
-u skey_test_123: \
-X DELETE

レスポンス

{
"object": "customer",
"id": "cust_test_123456",
"livemode": false,
"deleted": true
}

顧客カードの管理

顧客カードを一覧表示

const customer = await omise.customers.retrieve('cust_test_123456');
const cards = customer.cards.data;

cards.forEach(card => {
console.log(`${card.brand} ending in ${card.last_digits}`);
console.log(`Expires: ${card.expiration_month}/${card.expiration_year}`);
});
customer = omise.Customer.retrieve('cust_test_123456')
cards = customer.cards.data

for card in cards:
print(f'{card.brand} ending in {card.last_digits}')
print(f'Expires: {card.expiration_month}/{card.expiration_year}')

顧客にカードを追加

// トークンを使用してカードを追加
await omise.customers.update('cust_test_123456', {
card: 'tokn_test_new_card'
});

// 更新された顧客を取得
const customer = await omise.customers.retrieve('cust_test_123456');
console.log(`Total cards: ${customer.cards.total}`);
# トークンを使用してカードを追加
customer = omise.Customer.retrieve('cust_test_123456')
customer.update(card='tokn_test_new_card')

# 更新された顧客を取得
customer.reload()
print(f'Total cards: {customer.cards.total}')

顧客からカードを削除

// 削除するカードIDを取得
const customer = await omise.customers.retrieve('cust_test_123456');
const cardToRemove = customer.cards.data[0].id;

// カードを削除
await omise.customers.destroyCard('cust_test_123456', cardToRemove);

// 削除を確認
const updatedCustomer = await omise.customers.retrieve('cust_test_123456');
console.log(`Remaining cards: ${updatedCustomer.cards.total}`);
# 削除するカードIDを取得
customer = omise.Customer.retrieve('cust_test_123456')
card_to_remove = customer.cards.data[0].id

# カードを削除
customer.destroy_card(card_to_remove)

# 削除を確認
customer.reload()
print(f'Remaining cards: {customer.cards.total}')
curl https://api.omise.co/customers/cust_test_123456/cards/card_test_789 \
-u skey_test_123: \
-X DELETE

顧客への請求

デフォルトカードに請求

const charge = await omise.charges.create({
customer: 'cust_test_123456',
amount: 100000, // 1,000.00 THB
currency: 'THB',
description: 'Monthly subscription - January 2024'
});
charge = omise.Charge.create(
customer='cust_test_123456',
amount=100000,
currency='THB',
description='Monthly subscription - January 2024'
)

特定のカードに請求

const charge = await omise.charges.create({
customer: 'cust_test_123456',
card: 'card_test_789012',
amount: 100000,
currency: 'THB',
description: 'One-time purchase'
});
charge = omise.Charge.create(
customer='cust_test_123456',
card='card_test_789012',
amount=100000,
currency='THB',
description='One-time purchase'
)

3Dセキュアで請求

const charge = await omise.charges.create({
customer: 'cust_test_123456',
amount: 100000,
currency: 'THB',
return_uri: 'https://example.com/payment/complete',
metadata: {
subscription_id: 'sub_123',
billing_cycle: 'January 2024'
}
});

if (charge.authorize_uri) {
// 3Dセキュア認証のために顧客をリダイレクト
console.log('Authorize at:', charge.authorize_uri);
}

よくあるユースケース

サブスクリプションサービス

class SubscriptionService {
constructor(omise) {
this.omise = omise;
}

async createSubscription(email, plan, cardToken) {
// 顧客を作成
const customer = await this.omise.customers.create({
email,
description: `${plan} subscription`,
card: cardToken,
metadata: {
plan,
subscribed_at: new Date().toISOString()
}
});

// 初期請求を作成
const charge = await this.omise.charges.create({
customer: customer.id,
amount: this.getPlanAmount(plan),
currency: 'THB',
description: `${plan} - First payment`
});

return { customer, charge };
}

async chargeSubscription(customerId, plan) {
const charge = await this.omise.charges.create({
customer: customerId,
amount: this.getPlanAmount(plan),
currency: 'THB',
description: `${plan} - Monthly payment`
});

return charge;
}

getPlanAmount(plan) {
const plans = {
basic: 29900, // 299.00 THB
premium: 59900, // 599.00 THB
enterprise: 99900 // 999.00 THB
};
return plans[plan] || 0;
}
}

// 使用例
const subscriptionService = new SubscriptionService(omise);
const subscription = await subscriptionService.createSubscription(
'john@example.com',
'premium',
'tokn_test_123456'
);

ワンクリックチェックアウト

class CheckoutService {
async processCheckout(userId, amount, currency) {
// ユーザーIDで既存の顧客を検索
const customer = await this.findCustomerByUserId(userId);

if (customer && customer.default_card) {
// 顧客が保存されたカードを持つ場合 - ワンクリックチェックアウト
return await omise.charges.create({
customer: customer.id,
amount,
currency,
description: 'One-click purchase'
});
} else {
// 新しい顧客または保存されたカードなし - 支払い情報を収集
return { requiresPaymentInfo: true };
}
}

async findCustomerByUserId(userId) {
const customers = await omise.customers.list({ limit: 100 });
return customers.data.find(
c => c.metadata?.user_id === userId.toString()
);
}
}

複数カード管理

class CardManager {
async addCard(customerId, cardToken, setAsDefault = false) {
// 新しいカードを追加
await omise.customers.update(customerId, {
card: cardToken
});

const customer = await omise.customers.retrieve(customerId);
const newCard = customer.cards.data[0];

// 要求に応じてデフォルトとして設定
if (setAsDefault) {
await omise.customers.update(customerId, {
default_card: newCard.id
});
}

return newCard;
}

async removeCard(customerId, cardId) {
const customer = await omise.customers.retrieve(customerId);

// 唯一の支払い方法の削除を防止
if (customer.cards.total <= 1) {
throw new Error('Cannot remove the only payment method');
}

// デフォルトカードを削除せずに削除することを防止
if (customer.default_card === cardId) {
const otherCard = customer.cards.data.find(c => c.id !== cardId);
await omise.customers.update(customerId, {
default_card: otherCard.id
});
}

await omise.customers.destroyCard(customerId, cardId);
}

async setDefaultCard(customerId, cardId) {
await omise.customers.update(customerId, {
default_card: cardId
});
}
}

顧客ライフサイクル管理

class CustomerLifecycleManager {
async upgradeSubscription(customerId, newPlan) {
const customer = await omise.customers.retrieve(customerId);

// 顧客メタデータを更新
await omise.customers.update(customerId, {
description: `${newPlan} subscription`,
metadata: {
...customer.metadata,
plan: newPlan,
upgraded_at: new Date().toISOString()
}
});

// 按分額を請求
const charge = await this.chargeProrated(customerId, newPlan);

return { customer, charge };
}

async cancelSubscription(customerId) {
const customer = await omise.customers.retrieve(customerId);

// メタデータを更新してキャンセルとしてマーク
await omise.customers.update(customerId, {
metadata: {
...customer.metadata,
status: 'cancelled',
cancelled_at: new Date().toISOString()
}
});

// オプションで全カードを削除
// await this.removeAllCards(customerId);
}

async reactivateSubscription(customerId, plan) {
// 顧客が支払い方法を持っていることを確認
const customer = await omise.customers.retrieve(customerId);

if (!customer.default_card) {
throw new Error('No payment method available');
}

// メタデータを更新
await omise.customers.update(customerId, {
metadata: {
...customer.metadata,
plan,
status: 'active',
reactivated_at: new Date().toISOString()
}
});

// 最初の支払いを請求
return await omise.charges.create({
customer: customerId,
amount: this.getPlanAmount(plan),
currency: 'THB',
description: `${plan} - Reactivation payment`
});
}
}

ベストプラクティス

メール管理

// 顧客を作成する前に常にメールアドレスを検証
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}

async function createCustomerSafely(email, data) {
if (!validateEmail(email)) {
throw new Error('Invalid email address');
}

// 既存の顧客を確認
const existing = await findCustomerByEmail(email);
if (existing) {
return existing;
}

return await omise.customers.create({
email,
...data
});
}

メタデータの使用

// メタデータを使用してシステムにリンク
const customer = await omise.customers.create({
email: 'john@example.com',
metadata: {
// ユーザーシステムにリンク
user_id: '12345',

// 顧客レベルを追跡
tier: 'premium',

// 重要な日付を保存
subscribed_at: new Date().toISOString(),

// ライフサイクルを追跡
status: 'active',

// 好みを保存
marketing_consent: 'true',

// カスタムフィールド
referral_code: 'FRIEND20'
}
});

エラーハンドリング

async function chargeCustomerSafely(customerId, amount, currency) {
try {
// 顧客が存在することを確認
const customer = await omise.customers.retrieve(customerId);

if (!customer.default_card) {
throw new Error('No payment method available');
}

// 請求を作成
const charge = await omise.charges.create({
customer: customerId,
amount,
currency
});

return { success: true, charge };

} catch (error) {
if (error.code === 'not_found') {
return { success: false, error: 'Customer not found' };
}

if (error.code === 'insufficient_funds') {
return { success: false, error: 'Insufficient funds' };
}

if (error.code === 'failed_processing') {
return { success: false, error: 'Payment processing failed' };
}

throw error;
}
}

セキュリティに関する考慮事項

// 顧客IDをURLまたはクライアント側のコードで公開しないでください
// 検索には独自のユーザーIDを使用してください

class SecureCustomerService {
async getCustomerByUserId(userId) {
// メタデータを使用して検索
const customers = await omise.customers.list({ limit: 100 });
return customers.data.find(
c => c.metadata?.user_id === userId.toString()
);
}

async chargeUser(userId, amount, currency) {
const customer = await this.getCustomerByUserId(userId);

if (!customer) {
throw new Error('Customer not found');
}

return await omise.charges.create({
customer: customer.id,
amount,
currency
});
}
}

トラブルシューティング

顧客が見つからない

// 操作を実行する前に常に顧客が存在することを確認
async function safeRetrieveCustomer(customerId) {
try {
return await omise.customers.retrieve(customerId);
} catch (error) {
if (error.code === 'not_found') {
console.error('Customer does not exist:', customerId);
return null;
}
throw error;
}
}

支払い方法がない

async function ensurePaymentMethod(customerId) {
const customer = await omise.customers.retrieve(customerId);

if (!customer.default_card) {
throw new Error('Customer has no payment method. Please add a card.');
}

return customer;
}

重複する顧客

// 重複する顧客を防止
async function getOrCreateCustomer(email, data) {
// 既存の顧客を検索
const existing = await findCustomerByEmail(email);

if (existing) {
console.log('Customer already exists:', existing.id);
return existing;
}

// 新しい顧客を作成
return await omise.customers.create({
email,
...data
});
}

よくあるご質問

一般的な質問

Q: 顧客を削除した場合、顧客データはどうなりますか?

A: 顧客を削除すると、関連するすべてのカードも削除されます。この操作は取り消すことができません。請求またはトランザクションはアカウント履歴に残ります。

Q: 削除された顧客を復元できますか?

A: いいえ、顧客の削除は永続的です。同じ情報で新しい顧客を作成する必要があります。

Q: 顧客がいくつのカードを持つことができますか?

A: 厳密な制限はありませんが、より良いユーザー体験のため、適切な数(通常3〜5個のアクティブカード)を管理することをお勧めします。

Q: 重複したメールアドレスを持つことができますか?

A: はい、メールフィールドは一意ではありません。必要に応じて、アプリケーションで一意性の管理を担当します。

Q: 顧客をユーザーシステムにリンクするにはどうすればよいですか?

A: メタデータフィールドを使用して内部ユーザーIDを保存します。これにより、ユーザーレコードに基づいてOmise顧客を簡単に検索できます。

Q: テストモードとライブモードで顧客IDは同じですか?

A: いいえ、テストモード顧客(cust_test_xxx)はライブモード顧客(cust_xxx)とは異なります。各モードで顧客を別々に作成する必要があります。

支払いに関する質問

Q: CVVなしで顧客に請求できますか?

A: はい、カードを顧客に保存すると、CVVなしで請求できます。カードトークンには既に必要なセキュリティ情報が含まれています。

Q: 顧客のカードの有効期限が切れたらどうなりますか?

A: 請求は失敗します。期限切れのカードを監視し、顧客に更新された支払い情報を積極的にリクエストする必要があります。

Q: 異なる通貨で顧客に請求できますか?

A: はい、アカウントの通貨制限を受けて、サポートされている通貨で請求を作成できます。

Q: サブスクリプション支払いが失敗した場合はどうしますか?

A: 指数バックオフを使用した再試行ロジックを実装します。顧客に失敗を通知し、支払い情報を更新する簡単な方法を提供してください。

ベストプラクティスに関する質問

Q: ワンタイム支払いのために顧客を作成する必要がありますか?

A: 真のワンタイム支払いには必要ありません。将来の使用のために支払い情報を保存する必要がある場合にのみ、顧客を作成してください。

Q: 顧客移行を処理するにはどうすればよいですか?

A: 古いシステムから顧客データをエクスポートし、Omise顧客を作成し、ユーザーに支払い情報を再入力させます(セキュリティ上の理由からカードデータを直接移行することはできません)。

Q: メタデータに何を保存する必要がありますか?

A: 内部ID、サブスクリプション状態、計画情報、ビジネスロジックに必要なその他のデータを保存します。機密情報は保存しないでください。

関連リソース

次のステップ