Customer Management
Learn how to create and manage customers in Omise for recurring payments, saved payment methods, and subscription billing. Customer objects store payment information and customer details for future transactions.
Overviewโ
The Customer API allows you to store customer information and payment methods for future use, enabling:
- Recurring billing and subscriptions
- One-click checkout experiences
- Multiple saved payment methods
- Customer lifecycle management
- Payment history tracking
Key Benefitsโ
- Simplified Recurring Payments - Charge customers without collecting card details each time
- Reduced PCI Scope - Store tokenized payment methods securely
- Better Customer Experience - Enable one-click purchases
- Flexible Payment Options - Support multiple cards per customer
- Comprehensive Tracking - Maintain customer payment history
When to Use Customersโ
Use customer objects when you need to:
- Accept recurring or subscription payments
- Offer saved payment methods
- Create scheduled charges
- Implement subscription services
- Build membership platforms
- Enable auto-renewal features
Creating Customersโ
Basic Customer Creationโ
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}"
Creating Customer with Cardโ
// Option 1: Create with token
const customer = await omise.customers.create({
email: 'john@example.com',
description: 'John Doe',
card: 'tokn_test_123456'
});
// Option 2: Two-step process
const customer = await omise.customers.create({
email: 'john@example.com'
});
await omise.customers.update(customer.id, {
card: 'tokn_test_123456'
});
# Option 1: Create with token
customer = omise.Customer.create(
email='john@example.com',
description='John Doe',
card='tokn_test_123456'
)
# Option 2: Two-step process
customer = omise.Customer.create(
email='john@example.com'
)
customer.update(card='tokn_test_123456')
Customer Responseโ
{
"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"
}
}
Retrieving Customersโ
Get Single Customerโ
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'];
List All Customersโ
// List with pagination
const customers = await omise.customers.list({
limit: 20,
offset: 0,
order: 'reverse_chronological'
});
customers.data.forEach(customer => {
console.log(`${customer.email} - ${customer.id}`);
});
// With filters
const premiumCustomers = await omise.customers.list({
limit: 100,
from: '2024-01-01T00:00:00Z',
to: '2024-12-31T23:59:59Z'
});
# List with pagination
customers = omise.Customer.list(
limit=20,
offset=0,
order='reverse_chronological'
)
for customer in customers.data:
print(f'{customer.email} - {customer.id}')
# With filters
premium_customers = omise.Customer.list(
limit=100,
from_date='2024-01-01T00:00:00Z',
to_date='2024-12-31T23:59:59Z'
)
Search Customersโ
// Search by email
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')
Updating Customersโ
Update Customer Informationโ
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')
]
]);
Change Default Cardโ
// Set default card
await omise.customers.update('cust_test_123456', {
default_card: 'card_test_789012'
});
// Verify change
const customer = await omise.customers.retrieve('cust_test_123456');
console.log('New default card:', customer.default_card);
# Set default card
customer = omise.Customer.retrieve('cust_test_123456')
customer.update(default_card='card_test_789012')
# Verify change
customer.reload()
print(f'New default card: {customer.default_card}')
Deleting Customersโ
Destroy Customerโ
// Delete customer and all associated cards
await omise.customers.destroy('cust_test_123456');
// Verify deletion
try {
await omise.customers.retrieve('cust_test_123456');
} catch (error) {
console.log('Customer successfully deleted');
}
# Delete customer
customer = omise.Customer.retrieve('cust_test_123456')
customer.destroy()
# Verify deletion
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
Responseโ
{
"object": "customer",
"id": "cust_test_123456",
"livemode": false,
"deleted": true
}
Managing Customer Cardsโ
List Customer Cardsโ
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}')
Add Card to Customerโ
// Add card using token
await omise.customers.update('cust_test_123456', {
card: 'tokn_test_new_card'
});
// Retrieve updated customer
const customer = await omise.customers.retrieve('cust_test_123456');
console.log(`Total cards: ${customer.cards.total}`);
# Add card using token
customer = omise.Customer.retrieve('cust_test_123456')
customer.update(card='tokn_test_new_card')
# Retrieve updated customer
customer.reload()
print(f'Total cards: {customer.cards.total}')
Remove Card from Customerโ
// Get card ID to remove
const customer = await omise.customers.retrieve('cust_test_123456');
const cardToRemove = customer.cards.data[0].id;
// Delete card
await omise.customers.destroyCard('cust_test_123456', cardToRemove);
// Verify removal
const updatedCustomer = await omise.customers.retrieve('cust_test_123456');
console.log(`Remaining cards: ${updatedCustomer.cards.total}`);
# Get card ID to remove
customer = omise.Customer.retrieve('cust_test_123456')
card_to_remove = customer.cards.data[0].id
# Delete card
customer.destroy_card(card_to_remove)
# Verify removal
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
Charging Customersโ
Charge Default Cardโ
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'
)
Charge Specific Cardโ
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'
)
Charge with 3D Secureโ
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) {
// Redirect customer for 3D Secure authentication
console.log('Authorize at:', charge.authorize_uri);
}
Common Use Casesโ
Subscription Serviceโ
class SubscriptionService {
constructor(omise) {
this.omise = omise;
}
async createSubscription(email, plan, cardToken) {
// Create customer
const customer = await this.omise.customers.create({
email,
description: `${plan} subscription`,
card: cardToken,
metadata: {
plan,
subscribed_at: new Date().toISOString()
}
});
// Create initial charge
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;
}
}
// Usage
const subscriptionService = new SubscriptionService(omise);
const subscription = await subscriptionService.createSubscription(
'john@example.com',
'premium',
'tokn_test_123456'
);
One-Click Checkoutโ
class CheckoutService {
async processCheckout(userId, amount, currency) {
// Find existing customer by user ID
const customer = await this.findCustomerByUserId(userId);
if (customer && customer.default_card) {
// Customer exists with saved card - one-click checkout
return await omise.charges.create({
customer: customer.id,
amount,
currency,
description: 'One-click purchase'
});
} else {
// New customer or no saved card - collect payment info
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()
);
}
}
Multi-Card Managementโ
class CardManager {
async addCard(customerId, cardToken, setAsDefault = false) {
// Add new card
await omise.customers.update(customerId, {
card: cardToken
});
const customer = await omise.customers.retrieve(customerId);
const newCard = customer.cards.data[0];
// Set as default if requested
if (setAsDefault) {
await omise.customers.update(customerId, {
default_card: newCard.id
});
}
return newCard;
}
async removeCard(customerId, cardId) {
const customer = await omise.customers.retrieve(customerId);
// Prevent removing the only card
if (customer.cards.total <= 1) {
throw new Error('Cannot remove the only payment method');
}
// Prevent removing default card without replacement
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
});
}
}
Customer Lifecycle Managementโ
class CustomerLifecycleManager {
async upgradeSubscription(customerId, newPlan) {
const customer = await omise.customers.retrieve(customerId);
// Update customer metadata
await omise.customers.update(customerId, {
description: `${newPlan} subscription`,
metadata: {
...customer.metadata,
plan: newPlan,
upgraded_at: new Date().toISOString()
}
});
// Charge prorated amount
const charge = await this.chargeProrated(customerId, newPlan);
return { customer, charge };
}
async cancelSubscription(customerId) {
const customer = await omise.customers.retrieve(customerId);
// Update metadata to mark as cancelled
await omise.customers.update(customerId, {
metadata: {
...customer.metadata,
status: 'cancelled',
cancelled_at: new Date().toISOString()
}
});
// Optionally remove all cards
// await this.removeAllCards(customerId);
}
async reactivateSubscription(customerId, plan) {
// Verify customer has payment method
const customer = await omise.customers.retrieve(customerId);
if (!customer.default_card) {
throw new Error('No payment method available');
}
// Update metadata
await omise.customers.update(customerId, {
metadata: {
...customer.metadata,
plan,
status: 'active',
reactivated_at: new Date().toISOString()
}
});
// Charge first payment
return await omise.charges.create({
customer: customerId,
amount: this.getPlanAmount(plan),
currency: 'THB',
description: `${plan} - Reactivation payment`
});
}
}
Best Practicesโ
Email Managementโ
// Always validate email before creating customer
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');
}
// Check for existing customer
const existing = await findCustomerByEmail(email);
if (existing) {
return existing;
}
return await omise.customers.create({
email,
...data
});
}
Metadata Usageโ
// Use metadata for linking to your system
const customer = await omise.customers.create({
email: 'john@example.com',
metadata: {
// Link to your user system
user_id: '12345',
// Track customer tier
tier: 'premium',
// Store important dates
subscribed_at: new Date().toISOString(),
// Track lifecycle
status: 'active',
// Store preferences
marketing_consent: 'true',
// Custom fields
referral_code: 'FRIEND20'
}
});
Error Handlingโ
async function chargeCustomerSafely(customerId, amount, currency) {
try {
// Verify customer exists
const customer = await omise.customers.retrieve(customerId);
if (!customer.default_card) {
throw new Error('No payment method available');
}
// Create charge
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;
}
}
Security Considerationsโ
// Never expose customer IDs in URLs or client-side code
// Use your own user IDs for lookups
class SecureCustomerService {
async getCustomerByUserId(userId) {
// Lookup using metadata
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
});
}
}
Troubleshootingโ
Customer Not Foundโ
// Always verify customer exists before operations
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;
}
}
No Payment Methodโ
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;
}
Duplicate Customersโ
// Prevent duplicate customers
async function getOrCreateCustomer(email, data) {
// Search for existing customer
const existing = await findCustomerByEmail(email);
if (existing) {
console.log('Customer already exists:', existing.id);
return existing;
}
// Create new customer
return await omise.customers.create({
email,
...data
});
}
FAQโ
General Questionsโ
Q: What happens to customer data when deleted?
A: When you delete a customer, all associated cards are also deleted. This action cannot be undone. Any charges or transactions remain in your account history.
Q: Can I restore a deleted customer?
A: No, customer deletion is permanent. You would need to create a new customer with the same information.
Q: How many cards can a customer have?
A: There's no strict limit, but we recommend managing a reasonable number (typically 3-5 active cards) for better user experience.
Q: Can I have duplicate email addresses?
A: Yes, the email field is not unique. You're responsible for managing uniqueness in your application if needed.
Q: How do I link customers to my user system?
A: Use the metadata field to store your internal user ID. This allows you to easily look up Omise customers based on your user records.
Q: Are customer IDs the same in test and live mode?
A: No, test mode customers (cust_test_xxx) are separate from live mode customers (cust_xxx). You'll need to create customers separately in each mode.
Payment Questionsโ
Q: Can I charge a customer without their CVV?
A: Yes, once a card is saved to a customer, you can charge it without CVV. The card token already includes the necessary security information.
Q: What happens if a customer's card expires?
A: Charges will fail. You should monitor for expiring cards and proactively request updated payment information from customers.
Q: Can I charge a customer in different currencies?
A: Yes, you can create charges in any supported currency, subject to your account's currency restrictions.
Q: How do I handle failed subscription payments?
A: Implement retry logic with exponential backoff. Notify customers of failed payments and provide an easy way to update payment information.
Best Practices Questionsโ
Q: Should I create a customer for one-time payments?
A: Not necessary for true one-time payments. Create customers only when you need to save payment information for future use.
Q: How do I handle customer migrations?
A: Export customer data from your old system, create Omise customers, and have users re-enter payment information (you cannot migrate card data directly for security reasons).
Q: What should I store in metadata?
A: Store your internal IDs, subscription status, plan information, and any other data needed for business logic. Don't store sensitive information.
Related Resourcesโ
- Recurring Payments Overview
- Saved Payment Methods
- Recurring Schedules
- Customers API Reference
- Cards API Reference
- Charges API Reference
- Tokens API Reference