AgentSkillsCN

Stripe

Stripe

SKILL.md

⚠️ MANDATORY: COPY THIS CODE EXACTLY - DO NOT MODIFY

Step 1: Install packages

```json { "stripe": "^14.0.0", "@stripe/stripe-js": "^2.0.0", "@stripe/react-stripe-js": "^2.0.0" } ```

Step 2: Stripe server config (copy exactly)

```typescript // lib/stripe.ts import Stripe from 'stripe';

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2023-10-16', }); ```

Step 3: Checkout API route (copy exactly)

```typescript // app/api/checkout/route.ts import { NextResponse } from 'next/server'; import { stripe } from '@/lib/stripe';

export async function POST(req: Request) { const { priceId } = await req.json();

const session = await stripe.checkout.sessions.create({ mode: '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}/cancel`, });

return NextResponse.json({ url: session.url }); } ```

Step 4: Webhook handler (copy exactly)

```typescript // app/api/webhooks/stripe/route.ts import { NextResponse } from 'next/server'; import { stripe } from '@/lib/stripe'; import { headers } from 'next/headers';

export async function POST(req: Request) { const body = await req.text(); const signature = headers().get('stripe-signature')!;

let event; try { event = stripe.webhooks.constructEvent( body, signature, process.env.STRIPE_WEBHOOK_SECRET! ); } catch (err: any) { return NextResponse.json({ error: 'Invalid signature' }, { status: 400 }); }

switch (event.type) { case 'checkout.session.completed': const session = event.data.object; console.log('Checkout completed:', session.id); break; case 'customer.subscription.deleted': const sub = event.data.object; console.log('Subscription cancelled:', sub.id); break; }

return NextResponse.json({ received: true }); } ```

Step 5: Checkout button (copy exactly)

```tsx // components/checkout-button.tsx 'use client'; import { useState } from 'react';

export function CheckoutButton({ priceId }: { priceId: string }) { const [loading, setLoading] = useState(false);

const handleCheckout = async () => { setLoading(true); const res = await fetch('/api/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ priceId }), }); const { url } = await res.json(); window.location.href = url; };

return ( <button onClick={handleCheckout} disabled={loading}> {loading ? 'Loading...' : 'Subscribe'} </button> ); } ```

Environment variables needed:

  • STRIPE_SECRET_KEY (sk_test_... or sk_live_...)
  • STRIPE_WEBHOOK_SECRET (whsec_...)
  • NEXT_PUBLIC_URL (your app URL)

❌ DO NOT:

  • Never expose STRIPE_SECRET_KEY to client
  • Never skip webhook signature verification
  • Never trust client-side subscription status

✅ DO:

  • Always verify webhook signatures
  • Use test keys (sk_test_) during development
  • Store customer IDs in your database