Alt description missing in image
Beta: Plugins coming soon!
@payments/striperesolversensureStripeCustomer
Alt description missing in image

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.userId and user.email are 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, metadata in following order:
  • fullNamenamefirstNameusername → guessed name from email
  • If newly created, updates your Users model with stripeCustomerId.
  • Upserts a Customer record in your DB linking userIdcustomerId with 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.