diff --git a/app/bookings/routes.py b/app/bookings/routes.py
index 668c62637d988d55648109128856a6d73e3c8710..ec50e6cd13608ea4438ec89291cd6b50ba2e23b4 100644
--- a/app/bookings/routes.py
+++ b/app/bookings/routes.py
@@ -1,9 +1,9 @@
-from flask import render_template, redirect, url_for, request, jsonify, session, flash, g
+from flask import render_template, redirect, url_for, request, jsonify, session, flash, g, send_file
 from app.bookings import bp
 from app.models import Listings, Bookings, ListingAvailability
 from app import db
 from app.logger import error_logger
-from app.main.utils import calculate_discount, pretty_time
+from app.main.utils import calculate_discount, pretty_time, create_receipt, create_plane_ticket
 import json
 from datetime import datetime
 from app import user_permission, permission_required
@@ -209,7 +209,7 @@ def listing(id):
     )
 
 # This route should be used after show_listing if used internally as this clears the ajax parameters before redirecting the user
-@bp.route('/payment/successful/<int:id>', methods=['GET'])
+@bp.route('/checkout/success/<int:id>', methods=['GET'])
 @permission_required(user_permission)
 def payment_complete(id):
     
@@ -220,7 +220,7 @@ def payment_complete(id):
         return redirect(url_for('main.index'))
 
     return render_template(
-        'bookings/payment_success.html',
+        'bookings/payment_success.html', id=booking.id
     )
 
 @bp.route('/checkout_post', methods=['POST'])
@@ -353,3 +353,25 @@ def process_images(listings):
             item.main_image_url = url_for('main.upload_file', filename='booking_image_not_found.jpg')
         # Must be a single quote JSON otherwise doesn't work in frontend
         item.image_urls = json.dumps([url_for('main.upload_file', filename=img.image_location) for img in item.listing_images]).replace('"', '&quot;')
+
+
+@bp.route('/payment_success/<int:id>')
+def payment_success(booking_id):
+    return render_template('payment_success.html', booking_id=booking_id)
+
+@bp.route('/generate-receipt/<int:id>')
+def generate_receipt(id):
+    booking = Bookings.search_booking(id)
+    listing = Listings.search_listing(booking.listing_id)
+    pdf = create_receipt(1, booking.seat_type, '2024-02-24', listing)
+    
+    return send_file(pdf, as_attachment=True, download_name='receipt.pdf')
+
+@bp.route('/generate-ticket/<int:id>')
+def generate_ticket(id):
+    booking = Bookings.search_booking(id)
+    listing = Listings.search_listing(booking.listing_id)
+    
+    pdf = create_plane_ticket(booking.seat_num, booking.seat_type, booking.depart_date, listing, booking_id)
+    
+    return send_file(pdf, as_attachment=True, download_name='plane_ticket.pdf')
diff --git a/app/main/utils.py b/app/main/utils.py
index fcaaffff9734bcb7a5831c3d3b688b9f119b2884..e362fda8837a905314a1702232b279ce4818b685 100644
--- a/app/main/utils.py
+++ b/app/main/utils.py
@@ -3,6 +3,12 @@
 from flask import current_app
 from datetime import time, datetime
 from datetime import datetime 
+from fpdf import FPDF
+import qrcode
+import barcode
+from barcode.writer import ImageWriter
+from io import BytesIO
+from PIL import Image
 
 def allowed_image_files(filename):
     return '.' in filename and filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS']
@@ -42,3 +48,80 @@ def pretty_time(unformatted_time, to_12_hour=True):
         formatted_time = unformatted_time.strftime("%H:%M")
     
     return formatted_time
