Stripe Integration
Complete guide to Stripe integration with DeesseJS
Stripe Integration
Complete guide to integrating Stripe payments with DeesseJS.
Setup
1. Create Stripe Account
- Sign up at stripe.com
- Get your API keys from the Dashboard
- Copy publishable and secret keys
2. Configure Environment Variables
# .env.local
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...3. Install Dependencies
npm install stripe @stripe/stripe-jsConfiguration
// deesse.config.ts
import { defineConfig } from '@deessejs/core'
export const config = defineConfig({
payments: {
provider: 'stripe',
credentials: {
publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
secretKey: process.env.STRIPE_SECRET_KEY,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
},
currency: 'usd',
locale: 'en',
features: {
checkout: true, // Enable Stripe Checkout
links: true, // Enable Payment Links
billingPortal: true, // Enable Customer Portal
},
},
})One-Time Payments
Server-Side
// app/api/create-payment-intent/route.ts
import { NextResponse } from 'next/server'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(request: Request) {
const { amount, currency = 'usd', metadata = {} } = await request.json()
// Create payment intent
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100), // Convert to cents
currency,
automatic_payment_methods: {
enabled: true,
},
metadata: {
orderId: metadata.orderId || '',
userId: metadata.userId || '',
},
})
// Save to database
await db.payments.create({
data: {
stripePaymentIntentId: paymentIntent.id,
amount,
currency,
status: 'pending',
},
})
return NextResponse.json({
clientSecret: paymentIntent.client_secret,
paymentIntentId: paymentIntent.id,
})
}Client-Side with Elements
// app/checkout/page.tsx
'use client'
import { loadStripe } from '@stripe/stripe-js'
import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js'
import { FormEvent, useState } from 'react'
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!)
function CheckoutForm() {
const stripe = useStripe()
const elements = useElements()
const [error, setError] = useState<string>()
const [processing, setProcessing] = useState(false)
const handleSubmit = async (e: FormEvent) => {
e.preventDefault()
setProcessing(true)
if (!stripe || !elements) return
// Create payment intent
const response = await fetch('/api/create-payment-intent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: 99.99 }),
})
const { clientSecret } = await response.json()
// Confirm payment
const { error: stripeError } = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement)!,
billing_details: {
name: 'Customer Name',
},
},
})
if (stripeError) {
setError(stripeError.message!)
} else {
// Payment successful
window.location.href = '/success'
}
setProcessing(false)
}
return (
<form onSubmit={handleSubmit}>
<CardElement />
{error && <div className="error">{error}</div>}
<button type="submit" disabled={processing || !stripe}>
{processing ? 'Processing...' : 'Pay $99.99'}
</button>
</form>
)
}
export default function CheckoutPage() {
return (
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
)
}Stripe Checkout
Stripe Checkout is a pre-built payment page hosted by Stripe.
Create Checkout Session
// app/api/create-checkout-session/route.ts
import { NextResponse } from 'next/server'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(request: Request) {
const { productId } = await request.json()
const product = await db.products.findUnique({
where: { id: productId },
})
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price_data: {
currency: 'usd',
product_data: {
name: product.name,
images: [product.image],
},
unit_amount: Math.round(product.price * 100),
},
quantity: 1,
},
],
mode: 'payment',
success_url: `${process.env.NEXT_PUBLIC_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/checkout`,
metadata: {
productId,
},
})
return NextResponse.json({ url: session.url })
}Redirect to Checkout
// app/products/[id]/page.tsx
'use client'
export function BuyButton({ productId }: { productId: string }) {
const handlePurchase = async () => {
const response = await fetch('/api/create-checkout-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId }),
})
const { url } = await response.json()
window.location.href = url
}
return <button onClick={handlePurchase}>Buy Now</button>
}Subscriptions
Create Product and Price in Stripe
// Use Stripe Dashboard or API
// Via API
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
// Create product
const product = await stripe.products.create({
name: 'Pro Plan',
description: 'Monthly subscription',
})
// Create price
const price = await stripe.prices.create({
product: product.id,
unit_amount: 2900, // $29.00
currency: 'usd',
recurring: {
interval: 'month',
},
})
console.log('Price ID:', price.id)Create Subscription
// app/api/create-subscription/route.ts
import { NextResponse } from 'next/server'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(request: Request) {
const { priceId, customerId, email } = await request.json()
// Create customer if not exists
let customer = customerId
if (!customer) {
const customerData = await stripe.customers.create({
email,
metadata: {
userId: 'user_123',
},
})
customer = customerData.id
}
// Create subscription
const subscription = await stripe.subscriptions.create({
customer,
items: [{ price: priceId }],
payment_behavior: 'default_incomplete',
payment_settings: {
save_default_payment_method: 'on_subscription',
},
expand: ['latest_invoice.payment_intent'],
})
return NextResponse.json({
subscriptionId: subscription.id,
clientSecret: subscription.latest_invoice.payment_intent.client_secret,
customerId: customer,
})
}Subscription Management
// Cancel subscription
export async function cancelSubscription(subscriptionId: string) {
await stripe.subscriptions.update(subscriptionId, {
cancel_at_period_end: true,
})
}
// Update subscription
export async function updateSubscription(
subscriptionId: string,
newPriceId: string
) {
await stripe.subscriptions.update(subscriptionId, {
items: [{ price: newPriceId }],
})
}
// Pause subscription
export async function pauseSubscription(subscriptionId: string) {
await stripe.subscriptions.update(subscriptionId, {
pause_collection: {
behavior: 'keep_as_draft',
},
})
}Webhooks
Handle Webhook Events
// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers'
import { NextResponse } from 'next/server'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!
export async function POST(request: Request) {
const body = await request.text()
const signature = headers().get('stripe-signature')!
let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(body, signature, webhookSecret)
} catch (err) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 })
}
switch (event.type) {
case 'payment_intent.succeeded':
await handlePaymentSucceeded(event.data.object)
break
case 'payment_intent.payment_failed':
await handlePaymentFailed(event.data.object)
break
case 'checkout.session.completed':
await handleCheckoutCompleted(event.data.object)
break
case 'customer.subscription.created':
await handleSubscriptionCreated(event.data.object)
break
case 'customer.subscription.deleted':
await handleSubscriptionDeleted(event.data.object)
break
case 'invoice.paid':
await handleInvoicePaid(event.data.object)
break
case 'invoice.payment_failed':
await handleInvoicePaymentFailed(event.data.object)
break
}
return NextResponse.json({ received: true })
}
async function handlePaymentSucceeded(paymentIntent: Stripe.PaymentIntent) {
// Update order status
await db.payments.update({
where: { stripePaymentIntentId: paymentIntent.id },
data: { status: 'completed' },
})
// Send confirmation email
await sendConfirmationEmail(paymentIntent.metadata.orderId)
}Customer Portal
Stripe Customer Portal allows customers to manage their subscriptions.
Create Portal Session
// app/api/create-portal-session/route.ts
import { NextResponse } from 'next/server'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(request: Request) {
const { customerId } = await request.json()
const session = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: `${process.env.NEXT_PUBLIC_URL}/account`,
})
return NextResponse.json({ url: session.url })
}Link to Portal
'use client'
export function ManageSubscriptionButton({ customerId }: { customerId: string }) {
const handleClick = async () => {
const response = await fetch('/api/create-portal-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ customerId }),
})
const { url } = await response.json()
window.location.href = url
}
return <button onClick={handleClick}>Manage Subscription</button>
}Testing
Test Cards
Success: 4242 4242 4242 4242
Requires authentication: 4000 0025 0000 3155
Decline: 4000 0000 0000 0002
Insufficient funds: 4000 0000 0000 9995
Expired card: 4000 0000 0000 0069Test Locally
# Install Stripe CLI
brew install stripe/stripe-cli/stripe
# Login
stripe login
# Forward webhooks
stripe listen --forward-to localhost:3000/api/webhooks/stripe
# Test webhook
stripe trigger payment_intent.succeededNext Steps
- Learn about Subscription Management
- Explore Payment Components
- Return to Payments Overview