Payment Integration Skill
Payment processing patterns for Stripe, PayPal, and LemonSqueezy. Covers checkout flows, webhook handling, subscription management, PCI compliance, and idempotent payment operations with production-ready code examples.
Description
This skill provides expert guidance on integrating payment processors (Stripe, PayPal, LemonSqueezy) into web applications. Covers one-time payments, subscriptions, webhook handling, refunds, and PCI compliance. All patterns use idempotent operations and proper error handling for production reliability.
Instructions
- Choose Provider: Select the right payment processor based on requirements (geography, payment methods, pricing)
- Implement Flow: Build the checkout flow (client-side → server-side → webhook confirmation)
- Handle Webhooks: Process payment events reliably with idempotency and retry handling
- Manage Subscriptions: Handle upgrades, downgrades, cancellations, and proration
- Secure: Ensure PCI compliance, never log card data, validate webhook signatures
Rules
- NEVER store raw card numbers, CVVs, or full card data on your server
- ALWAYS validate webhook signatures before processing events
- Use idempotency keys for every payment creation and update operation
- Store the payment provider's IDs (customer ID, subscription ID) in your database
- Implement webhook handlers as idempotent operations (safe to receive the same event twice)
- Always use test/sandbox credentials in development -- never real payment keys in dev/staging
- Handle all webhook event types you subscribe to, even if you just acknowledge them
- Log payment operations for audit trail but NEVER log sensitive card data
- Use Stripe's Payment Intents API (not the legacy Charges API)
Stripe Integration
Checkout Session (Server)
import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); async function createCheckoutSession(priceId: string, userId: string) { const session = await stripe.checkout.sessions.create({ mode: 'subscription', payment_method_types: ['card'], line_items: [{ price: priceId, quantity: 1 }], success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${process.env.APP_URL}/pricing`, client_reference_id: userId, metadata: { userId }, }, { idempotencyKey: `checkout-${userId}-${priceId}-${Date.now()}`, }); return session.url; }
Webhook Handler
import { NextRequest, NextResponse } from 'next/server'; export async function POST(req: NextRequest) { const body = await req.text(); const signature = req.headers.get('stripe-signature')!; let event: Stripe.Event; try { event = stripe.webhooks.constructEvent( body, signature, process.env.STRIPE_WEBHOOK_SECRET! ); } catch (err) { console.error('Webhook signature verification failed'); return NextResponse.json({ error: 'Invalid signature' }, { status: 400 }); } // Idempotency: check if we've already processed this event const processed = await db.webhookEvent.findUnique({ where: { stripeEventId: event.id }, }); if (processed) { return NextResponse.json({ received: true }); } switch (event.type) { case 'checkout.session.completed': await handleCheckoutComplete(event.data.object); break; case 'customer.subscription.updated': await handleSubscriptionUpdate(event.data.object); break; case 'customer.subscription.deleted': await handleSubscriptionCanceled(event.data.object); break; case 'invoice.payment_failed': await handlePaymentFailed(event.data.object); break; } // Record processed event await db.webhookEvent.create({ data: { stripeEventId: event.id, type: event.type, processedAt: new Date() }, }); return NextResponse.json({ received: true }); }
Customer Portal
async function createPortalSession(customerId: string) { const session = await stripe.billingPortal.sessions.create({ customer: customerId, return_url: `${process.env.APP_URL}/dashboard`, }); return session.url; }
Provider Comparison
| Feature | Stripe | PayPal | LemonSqueezy |
|---|---|---|---|
| Transaction Fee | 2.9% + $0.30 | 2.9% + $0.30 | 5% + $0.50 |
| Subscriptions | Built-in | Limited | Built-in |
| Global Payments | 135+ currencies | 200+ markets | 100+ countries |
| Tax Handling | Stripe Tax | Manual | Built-in (MoR) |
| Hosted Checkout | Yes | Yes | Yes |
| Webhook Reliability | Excellent | Good | Good |
| Best For | Full control | Brand recognition | Simplicity, SaaS |
Examples
"Set up Stripe subscription billing with webhooks"
"Implement a one-time payment checkout flow"
"Handle subscription upgrades and downgrades with proration"
"Add PayPal as an alternative payment method"
"Make our payment webhook handler idempotent"
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.