Technical Implementation
Here's how to implement the PDF course system on LinkedOut.es:
1. Database Schema
-- Courses table
CREATE TABLE courses (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
description TEXT,
pdf_file_path VARCHAR(500),
price DECIMAL(10,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Course purchases
CREATE TABLE course_purchases (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
course_id INT,
payment_id VARCHAR(100),
purchase_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status ENUM('pending', 'completed', 'refunded')
);
-- Exam questions
CREATE TABLE exam_questions (
id INT PRIMARY KEY AUTO_INCREMENT,
course_id INT,
question TEXT NOT NULL,
option_a VARCHAR(255),
option_b VARCHAR(255),
option_c VARCHAR(255),
option_d VARCHAR(255),
correct_answer ENUM('a', 'b', 'c', 'd'),
points INT DEFAULT 1
);
-- Exam attempts
CREATE TABLE exam_attempts (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
course_id INT,
score INT,
max_score INT,
passed BOOLEAN,
attempt_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Certificates
CREATE TABLE certificates (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
course_id INT,
certificate_id VARCHAR(50) UNIQUE,
issue_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
verification_url VARCHAR(500)
);
2. Course Purchase Flow
// PHP example for course purchase
function purchaseCourse($userId, $courseId, $paymentId) {
// Verify payment with Stripe/PayPal
$paymentVerified = verifyPayment($paymentId);
if ($paymentVerified) {
// Record purchase
$stmt = $pdo->prepare("INSERT INTO course_purchases (user_id, course_id, payment_id, status) VALUES (?, ?, ?, 'completed')");
$stmt->execute([$userId, $courseId, $paymentId]);
// Generate download link
$downloadLink = generateSecureDownloadLink($courseId, $userId);
// Send confirmation email
sendCourseAccessEmail($userId, $courseId, $downloadLink);
return ['success' => true, 'download_link' => $downloadLink];
}
return ['success' => false, 'error' => 'Payment verification failed'];
}
3. Exam System
// JavaScript for exam interface
class CourseExam {
constructor(courseId, userId) {
this.courseId = courseId;
this.userId = userId;
this.questions = [];
this.answers = {};
this.timeLimit = 3600; // 1 hour in seconds
}
async loadQuestions() {
const response = await fetch(`/api/exam-questions/${this.courseId}`);
this.questions = await response.json();
this.renderQuestions();
this.startTimer();
}
submitAnswer(questionId, answer) {
this.answers[questionId] = answer;
this.saveProgress();
}
async submitExam() {
const response = await fetch('/api/submit-exam', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
courseId: this.courseId,
userId: this.userId,
answers: this.answers
})
});
const result = await response.json();
this.showResults(result);
if (result.passed) {
this.generateCertificate();
}
}
}
4. Certificate Generation
// PHP for certificate generation
function generateCertificate($userId, $courseId) {
// Generate unique certificate ID
$certificateId = 'CERT-' . strtoupper(uniqid());
// Get user and course details
$user = getUserById($userId);
$course = getCourseById($courseId);
// Create certificate PDF
$pdf = new TCPDF();
$pdf->AddPage();
$pdf->SetFont('helvetica', 'B', 24);
$pdf->Cell(0, 20, 'Certificate of Completion', 0, 1, 'C');
$pdf->SetFont('helvetica', '', 16);
$pdf->Cell(0, 15, 'This certifies that', 0, 1, 'C');
$pdf->SetFont('helvetica', 'B', 20);
$pdf->Cell(0, 20, $user['name'], 0, 1, 'C');
$pdf->SetFont('helvetica', '', 16);
$pdf->Cell(0, 15, 'has successfully completed', 0, 1, 'C');
$pdf->SetFont('helvetica', 'B', 18);
$pdf->Cell(0, 20, $course['title'], 0, 1, 'C');
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 15, 'Certificate ID: ' . $certificateId, 0, 1, 'C');
$pdf->Cell(0, 10, 'Date: ' . date('F j, Y'), 0, 1, 'C');
// Save certificate
$certificatePath = "/certificates/{$certificateId}.pdf";
$pdf->Output($certificatePath, 'F');
// Save to database
$stmt = $pdo->prepare("INSERT INTO certificates (user_id, course_id, certificate_id, verification_url) VALUES (?, ?, ?, ?)");
$verificationUrl = "https://linkedout.es/verify/{$certificateId}";
$stmt->execute([$userId, $courseId, $certificateId, $verificationUrl]);
return $certificateId;
}