npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

nuxt-stripe-payments

v1.3.0

Published

Lightweight Stripe payment integration for Nuxt 3 with support for all payment methods

Readme

Nuxt Stripe Payments

npm version npm downloads License Nuxt

Lightweight Stripe payment integration for Nuxt 3 with support for all payment methods including Apple Pay, Google Pay, SEPA, EPS, and more.

Features

Zero Dependencies - Loads Stripe.js dynamically, no heavy dependencies
All Payment Methods - Cards, Apple Pay, Google Pay, SEPA, EPS, Revolut Pay, etc.
Subscriptions - Full support for Stripe subscriptions with Checkout Sessions
Subscription Management - Cancel, update, resume, and manage subscriptions easily
TypeScript Support - Full type safety out of the box
Auto-configured - Works with Nuxt's auto-imports
Customizable - Full control over appearance and behavior
SSR Compatible - Works with Nuxt's server-side rendering

Quick Setup

  1. Add nuxt-stripe-payments dependency to your project
npm install nuxt-stripe-payments stripe
# or
yarn add nuxt-stripe-payments stripe
# or
pnpm add nuxt-stripe-payments stripe
  1. Add nuxt-stripe-payments to the modules section of nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    'nuxt-stripe-payments'
  ],
  
  stripePayments: {
    // Set your default Stripe publishable key
    publishableKey: process.env.NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
    // or hardcode for development: publishableKey: 'pk_test_xxxxx',
    defaultCurrency: 'eur',
    apiEndpoint: '/api/create-payment-intent'
  }
})
  1. Add your Stripe keys to .env:
NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxx
  1. Create a backend endpoint for payment intents

Create server/api/create-payment-intent.post.ts:

import Stripe from 'stripe'

export default defineEventHandler(async (event) => {
    const { 
        amount, 
        currency, 
        metadata,
        customerId,      // Existing customer ID (optional)
        customerData     // New customer data (optional)
    } = await readBody(event)
    const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
    
    let customer: Stripe.Customer | null = null
    
    // Create or retrieve customer
    if (customerId) {
        customer = await stripe.customers.retrieve(customerId) as Stripe.Customer
    } else if (customerData) {
        // Create new customer
        const customerParams: Stripe.CustomerCreateParams = {
            email: customerData.email,
            name: customerData.firstName && customerData.lastName 
                ? `${customerData.firstName} ${customerData.lastName}` 
                : undefined,
            metadata: {
                ...(customerData.company && { company: customerData.company }),
                ...(customerData.vatId && { vat_id: customerData.vatId })
            }
        }
        
        // Add address if provided
        if (customerData.address) {
            customerParams.address = {
                line1: customerData.address.line1,
                line2: customerData.address.line2,
                city: customerData.address.city,
                state: customerData.address.state,
                postal_code: customerData.address.postal_code,
                country: customerData.address.country
            }
        }
        
        if (customerParams.email || customerParams.name) {
            customer = await stripe.customers.create(customerParams)
        }
    }
    
    const paymentIntent = await stripe.paymentIntents.create({
        amount,
        currency,
        automatic_payment_methods: {
            enabled: true,
            allow_redirects: 'always'
        },
        metadata: metadata || {},
        ...(customer && { customer: customer.id })
    })
    
    return {
        clientSecret: paymentIntent.client_secret,
        ...(customer && { customerId: customer.id })
    }
})

That's it! You can now use the StripePayment component in your application ✨

Usage

Basic Example

<template>
  <div>
    <!-- Uses publishableKey from nuxt.config.ts -->
    <StripePayment
      :amount="1099"
      @success="handleSuccess"
      @error="handleError"
    />
  </div>
</template>

<script setup>
const handleSuccess = (paymentIntent) => {
  console.log('Payment successful!', paymentIntent)
  navigateTo('/success')
}

const handleError = (error) => {
  console.error('Payment failed:', error)
}
</script>

Advanced Example with Overrides

