import {
mutationPreview,
parseAction,
stripeDate,
stripeList,
stripeRequest,
stripeSearch,
type StripeAuthOptions,
type StripeDryRun,
} from './stripe-core.ts'
export function summarizeCustomer(customer: any) {
if (!customer || typeof customer !== 'object') return null
return {
id: customer.id ?? null,
email: customer.email ?? null,
name: customer.name ?? null,
description: customer.description ?? null,
created: stripeDate(customer.created),
currency: customer.currency ?? null,
delinquent: Boolean(customer.delinquent),
metadata: customer.metadata ?? {},
}
}
export type ListCustomersInput = StripeAuthOptions & {
email?: string
maxItems?: number
}
/** List customers, optionally filtered by exact email. */
export async function listCustomers(input: ListCustomersInput = {}) {
const { items, hasMore } = await stripeList('customers', {
account: input.account,
secretName: input.secretName,
maxItems: input.maxItems ?? 25,
query: { email: input.email },
})
return { hasMore, customers: items.map(summarizeCustomer) }
}
/**
* Search customers with Stripe's search query language.
* @example searchCustomers({ searchQuery: "email:'ada@example.com'" })
*/
export async function searchCustomers(
input: StripeAuthOptions & { searchQuery: string; maxItems?: number },
) {
const { items, hasMore } = await stripeSearch('customers/search', {
account: input.account,
secretName: input.secretName,
searchQuery: input.searchQuery,
maxItems: input.maxItems ?? 25,
})
return { hasMore, customers: items.map(summarizeCustomer) }
}
/** Get one customer by id (full Stripe object). */
export async function getCustomer(input: StripeAuthOptions & { customerId: string }) {
return await stripeRequest({
path: 'customers/' + input.customerId,
account: input.account,
secretName: input.secretName,
})
}
export type CreateCustomerInput = StripeAuthOptions & {
email?: string
name?: string
description?: string
phone?: string
metadata?: Record<string, string>
idempotencyKey?: string
dryRun?: boolean
}
/** Create a customer. Pass `dryRun: true` to preview without calling Stripe. */
export async function createCustomer(
input: CreateCustomerInput = {},
): Promise<StripeDryRun | ReturnType<typeof summarizeCustomer>> {
const body = {
email: input.email,
name: input.name,
description: input.description,
phone: input.phone,
metadata: input.metadata,
}
const preview = mutationPreview(input, {
action: 'create customer',
method: 'POST',
path: 'customers',
body,
})
if (preview) return preview
const customer = await stripeRequest({
path: 'customers',
method: 'POST',
account: input.account,
secretName: input.secretName,
idempotencyKey: input.idempotencyKey,
body,
})
return summarizeCustomer(customer)
}
export type UpdateCustomerInput = StripeAuthOptions & {
customerId: string
email?: string
name?: string
description?: string
phone?: string
metadata?: Record<string, string>
dryRun?: boolean
}
/** Update mutable customer fields. Pass `dryRun: true` to preview. */
export async function updateCustomer(input: UpdateCustomerInput) {
const body = {
email: input.email,
name: input.name,
description: input.description,
phone: input.phone,
metadata: input.metadata,
}
const preview = mutationPreview(input, {
action: 'update customer ' + input.customerId,
method: 'POST',
path: 'customers/' + input.customerId,
body,
})
if (preview) return preview
const customer = await stripeRequest({
path: 'customers/' + input.customerId,
method: 'POST',
account: input.account,
secretName: input.secretName,
body,
})
return summarizeCustomer(customer)
}
/** Permanently delete a customer. Requires confirm: true. Pass dryRun: true to preview. */
export async function deleteCustomer(
input: StripeAuthOptions & { customerId: string; confirm?: boolean; dryRun?: boolean },
) {
const preview = mutationPreview(input, {
action: 'delete customer ' + input.customerId,
method: 'DELETE',
path: 'customers/' + input.customerId,
requireConfirm: true,
})
if (preview) return preview
return await stripeRequest({
path: 'customers/' + input.customerId,
method: 'DELETE',
account: input.account,
secretName: input.secretName,
})
}
const customerActions = [
'list-customers',
'search-customers',
'get-customer',
'create-customer',
'update-customer',
'delete-customer',
] as const
/**
* Customers dispatcher. Defaults to list-customers.
*/
export default async function customers(input: Record<string, unknown> = {}) {
const action = parseAction(input.action, customerActions, 'list-customers', 'customers')
switch (action) {
case 'list-customers':
return await listCustomers(input as ListCustomersInput)
case 'search-customers':
return await searchCustomers(input as never)
case 'get-customer':
return await getCustomer(input as never)
case 'create-customer':
return await createCustomer(input as CreateCustomerInput)
case 'update-customer':
return await updateCustomer(input as UpdateCustomerInput)
case 'delete-customer':
return await deleteCustomer(input as never)
default: {
const exhaustive: never = action
throw new Error('Unhandled customers action: ' + String(exhaustive))
}
}
}