Skip to main content

How to Generate Entry Pass and Stripe Receipt Downloads in Flask?

How to Generate Entry Pass and Stripe Receipt Downloads in Flask?

In this tutorial, we will enhance our Flask-based Jersey City Pickleball Club booking system by adding two useful features: a PDF entry pass and Stripe payment receipt access. We will use ReportLab to generate a personalised entry pass containing booking details such as member name, court, date, time slot, and booking fee information. We will also integrate with the Stripe API to retrieve the official payment receipt, allowing users to access their transaction records directly from the dashboard.

Prerequisite:

This tutorial is part of the Flask Landing Page and Reservation System Series.

📚 View the Complete Flask Landing Page and Reservation System Series

⬅ Previous Part                                                                                                                      
 ➡ Next Part

Preliminary:
Before I begin, please activate the virtual environment and install the required dependencies.
python -m venv venv
venv\Scripts\activate
pip install flask reportlab stripe
Then, we need to set up the file and folder structure, as below:
Files and folders
In this tutorial, I will focus on generating my own documents (ReportLab) and connecting to external payment documents (Stripe API). Therefore, all the files and folders remain the same, except that I will create new entry_pass.py and receipt.py files.

Step 1: Generating an entry pass
Generating an entry pass
extracted app.py
from entry_pass import entry_pass_bp
app.register_blueprint(entry_pass_bp)
Create a blueprint and register it in app.py
Generating an entry pass logic

entry_pass.py
from io import BytesIO
from flask import Blueprint, send_file, abort
from reportlab.lib.colors import darkgreen, black
from reportlab.lib.units import inch
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfgen import canvas

from flask_login import login_required, current_user
from db import bookings_table

entry_pass_bp = Blueprint("entry_pass", __name__)

@entry_pass_bp.route("/download-pass/", methods=["GET", "POST"])
@login_required
def download_pass(booking_id):
    ...
    
    booking = bookings_table.get(doc_id=booking_id)

    if not booking:
        abort(404)

    # Prevent users downloading someone else's pass
    if booking["username"] != current_user.username:
        abort(403)

    buffer = BytesIO()

    pdf = canvas.Canvas(buffer)
    width, height = pdf._pagesize

    # ----------------------------------------------------
    # Title
    # ----------------------------------------------------
    pdf.setFont("Helvetica-Bold", 20)
    pdf.setFillColor(darkgreen)
    pdf.drawCentredString(
        width / 2,
        height - 50,
        "Jersey City Pickleball Club"
    )

    pdf.setFillColor(black)
    pdf.setFont("Helvetica-Bold", 16)
    pdf.drawCentredString(
        width / 2,
        height - 80,
        "ENTRY PASS"
    )

    # ----------------------------------------------------
    # Border
    # ----------------------------------------------------
    margin = 40

    pdf.rect(
        margin,
        130,
        width - 80,
        height - 180
    )

    # ----------------------------------------------------
    # Booking details
    # ----------------------------------------------------
    y = height - 130

    pdf.setFont("Helvetica", 12)

    line_gap = 28

    pdf.drawString(70, y, f"Booking Name : {booking['username']}")
    y -= line_gap

    pdf.drawString(70, y, f"Court : {booking['court']}")
    y -= line_gap

    pdf.drawString(70, y, f"Date : {booking['date']}")
    y -= line_gap

    pdf.drawString(70, y, f"Time Slot : {booking['time_slot']}")
    y -= line_gap

    pdf.drawString(70, y, "Booking Fee : USD 20.00")
    y -= 40

    # Divider
    pdf.line(70, y, width - 70, y)

    y -= 30

    pdf.setFont("Helvetica", 11)

    pdf.drawString(
        70,
        y,
        "Please present this entry pass before entering the court."
    )

    y -= 25

    pdf.drawString(
        70,
        y,
        "Booking Fee: Non-refundable whether or not you attend."
    )

    y -= 45

    pdf.setFont("Helvetica-Oblique", 10)

    pdf.drawCentredString(
        width / 2,
        y,
        "Thank you for booking with Jersey City Pickleball Club."
    )

    # ----------------------------------------------------
    # Footer
    # ----------------------------------------------------
    pdf.setFont("Helvetica", 8)

    pdf.drawCentredString(
        width / 2,
        25,
        "Generated by Jersey City Pickleball Club Booking System"
    )

    pdf.save()

    buffer.seek(0)

    filename = f"EntryPass_{booking_id}.pdf"

    return send_file(
        buffer,
        as_attachment=True,
        download_name=filename,
        mimetype="application/pdf"
    )