<template>
  <StripePayment
    :amount="2499"
    currency="usd"
    api-endpoint="/api/custom-payment"
    return-url="https://yourdomain.com/complete"
    button-text="Subscribe Now"
    button-class="custom-btn"
    :appearance="stripeAppearance"
    :metadata="paymentMetadata"
    @success="handleSuccess"
    @error="handleError"
    @ready="handleReady"
  />
</template>

<script setup>
const stripeAppearance = {
  theme: 'stripe',
  variables: {
    colorPrimary: '#0570de'
  }
}

const paymentMetadata = {
  orderNumber: '12345',
  customerId: 'cus_abc123',
  source: 'web'
}
</script>

Using Different Keys per Component

<template>
  <StripePayment
    publishable-key="pk_test_different_account_xxx"
    :amount="1099"
    @success="handleSuccess"
  />
</template>

Programmatic Submit

<template>
  <div>
    <StripePayment
      ref="paymentRef"
      :amount="1099"
      hide-button
      @success="handleSuccess"
    />
    
    <button @click="submitPayment">
      Pay Now
    </button>
  </div>
</template>

<script setup>
const paymentRef = ref(null)

const submitPayment = () => {
  paymentRef.value?.submit()
}
</script>

Customer Data Collection

The module supports collecting customer data and automatically creating Stripe Customers during payment processing. This is useful for tracking customers, managing subscriptions, and compliance (e.g., VAT handling).

Basic Example with Customer Data

<template>
  <StripePayment
    :amount="1099"
    :customer-data="customerInfo"
    @success="handleSuccess"
  />
</template>

<script setup>
const customerInfo = {
  email: '[email protected]',
  firstName: 'John',
  lastName: 'Doe',
  company: 'Acme Corp',
  address: {
    line1: '123 Main St',
    city: 'New York',
    state: 'NY',
    postal_code: '10001',
    country: 'US'
  },
  vatId: 'VAT123456'
}

const handleSuccess = (paymentIntent) => {
  console.log('Payment successful!', paymentIntent)
  console.log('Customer ID:', paymentIntent.customerId)
  navigateTo('/success')
}
</script>

Using Existing Customer

If you already have a Stripe Customer ID, you can pass it directly:

<template>
  <StripePayment
    :amount="1099"
    customer-id="cus_xxxxxxxxxxxxx"
    @success="handleSuccess"
  />
</template>

Partial Customer Data

You can provide partial customer data - only the fields you have:

<template>
  <StripePayment
    :amount="1099"
    :customer-data="{ email: '[email protected]', firstName: 'John', lastName: 'Doe' }"
    @success="handleSuccess"
  />
</template>

Module-Level Configuration

Enable customer data collection globally in nuxt.config.ts:

export default defineNuxtConfig({
  stripePayments: {
    publishableKey: process.env.NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
    collectCustomerData: true,  // Enable globally
    customerDataFields: ['email', 'name', 'company', 'address', 'vat']
  }
})

Note: Even with module-level configuration, you still need to provide the customerData prop or customerId prop to the component. The module config only sets defaults for which fields to collect if you're building a form.

Subscriptions

The module includes full support for Stripe subscriptions using Checkout Sessions. Subscriptions redirect users to Stripe's hosted checkout page for a seamless experience.

Getting a Stripe Price ID

Before using the StripeSubscription component, you need to create a Price in your Stripe Dashboard. Here's how:

Option 1: Stripe Dashboard (Recommended for beginners)

  1. Go to Stripe DashboardProducts
  2. Click "Add product" or select an existing product
  3. Set up your pricing:
    • Choose Recurring for subscriptions
    • Set the billing period (monthly, yearly, etc.)
    • Set the price amount
    • Configure any additional options (trial period, metered billing, etc.)
  4. Click "Save product"
  5. Copy the Price ID (starts with price_) from the product page

Option 2: Stripe API (For programmatic setup)

import Stripe from 'stripe'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)

// Create a product first
const product = await stripe.products.create({
  name: 'Premium Plan',
  description: 'Monthly subscription to premium features'
})

