An Online Cinema is a digital platform that allows users to select, watch, and purchase access to movies and other video materials via the internet.
This is the backend API for the FastAPI Online Cinema project.
Follow these steps to set up your development environment and run the project.
git clone https://github.com/Iventyk/fastapi-online-cinema.git
cd fastapi-online-cinemaMake sure you have Poetry installed.
poetry install
This will create a virtual environment automatically and install all required dependencies.
poetry run uvicorn src.main:app --reload
The server will run on http://127.0.0.1:8000 by default. Use --reload to enable auto-reload on code changes.
Create .env from .env.sample.
Windows (PowerShell):
Copy-Item .env.sample .env
Linux/macOS/WSL/Git Bash:
cp .env.sample .env
docker compose up -d --build
Migrations are not executed automatically on up. Run them explicitly:
docker compose --profile migrate run --rm migrator
API docs: http://127.0.0.1:8000/docs
MailHog UI: http://127.0.0.1:8025
MinIO Console: http://127.0.0.1:9001
docker compose down
imports:
from src.schemas import CurrentUser
from src.security import JWTAuthManagerInterface, get_current_user
from src.config import get_jwt_manager, get_settings, Settings
from src.databases import get_db
Realizations:
Annotated[Settings, Depends(get_settings)]
- retrieve settings object;
db: Annotated[AsyncSession, Depends(get_db)]
- retrieve db session;
jwt_manager: Annotated[JWTAuthManagerInterface, Depends(get_jwt_manager)]
- retrieve jwt_manager
current_user: Annotated[CurrentUser, Depends(get_current_user)]
- retrieve current authorized user
UserGroupEnum - Defines user access levels within the system:
USER – default application user
MODERATOR – elevated permissions
ADMIN – full administrative access
GenderEnum - Used in user profiles:
MALE
FEMALE
User Groups
UserGroupModel
Represents a role/group assigned to users.
Fields:
id – primary key
name – unique group name (UserGroupEnum)
Relationships:
One-to-many with UserModel (UserGroupModel.users)
Notes:
Each user belongs to at most one group
Groups are used for authorization and role-based access control (RBAC)
UserModel
UserModel
Represents an application user and authentication identity.
Fields:
id – primary key
email – unique email address (indexed)
hashed_password – securely stored password hash
is_active – account activation status
created_at – creation timestamp
updated_at – last update timestamp
group_id – foreign key to user_groups
Relationships:
Many-to-one with UserGroupModel
One-to-one with UserProfileModel
One-to-one with ActivationTokenModel
One-to-one with PasswordResetTokenModel
One-to-many with RefreshTokenModel
Security Design:
Passwords are write-only
Password hashing and validation are enforced at the model level
Raw passwords are never stored or exposed
Utility Methods:
create(...) – factory method for safe user creation
check_password(...) – verifies password against stored hash
has_group(...) – role membership check
UserProfileModel
UserProfileModel
Stores optional personal and demographic information for a user.
Fields:
id – primary key
first_name
last_name
avatar – URL or path to profile image
gender – GenderEnum
date_of_birth
info – free-form additional information
user_id – unique foreign key to users
Relationships:
One-to-one with UserModel
Notes:
Profile is optional but strictly one profile per user
Automatically deleted when the user is deleted
Token System
All tokens inherit from a shared abstract base.
TokenBaseModel (Abstract)
Base model for all user-related tokens.
Common Fields:
id – primary key
token – unique token string
expires_at – expiration datetime (UTC)
user_id – foreign key to users
Notes:
Tokens are time-limited
Cascade deletion ensures cleanup when a user is removed
StatusEnum - Defines the lifecycle of an order. Used in the Shopping Cart module to validate purchase history before adding items (preventing duplicate purchases).
PENDING – payment is in progress
PAID – movie is already purchased (cannot be added to cart again)
CANCELED – transaction failed
Shopping Cart Models:
Cart - Represents a user's storage for movies.
Fields:
id – primary key
user_id – unique foreign key to users
Relationships:
One-to-one with UserModel (Cart.user)
One-to-many with CartItemModel (Cart.items)
Notes:
Automatically created for the user if it doesn't exist during item addition.
Strictly one cart per user.
CartItem - Represents a specific movie added to a cart.
Fields:
id – primary key
cart_id – foreign key to carts
movie_id – foreign key to movies
Relationships:
Many-to-one with CartModel
Many-to-one with MovieModel
Notes:
Acts as a link between the user's cart and the movie catalog.
Includes validation to prevent duplicate items in the same cart.
Validation - The module enforces strict rules to ensure data integrity and security.
1. User Validation:
- Ensures the user exists before any operation.
- Function: validate_user(user_id)
2. Role-Based Access Control (RBAC):
- Users can only access/modify their own carts.
- Moderators and Admins have elevated permissions to view/clear/extend any user's cart.
- Function: validate_user_permission(...)
3. Movie Availability:
- Checks if the requested movie exists in the database.
- Function: validate_movie(movie_id)
4. Cross-Module Purchase Validation (Relation to Orders):
- Before adding a movie to the cart, the system checks the Order history.
- Prevents adding a movie if it is already in a PAID or PENDING order.
- Function: validate_movie_purchase_status(...)
Business Logic && Cart Services
Function - sync_guest_cart_to_user - Handles the transition of shopping cart data from a guest session (local storage/cookies) to the persistent database when a user logs in or registers.
Trigger Points:
- Successful Registration
- Successful Login
Workflow:
1. Validation: Checks if any movie IDs were passed from the guest session.
2. Cart Retrieval: Fetches the user's persistent cart from the database.
- Auto-creation: If the user has no cart, a new one is created immediately.
3. Smart Merge (Deduplication):
- Identifies movies already present in the user's database cart.
- Calculates the difference: `Movies to Add = Guest IDs - Existing DB IDs`.
- Prevents duplicate entries for the same movie.
4. Persistence: Bulk inserts the new items into the `cart_items` table and commits the transaction.
Goal: ensures a seamless user experience where items added before authentication are not lost.
Order Status Lifecycle – Tracks the state of a transaction from creation to finalization.
PENDING – payment is in progress
PAID – movie is already purchased (cannot be added to cart again)
CANCELED – transaction failed
Orders Models:
Order - Represents a collection of movies a user intends to purchase.
Fields:
id – primary key
user_id – foreign key to users
status – enum (PENDING, PAID, CANCELED)
total_amount – total price (Decimal)
created_at – timestamp
Relationships:
One-to-many with OrderItemModel (Order.items)
Many-to-one with UserModel (Order.user)
OrderItem - A snapshot of a movie within a specific order.
Fields:
id – primary key
order_id – foreign key to orders
movie_id – foreign key to movies
price_at_order – the price of the movie at the time the order was created (Decimal)
Notes:
Ensures historical financial accuracy even if movie prices change later.
Validation & Business Logic - Ensures data integrity and security.
1. Smart Creation (Filtering):
- Excludes movies already purchased (PAID).
- Excludes movies already in another PENDING order.
- Filters out unavailable (deleted or region-locked) movies.
- Function: create_order(...) -> returns list of 'removed_items'.
2. Integrity Checks:
- Prevents creating an order from an empty cart.
- Ensures users can only access their own orders.
3. Price Revalidation:
- Re-scans current movie prices before payment finalization.
- Updates 'total_amount' and 'price_at_order' if database prices changed.
- Function: revalidate_order_prices(order_id)
4. Order Cancellation:
- Users can cancel PENDING orders before payment.
- PAID orders require a refund request (direct cancellation blocked).
API Endpoints:
User Functionality:
POST /orders/— Create a new order from cart (with auto-filtering).GET /orders/— Get all orders of the authenticated user.PATCH /orders/{order_id}/cancel— Cancel a specific pending order.
Admin/Moderator Functionality:
GET /orders/admin— Get all system orders with filters:- By User ID
- By Status (pending, paid, canceled)
- By Date Range
The payment system allows users to pay for orders using Stripe and receive email notifications about their payment status. It also supports admin views and webhook handling for transaction validation.
- Users can create a payment for an order.
- After successful payment:
- The order status is updated to
PAID. - The user receives an email confirmation.
- The order status is updated to
- Users can view a history of all their payments, including:
- Date and time of payment
- Amount
- Status (
pending,successful,canceled,refunded) - Itemized details of each order
POST /payments/{order_id}— Create payment for a specific order. Returnsclient_secretfor Stripe.GET /payments/— Get all payments of the authenticated user.
- Admins can view all payments with optional filters:
- By user ID
- By status (
successful,canceled,refunded)
- Only users with
ADMINpermission can access admin endpoints.
GET /payments/admin— Get all payments (admin only) with filters.
- Uses Stripe as the payment gateway.
- Validates:
- Total amount of the order
- Order status (
PENDING) - User authentication
- Creates
PaymentandPaymentItemrecords in the database. - Triggers Celery tasks to send payment notification emails:
send_payment_success_email_task— sent after successful paymentsend_payment_failed_email_task— sent if payment fails
POST /webhooks/stripe— Receives Stripe events to validate payments and update order/payment statuses.- Updates Payment and Order status automatically based on webhook event type.
- Sends email notifications for successful or failed payments via Celery tasks.
- Payment
id: intuser_id: int— foreign key to usersorder_id: int— foreign key to ordersamount: Decimalstatus: PaymentStatusEnum(SUCCESSFUL,CANCELED,REFUNDED)external_payment_id: str | Nonecreated_at: datetime
- PaymentItem
id: intpayment_id: int— foreign key to Paymentorder_item_id: int— foreign key to OrderItemprice_at_payment: Decimal
- Payment Success
- Triggered after payment is successfully processed.
- Includes order details and amount.
- Payment Failed
- Triggered if payment fails or is declined.
- Provides instructions to the user to retry or select a different payment method.
All email sending is handled asynchronously via Celery tasks, ensuring non-blocking behavior during API calls.
This section explains how to set up and test Stripe payments for the application.
- Register or log in to your Stripe Dashboard.
- Create an API key and a webhook secret for your environment.
- Add them to your
.envor environment variables:
STRIPE_API_KEY=your_stripe_api_key
STRIPE_WEBHOOK_SECRET=your_stripe_webhook_secret
If you haven't installed it yet, follow instructions here: Stripe CLI.
Run:
stripe login
Follow the browser authentication flow.
Forward Stripe events to your local API:
stripe listen --forward-to localhost:8000/webhooks/stripe
This allows your local application to receive Stripe webhook events in real time.
Create a payment intent and confirm it using the Stripe CLI:
stripe payment_intents confirm <PAYMENT_INTENT_ID> --payment-method pm_card_visa
Replace <PAYMENT_INTENT_ID> with the actual payment intent ID from your Stripe dashboard.
- Payment success/failure emails are sent automatically via Celery tasks.
- Ensure MailHog (or your SMTP server) is running to view test emails.
- Use pm_card_visa for testing successful payments, and other test payment methods for simulating different scenarios.