A virtual goods automated card delivery platform built on Next.js 16, supporting Linux DO Credit payments.
π δΈζζζ‘£ README
π Detailed Deployment Guide: docs/DEPLOY.md
- Product Display - Grid layout product listings, category navigation, client-side category filtering (no page reload)
- Product Details - Markdown rich text support, popular tags, discount pricing, image carousel
- Stock Status - Real-time inventory count display, sales statistics
- Announcement System - Homepage announcement carousel banner (supports Markdown, scheduled publish/unpublish)
- Full-Text Search - Header search entry + results page filtering / multiple sort options / pagination
- Restock Request - Users can request restocking when out of stock, showing request count and recent requester avatars
- Leaderboard - TOP 50 customer spending leaderboard (
/leaderboard), showing cumulative spending and order count - My Orders - View personal order history, order details, card key information (
/order/my) - Payment Receipt - Generate shareable payment success receipts (
/order/receipt/:orderNo), no sensitive info included - ISR Cache - 60-second Incremental Static Regeneration for improved page load performance
- User Orders - Login via Linux DO Connect OAuth2, orders/queries linked to account
- Admin Panel - Admin password login (
ADMIN_PASSWORD), or configureADMIN_USERNAMESto allow specific Linux DO usernames to log in as admin - Session Management - JWT session strategy based on NextAuth v5
- Supports Linux DO Credit payments
- Automatic card key delivery upon successful payment
- Auto-release of locked inventory on order timeout (lazy load + throttle strategy)
- Idempotent payment callback handling to prevent duplicate deliveries
- Card key locking uses database transactions +
FOR UPDATEfor atomicity
- Users can apply for refunds (with reason), reviewed by admin
- Client Mode (default): Bypasses CORS/CF restrictions via browser form submission (no proxy needed)
- Proxy Mode: Calls LDC Credit refund API via server-side proxy
- Disabled Mode: Refund feature can be turned off
- Automatic card key reclamation on successful refund (status restored to available)
- Bulk Import - Supports newline/comma-separated input, optional auto-deduplication
- Duplicate Detection - Input dedup + database dedup, import summary report
- Stock Alerts - Automatic alerts for products below threshold (default < 10)
- Card Key Editing - Modify card key content (available status only)
- Bulk Delete - Delete unsold card keys
- Reset Locked - Release card keys in locked status
- Export - Export card key list by status (CSV)
- Clean Duplicates - Automatically clean duplicate card keys (keeps earliest created)
- Dashboard - Today's sales/orders (day-over-day growth), to-dos, 7-day sales trend chart, recent orders, stock alerts
- Product Management - Create/edit/copy products, bulk publish/unpublish/delete, set price/original price, popular marking, purchase quantity limit, sort weight
- Category Management - Add/edit/delete categories, enable/disable, sort, product count stats
- Order Management - Order list, multi-dimensional filtering (status/payment method/search), order details, manual order completion, bulk delete, CSV export (up to 5,000 records)
- Refund Review - View refund application details, approve/reject refunds, admin notes
- Card Key Management - View inventory by product, bulk import/delete/export, status filtering
- Announcement Management - Add/edit/delete announcements, Markdown content, effective time range, enable/disable, sort
- Customer Management - Customer list, order count and cumulative spending stats
- System Config - Site name/description/icon, order expiration time, Telegram notification settings
- Telegram Notifications - Real-time push for restock requests (product name, stock, requesting user, time)
- Test Function - Test Telegram configuration from the admin panel
- Async Delivery - Non-blocking, silent failure does not affect business flow
- Built on Shadcn/UI + Tailwind CSS v4
- Dark/light mode toggle
- Responsive design, mobile-friendly
- 30+ general-purpose UI components
- Framework: Next.js 16 (App Router, Server Actions)
- Language: TypeScript
- Database: PostgreSQL (Neon/Supabase recommended)
- ORM: Drizzle ORM
- UI: Shadcn/UI + Tailwind CSS
- Auth: NextAuth.js v5
- Payment: Linux DO Credit
- Click the "Deploy with Vercel" button above
- Configure environment variables in Vercel
- Wait for deployment to complete
- Initialize database schema (Production deployments automatically run
pnpm db:baseline && pnpm db:migrate; run manually if it fails)
git clone https://github.com/gptkong/ldc-store.git
cd ldc-store
pnpm installCopy the example env file and edit it:
cp .env.example .envEdit .env with your actual values:
# Database (Neon recommended: https://neon.tech)
DATABASE_URL="postgresql://user:password@host/database?sslmode=require"
# NextAuth secret (generate: openssl rand -base64 32)
AUTH_SECRET="your-auth-secret"
AUTH_TRUST_HOST=true
# Admin password
ADMIN_PASSWORD="your-admin-password"
# Admin username whitelist (optional, comma-separated; grants admin role on match)
ADMIN_USERNAMES="admin1,admin2"
# Linux DO Credit payment
LDC_CLIENT_ID="your_client_id"
LDC_CLIENT_SECRET="your_client_secret"
LDC_GATEWAY="https://credit.linux.do/epay"
# Linux DO OAuth2 login (required for user orders/queries)
LINUXDO_CLIENT_ID="your_linuxdo_client_id"
LINUXDO_CLIENT_SECRET="your_linuxdo_client_secret"
# Site name (optional, shown in header title and page titles)
NEXT_PUBLIC_SITE_NAME="LDC Store"
# Site description (optional, used for SEO)
NEXT_PUBLIC_SITE_DESCRIPTION="Virtual goods automated card delivery platform powered by Linux DO Credit"
# Order expiration time (minutes)
ORDER_EXPIRE_MINUTES=5
# Stats timezone (optional, default Asia/Shanghai / UTC+8)
# Used for the day boundary in "Today's Sales" and similar dashboard stats
STATS_TIMEZONE="Asia/Shanghai"# New database: run migrations to create schema
pnpm db:migrate
# Existing database: if you previously used db:push (no migration history), baseline first
# pnpm db:baseline
# pnpm db:migrate
# Seed example data (optional)
pnpm db:seedpnpm devVisit:
- Storefront: http://localhost:3000
- Admin panel: http://localhost:3000/admin
pnpm test
pnpm test:coverageMore testing info and coverage baseline thresholds: docs/TESTING_PLAN.md (CI uploads coverage/ artifacts; open coverage/index.html locally to view the report)
Visit /admin:
- Password login: enter
ADMIN_PASSWORD - Linux DO login (optional): configure
ADMIN_USERNAMESto allow whitelisted users to log in as admin
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
β | - | PostgreSQL connection string |
AUTH_SECRET |
β | - | NextAuth encryption key (run openssl rand -base64 32) |
AUTH_TRUST_HOST |
β | true |
Trust host header (must be true for Vercel) |
ADMIN_PASSWORD |
β | - | Admin login password |
LDC_CLIENT_ID |
β | - | Linux DO Credit Client ID |
LDC_CLIENT_SECRET |
β | - | Linux DO Credit Client Secret |
LDC_GATEWAY |
β | https://credit.linux.do/epay |
Payment gateway URL |
LDC_REFUND_MODE |
β | client |
Refund mode: client / proxy / disabled |
LDC_PROXY_URL |
β | - | LDC API proxy URL (proxy mode, bypasses Cloudflare) |
ADMIN_USERNAMES |
β | - | Linux DO admin username whitelist (comma-separated), grants admin role |
LINUXDO_CLIENT_ID |
β | - | Linux DO OAuth2 Client ID (required for user orders/queries) |
LINUXDO_CLIENT_SECRET |
β | - | Linux DO OAuth2 Client Secret (required for user orders/queries) |
LINUXDO_AUTHORIZATION_URL |
β | - | Custom OAuth2 authorization endpoint |
LINUXDO_TOKEN_URL |
β | - | Custom OAuth2 token endpoint |
LINUXDO_USERINFO_URL |
β | - | Custom OAuth2 user info endpoint |
NEXT_PUBLIC_SITE_NAME |
β | - | Site name (shown in header and page titles) |
NEXT_PUBLIC_SITE_DESCRIPTION |
β | - | Site description (for SEO) |
ORDER_EXPIRE_MINUTES |
β | 5 |
Order expiration time (minutes) |
STATS_TIMEZONE |
β | Asia/Shanghai |
Stats timezone for "today" reports (IANA timezone name recommended) |
- Database stores timestamps using
timestamp with time zone(timestamptz), stored internally as UTC - Frontend displays times in the user's local browser timezone (e.g., order list timestamps)
- "Today" dashboard stats use the day boundary defined by
STATS_TIMEZONE, defaulting to China time (Asia/Shanghai)
- Visit the Linux DO Credit Console
- Create a new application to get your
pidandkey - Configure callback addresses:
- Notify URL:
https://your-domain.com/api/payment/notify - Return URL:
https://your-domain.com/order/result
- Notify URL:
Because the Linux DO Credit API is protected by Cloudflare, direct server-side calls from Vercel and similar hosts will be blocked. This project supports two refund modes:
| Mode | Environment Variable | Description |
|---|---|---|
| Client Mode | LDC_REFUND_MODE=client (default) |
Bypasses CORS/CF via browser form submission, no proxy needed |
| Proxy Mode | LDC_REFUND_MODE=proxy + LDC_PROXY_URL |
Calls LDC API via server-side proxy |
| Disabled | LDC_REFUND_MODE=disabled |
Disables the refund feature |
Enabled by default, no extra configuration needed. How it works:
- Admin clicks "Approve Refund", which opens a new window
- The new window POSTs to the LDC API via an HTML form (form submission is not subject to CORS restrictions)
- The window displays the LDC API response
- After admin confirms the refund, the system updates the order status
π‘ Tip: If you encounter a Cloudflare challenge, the admin should first visit
credit.linux.doin the browser to pass verification, then retry the refund.
If client mode doesn't meet your needs, you can configure a proxy service:
- Deploy gin-flaresolverr-proxy
- Configure the environment variables:
LDC_REFUND_MODE=proxy
LDC_PROXY_URL="https://your-proxy-domain.com/api"
β οΈ Note: Proxy functionality may break if the Linux DO Credit API changes. Monitor the upstream repository for updates.
Users must log in with a Linux DO account (OAuth2) to place or view orders.
- Visit the Linux DO Connect console
- Click My App Integrations β Apply for New Integration
- Fill in app information; set Callback URL to:
https://your-domain.com/api/auth/callback/linux-do - After approval, obtain your
Client IDandClient Secret
Add to .env:
LINUXDO_CLIENT_ID="your_client_id"
LINUXDO_CLIENT_SECRET="your_client_secret"| Field | Description |
|---|---|
id |
Unique user identifier (immutable) |
username |
Forum username |
name |
Forum display name (mutable) |
avatar_template |
User avatar template URL (multiple sizes supported) |
active |
Account active status |
trust_level |
Trust level (0β4) |
silenced |
Silenced status |
| Endpoint | URL |
|---|---|
| Authorization | https://connect.linux.do/oauth2/authorize |
| Token | https://connect.linux.do/oauth2/token |
| User Info | https://connect.linux.do/api/user |
To customize endpoints, configure:
LINUXDO_AUTHORIZATION_URLLINUXDO_TOKEN_URLLINUXDO_USERINFO_URL
ldc-store/
βββ app/
β βββ (store)/ # Storefront
β βββ (admin)/ # Admin panel
β βββ api/ # API routes
βββ components/
β βββ ui/ # Shadcn UI
β βββ store/ # Storefront components
β βββ admin/ # Admin components
βββ lib/
β βββ db/ # Database config
β βββ actions/ # Server Actions
β βββ payment/ # Payment integration
β βββ validations/ # Zod validations
βββ ...
# Generate migration files
pnpm db:generate
# Baseline existing database (use when you previously used db:push with no migration history)
pnpm db:baseline
# Push schema (not recommended; bypasses migration history, may break future migrates)
pnpm db:push
# Run migrations (recommended for new and production databases)
pnpm db:migrate
# Open database visualization tool
pnpm db:studio
# Seed example data
pnpm db:seed
# Reset database (DANGEROUS!)
pnpm db:resetThe repository includes a generation script: provide a square PNG and it outputs brand icons and favicon.ico:
- Output:
public/ldc-mart.png,app/favicon.ico - Dependency:
sharp(declared in devDependencies)
# Use a custom input image (square PNG)
pnpm tsx scripts/generate-favicon.ts path/to/icon.png
# Without an argument, tries to use the built-in image (e.g. public/ldc-mart.png)
pnpm tsx scripts/generate-favicon.tsMIT