Location: /dashboard/admin/payment-requests
Features:
- Real-time payment request tracking
- Filter by status (Pending, Contacted, Payment Received, Completed)
- Statistics cards showing counts by status
- Total revenue from completed manual payments
- Sortable table with student details
- Quick access to manage each request
Status Workflow:
- Pending - Student submitted request, awaiting admin response
- Contacted - Admin sent payment instructions
- Payment Received - Admin confirmed payment
- Completed - Student enrolled in courses
- Cancelled - Request cancelled
Complete Process:
Step 1: Student Requests Payment Info
- Student clicks "Contact for Payment" on manual product
- Fills form with name, email, phone, message
- Request saved to database with
status = 'pending'
Step 2: Admin Reviews Request
- Navigate to
/dashboard/admin/payment-requests - See pending request in table
- Click "Manage" to open details dialog
Step 3: Admin Sends Payment Instructions
- Fill in payment method (e.g., "Bank Transfer")
- Add payment instructions (bank details, etc.)
- Update status to "Contacted"
- System sends email to student (TODO: email integration)
Step 4: Student Makes Payment
- Student receives payment instructions
- Makes payment via agreed method
- Informs admin (email, phone, etc.)
Step 5: Admin Confirms Payment
- Admin marks request as "Payment Received"
- Optionally generates invoice
- Clicks "Confirm Payment & Enroll Student"
Step 6: Automatic Enrollment
- System enrolls student in all courses linked to product
- Creates transaction record
- Updates request status to "Completed"
- Student immediately gets access to courses
Features:
- Professional HTML invoice template
- Automatic invoice numbering (format:
INV-{timestamp}-{requestId}) - Company branding (customizable via env vars)
- Student details (name, email, phone)
- Product details with description
- Payment instructions included
- Print-friendly CSS styling
- Accessible via URL:
/api/invoices/{invoiceNumber}
How to Generate:
- Open payment request in admin panel
- Click "Generate Invoice"
- Invoice number created and saved
- View invoice at
/api/invoices/INV-... - Print or save as PDF using browser
Customization:
Add to .env.local:
COMPANY_NAME="Your LMS Platform"
COMPANY_ADDRESS="123 Main St, City, Country"
COMPANY_EMAIL="billing@yourlms.com"
COMPANY_PHONE="+1 234 567 8900"CREATE TABLE payment_requests (
request_id BIGSERIAL PRIMARY KEY,
user_id UUID NOT NULL REFERENCES profiles(id),
product_id INTEGER NOT NULL REFERENCES products(product_id),
-- Contact info
contact_name TEXT NOT NULL,
contact_email TEXT NOT NULL,
contact_phone TEXT,
message TEXT,
-- Status tracking
status VARCHAR(20) NOT NULL DEFAULT 'pending',
-- Payment details (admin fills)
payment_method TEXT,
payment_instructions TEXT,
payment_deadline TIMESTAMPTZ,
payment_confirmed_at TIMESTAMPTZ,
payment_amount NUMERIC(10, 2),
payment_currency VARCHAR(3),
-- Invoice
invoice_number TEXT UNIQUE,
invoice_generated_at TIMESTAMPTZ,
-- Admin tracking
processed_by UUID REFERENCES profiles(id),
admin_notes TEXT,
-- Timestamps
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);- Browse available courses
- Find product with "Manual/Offline Payment"
- Click "Contact for Payment"
- Fill contact form and submit
- Receive payment instructions via email
- Make payment
- Get enrolled automatically after admin confirmation
- Receive notification of new payment request
- Navigate to Payment Requests dashboard
- Review student details and product
- Generate invoice (optional)
- Send payment instructions
- Mark as "Contacted"
- Wait for student payment
- Confirm payment received
- Click "Confirm Payment & Enroll Student"
- Student automatically enrolled
Create Manual Payment Product:
1. Go to /dashboard/admin/products/new
2. Fill product details
3. Select "Manual/Offline Payment" as payment method
4. Add courses
5. SaveProcess Payment Requests:
1. Go to /dashboard/admin/payment-requests
2. Click "Manage" on pending request
3. Fill payment instructions
4. Update status to "Contacted"
5. Wait for payment
6. Mark as "Payment Received"
7. Click "Confirm Payment & Enroll Student"Request Payment Info:
1. Browse courses
2. Find course with "Contact for Payment" button
3. Click and fill form
4. Submit request
5. Wait for payment instructions
6. Make payment
7. Get enrolledsupabase/migrations/20260201160000_create_payment_requests_table.sql
app/actions/payment-requests.ts- CRUD operations for payment requestscreatePaymentRequest()- Student creates requestupdatePaymentRequest()- Admin updates status/detailsconfirmPaymentAndEnroll()- Admin enrolls studentgenerateInvoice()- Creates invoice
app/dashboard/admin/payment-requests/page.tsx- Dashboard with stats and tabsapp/api/invoices/[invoiceNumber]/route.ts- Invoice viewer
components/admin/payment-requests-table.tsx- Requests tablecomponents/admin/payment-request-dialog.tsx- Management dialogcomponents/student/manual-payment-dialog.tsx- Contact form (updated)
lib/invoice-generator.ts- HTML invoice template
Student Side:
- Find manual payment product
- Open contact form
- Submit payment request
- Verify success message
- Check database has pending request
Admin Side:
- See pending request in dashboard
- Stats show correct counts
- Open payment request dialog
- Generate invoice
- View invoice HTML
- Update payment instructions
- Change status to "Contacted"
- Mark as "Payment Received"
- Confirm and enroll student
- Verify student has access to courses
Database Verification:
-- Check payment requests
SELECT * FROM payment_requests ORDER BY created_at DESC;
-- Check enrollments created
SELECT e.*, c.title
FROM enrollments e
JOIN courses c ON e.course_id = c.course_id
WHERE e.user_id = (SELECT user_id FROM payment_requests WHERE request_id = X);
-- Check transactions
SELECT * FROM transactions
WHERE metadata->>'payment_request_id' = 'X';Create test file:
// tests/admin/payment-workflow.spec.ts
test('complete manual payment workflow', async ({ page }) => {
// Student creates request
await loginAsStudent(page)
await page.goto('/dashboard/student')
await page.click('text=Contact for Payment')
await page.fill('input[name="name"]', 'Test Student')
await page.fill('input[name="email"]', 'test@example.com')
await page.click('button:has-text("Request Payment Info")')
// Admin processes request
await loginAsAdmin(page)
await page.goto('/dashboard/admin/payment-requests')
await page.click('button:has-text("Manage")')
await page.fill('textarea[name="paymentInstructions"]', 'Bank details...')
await page.click('button:has-text("Update Request")')
// Admin enrolls student
await page.click('button:has-text("Confirm Payment & Enroll Student")')
await expect(page.locator('text=Student enrolled')).toBeVisible()
})Optional (for invoice branding):
COMPANY_NAME="Your LMS Platform"
COMPANY_ADDRESS="123 Education Street, Learning City"
COMPANY_EMAIL="billing@yourlms.com"
COMPANY_PHONE="+1 (555) 123-4567"To enable email notifications, implement in app/actions/payment-requests.ts:
// After creating payment request
await sendEmail({
to: 'admin@yourlms.com',
subject: 'New Payment Request',
template: 'payment-request-admin',
data: { request }
})
// After admin sends instructions
await sendEmail({
to: request.contact_email,
subject: 'Payment Instructions',
template: 'payment-instructions',
data: { instructions, request }
})
// After enrollment
await sendEmail({
to: request.contact_email,
subject: 'Welcome! You're Enrolled',
template: 'enrollment-confirmation',
data: { courses, request }
})The admin dashboard now shows:
- Pending Payments - Count of requests awaiting action
- Total Revenue - Includes manual payment transactions
- Quick access button to Payment Requests
Payment Requests page shows:
- Pending count
- Contacted count
- Payment Received count
- Completed count
- Total revenue from completed payments
Each status has a color-coded badge:
- 🟡 Pending - Yellow
- 🔵 Contacted - Blue
- 🟣 Payment Received - Purple
- 🟢 Completed - Green
- 🔴 Cancelled - Red
Features:
- Student details section
- Product and amount info
- Status selector
- Payment method input
- Payment instructions textarea
- Internal admin notes
- Quick action buttons
- Generate invoice button
- Enroll student button (when payment received)
- ✅ RLS policies protect payment requests
- ✅ Only admins can update requests
- ✅ Only admins can confirm payments
- ✅ Students can only view their own requests
- ✅ Invoice access verified (student or admin only)
- ✅ Enrollment prevents duplicates
- ✅ Transaction records created for audit trail
- ✅ Timestamps track all status changes
- ✅ Admin who processed request is tracked
- Email notifications (admin and student)
- SMS notifications option
- PDF invoice generation (currently HTML)
- Payment receipt upload
- Bulk payment processing
- Export payment requests to CSV
- Payment reminder system
- Automatic invoice numbering sequences
- Multi-currency support
- Payment plan options
createPaymentRequest(data)
- Creates new payment request
- Returns:
{ success, data: PaymentRequest } - Validates: product exists, no duplicate requests
updatePaymentRequest(requestId, updates)
- Updates request status and details
- Returns:
{ success } - Admin only
confirmPaymentAndEnroll(requestId)
- Enrolls student in courses
- Creates transaction record
- Marks request as completed
- Returns:
{ success } - Admin only
generateInvoice(requestId)
- Creates invoice number
- Returns:
{ success, data: { invoiceNumber, invoiceUrl } } - Admin only
GET /api/invoices/[invoiceNumber]
- Returns HTML invoice
- Accessible by student (owner) or admin
- Print-friendly format
You now have a complete manual payment system that:
- ✅ Tracks all payment requests in a dedicated dashboard
- ✅ Provides step-by-step workflow from request to enrollment
- ✅ Generates professional invoices with company branding
- ✅ Automates enrollment after payment confirmation
- ✅ Maintains full audit trail of all actions
- ✅ Integrates seamlessly with existing product system
- ✅ Scales easily for future payment providers
Perfect for offline sales while maintaining infrastructure for future online payments!