1. Route Definition
  • Registers this URL under the entry_pass blueprint
  • Flask converts the URL parameter into an integer.
    • Only logged-in users can download the entry pass.
    • If a visitor is not authenticated, Flask-Login redirects them to the login page.

2. Retrieve Booking from TinyDB
  • Booking data is stored in TinyDB, and doc_id is TinyDB's internal document ID.
3. Check Booking Exists
  • If the booking cannot be found, the server stops here.
4. Security Check
  • This prevents users from downloading other people's passes, and only the current logged-in user.
  • Without this check, someone could guess and download other users' tickets.
5. Create PDF Memory Buffer
  • Advantages:
    • No temporary files
    • Cleaner server
    • Better for cloud deployment
6. Create PDF Object

  • Creates a PDF document, and it canvas is from ReportLab.

7. Get Page Size

  • Letter size: width = 612 points height = 792 points

8. Draw Club Title

  • Sets:
    • Font: Helvetica Bold
    • Size: 20
  • Change text colour to dark green.
  • Draw: Jersey City Pickleball Club at the top centre.
9. Draw a border.
  • Creates a rectangle around the ticket
    • Left margin: 40 
    • Bottom: 130 
    • Rectangle width: page width - 80 
    • Rectangle height: page height - 180
10. Booking Information
  • Start in a vertical position and set normal text.
  • PDF shows: Booking Name: Kelvin
  • Then: Moves the next line downward.
11. Divider Line
  • Draws a horizontal line: Separating booking details and instructions.
12. Instructions
  • Adds rules: Please present this entry pass before entering the court. Booking Fee: Non-refundable whether or not you attend.
13. Footer Message
  • Displays: Thank you for booking with Jersey City Pickleball Club.
14. Save PDF
  • PDF completed
15. Reset Buffer Position
  • Moves the file pointer back to the beginning.
  • Without this: might send an empty file because the pointer is at the end.
16. Generate Filename
  • Create a name such as EntryPass_15.pdf
17. Send PDF to User
  • buffer: The generated PDF.
  • as_attachment=True: Forces download instead of opening inside the browser.
  • download_name: Controls filename:
  • mimetype: Tells the browser, "This is a PDF file."
---------------------------------------------------------------------------------------------------------------------------------------------------

🎁 Get Your FREE Flask Cheat Sheet

Get Your FREE Flask Cheat Sheet

Get more Flask, Python automation, Docker, and HTMX tutorials delivered to your inbox.

✓ Practical coding tutorials
✓ Automation tips for SMEs
✓ New project ideas and templates

Download my FREE Flask Cheat Sheet (PDF)


---------------------------------------------------------------------------------------------------------------------------------------------------

Step 2: Create a Stripe receipt
Create a Stripe receipt image

extracted app.py
from receipt import receipt_bp
app.register_blueprint(receipt_bp)
Create a blueprint and register it in app.py

Create a Stripe receipt logic

receipt.py
from flask import Blueprint, redirect, abort, current_app
from flask_login import login_required, current_user
from tinydb import Query
import stripe

from db import bookings_table

receipt_bp = Blueprint("receipt", __name__)

@receipt_bp.route("/download-receipt/")
@login_required
def download_receipt(booking_id):
    booking = bookings_table.get(doc_id=booking_id)
    print("Booking:", booking)
    if not booking:
        abort(404)

    # Prevent users viewing another user's receipt
    if booking["username"] != current_user.username:
        abort(403)
  
    session_id = booking.get("stripe_session")
    print("Stripe session:",
          booking.get("stripe_session"))
    if not session_id:
        return "Stripe Session ID not found.", 404
    
    stripe.api_key = current_app.config["STRIPE_SECRET_KEY"]
    session = stripe.checkout.Session.retrieve(session_id)

    payment_intent = stripe.PaymentIntent.retrieve(
        session.payment_intent
    )

    charge = stripe.Charge.retrieve(
        payment_intent.latest_charge
    )

    return redirect(charge.receipt_url)
