https://github.com/pathwaysdigital/nuxt-stripe-payments
Lightweight Stripe payment integration for Nuxt 3 with support for all payment methods including Apple Pay, Google Pay, SEPA, EPS, and more.
https://github.com/pathwaysdigital/nuxt-stripe-payments
creditcard eps nuxt revolut stripe stripe-payment stripe-payments vue
Last synced: about 2 months ago
JSON representation
Lightweight Stripe payment integration for Nuxt 3 with support for all payment methods including Apple Pay, Google Pay, SEPA, EPS, and more.
- Host: GitHub
- URL: https://github.com/pathwaysdigital/nuxt-stripe-payments
- Owner: pathwaysdigital
- License: mit
- Created: 2025-11-06T10:42:32.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2025-11-23T11:39:46.000Z (7 months ago)
- Last Synced: 2026-03-12T19:34:09.997Z (4 months ago)
- Topics: creditcard, eps, nuxt, revolut, stripe, stripe-payment, stripe-payments, vue
- Language: TypeScript
- Homepage: https://pathways-digital.com
- Size: 185 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Nuxt Stripe Payments
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
[![Nuxt][nuxt-src]][nuxt-href]
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
```bash
npm install nuxt-stripe-payments stripe
# or
yarn add nuxt-stripe-payments stripe
# or
pnpm add nuxt-stripe-payments stripe
```
2. Add `nuxt-stripe-payments` to the `modules` section of `nuxt.config.ts`
```js
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'
}
})
```
3. Add your Stripe keys to `.env`:
```env
NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxx
```
4. Create a backend endpoint for payment intents
Create `server/api/create-payment-intent.post.ts`:
```typescript
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
```vue
const handleSuccess = (paymentIntent) => {
console.log('Payment successful!', paymentIntent)
navigateTo('/success')
}
const handleError = (error) => {
console.error('Payment failed:', error)
}
```
### Advanced Example with Overrides
```vue
const stripeAppearance = {
theme: 'stripe',
variables: {
colorPrimary: '#0570de'
}
}
const paymentMetadata = {
orderNumber: '12345',
customerId: 'cus_abc123',
source: 'web'
}
```
### Using Different Keys per Component
```vue
```
### Programmatic Submit
```vue
Pay Now
const paymentRef = ref(null)
const submitPayment = () => {
paymentRef.value?.submit()
}
```
### 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
```vue
const customerInfo = {
email: 'customer@example.com',
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')
}
```
#### Using Existing Customer
If you already have a Stripe Customer ID, you can pass it directly:
```vue
```
#### Partial Customer Data
You can provide partial customer data - only the fields you have:
```vue
```
#### Module-Level Configuration
Enable customer data collection globally in `nuxt.config.ts`:
```typescript
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 Dashboard](https://dashboard.stripe.com) → **Products**
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)**
```typescript
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)**
```bash
# 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
```vue
const handleSuccess = (session) => {
console.log('Checkout session created:', session)
// User will be redirected to Stripe Checkout
}
const handleError = (error) => {
console.error('Subscription error:', error)
}
```
### Subscription with Trial Period
```vue
```
### Multiple Prices / Add-ons
```vue
```
### Subscription Management
Use the `useStripeSubscription` composable to manage subscriptions:
```vue
Cancel Subscription
Manage Subscription
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)
}
}
```
### Update Subscription
```vue
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)
}
}
```
### Backend Endpoints for Subscriptions
Create `server/api/create-checkout-session.post.ts`:
```typescript
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:
```typescript
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:
```typescript
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`:
```typescript
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:
```vue
```
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:
```vue
```
```css
.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](https://www.shadcn-vue.com/) Button component if it's installed in your project:
```vue
```
**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:
```bash
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:
```vue
Complete Purchase
const paymentRef = ref(null)
const submitPayment = () => {
paymentRef.value?.submit()
}
```
## 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:
```typescript
const { loadStripe, stripeInstance, isLoaded } = useStripe()
// Load Stripe
const stripe = await loadStripe('pk_test_xxxxx')
```
### useStripeSubscription()
Composable for managing Stripe subscriptions:
```typescript
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
```bash
# 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](./LICENSE)
[npm-version-src]: https://img.shields.io/npm/v/nuxt-stripe-payments/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-version-href]: https://npmjs.com/package/nuxt-stripe-payments
[npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-stripe-payments.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-downloads-href]: https://npmjs.com/package/nuxt-stripe-payments
[license-src]: https://img.shields.io/npm/l/nuxt-stripe-payments.svg?style=flat&colorA=18181B&colorB=28CF8D
[license-href]: https://npmjs.com/package/nuxt-stripe-payments
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
[nuxt-href]: https://nuxt.com