// Create a price for the product
const price = await stripe.prices.create({
  product: product.id,
  unit_amount: 2999, // $29.99 in cents
  currency: 'usd',
  recurring: {
    interval: 'month'
  }
})

console.log('Price ID:', price.id) // Use this in your component

Option 3: Stripe CLI (For testing)

# Create a test price
stripe prices create \
  --product prod_test123 \
  --unit-amount 2999 \
  --currency usd \
  --recurring interval=month

The price ID will look like: price_1ABC123def456GHI789jkl012

Basic Subscription Example

<template>
  <StripeSubscription
    price-id="price_xxxxxxxxxxxxx"
    @success="handleSuccess"
    @error="handleError"
  />
</template>

<script setup>
const handleSuccess = (session) => {
  console.log('Checkout session created:', session)
  // User will be redirected to Stripe Checkout
}

const handleError = (error) => {
  console.error('Subscription error:', error)
}
</script>

Subscription with Trial Period

<template>
  <StripeSubscription
    price-id="price_xxxxxxxxxxxxx"
    :trial-period-days="14"
    customer-email="[email protected]"
    @success="handleSuccess"
  />
</template>

Multiple Prices / Add-ons

<template>
  <StripeSubscription
    :price-id="['price_basic', 'price_addon']"
    :quantity="1"
    @success="handleSuccess"
  />
</template>

Subscription Management

Use the useStripeSubscription composable to manage subscriptions:

<template>
  <div>
    <button @click="cancelSub">Cancel Subscription</button>
    <button @click="openPortal">Manage Subscription</button>
  </div>
</template>

<script setup>
const { cancelSubscription, createPortalSession } = useStripeSubscription()
const subscriptionId = 'sub_xxxxxxxxxxxxx'
const customerId = 'cus_xxxxxxxxxxxxx'

const cancelSub = async () => {
  try {
    // Cancel at period end (recommended)
    await cancelSubscription(subscriptionId, false)
    alert('Subscription will cancel at period end')
  } catch (error) {
    console.error('Failed to cancel:', error)
  }
}

const openPortal = async () => {
  try {
    const { url } = await createPortalSession(customerId)
    window.location.href = url
  } catch (error) {
    console.error('Failed to open portal:', error)
  }
}
</script>

Update Subscription

<script setup>
const { updateSubscription } = useStripeSubscription()

const upgradePlan = async () => {
  try {
    await updateSubscription('sub_xxxxxxxxxxxxx', {
      priceId: 'price_premium',
      quantity: 1
    })
    alert('Subscription updated!')
  } catch (error) {
    console.error('Failed to update:', error)
  }
}
</script>

Backend Endpoints for Subscriptions

Create server/api/create-checkout-session.post.ts:

import Stripe from 'stripe'

export default defineEventHandler(async (event) => {
    const {
        priceId,
        customerId,
        customerEmail,
        mode = 'subscription',
        allowPromotionCodes = false,
        trialPeriodDays,
        quantity = 1,
        metadata = {},
        successUrl,
        cancelUrl
    } = await readBody(event)
    
    const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
    
    const lineItems = Array.isArray(priceId) 
        ? priceId.map((id: string) => ({ price: id, quantity }))
        : [{ price: priceId, quantity }]
    
    const session = await stripe.checkout.sessions.create({
        mode,
        line_items: lineItems,
        success_url: successUrl || `${getRequestURL(event).origin}/subscription-success?session_id={CHECKOUT_SESSION_ID}`,
        cancel_url: cancelUrl || `${getRequestURL(event).origin}/subscription-cancel`,
        allow_promotion_codes: allowPromotionCodes,
        customer: customerId,
        customer_email: customerEmail,
        subscription_data: trialPeriodDays ? {
            trial_period_days: trialPeriodDays
        } : undefined,
        metadata
    })
    
    return {
        sessionId: session.id,
        url: session.url
    }
})

Create server/api/subscriptions/[id].put.ts for updating subscriptions:

import Stripe from 'stripe'

