web-payments
Handles Stripe payment integration, including checkout flows, recurring subscriptions, and webhook management.
Install
mkdir -p .claude/skills/web-payments && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7650" && unzip -o skill.zip -d .claude/skills/web-payments && rm skill.zipInstalls to .claude/skills/web-payments
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Stripe Checkout, subscriptions, webhooks, customer portalKey capabilities
- →Create Stripe Checkout sessions
- →Implement embedded payment forms
- →Verify webhook signatures
- →Handle subscription lifecycle events
- →Manage customer billing portals
How it works
It facilitates server-side creation of Stripe Checkout sessions and provides secure webhook verification logic to handle payment events.
Inputs & outputs
When to use web-payments
- →Setting up a subscription billing model
- →Adding a Stripe Checkout page
- →Handling subscription lifecycle webhooks
- →Implementing a customer billing portal
About this skill
Web Payments Skill (Stripe)
For integrating Stripe payments into web applications - one-time payments, subscriptions, and checkout flows.
Sources: Stripe Checkout | Payment Element Best Practices | Building Solid Stripe Integrations | Subscriptions
Setup
1. Create Stripe Account
- Go to https://dashboard.stripe.com/register
- Complete business verification
- Get API keys from https://dashboard.stripe.com/apikeys
2. Environment Variables
# .env
STRIPE_SECRET_KEY=sk_test_xxx # Server-side only
STRIPE_PUBLISHABLE_KEY=pk_test_xxx # Client-side safe
STRIPE_WEBHOOK_SECRET=whsec_xxx # For webhook verification
# Production
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_PUBLISHABLE_KEY=pk_live_xxx
3. Install SDK
# Node.js
npm install stripe @stripe/stripe-js
# Python
pip install stripe
Integration Options
| Method | Best For | Complexity |
|---|---|---|
| Checkout (Hosted) | Quick setup, Stripe-hosted page | Low |
| Checkout (Embedded) | Custom site, embedded form | Low |
| Payment Element | Full customization, complex flows | Medium |
| Custom Form | Complete control (rare) | High |
Recommendation: Start with Checkout, migrate to Payment Element if needed.
Stripe Checkout (Recommended)
Server: Create Checkout Session
Node.js / Next.js
// app/api/checkout/route.ts (Next.js App Router)
import Stripe from "stripe";
import { NextResponse } from "next/server";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: Request) {
const { priceId, mode = "payment" } = await request.json();
try {
const session = await stripe.checkout.sessions.create({
mode: mode as "payment" | "subscription",
payment_method_types: ["card"],
line_items: [
{
price: priceId,
quantity: 1,
},
],
success_url: `${process.env.NEXT_PUBLIC_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/canceled`,
// Optional: Link to existing customer
// customer: customerId,
// Optional: Collect shipping
// shipping_address_collection: { allowed_countries: ["US", "CA"] },
// Optional: Add metadata for tracking
metadata: {
userId: "user_123",
source: "pricing_page",
},
});
return NextResponse.json({ sessionId: session.id, url: session.url });
} catch (error) {
console.error("Stripe error:", error);
return NextResponse.json({ error: "Failed to create session" }, { status: 500 });
}
}
Python / FastAPI
# app/api/checkout.py
import stripe
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
import os
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
router = APIRouter()
class CheckoutRequest(BaseModel):
price_id: str
mode: str = "payment" # or "subscription"
@router.post("/api/checkout")
async def create_checkout_session(request: CheckoutRequest):
try:
session = stripe.checkout.Session.create(
mode=request.mode,
payment_method_types=["card"],
line_items=[{
"price": request.price_id,
"quantity": 1,
}],
success_url=f"{os.environ['APP_URL']}/success?session_id={{CHECKOUT_SESSION_ID}}",
cancel_url=f"{os.environ['APP_URL']}/canceled",
metadata={
"user_id": "user_123",
},
)
return {"session_id": session.id, "url": session.url}
except stripe.error.StripeError as e:
raise HTTPException(status_code=400, detail=str(e))
Client: Redirect to Checkout
// components/CheckoutButton.tsx
"use client";
import { loadStripe } from "@stripe/stripe-js";
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
export function CheckoutButton({ priceId }: { priceId: string }) {
const handleCheckout = async () => {
const response = await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ priceId }),
});
const { url } = await response.json();
// Redirect to Stripe Checkout
window.location.href = url;
};
return (
<button onClick={handleCheckout}>
Subscribe Now
</button>
);
}
Embedded Checkout
For keeping users on your site:
// components/EmbeddedCheckout.tsx
"use client";
import { useEffect, useState } from "react";
import { loadStripe } from "@stripe/stripe-js";
import {
EmbeddedCheckoutProvider,
EmbeddedCheckout,
} from "@stripe/react-stripe-js";
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
export function EmbeddedCheckoutForm({ priceId }: { priceId: string }) {
const [clientSecret, setClientSecret] = useState("");
useEffect(() => {
fetch("/api/checkout/embedded", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ priceId }),
})
.then((res) => res.json())
.then((data) => setClientSecret(data.clientSecret));
}, [priceId]);
if (!clientSecret) return <div>Loading...</div>;
return (
<EmbeddedCheckoutProvider stripe={stripePromise} options={{ clientSecret }}>
<EmbeddedCheckout />
</EmbeddedCheckoutProvider>
);
}
Server endpoint for embedded:
// app/api/checkout/embedded/route.ts
export async function POST(request: Request) {
const { priceId } = await request.json();
const session = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [{ price: priceId, quantity: 1 }],
ui_mode: "embedded",
return_url: `${process.env.NEXT_PUBLIC_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
});
return NextResponse.json({ clientSecret: session.client_secret });
}
Webhooks (Critical)
Never trust client-side data. Always verify payments via webhooks.
Webhook Endpoint
// app/api/webhooks/stripe/route.ts
import Stripe from "stripe";
import { headers } from "next/headers";
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;
// Verify webhook signature
try {
event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
} catch (err) {
console.error("Webhook signature verification failed");
return new Response("Invalid signature", { status: 400 });
}
// Handle events
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object as Stripe.Checkout.Session;
await handleCheckoutComplete(session);
break;
}
case "customer.subscription.created":
case "customer.subscription.updated": {
const subscription = event.data.object as Stripe.Subscription;
await handleSubscriptionUpdate(subscription);
break;
}
case "customer.subscription.deleted": {
const subscription = event.data.object as Stripe.Subscription;
await handleSubscriptionCanceled(subscription);
break;
}
case "invoice.payment_failed": {
const invoice = event.data.object as Stripe.Invoice;
await handlePaymentFailed(invoice);
break;
}
default:
console.log(`Unhandled event type: ${event.type}`);
}
// Return 200 quickly - process async if needed
return new Response("OK", { status: 200 });
}
async function handleCheckoutComplete(session: Stripe.Checkout.Session) {
const userId = session.metadata?.userId;
const customerId = session.customer as string;
const subscriptionId = session.subscription as string;
// Update your database
await db.user.update({
where: { id: userId },
data: {
stripeCustomerId: customerId,
stripeSubscriptionId: subscriptionId,
subscriptionStatus: "active",
},
});
}
Python Webhook
# app/api/webhooks.py
import stripe
from fastapi import APIRouter, Request, HTTPException
router = APIRouter()
@router.post("/api/webhooks/stripe")
async def stripe_webhook(request: Request):
payload = await request.body()
sig_header = request.headers.get("stripe-signature")
try:
event = stripe.Webhook.construct_event(
payload, sig_header, os.environ["STRIPE_WEBHOOK_SECRET"]
)
except ValueError:
raise HTTPException(status_code=400, detail="Invalid payload")
except stripe.error.SignatureVerificationError:
raise HTTPException(status_code=400, detail="Invalid signature")
# Handle events
if event["type"] == "checkout.session.completed":
session = event["data"]["object"]
await handle_checkout_complete(session)
elif event["type"] == "customer.subscription.deleted":
subscription = event["data"]["object"]
await handle_subscription_canceled(subscription)
return {"status": "success"}
Key Webhook Events
| Event | When | Action |
|---|---|---|
checkout.session.completed | Payment successful | Provision access |
customer.subscription.created | New subscription | Store subscription ID |
customer.subscription.updated | Plan change | Update plan in DB |
customer.subscription.deleted | Canceled | Revoke access |
invoice.payment_failed | Payment failed | Notify user, retry |
invoice.paid | Renewal successful | Extend access |
Content truncated.
When not to use it
- →Client-side payment creation
- →Floating-point currency math
Prerequisites
Limitations
- →Requires server-side environment for key security
- →Strictly requires webhook verification for integrity
How it compares
It mandates server-side session creation and webhook verification, preventing common client-side security anti-patterns.
Compared to similar skills
web-payments side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| web-payments (this skill) | 1 | 4mo | Review | Intermediate |
| telegram-bot-builder | 106 | 6mo | Review | Intermediate |
| stripe-integration | 48 | 2mo | No flags | Advanced |
| hubspot-integration | 5 | 6mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by alinaqi
View all by alinaqi →You might also like
telegram-bot-builder
davila7
Expert in building Telegram bots that solve real problems - from simple automation to complex AI-powered bots. Covers bot architecture, the Telegram Bot API, user experience, monetization strategies, and scaling bots to thousands of users. Use when: telegram bot, bot api, telegram automation, chat bot telegram, tg bot.
stripe-integration
wshobson
Implement Stripe payment processing for robust, PCI-compliant payment flows including checkout, subscriptions, and webhooks. Use when integrating Stripe payments, building subscription systems, or implementing secure checkout flows.
hubspot-integration
davila7
Expert patterns for HubSpot CRM integration including OAuth authentication, CRM objects, associations, batch operations, webhooks, and custom objects. Covers Node.js and Python SDKs. Use when: hubspot, hubspot api, hubspot crm, hubspot integration, contacts api.
backend-architect
sickn33
Expert backend architect specializing in scalable API design, microservices architecture, and distributed systems. Masters REST/GraphQL/gRPC APIs, event-driven architectures, service mesh patterns, and modern backend frameworks. Handles service boundary definition, inter-service communication, resilience patterns, and observability. Use PROACTIVELY when creating new backend services or APIs.
building-websocket-server
jeremylongshore
Build scalable WebSocket servers for real-time bidirectional communication. Use when enabling real-time bidirectional communication. Trigger with phrases like "build WebSocket server", "add real-time API", or "implement WebSocket".
plaid-fintech
davila7
Expert patterns for Plaid API integration including Link token flows, transactions sync, identity verification, Auth for ACH, balance checks, webhook handling, and fintech compliance best practices. Use when: plaid, bank account linking, bank connection, ach, account aggregation.