ensureStripeCustomer()
Finds or creates a Stripe Customer for the given user, then ensures a corresponding Customer record exists in your DB. Mirrors the ensureCustomer contract from @payments/driver/driver.signature.ts.
Usage - Driver
import { ensureCustomer } from '@payments/driver'If you have ‘stripe’ set up as your main payments driver in @app/config ofcourse.
const { user, customer } = await ensureCustomer({
user, // Can be partial, must at least include 'email' and 'userId' fields
customerOverrides: { name: 'Jane Doe' },
metadata: { onboardingStep: 'billing' },
})Usage - Directly
import { ensureStripeCustomer } from '@payments/stripe/resolvers/ensureStripeCustomer.resolver'const { user, customer } = await ensureStripeCustomer({
user, // Can be partial, must at least include 'email' and 'userId' fields
customerOverrides: { name: 'Jane Doe' },
metadata: { onboardingStep: 'billing' },
})Implementation details
- Validates
user.userIdanduser.emailare present. - Attempts to find an existing Stripe customer by email; otherwise creates one with inferred name and metadata.
- → Inferred names are checked for
user,customerOverrides,metadatain following order: - →
fullName→name→firstName→username→ guessed name fromemail - If newly created, updates your
Usersmodel withstripeCustomerId. - Upserts a
Customerrecord in your DB linkinguserId↔customerIdwith provider'stripe'.
ensureStripeCustomer.resolver.ts
export const ensureStripeCustomer = createResolver(async ({ args, parseArgs, formatOutput }) => {
// Args
const { metadata, customerOverrides } = parseArgs(args)
// Auth + Guardrails...
let user = args.user as User
if (!user) throw new Error('...')
// Fallback names for new customers if none exist yet for this email
const fullName = guessDisplayName(user, customerOverrides, metadata)
// Upsert the customer, update User record with stripe customer id
const stripeCustomer = await findOrCreateCustomer({ email: user.email, name: fullName, metadata: { ...user } })
if (!user.stripeCustomerId) user = await Users.updateOne({ userId: user.userId }, { stripeCustomerId: stripeCustomer.id })
const customer = await Customers.findOrCreate({ provider: 'stripe', userId: user.userId, customerId: stripeCustomer.id }, { /* fields */ })
// Return the updated user and customer records
return formatOutput({ provider: 'stripe', user, customer })
})EnsureCustomerInput
Follows the driver signature EnsureCustomerInput schema:
import { EnsureCustomerInput } from '@payments/driver/driver.signature'export const EnsureCustomerInput = schema('EnsureCustomerInput', {
user: User,
customerOverrides: z.object({
email: Customer.shape.email.nullish().describe('Override the email on the Customer record for this checkout session'),
name: Customer.shape.name.nullish().describe('Override the name on the Customer record for this checkout session'),
address: z.object({
line1: z.string().nullish().describe('Address line 1'),
city: z.string().nullish().describe('City'),
state: z.string().nullish().describe('State/Province'),
postalCode: z.string().nullish().describe('Postal/ZIP code'),
country: z.string().nullish().describe('Country code (ISO 3166-1 alpha-2)'),
}).nullish().describe('Override the address on the Customer record for this checkout session'),
taxId: z.string().nullish().describe('Override the tax ID on the Customer record for this checkout session'),
}).nullish().describe('Object containing fields to override on the Customer record for this checkout session'),
metadata: z.record(z.string(), z.any()).nullish().describe('Optional metadata to attach to the checkout session'),
})EnsureCustomerOutput
Follows the driver signature EnsureCustomerOutput schema:
import { EnsureCustomerOutput } from '@payments/driver/driver.signature'export const EnsureCustomerOutput = schema('EnsureCustomerOutput', {
provider: PaymentProvidable.shape.provider,
user: User,
customer: Customer,
})Recommendations
💡
It’s good practice to call ensureCustomer() as a prerequisite to startCheckout() and startPortalSession() calls. This ensures you always have a single customer in your payment provider (like Stripe) for each user email.
