Comprehensive Stripe checkout integration for Google Antigravity with subscriptions and webhooks.
# Stripe Checkout Integration for Google Antigravity
Implement secure payment processing with Stripe in your Google Antigravity applications. This guide covers subscriptions, one-time payments, and webhook handling.
## Server-Side Setup
```typescript
// lib/stripe.ts
import Stripe from "stripe";
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2023-10-16",
typescript: true,
});
export async function createCustomer(email: string, name: string, metadata?: Record<string, string>): Promise<Stripe.Customer> {
return stripe.customers.create({ email, name, metadata });
}
export async function createCheckoutSession(
customerId: string,
priceId: string,
mode: "payment" | "subscription" = "subscription"
): Promise<Stripe.Checkout.Session> {
return stripe.checkout.sessions.create({
customer: customerId,
payment_method_types: ["card"],
line_items: [{ price: priceId, quantity: 1 }],
mode,
success_url: `${process.env.NEXT_PUBLIC_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
subscription_data: mode === "subscription" ? { trial_period_days: 14, metadata: { customerId } } : undefined,
});
}
export async function createPortalSession(customerId: string): Promise<Stripe.BillingPortal.Session> {
return stripe.billingPortal.sessions.create({
customer: customerId,
return_url: `${process.env.NEXT_PUBLIC_URL}/account`,
});
}
```
## Checkout API Route
```typescript
// app/api/checkout/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createCheckoutSession, createCustomer } from "@/lib/stripe";
import { createClient } from "@/lib/supabase/server";
export async function POST(request: NextRequest) {
try {
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
const { priceId } = await request.json();
const { data: profile } = await supabase.from("profiles").select("stripe_customer_id").eq("id", user.id).single();
let customerId = profile?.stripe_customer_id;
if (!customerId) {
const customer = await createCustomer(user.email!, user.user_metadata.name || user.email!, { userId: user.id });
customerId = customer.id;
await supabase.from("profiles").update({ stripe_customer_id: customerId }).eq("id", user.id);
}
const session = await createCheckoutSession(customerId, priceId);
return NextResponse.json({ url: session.url });
} catch (error) {
console.error("Checkout error:", error);
return NextResponse.json({ error: "Failed to create checkout session" }, { status: 500 });
}
}
```
## Webhook Handler
```typescript
// app/api/webhooks/stripe/route.ts
import { NextRequest, NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import { createClient } from "@/lib/supabase/admin";
import Stripe from "stripe";
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
export async function POST(request: NextRequest) {
const body = await request.text();
const signature = request.headers.get("stripe-signature")!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
} catch (err) {
console.error("Webhook signature verification failed");
return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
}
const supabase = createClient();
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object as Stripe.Checkout.Session;
const { data: profile } = await supabase.from("profiles").select("id").eq("stripe_customer_id", session.customer).single();
if (profile) {
await supabase.from("profiles").update({ subscription_status: "active", subscription_id: session.subscription as string }).eq("id", profile.id);
}
break;
}
case "customer.subscription.updated":
case "customer.subscription.deleted": {
const subscription = event.data.object as Stripe.Subscription;
await supabase.from("profiles").update({
subscription_status: subscription.status,
current_period_end: new Date(subscription.current_period_end * 1000).toISOString(),
}).eq("stripe_customer_id", subscription.customer);
break;
}
case "invoice.payment_failed": {
const invoice = event.data.object as Stripe.Invoice;
await supabase.from("profiles").update({ subscription_status: "past_due" }).eq("stripe_customer_id", invoice.customer);
break;
}
}
return NextResponse.json({ received: true });
}
```
## Client Component
```typescript
// components/PricingCard.tsx
"use client";
import { useState } from "react";
export function PricingCard({ priceId, name, price, features }: { priceId: string; name: string; price: number; features: string[] }) {
const [loading, setLoading] = useState(false);
const handleSubscribe = async () => {
setLoading(true);
try {
const response = await fetch("/api/checkout", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ priceId }) });
const { url } = await response.json();
window.location.href = url;
} catch (error) {
console.error("Checkout error:", error);
} finally {
setLoading(false);
}
};
return (
<div className="pricing-card">
<h3>{name}</h3>
<p className="price">${price}/mo</p>
<ul>{features.map((f) => <li key={f}>{f}</li>)}</ul>
<button onClick={handleSubscribe} disabled={loading}>{loading ? "Loading..." : "Subscribe"}</button>
</div>
);
}
```
## Best Practices
1. **Idempotency**: Handle webhook events idempotently to prevent duplicate processing
2. **Signature Verification**: Always verify webhook signatures in production
3. **Error Handling**: Implement proper error handling and logging for payment failures
4. **Testing**: Use Stripe test mode and CLI for local webhook testing
5. **Security**: Never expose secret keys; use environment variablesThis stripe prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their stripe implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For stripe projects, consider mentioning your framework version, coding style, and any specific libraries you're using.