+
+
+def create_receipt(seat_num, seat_type, depart_date, listing):
+    pdf = FPDF()
+    pdf.add_page()
+    pdf.set_font("Arial", size=12)
+    
+    pdf.cell(200, 10, txt="Receipt", ln=True, align='C')
+
+    pdf.cell(200, 10, txt=f"Seat Number: {seat_num}", ln=True)
+    pdf.cell(200, 10, txt=f"Seat Type: {seat_type}", ln=True)
+    pdf.cell(200, 10, txt=f"Departure Date: {depart_date}", ln=True)
+    pdf.cell(200, 10, txt=f"Departure Location: {listing.depart_location}", ln=True)
+    pdf.cell(200, 10, txt=f"Destination Location: {listing.destination_location}", ln=True)
+
+    if seat_type.lower() == 'economy':
+        cost = listing.economy_fair_cost
+    elif seat_type.lower() == 'business':
+        cost = listing.business_fair_cost
+    
+    pdf.cell(200, 10, txt=f"Total Cost: {cost}", ln=True)
+
+    output = BytesIO()
+    pdf.output(output)
+    output.seek(0)
+
+    return output
+
+def create_plane_ticket(seat_num, seat_type, depart_date, listing, booking_id):
+    for i in range(seat_num):
+        seat_number = f"{seat_num + 1 - i:02d}"
+        ticket_number = f"{booking_id}-{seat_number}"
+
+        pdf = FPDF()
+        pdf.add_page()
+        pdf.set_font("Arial", size=12)
+        
+        pdf.cell(200, 10, txt="Plane Ticket", ln=True, align='C')
+
+        pdf.cell(200, 10, txt=f"Seat Number: {seat_number}", ln=True)
+        pdf.cell(200, 10, txt=f"Seat Type: {seat_type}", ln=True)
+        pdf.cell(200, 10, txt=f"Departure Date: {depart_date}", ln=True)
+        pdf.cell(200, 10, txt=f"Departure Location: {listing.depart_location}", ln=True)
+        pdf.cell(200, 10, txt=f"Departure Time: {listing.depart_time}", ln=True)
+        pdf.cell(200, 10, txt=f"Destination Location: {listing.destination_location}", ln=True)
+        pdf.cell(200, 10, txt=f"Destination Time: {listing.destination_time}", ln=True)
+        pdf.cell(200, 10, txt=f"Transport Type: {listing.transport_type}", ln=True)
+
+        qr_img = create_qr_code(ticket_number)
+        qr_img_path = f"qr_code_{ticket_number}.png"
+        qr_img.save(qr_img_path)
+        pdf.image(qr_img_path, x=10, y=120, w=50)
+
+        barcode_img = create_barcode(ticket_number)
+        barcode_img_path = f"barcode_{ticket_number}.png"
+        barcode_img.save(barcode_img_path)
+        pdf.image(barcode_img_path, x=80, y=120, w=100)
+
+        output = BytesIO()
+        pdf.output(output)
+        output.seek(0)
+
+        return output
+
+def create_qr_code(data):
+    qr = qrcode.QRCode(version=1, box_size=10, border=5)
+    qr.add_data(data)
+    qr.make(fit=True)
+    img = qr.make_image(fill='black', back_color='white')
+    return img
+
+def create_barcode(data):
+    CODE128 = barcode.get_barcode_class('code128')
+    code = CODE128(data, writer=ImageWriter())
+    buffer = BytesIO()
+    code.write(buffer)
+    return Image.open(buffer)
\ No newline at end of file
diff --git a/app/templates/bookings/payment_success.html b/app/templates/bookings/payment_success.html
new file mode 100644
index 0000000000000000000000000000000000000000..65a99ea3eb9010aee138b4406afb02c653c4b0d4
--- /dev/null
+++ b/app/templates/bookings/payment_success.html
@@ -0,0 +1,15 @@
+{% extends 'base.html' %}
+{% block content %}
+
+<body>
+    <h1>Payment Successful!</h1>
+    <p>Your payment has been processed successfully.</p>
+    <p>Click the buttons below to download your receipt and plane ticket:</p>
+    <form action="{{ url_for('bookings.generate_receipt', id=id) }}" method="get">
+        <button type="submit">Download Receipt</button>
+    </form>
+    <form action="{{ url_for('bookings.generate_ticket', id=id) }}" method="get">
+        <button type="submit">Download Plane Ticket</button>
+    </form>     
+</body>
+{% endblock %}
diff --git a/requirements.txt b/requirements.txt
index 9945e842e5a7f4f54a60ed3fea364d9b5dc048a3..f8a540d7b7b5aa0c6dafef7ae94021d436a69393 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,4 +10,7 @@ debugpy
 flask-wtf
 flask-security
 flask-principal
-setuptools
\ No newline at end of file
+setuptools
+fpdf2
+qrcode[pil]
+python-barcode
\ No newline at end of file