Get Started

Configuration

A zero-config setup
Head to your Stripe dashboard to the API keys.

This module takes full advantage of the Nuxt Runtime Configuration so you don't have to configure anything.

Just add your Stripe keys and you are good to go!

.env
# Publishable Key is prefixed with `pk_`
NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_<...>"

# Secret Key is prefixed with `sk_`
NUXT_STRIPE_SECRET="sk_"

# Webhook Secret is prefixed with `whsec_`
NUXT_STRIPE_WEBHOOK_SECRET="whsec_"

That's it! You can now use the Stripe client-side and server-side with no additional configuration!

Configuration

If you want to customize the module, you can do so by adding a stripe section to your nuxt.config, belows is an example:

nuxt.config.ts
export default defineNuxtConfig({
  stripe: {
    client: {
      options: {
        locale: 'en'
      }
    },

    server: {
      options: {
        apiVersion: '...'
      }
    },

    webhook: {
      /**
       * Default to `/api/stripe/webhook`
       */
      listener: false // Disable the Stripe CLI listener
    },
  }
})

publishableKey

The publishable key is used client-side to initialize the Stripe client.

nuxt.config.ts
export default defineNuxtConfig({
  stripe: {
    publishableKey: '',
  }
})

You typically want to set this value in the NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY environment variables.

.env
NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_<...>"
Head over the Nuxt Runtime Configuration to learn more on how to environment variables are handled in Nuxt.

client

The client options are passed directly to the Stripe client, you can use it to customize the locale for example.

nuxt.config.ts
export default defineNuxtConfig({
  stripe: {
    client: {
      locale: 'en'
    }
  }
})

Head over the official Stripe documentation to learn more about the available options.

You can override the default in the createClient method:

Checkout.vue
<script setup lang="ts">
const { createClient: createStripeClient } = useStripe()

const stripe = await createStripeClient({
  stripeAccount: 'acct_',
  locale: 'en'
})
</script>

createClient is a thin wrapper around the window.Stripe constructor, you can pass any option you want, they will be passed directly to the Stripe client.


webhookPath

This option is not exposed to useRuntimeConfig() as it's only needed in development.
If you need access the webhook path location at runtime, here's how you can do it.

The webhook path is mainly used for local testing, in production it generally does not matter where your webhook endpoint is located.

nuxt.config.ts
export default defineNuxtConfig({
  stripe: {
    webhookPath: 'api/stripe-webhook-path'
  }
})

And make sure your endpoint actually exists:

server/api/stripe-webhook-path.ts
export default stripeWebhookHandler((event) => {
  // ...
})

Expose the Webhook Path to useRuntimeConfig()

To access the webhook path at runtime, add it to the runtimeConfig in the nuxt.config:

nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    stripe: {
      webhookPath: 'api/stripe-webhook-path'
    },
    // If you need it on the client
    public: {
      stripe: {
        webhookPath: 'api/stripe-webhook-path'
      }
    }
  }
})

With T3 env

T3 env is a convenience tool to check and validate environment variables on dev and built, it helps forgetting to set env variables.

Install the @t3-oss/env-nuxt package along with zod:

pnpm add @t3-oss/env-nuxt zod

Create the env.ts file, you can choose any name you want.

env.ts
import { createEnv } from '@t3-oss/env-nuxt'
import { z } from 'zod'

export const env = createEnv({
  // Don't trigger validation on `$ nuxt prepare`
  skipValidation: process.env.npm_lifecycle_event === 'postinstall',

  emptyStringAsUndefined: true,

  client: {
    NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().min(1),
  },
  server: {
    NUXT_STRIPE_SECRET: z.string().min(1),
    NUXT_STRIPE_WEBHOOK_SECRET: z.string().min(1),
  }
})

Then in your nuxt.config.ts:

nuxt.config.ts
import './env'

export default defineNuxtConfig({
})

That's it, importing the file will trigger the validation when you run either build or dev.

If you plan to actually use import { env } from './env' in your code, it is strongly recoomended you create two files, env.server.ts and env.client.ts othrwhise, you gonna expose the zod schema to the client.
The schema contains your env variables names, types and default values.
Read the alert note here on the T3-env official doc

Webhook

You need to install the Stripe CLI for local testing.

This module comes with a built-in webhook handler, you can use it to receive webhook events from Stripe.

Create the file api/stripe/webhook.ts:

server/api/stripe/webhook.ts
export default stripeWebhookHandler((event, context) => {
  switch (stripeEvent.type) {
    case 'payment_intent.succeeded': {
      return {
        customEventCode: 123,
        result: 'This message is available in the Stripe dashboard!'
      }
    }
  }

  // You can access the default Stripe instance
  // and the original H3 event from the context
  const h3event = context.$event
  const stripe = context.stripe
})

Any value you return will be available in the Stripe dashboard:

{
  "ok": true,
  "data": {
    "customEventCode": 123,
    "result": "This message is available in the Stripe dashboard!"
  }
}

The module always return { ok: true } field, followed by the data you return.

When no value is returned, the payload will only contains { ok: true }.

Be careful altering the H3event when you are using the build in event handler. If you absolutely need a cutting-edge custom behavior, handle the webhook manually.

Error Handling

Unhandled exception will be handled by Nuxt itself, this module do not attempt to catch an exception.

api/stripe/webhook.ts
export default stripeWebhookHandler(() => {
  throw new Error('This error will be returned to Stripe!')
})

Event Handling

Head over the Advanced Custom Webhook section to learn how to fully customize the webhook handler.

The event is a Stripe.Event object, you can use it to access the event type and data.

We have setup a little example on how to handle the different event types in TypeScript:

server/api/stripe/webhook.ts
export default stripeWebhookHandler((event) => {
  switch (stripeEvent.type) {
    case 'payment_intent.succeeded': {
      const paymentIntent = stripeEvent.data.object as Stripe.PaymentIntent
      break
    }

    case 'checkout.session.expired': {
      const session = stripeEvent.data.object as Stripe.Checkout.Session
      break
    }

    case 'customer.created' : {
      const customer = stripeEvent.data.object as Stripe.Customer
      break
    }
  }
})

Webhook Path

This section is only relevant if you want to use the local testing functionality.

By now surely you have notice we have use the webhook path /api/stripe/webhook quite alot as that's the default path this module uses, you can customize it in the module options:

nuxt.config.ts
export default defineNuxtConfig({
  stripe: {
    webhook: {
      listener: '/api/stripe/webhook'
    }
  }
})

However, this settings is important only if you want to use this module local testing functionality as this module need to know where to proxy the webhook requests.

You can turn off the local testing by setting webhook.listener to false:

nuxt.config.ts
export default defineNuxtConfig({
  stripe: {
    webhook: {
      listener: false
    }
  }
})

And that's pretty much it, head over the Stripe CLI section to start testing your webhook locally!