export default defineEventHandler(async (event) => {
    const id = getRouterParam(event, 'id')
    const { priceId, quantity, metadata } = await readBody(event)
    const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
    
    const subscription = await stripe.subscriptions.retrieve(id!)
    const subscriptionItemId = subscription.items.data[0]?.id
    
    const updated = await stripe.subscriptions.update(id!, {
        items: [{
            id: subscriptionItemId,
            price: priceId,
            quantity
        }],
        metadata: metadata || {}
    })
    
    return updated
})

Create server/api/subscriptions/[id]/cancel.post.ts for canceling:

import Stripe from 'stripe'

export default defineEventHandler(async (event) => {
    const id = getRouterParam(event, 'id')
    const { immediately = false } = await readBody(event)
    const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
    
    if (immediately) {
        return await stripe.subscriptions.cancel(id!)
    } else {
        return await stripe.subscriptions.update(id!, {
            cancel_at_period_end: true
        })
    }
})

See the playground/server/api directory for complete examples of all subscription endpoints.

Module Configuration

Configure default values in nuxt.config.ts:

export default defineNuxtConfig({
  stripePayments: {
    // Required: Your Stripe publishable key
    publishableKey: process.env.NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
    
    // Optional: Default currency for all payments
    defaultCurrency: 'eur', // 'usd', 'gbp', etc.
    
    // Optional: Default API endpoint
    apiEndpoint: '/api/create-payment-intent'
  }
})

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | publishableKey | string | '' | Default Stripe publishable key | | defaultCurrency | string | 'eur' | Default currency for payments | | apiEndpoint | string | '/api/create-payment-intent' | Default API endpoint for payment intents | | checkoutSessionEndpoint | string | '/api/create-checkout-session' | Default API endpoint for checkout sessions (subscriptions) | | subscriptionEndpoint | string | '/api/subscriptions' | Default API endpoint for subscription management | | collectCustomerData | boolean | false | Enable customer data collection globally | | customerDataFields | string[] | ['email', 'name', 'address'] | Fields to collect when collectCustomerData is enabled |

All configuration options can be overridden per component via props.

Component Props

StripePayment Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | publishableKey | string | from config | Stripe publishable key (overrides config) | | amount | number | required | Amount in cents | | currency | string | from config | Currency code (overrides config) | | apiEndpoint | string | '/api/create-payment-intent' | Backend endpoint | | returnUrl | string | '' | Redirect URL after payment | | buttonText | string | 'Pay Now' | Submit button text | | submittingText | string | 'Processing...' | Loading button text | | loadingText | string | 'Loading payment form...' | Initial loading text | | buttonClass | string | Default styles | Custom button CSS classes | | hideButton | boolean | false | Hide built-in button | | appearance | object | {} | Stripe Elements appearance | | metadata | object | {} | Custom metadata to attach to the payment | | collectCustomerData | boolean | from config | Enable customer data collection (overrides config) | | customerDataFields | string[] | from config | Fields to collect (overrides config) | | customerData | object | undefined | Customer data object (email, firstName, lastName, company, address, vatId) | | customerId | string | undefined | Existing Stripe customer ID (skips customer creation) |

StripeSubscription Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | priceId | string \| string[] | required | Stripe price ID(s) for subscription | | customerId | string | '' | Existing Stripe customer ID | | customerEmail | string | '' | Customer email (creates customer if not exists) | | checkoutSessionEndpoint | string | from config | Backend endpoint for checkout sessions | | successUrl | string | '' | URL to redirect after successful checkout | | cancelUrl | string | '' | URL to redirect if checkout is canceled | | buttonText | string | 'Subscribe Now' | Button text | | submittingText | string | 'Redirecting...' | Loading button text | | buttonClass | string | Default styles | Custom button CSS classes | | hideButton | boolean | false | Hide built-in button | | metadata | object | {} | Custom metadata to attach to subscription | | mode | 'subscription' \| 'setup' \| 'payment' | 'subscription' | Checkout session mode | | allowPromotionCodes | boolean | false | Allow promotion codes in checkout | | trialPeriodDays | number | undefined | Number of trial days | | quantity | number | 1 | Subscription quantity | | billingAddressCollection | 'auto' \| 'required' | 'auto' | Billing address collection | | collectShippingAddress | boolean | false | Collect shipping address |