1. Route Definition
  • If/download-receipt/25, Flask converts to booking_id = 25
  • The @login_required decorator ensures only authenticated users can access receipts.
2. Debug Output
  • Useful during development to confirm the route is receiving the correct booking ID.
3. Retrieve Booking from TinyDB
  • The code searches by TinyDB's internal document ID, such as doc_id = 25, and returns booking number 25.
4. Check Booking Exists
  • If the booking does not exist, then the request stops.
5. Security Check
  • This prevents users from accessing another customer's receipt. Therefore, this is an important security feature.
6. Get Stripe Session ID
  • When the payment was completed, this ID connects your booking record to Stripe.
 7. Check Stripe Session Exists
  • If the booking has no Stripe payment record, there is no receipt available. It returns Stripe Session ID not found.
8. Configure Stripe API
  • My Stripe secret key is loaded from Flask configuration, and this allows your application to communicate with Stripe.
9. Retrieve Stripe Checkout Session
  • A checkout session contains information such as
    • customer
    • amount paid
    • payment status
    • payment intent ID
10. Retrieve Payment Intent
  • The Payment Intent represents the payment attempt.
11. Retrieve Charge
  • The Charge object contains the final payment information.
12. Redirect User to Stripe Receipt
  • Instead of creating your own PDF receipt, the user is sent to Stripe's official receipt page.
Bonus:

Difference between entry pass and receipt


Final wrap-up:
The receipt integration completes the payment workflow by allowing customers to access their official Stripe payment receipt directly from the booking dashboard. Instead of generating a separate receipt manually, the application securely retrieves the Stripe payment details using the stored Checkout Session ID, verifies the booking belongs to the logged-in user, and redirects the customer to Stripe’s hosted receipt page. Together with the generated entry pass, users receive both a proof of payment and a convenient ticket for court access, creating a more complete and professional booking experience.

Published: July 2026
Last Updated: July 2026

Related posts

How to Email PDF Invoices Automatically Using Python?

---------------------------------------------------------------------------------------------------------------------------------------------------

Thanks for reading! 

If you haven't subscribed yet, join my newsletter to receive future Python and Flask tutorials.


---------------------------------------------------------------------------------------------------------------------------------------------------
Need a similar system for your business? 
I build custom Flask web applications and Python automation solutions for SMEs and solopreneurs. 

---------------------------------------------------------------------------------------------------------------------------------------------------

About the Author 
Kelvin Loh is a Python developer focused on Flask, desktop applications, and business automation solutions. He shares practical tutorials and real-world coding projects to help developers and small businesses build useful applications.

Comments

Popular Posts

How to Build an Audiobook Workflow Desktop System with Python?

In this tutorial, we will build a simple audiobook player using Python and CustomTkinter. You will learn how to convert text into speech using gTTS and play it with PyGame. We will also implement play, pause, and stop controls, like those in a real audio player. By the end, you will have a clean and functional desktop audiobook app. Prerequisite: This tutorial is part of the standalone tutorial. 📚 View the standalone tutorial Preliminary   Before I begin, it is recommended to activate the virtual environment before installing the relevant dependencies. python -m venv venv venv\Scripts\activate pip install customtkinter pillow gTTS pygame CTkMessagebox pypdf Then, the following steps include setting up the file structure, app.py, and two additional folders: the uploads and media folders. The media folder contains the icons necessary to build the app; there are read, pause, and stop icons, as shown on the diagram. Step 1: Build up the app interface I have 4 sections here: the...

How to Set Up PgAdmin and Adminer Using Docker Compose?

If you are new to database management with Docker, this tutorial will guide you through setting up both PgAdmin and Adminer using Docker containers. By containerising these tools, you can quickly launch lightweight and portable database management environments without installing them directly on your operating system. This approach also makes it easier to manage configurations, updates, and multiple projects across different devices.