import {
formatStripeAmount,
parseAction,
stripeDate,
stripeList,
stripeRequest,
type StripeAuthOptions,
} from './stripe-core.ts'
/**
* Get the Stripe account profile for the authenticated key.
* @example
* import { getAccount } from 'kody:@kody/stripe/account'
* const account = await getAccount()
*/
export async function getAccount(input: StripeAuthOptions = {}) {
const account = await stripeRequest({ path: 'account', ...input })
return {
id: account.id ?? null,
displayName: account.settings?.dashboard?.display_name ?? account.business_profile?.name ?? null,
email: account.email ?? null,
country: account.country ?? null,
defaultCurrency: account.default_currency ?? null,
chargesEnabled: Boolean(account.charges_enabled),
payoutsEnabled: Boolean(account.payouts_enabled),
livemode: Boolean(account.livemode),
}
}
/** Get available and pending balance amounts. */
export async function getBalance(input: StripeAuthOptions = {}) {
const balance = await stripeRequest({ path: 'balance', ...input })
const shape = (entries: any[]) =>
(entries ?? []).map((entry) => ({
amount: entry.amount,
currency: entry.currency,
display: formatStripeAmount(entry.amount, entry.currency),
}))
return {
livemode: Boolean(balance.livemode),
available: shape(balance.available),
pending: shape(balance.pending),
}
}
export type ListBalanceTransactionsInput = StripeAuthOptions & {
/** Filter by type, e.g. 'charge', 'refund', 'payout'. */
type?: string
/** Created window: ISO strings, Date, or unix seconds. */
createdGte?: string | number | Date
createdLte?: string | number | Date
maxItems?: number
}
export function toUnixSeconds(value: string | number | Date | undefined) {
if (value == null) return undefined
if (typeof value === 'number') return value
const date = value instanceof Date ? value : new Date(value)
if (Number.isNaN(date.getTime())) throw new Error('Invalid date: ' + String(value))
return Math.floor(date.getTime() / 1000)
}
/** List balance transactions (money movements) with compact summaries. */
export async function listBalanceTransactions(input: ListBalanceTransactionsInput = {}) {
const { items, hasMore } = await stripeList('balance_transactions', {
account: input.account,
secretName: input.secretName,
maxItems: input.maxItems ?? 25,
query: {
type: input.type,
created: {
gte: toUnixSeconds(input.createdGte),
lte: toUnixSeconds(input.createdLte),
},
},
})
return {
hasMore,
transactions: items.map((txn) => ({
id: txn.id,
type: txn.type,
status: txn.status,
amount: txn.amount,
fee: txn.fee,
net: txn.net,
currency: txn.currency,
display: formatStripeAmount(txn.amount, txn.currency),
description: txn.description ?? null,
created: stripeDate(txn.created),
availableOn: stripeDate(txn.available_on),
})),
}
}
export type ListPayoutsInput = StripeAuthOptions & {
status?: 'pending' | 'paid' | 'failed' | 'canceled' | 'in_transit'
maxItems?: number
}
/** List payouts to the connected bank account. */
export async function listPayouts(input: ListPayoutsInput = {}) {
const { items, hasMore } = await stripeList('payouts', {
account: input.account,
secretName: input.secretName,
maxItems: input.maxItems ?? 25,
query: { status: input.status },
})
return {
hasMore,
payouts: items.map((payout) => ({
id: payout.id,
status: payout.status,
amount: payout.amount,
currency: payout.currency,
display: formatStripeAmount(payout.amount, payout.currency),
arrivalDate: stripeDate(payout.arrival_date),
created: stripeDate(payout.created),
method: payout.method ?? null,
description: payout.description ?? null,
})),
}
}
export type ListEventsInput = StripeAuthOptions & {
/** Event type filter, e.g. 'invoice.paid' or 'charge.*'. */
type?: string
maxItems?: number
}
/** List recent Stripe events (useful for debugging webhooks and activity). */
export async function listEvents(input: ListEventsInput = {}) {
const { items, hasMore } = await stripeList('events', {
account: input.account,
secretName: input.secretName,
maxItems: input.maxItems ?? 25,
query: { type: input.type },
})
return {
hasMore,
events: items.map((event) => ({
id: event.id,
type: event.type,
created: stripeDate(event.created),
livemode: Boolean(event.livemode),
objectId: event.data?.object?.id ?? null,
objectType: event.data?.object?.object ?? null,
})),
}
}
const accountActions = [
'get-account',
'get-balance',
'list-balance-transactions',
'list-payouts',
'list-events',
] as const
/**
* Account/balance dispatcher. Defaults to get-account.
* Actions: get-account, get-balance, list-balance-transactions, list-payouts, list-events.
*/
export default async function account(input: Record<string, unknown> = {}) {
const action = parseAction(input.action, accountActions, 'get-account', 'account')
switch (action) {
case 'get-account':
return await getAccount(input)
case 'get-balance':
return await getBalance(input)
case 'list-balance-transactions':
return await listBalanceTransactions(input as ListBalanceTransactionsInput)
case 'list-payouts':
return await listPayouts(input as ListPayoutsInput)
case 'list-events':
return await listEvents(input as ListEventsInput)
default: {
const exhaustive: never = action
throw new Error('Unhandled account action: ' + String(exhaustive))
}
}
}