Styling

Default Styling

The component comes with clean, framework-agnostic CSS styling that works out of the box without any CSS framework:

<StripePayment :amount="1099" />

The default button uses vanilla CSS with a nice blue theme, hover states, and disabled states.

Custom Button Styling

You can customize the button with your own CSS classes:

<StripePayment
  :amount="1099"
  button-class="my-custom-button"
/>
.my-custom-button {
  background: linear-gradient(to right, #667eea, #764ba2);
  color: white;
  padding: 12px 24px;
  border-radius: 8px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}

.my-custom-button:hover:not(:disabled) {
  opacity: 0.9;
}

.my-custom-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

shadcn-vue Integration

The component automatically detects and uses shadcn-vue Button component if it's installed in your project:

<!-- If shadcn-vue is installed, this automatically uses the shadcn Button -->
<StripePayment :amount="1099" />

How it works:

  • The component tries to import @/components/ui/button/Button.vue
  • If found, it uses the shadcn Button with variant="default"
  • If not found, it falls back to a native HTML button with custom styling
  • When using shadcn, the buttonClass prop is ignored to preserve shadcn's styling

Manual shadcn setup:

If you want to ensure shadcn-vue is being used, install it first:

npx shadcn-vue@latest init
npx shadcn-vue@latest add button

Then use the payment component as normal - it will automatically use your shadcn Button!

Hiding the Default Button

If you want complete control over the button styling or layout:

<template>
  <div>
    <StripePayment
      ref="paymentRef"
      :amount="1099"
      hide-button
      @success="handleSuccess"
    />
    
    <!-- Your custom button -->
    <YourCustomButton @click="submitPayment">
      Complete Purchase
    </YourCustomButton>
  </div>
</template>

<script setup>
const paymentRef = ref(null)

const submitPayment = () => {
  paymentRef.value?.submit()
}
</script>

Events

| Event | Payload | Description | |-------|---------|-------------| | @success | paymentIntent | Payment succeeded. Includes customerId if customer was created or provided | | @error | errorMessage | Error occurred | | @ready | - | Form ready for input |

Composables

useStripe()

Composable to load Stripe.js script dynamically:

const { loadStripe, stripeInstance, isLoaded } = useStripe()

// Load Stripe
const stripe = await loadStripe('pk_test_xxxxx')

useStripeSubscription()

Composable for managing Stripe subscriptions:

const {
  cancelSubscription,
  updateSubscription,
  getSubscription,
  resumeSubscription,
  createPortalSession,
  listSubscriptions
} = useStripeSubscription()

// Cancel subscription
await cancelSubscription('sub_xxxxx', false) // false = cancel at period end

// Update subscription
await updateSubscription('sub_xxxxx', {
  priceId: 'price_new',
  quantity: 2
})

// Get subscription
const subscription = await getSubscription('sub_xxxxx')

// Resume canceled subscription
await resumeSubscription('sub_xxxxx')

// Open customer portal
const { url } = await createPortalSession('cus_xxxxx')
window.location.href = url

// List customer subscriptions
const { subscriptions } = await listSubscriptions('cus_xxxxx')

Payment Methods Supported

The module automatically enables all Stripe payment methods:

  • 💳 Credit/Debit Cards
  • 🍎 Apple Pay
  • 🤖 Google Pay
  • 🏦 SEPA Direct Debit
  • 🇦🇹 EPS (Austria)
  • 🇩🇪 Giropay (Germany)
  • 💶 Sofort
  • 💜 Revolut Pay
  • And many more...

Testing

Use Stripe test cards:

  • Success: 4242 4242 4242 4242
  • Requires 3DS: 4000 0025 0000 3155
  • Declined: 4000 0000 0000 9995

Development

# Install dependencies
npm install

# Generate type stubs
npm run dev:prepare

# Develop with the playground
npm run dev

# Build the playground
npm run dev:build

# Run tests
npm run test

# Release new version
npm run release

License

MIT License