All works
Stripe Payment Gateway
AppsPaymentsNext.jsStripeDrizzle ORM

Stripe Payment Gateway

Wayan Phantom Megaditha

Wayan Phantom Megaditha

Fullstack Developer

Live ↗GitHub ↗

The Problem

Most Stripe tutorials show you how to add a checkout button. They stop there. The parts that actually matter in production — webhook verification, idempotency, server-side pricing, and refund flows — are left out. This project covers all of them in a single working sandbox.


How It Works

  1. A product listing page displays items with buy buttons. Prices live in a server-side file. The browser only sends a productId, never a price.
  2. A POST request to /api/checkout creates a Stripe Checkout session server-side and redirects the user to Stripe's hosted payment page.
  3. After payment, Stripe redirects to /success?order=id. The page starts as "pending" and polls for status. It only flips to "paid" after the webhook arrives, not after the redirect.
  4. The webhook handler at /api/webhook verifies the Stripe signature, checks a processed_events table for duplicates, and updates the order status in PostgreSQL.
  5. A refund button calls /api/refund which creates a refund in Stripe. The order status only updates to "refunded" after the charge.refunded webhook arrives.
  6. The entire order lifecycle (pending, paid, refunded) is tracked in a Neon PostgreSQL database via Drizzle ORM.

Tech Stack

Next.js 16App Router with TypeScript, Route Handlers for all API endpoints
StripeCheckout sessions, webhook events, refunds API in sandbox mode
Drizzle ORMSchema-first, type-safe migrations and queries
Neon PostgreSQLServerless Postgres, separate pooled and direct connection URLs
Tailwind CSS v4Utility-first styling, no component library needed for a sandbox

What I Would Improve

The success page polling is a simple auto-refresh interval. Replacing it with a WebSocket or Server-Sent Events connection would give instant status updates without repeated HTTP round trips. The product catalog is hardcoded in a server file. A real implementation would pull prices from a database or Stripe's product catalog API, which also enables promotional pricing and inventory management. Adding email confirmation via Resend or similar after the paid webhook would complete the typical checkout experience.

Screenshots

Wayan Phantom Megaditha

Wayan Phantom Megaditha

Fullstack Engineer · AI & Web3

Next.jsStripeDrizzle ORMPostgreSQLWebhooks