Live URL: unicasttvweb.vercel.app
UniCast TV is a full-stack IPTV subscription web application built for a UK-based streaming service provider. The client required a complete customer-facing platform to replace a manual order process that relied entirely on direct messaging. The site handles package discovery, customer registration, subscription ordering through multiple payment channels, and post-purchase credential delivery — all without manual intervention for standard orders.
The platform serves two distinct user types: end customers browsing and purchasing IPTV packages, and administrators managing the full order and user lifecycle from a protected back-office panel.
| Layer | Technology | Rationale |
|---|---|---|
| Framework | Next.js 14 (App Router) | Server components and API routes in a single deployment unit; reduces infrastructure complexity |
| Database & Auth | Supabase (PostgreSQL + Auth) | Managed Postgres with row-level security; built-in JWT auth eliminates a separate auth service |
| Auth Middleware | @supabase/ssr |
Correct SSR-safe session handling for Next.js App Router; resolves cookie-sync issues present in older client patterns |
| Styling | Tailwind CSS with CSS variables | Token-based theming allows the entire color scheme to be swapped at the config level |
| Payments | Stripe | Card payment processing via server-side checkout sessions and webhook-verified fulfillment |
| Form Handling | React Hook Form + Zod | Client-side validation with schema-derived TypeScript types |
| Animations | Framer Motion | Page transitions and component entrance animations |
| Notifications | Sonner | Non-blocking toast notifications for order and auth feedback |
| Deployment | Vercel | Zero-config deployment for Next.js; edge middleware support for session refresh |
The application uses the Next.js App Router with a clear separation between public routes (no auth required), protected user routes (/dashboard/*), and protected admin routes (/admin/*). Route protection is enforced at the edge via middleware.ts using @supabase/ssr, which refreshes expired sessions on every request before the route handler executes.
The UI is built on a centralized brand configuration file (src/config/brand.ts) that controls the company name, tagline, contact details, navigation structure, social links, and package fallback data. This design decision allows the codebase to be redeployed for a different client by editing a single file.
All server-side logic runs as Next.js API routes co-located with the application:
POST /api/stripe/checkout— creates a Stripe Checkout session for a given package, attaches order metadata, and returns a redirect URLPOST /api/stripe/webhook— receives Stripe webhook events, verifies the signature, and updates the corresponding order status in Supabase oncheckout.session.completedPOST /api/admin/subscription— admin-only endpoint to write IPTV credentials (M3U URL, username, password, expiry) directly to a user's subscription record
The Supabase service role key is used exclusively in server-side API routes. Client-side code uses only the anon key, with access controlled by Row Level Security policies defined in the database migration files.
The schema consists of five primary tables: profiles, packages, orders, subscriptions, and messages. Key design decisions:
profilesextendsauth.usersvia a foreign key withON DELETE CASCADE, keeping application data and auth data in sync automaticallyorderssnapshots the package name and price at the time of purchase, preventing historical order data from changing if a package is later edited or deletedpackagesstores all pricing in the database with no hardcoded values in the application layer, allowing the admin to update prices without a redeployment- RLS policies enforce that users can only read their own orders and subscription data; the service role key bypasses RLS only in verified server-side contexts
- Package Listing — Packages are fetched from the Supabase
packagestable at request time; a static fallback frombrand.tsis used if the database is unreachable - Multi-Channel Ordering — Customers can place orders via Stripe card payment, PayPal redirect, bank transfer (manual), or WhatsApp message; order method is recorded on the order record
- Contact Form — Submissions are written to the
messagestable and surfaced in the admin panel - Content Pages — Static informational pages for about, tutorials, IPTV app recommendations, and a referral program
- Order History — Full list of the authenticated user's orders with status indicators
- Subscription Credentials — Displays the M3U playlist URL, username, password, and expiry date once an admin has fulfilled the order
- Profile Settings — Name, phone number, and password management
Access is restricted to users with the admin role set in the profiles table. The panel provides:
- Package Management — Create, edit, reorder, and deactivate packages; all changes reflect on the public site immediately
- Order Management — View all orders across all users; update order status; manually fulfill orders by writing subscription credentials directly to the user record
- User Management — View registered users and their associated metadata
- Message Inbox — Read and action contact form submissions
- Site Content Editor — Inline editing of selected public-facing content without requiring a code change or redeployment
- Email and password registration with Supabase Auth
- Password reset via email link
- Session persistence handled by
@supabase/ssracross server components, client components, and API routes - Authenticated users are automatically redirected away from
/loginand/registerby middleware
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_APP_URL |
Yes | Fully qualified public URL of the deployment |
NEXT_PUBLIC_SUPABASE_URL |
Yes | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Yes | Supabase anon key (safe for browser) |
SUPABASE_SERVICE_ROLE_KEY |
Yes | Supabase service role key — server-side only, never expose to client |
NEXT_PUBLIC_WHATSAPP_NUMBER |
Yes | WhatsApp contact number with country code |
NEXT_PUBLIC_SUPPORT_EMAIL |
Yes | Support email address displayed in UI |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
Stripe | Stripe publishable key |
STRIPE_SECRET_KEY |
Stripe | Stripe secret key — server-side only |
STRIPE_WEBHOOK_SECRET |
Stripe | Webhook signing secret for event verification |
NEXT_PUBLIC_PAYPAL_CLIENT_ID |
Optional | PayPal client ID if PayPal checkout is enabled |
NEXT_PUBLIC_GA_ID |
Optional | Google Analytics measurement ID |
NEXT_PUBLIC_MAINTENANCE_MODE |
Optional | Set to true to display a maintenance page |
Variables prefixed with
NEXT_PUBLIC_are bundled into the client build and visible to end users. Do not assign secret keys to variables with this prefix.
ShadowCode is a web design and development studio building clean, professional websites for businesses that want to stand out online.
Website: shadowcodeweb.vercel.app
If you need a website for your business, get in touch directly.