How to Build an Online Course Website with Base44

How to Build an Online Course Website with Base44: Stripe Checkout and Cloudflare Stream

If you want to build an online course website for the US or UK market, you need more than a simple landing page. A real online course platform needs user accounts, public course pages, checkout, payment confirmation, student access, video lessons, progress tracking, and an admin dashboard to manage everything.

Base44 is useful because you can build the app step by step using structured prompts. Instead of asking the AI to generate the entire course platform in one request, you should separate the build into clear phases: foundation, public pages, admin, learning experience, video player, Stripe Checkout, and Cloudflare Stream configuration.

In this guide, I will show you how to build an online course website with Base44 for international users, especially the US/UK market, using Stripe Checkout as the main payment method and Cloudflare Stream for course video hosting.

The sample brand in this guide is DB Uni, a digital university platform for Global Business, Affiliate Marketing, Ecommerce, and applied AI. You can replace this brand with your own academy, coaching program, creator education business, or digital course brand.

By the end of this tutorial, you will understand how to build an online course platform that can:

  • Show public course pages
  • Let students register and log in
  • Sell one-time online courses with Stripe Checkout
  • Automatically unlock courses after payment
  • Let students access purchased courses
  • Let admins create courses and lessons
  • Upload and stream course videos with Cloudflare Stream
  • Track student learning progress

What should an online course website include?

A proper online course platform should include several core systems.

First, it needs authentication. Students should be able to register, log in, reset their password, sign in with Google, and access protected learning pages after purchasing a course.

Second, it needs public-facing pages. These include a homepage, course listing page, course detail page, course curriculum, pricing, course preview, and a clear “Buy now” button.

Third, it needs an admin area. The owner should be able to create courses, manage lessons, upload thumbnails, upload videos, manage students, check orders, and configure payment gateways.

Fourth, it needs a learning area. After payment, the student should be automatically enrolled and redirected to a course player where they can watch lessons, track progress, download attachments, and continue learning from where they left off.

Fifth, it needs a payment system. For the US/UK market, Stripe Checkout is one of the easiest and most practical options because users can pay by card, Apple Pay, Google Pay, and other supported methods depending on your Stripe configuration.

Finally, it needs a video hosting system. Instead of storing large video files directly inside the app, you can use Cloudflare Stream to upload, process, and deliver course videos.

If you want to build an online course business, these systems are the foundation. You can later add quizzes, certificates, bundles, subscriptions, communities, live cohort classes, or affiliate programs.


Why use Stripe Checkout to build an online course website?

Stripe Checkout is one of the easiest ways to add payments to an online course platform.

A simple Stripe Checkout flow works like this:

  1. Student clicks “Buy now”.
  2. App creates an internal Order.
  3. Backend creates a Stripe Checkout Session.
  4. Stripe returns a Checkout URL.
  5. Student is redirected to Stripe Checkout.
  6. Student pays on Stripe’s hosted checkout page.
  7. Stripe redirects the student back to your success page.
  8. Stripe sends a webhook event such as checkout.session.completed.
  9. App verifies the webhook and activates the student’s enrollment.

This is simpler than PayPal because you do not need a two-step create order → approve → capture flow in the frontend. With Stripe Checkout, the hosted checkout page handles the payment experience, and your backend focuses on creating the session and processing the webhook.

The most important thing is this:

Do not activate course access only because the user returns to the success page.

The reliable confirmation should come from the Stripe webhook after payment is completed.

Why?

Because a user may complete payment successfully but close the browser before the success page loads. If you only activate access from the success page, you may miss valid payments.

The safer online course fulfillment flow is:

Stripe Checkout → Webhook confirmation → Order completed → Enrollment activated

This is the payment architecture you should use when you build an online course website with Base44.


Why use Base44 to build an online course platform?

Base44 is useful when you want to build an app by describing the product through prompts.

For an online course platform, Base44 can help you create:

  • Database entities
  • Pages
  • Admin dashboard
  • Authentication flow
  • Course editor
  • Student learning area
  • Backend functions
  • Stripe Checkout logic
  • Webhook handling
  • App configuration
  • Role-based access control

The main benefit is speed. Instead of manually coding every page, you can guide Base44 phase by phase.

However, you should not ask Base44 to build the entire app in one prompt. A course platform has too many moving parts, including authentication, course access, Stripe Checkout, webhook verification, Cloudflare Stream, admin permissions, and enrollment logic.

The better approach is to build the app by phase.


Recommended build strategy

The best way to build an online course website with Base44 is to separate the project into phases.

Do not ask the AI to build the full platform in one prompt.

A full course platform has too many moving parts:

  • Authentication
  • Roles
  • Course data
  • Lesson data
  • Video uploads
  • Checkout
  • Webhooks
  • Enrollment activation
  • Admin settings
  • Protected learning pages

Instead, build it step by step:

  1. Foundation and authentication
  2. Public course pages
  3. Admin area
  4. Learning experience and video player
  5. Stripe Checkout
  6. Cloudflare Stream configuration
  7. Testing and go-live

This makes the project easier to debug and much less likely to break.

If you want to build an online course website that can actually sell and deliver content, this phase-by-phase structure is much safer than trying to generate everything at once.


Phase 1 — Foundation, entities, and authentication

The first phase creates the base of the entire app: brand, design system, database entities, role-based security, and authentication pages.

Enter this prompt into Base44:

Create an app called "DB Uni" — a digital university platform for Global Business, including Affiliate Marketing, Ecommerce, and applied AI.

Design:
- Brand: "DB Uni"
- Positioning: "Digital University"
- Colors: primary purple #6332F6, accent orange #FF5C00, light background #FDFDFD, dark background #0A0A0C
- Fonts: Plus Jakarta Sans for headings, Inter for body text
- Use a glassmorphic navbar, gradient text, magnetic buttons, and course cards with hover-lift effects
- Fully responsive for mobile and desktop
- Support dark mode

Create the following entities:

1. Course
Fields:
- title
- slug
- description
- full_description
- thumbnail_url
- preview_video_url
- price
- original_price
- currency (default USD)
- category (global_affiliate/global_ecommerce/ai/other)
- level (beginner/intermediate/advanced)
- language (default en)
- format (video/live_zoom)
- zoom_link
- total_lessons
- total_hours
- is_published
- is_featured
- enrollment_count
- rating
- rating_count
- what_you_learn[]
- requirements[]
- tags[]

RLS:
- Public read
- Admin-only write

2. Section
Fields:
- course_id
- title
- order
- description

RLS:
- Public read
- Admin-only write

3. Lesson
Fields:
- section_id
- course_id
- title
- description
- video_url
- duration_seconds
- order
- is_preview
- attachments[]

RLS:
- Public read
- Admin-only write

4. Category
Fields:
- name
- slug
- description
- icon
- order
- is_active

RLS:
- Public read
- Admin-only write

5. Enrollment
Fields:
- user_id
- course_id
- payment_id
- status
- enrolled_at
- activated_at
- completed_lessons[]
- last_lesson_id
- progress_percent
- completed_at

RLS:
- Use a top-level $or rule:
  [{data.user_id: {{user.id}}}, {user_condition: {role: admin}}]
- Users can create, read, and update their own enrollments
- Admin can access all enrollments

6. Order
Fields:
- user_id
- course_id
- course_title
- amount
- currency (default USD)
- payment_method (stripe)
- payment_status (pending/completed/failed/refunded)
- stripe_session_id
- stripe_payment_intent_id
- paid_at
- user_email
- user_name

RLS:
- Use a top-level $or rule:
  [{data.user_id: {{user.id}}}, {user_condition: {role: admin}}]
- Users can create and read their own orders
- Admin can access all orders

7. WebhookLog
Fields:
- provider
- eventId
- eventType
- orderId
- payload
- headersSnapshot
- status
- errorMessage
- amountReceived
- amountExpected
- created_at

RLS:
- Admin only

8. AppConfig
Fields:
- enable_stripe (default true)
- stripe_secret_key
- stripe_webhook_secret
- cloudflare_account_id
- cloudflare_api_token

RLS:
- Admin only

Create authentication pages using the Base44 boilerplate:
- Login with email/password and Google OAuth
- Register with email/password and Google OAuth
- Use the OTP flow for registration: register → verifyOtp → setToken → redirect
- ForgotPassword
- ResetPassword using ?token=
- AuthContext that checks public app settings and token
- ProtectedRoute for pages that require login

After this phase, test registration, login, Google OAuth, password reset, and admin role access.

Pay close attention to RLS. Courses should be publicly readable, but only admins should edit them. AppConfig should be admin-only because it stores sensitive values such as Stripe secret key, Stripe webhook secret, and Cloudflare API token.

This foundation is important because every later feature depends on it. If authentication, RLS, or entity design is wrong, Stripe Checkout and course enrollment will be much harder to debug later.


Phase 2 — Public course pages

The second phase creates the pages visitors will see before buying.

These pages matter because they help users understand the course, see the curriculum, evaluate the offer, and decide whether to purchase.

Enter this prompt:

Create public pages for DB Uni:

1. Home page:
- Hero section with gradient text
- Primary CTA: "Explore courses"
- 3 category pillars:
  - Global Affiliate
  - Global Ecommerce
  - AI Mastery
- Each category pillar should include an icon and short description
- Featured courses grid using courses where is_featured = true
- Course cards should show thumbnail, title, price, and rating
- Stats section showing number of courses, students, and learning hours
- Footer

2. Courses page:
- Show the list of published courses
- Support category filtering via query param ?category=
- Show courses in a responsive grid
- Sort courses by created_date
- Course card should show:
  - thumbnail
  - title
  - formatted price
  - level badge
  - enrollment_count

3. CourseDetail page:
- URL format: /courses/:slug
- Do not use database ID in the public course URL
- Load course by slug
- Hero section with thumbnail, title, description, price, and "Buy now" CTA
- The "Buy now" CTA should link to /checkout/:courseId
- Show preview video if available
- Show curriculum accordion: Section → Lesson
- Preview lessons can be viewed for free
- Check enrollment status:
  - If the student is already enrolled, hide the "Buy now" button
  - Show a "Go to course" button instead
- Show what_you_learn[]
- Show requirements[]
- Use utils/price.js to format prices
- USD example: "$20"

Use slug URLs such as:

/courses/global-affiliate-101

This is better than exposing database IDs in public course URLs.

A clean course detail page is important if you want to build an online course website that can convert visitors into paying students. It should clearly explain what the course teaches, who it is for, what students will learn, and why they should buy now.


Phase 3 — Admin area

Once the public pages are ready, you need an admin area to manage the platform.

The admin area is where you create courses, organize lessons, upload videos, manage users, view orders, and configure Stripe and Cloudflare Stream.

Enter this prompt:

Create an admin area for DB Uni. Only users with role = admin can access this area.

1. AdminDashboard (/admin):
- Sidebar navigation:
  - Overview
  - Courses
  - Categories
  - Students
  - Orders
  - Settings
- Stats cards:
  - total revenue
  - orders
  - students
  - courses
- 7-day revenue chart using Recharts AreaChart
- Recent orders list
- Recent courses list

2. AdminCourses (/admin/courses):
- Course table
- Search courses
- Publish/unpublish toggle
- Delete course
- "Create course" button linking to /admin/courses/new

3. CourseEditor (/admin/courses/new and /admin/courses/:id/edit):
- Course form fields:
  - title
  - auto-generate slug from title
  - description
  - full_description using rich text editor
  - thumbnail upload
  - price
  - original_price
  - currency
  - category
  - level
  - format (video/live_zoom)
  - zoom_link
  - what_you_learn[]
  - requirements[]
  - tags[]
- Curriculum tab:
  - CRUD sections
  - CRUD lessons
  - drag-and-drop ordering
- Upload video via Cloudflare Stream using tus-js-client
- Save Cloudflare video URL as "cf:{mediaId}"
- Upload lesson attachments such as PDF and docs

4. CategoryManager (/admin/categories):
- CRUD categories

5. UserManagement (/admin/users):
- User list
- Change user role

6. AdminOrders (/admin/orders):
- Show a table of Orders
- Filter by payment_status
- Show course title, amount, currency, user email, stripe_session_id, stripe_payment_intent_id, and paid_at
- Allow admin to inspect failed, pending, and completed orders

7. AdminSettings (/admin/settings):
- Toggle enable_stripe
- Stripe fields:
  - stripe_secret_key
  - stripe_webhook_secret
- Cloudflare fields:
  - cloudflare_account_id
  - cloudflare_api_token
- Save all settings to AppConfig

After this phase, go to AdminSettings and confirm that Stripe and Cloudflare fields are available.

Also test whether normal users are blocked from admin pages. This matters because your Stripe keys, Cloudflare token, course management tools, and order data should never be accessible to regular students.


Phase 4 — Learning experience and video player

The fourth phase creates the student learning area.

This is the part students use after purchasing a course. It should feel clean, focused, and easy to navigate.

Enter this prompt:

Create the learning experience for DB Uni:

1. MyCourses page:
- Route: /my-courses
- Show enrolled courses where Enrollment status = active
- Each course card should show:
  - thumbnail
  - title
  - progress percentage
  - completion badge if progress = 100%
- CTA:
  - Start learning
  - Continue learning
  - Review course
- CTA should link to /learn/:courseId

2. LearnPlayer page:
- Route: /learn/:courseId
- Full-screen layout
- No normal navbar
- Top bar should show:
  - course title
  - current lesson title
  - progress percentage
  - sidebar toggle
- Main video area should use ProtectedVideoPlayer
- Sidebar should show curriculum tree:
  - Section → Lesson
  - completed lesson icon
  - highlighted current lesson
- Track learning progress:
  - completed_lessons[]
  - progress_percent
  - last_lesson_id
- Auto-mark lesson as completed after the student watches 85% of the video
- Navigation:
  - previous lesson
  - next lesson
- Show "Course completed!" badge when progress reaches 100%
- Show downloadable lesson attachments

3. ProtectedVideoPlayer:
- Render video through <canvas> to reduce direct screenshot capture
- Roaming watermark:
  "{userName} · ID: {userId.slice(-8)}"
- Watermark animation should float every 18 seconds
- Use mix-blend-mode difference
- Block PrintScreen
- Block Ctrl+Shift+S
- Block Cmd+Shift+3/4/5
- When screenshot keys are detected, fill canvas black and show toast:
  "Protected content"
- Block right-click
- Blur video when tab is hidden or window loses focus
- Custom controls:
  - play/pause
  - seek
  - skip ±10s
  - mute
  - fullscreen
  - progress bar
- If video_url starts with "cf:", delegate playback to CloudflarePlayer

4. CloudflarePlayer:
- Accept mediaId in the format "cf:..."
- Use Cloudflare Stream player
- Pass watermark data:
  - userName
  - userId
- Trigger onComplete callback when video ends

No browser-based video protection can fully prevent piracy. However, canvas rendering, screenshot blocking behavior, and user-specific watermarking can reduce unauthorized sharing.

If you want to build an online course business with premium video lessons, Cloudflare Stream plus a protected player is a better approach than uploading large videos directly into your app.


Stripe setup before Phase 5

Before asking Base44 to build the Stripe payment system, set up your Stripe account first.

Step 1 — Create or log in to your Stripe account

Create or log in to your Stripe account and make sure your account is ready to accept payments.

For testing, use Stripe test mode first. Stripe gives you test API keys and test card numbers so you can test the flow without charging real money.

Step 2 — Get your Stripe Secret Key

In the Stripe Dashboard, go to Developers → API keys.

Copy your test secret key first:

sk_test_...

Later, when you are ready for production, replace it with your live secret key:

sk_live_...

You will paste this into DB Uni Admin Settings:

stripe_secret_key = your Stripe secret key

Do not expose the Stripe secret key in frontend code.

Step 3 — Create a Stripe webhook endpoint

In the Stripe Dashboard, go to Developers → Webhooks and create a new endpoint.

The endpoint should point to your Base44 Function:

https://<app-id>.functions.base44.app/stripeWebhook

Subscribe to this event:

checkout.session.completed

For one-time course purchases, checkout.session.completed is usually the key event to listen for because it tells your system that the customer completed Stripe Checkout.

After creating the webhook endpoint, Stripe gives you a signing secret:

whsec_...

Copy it and paste it into DB Uni Admin Settings:

stripe_webhook_secret = your Stripe webhook signing secret

Step 4 — Configure Stripe in Admin Settings

In DB Uni, go to:

/admin/settings

Enter:

enable_stripe = true
stripe_secret_key = your Stripe secret key
stripe_webhook_secret = your Stripe webhook signing secret

For the US/UK version, the default course currency should be:

USD

Step 5 — Test Stripe Checkout

Create a test course, for example:

Course price: $10
Currency: USD

Then log in as a test student and try buying the course.

You should verify:

  • The internal Order is created.
  • Stripe Checkout Session is created.
  • Student is redirected to Stripe Checkout.
  • Student completes payment using a Stripe test card.
  • Stripe redirects the student to the success page.
  • Stripe webhook checkout.session.completed is received.
  • Webhook signature is verified using the raw request body and webhook secret.
  • Order becomes completed.
  • Enrollment becomes active.
  • Student can access /learn/:courseId.

Common mistakes to avoid:

  • Using the wrong key mode: sk_test with live webhook, or sk_live with test webhook.
  • Not using the raw request body when verifying Stripe webhook signatures.
  • Activating course access from the success page instead of the webhook.
  • Not using idempotency or event deduplication for webhook events.
  • Not comparing the paid amount with the expected course amount.

Phase 5 — Stripe payment system

After Stripe setup is complete, enter the following prompt into Base44.

Create a Stripe Checkout payment system for DB Uni for the international market.

Additional entities:

1. Order
Fields:
- user_id
- course_id
- course_title
- amount
- currency (default USD)
- payment_method (stripe)
- payment_status (pending/completed/failed/refunded)
- stripe_session_id
- stripe_payment_intent_id
- paid_at
- user_email
- user_name

RLS:
- Use a top-level $or rule:
  [{data.user_id: {{user.id}}}, {user_condition: {role: admin}}]
- Users can create and read their own orders.
- Admin can access all orders.

2. WebhookLog
Fields:
- provider
- eventId
- eventType
- orderId
- payload
- headersSnapshot
- status
- errorMessage
- amountReceived
- amountExpected
- created_at

RLS:
- Admin only.

Backend functions:

1. createStripeSession

Requirements:
- Auth: me()
- Load AppConfig using asServiceRole:
  - stripe_secret_key
- Fetch the course from the database by course_id.
- Do not trust amount, currency, course_title, or user data directly from frontend params.
- Use the real amount, currency, and course_title stored in the Course entity.
- Create an internal Order first:
  - user_id = current user id
  - course_id = selected course id
  - course_title = course.title
  - amount = course.price
  - currency = course.currency || "USD"
  - payment_method = "stripe"
  - payment_status = "pending"
  - user_email = current user email
  - user_name = current user name

Amount handling:
- Stripe expects amount in the smallest currency unit.
- For USD, amount should be converted to cents:
  unit_amount = Math.round(course.price * 100)
- Example:
  $20 → 2000
  $99.99 → 9999
- If currency is VND, Stripe uses zero-decimal currency behavior:
  unit_amount = Math.round(course.price)
- For the US/UK version, default currency should be USD.

Create Stripe Checkout Session:
- POST https://api.stripe.com/v1/checkout/sessions
- Use Authorization: Bearer stripe_secret_key
- mode = "payment"
- payment_method_types[] = "card"
- client_reference_id = internal order id
- customer_email = current user email
- line_items[0][price_data][currency] = order.currency.toLowerCase()
- line_items[0][price_data][product_data][name] = course.title
- line_items[0][price_data][product_data][description] = course.description || ""
- line_items[0][price_data][unit_amount] = unit_amount
- line_items[0][quantity] = 1
- metadata[order_id] = internal order id
- metadata[course_id] = course.id
- metadata[user_id] = current user id
- success_url = `${origin}/payment/stripe-success?order_id=${order.id}&course_id=${course.id}&session_id={CHECKOUT_SESSION_ID}`
- cancel_url = `${origin}/checkout/${course.id}`

Success handling:
- Save stripe_session_id to the internal Order.
- Return:
  { checkoutUrl, sessionId, orderId }
- Frontend should redirect the user to checkoutUrl.

Error handling:
- If Stripe session creation fails, return a clear backend error.
- Do not show only a generic error on the frontend.

2. stripeWebhook

Purpose:
- Confirm completed Stripe Checkout payment and activate the course.

Requirements:
- Use allowUnauthenticated: true + asServiceRole.
- Read the raw request body.
- Read Stripe-Signature header.
- Verify the webhook signature using stripe_webhook_secret.
- Do not parse or mutate the body before signature verification.
- Deduplicate events by Stripe event.id and store the event in WebhookLog.
- Only process:
  checkout.session.completed

When event type is checkout.session.completed:
- Get the Checkout Session from event.data.object.
- Find the internal Order using:
  - session.client_reference_id
  - or session.metadata.order_id
- If the Order is already completed, return success immediately.
- Verify:
  - session.payment_status === "paid"
  - session.mode === "payment"
  - session.amount_total matches the expected course amount
  - session.currency matches the order currency
- Save:
  - stripe_session_id = session.id
  - stripe_payment_intent_id = session.payment_intent
  - payment_status = "completed"
  - paid_at = now
- Call handlePaymentSuccess.
- Update WebhookLog status to processed.

If verification fails:
- Update WebhookLog status to invalid or error.
- Do not activate enrollment.

3. handlePaymentSuccess

Requirements:
- Use allowUnauthenticated: true + asServiceRole.
- Create or update Enrollment:
  - user_id = order.user_id
  - course_id = order.course_id
  - payment_id = order.id
  - status = "active"
  - activated_at = now
- Increase Course.enrollment_count.
- If course.format === "live_zoom", send an email with the Zoom link.
- If course.format === "video", only activate enrollment.

4. getPublicPaymentConfig

Requirements:
- Use allowUnauthenticated: true + asServiceRole.
- Return only public payment flags:
  {
    enable_stripe
  }
- Do not return secrets.

Frontend:

1. Checkout page (/checkout/:courseId)

Requirements:
- Call getPublicPaymentConfig.
- Show Stripe as the default and primary payment method.
- Show course summary:
  - title
  - price
  - currency
  - thumbnail
- When user clicks "Pay with Stripe":
  - call createStripeSession with:
    {
      course_id,
      origin: window.location.origin
    }
- If res.data.checkoutUrl exists:
  window.location.href = checkoutUrl
- If there is an error:
  show toast.error with the exact backend error message.
- Do not show only a generic error.

2. StripeSuccess page (/payment/stripe-success)

Requirements:
- Full-screen status page.
- Read order_id, course_id, and session_id from URL params.
- Show a success message:
  "Payment received. We are activating your course."
- Do not directly activate enrollment from this page.
- Poll the Order status every 2-3 seconds for up to 30 seconds.
- If Order payment_status becomes "completed":
  - show success state
  - redirect to /learn/:courseId
- If not completed after timeout:
  - show message:
    "Your payment is being verified. Please check My Courses in a moment."
  - show button to /my-courses
  - show button to course detail page

3. AdminOrders page (/admin/orders)

Requirements:
- Show a table of Orders.
- Filter by payment_status.
- Show Stripe session ID.
- Show Stripe payment intent ID.
- Show course title, amount, currency, user email, and paid_at.
- Allow admin to inspect failed, pending, and completed orders.

After this phase, test the full Stripe test mode flow:

  • Internal Order is created.
  • Stripe Checkout Session is created.
  • Student is redirected to Stripe Checkout.
  • Student completes payment.
  • Stripe sends checkout.session.completed.
  • Webhook verifies signature.
  • Order becomes completed.
  • Enrollment becomes active.
  • Student can access the course.

Stripe configuration after build

After the app has been built, complete Stripe setup:

  1. Create or log in to your Stripe account.
  2. Go to Developers → API keys.
  3. Copy your test secret key.
  4. Go to Developers → Webhooks.
  5. Create a webhook endpoint pointing to:
https://<app-id>.functions.base44.app/stripeWebhook
  1. Subscribe to:
checkout.session.completed
  1. Copy the webhook signing secret:
whsec_...
  1. Go to DB Uni → Admin → Settings.
  2. Enter:
enable_stripe = true
stripe_secret_key = your Stripe secret key
stripe_webhook_secret = your webhook signing secret
  1. Create a test course.
  2. Buy the course with a Stripe test card.
  3. Confirm the course is automatically activated after webhook confirmation.

This is one of the most important parts of the entire build. If Stripe works but enrollment does not activate, check the webhook first.


Cloudflare Stream configuration for course videos

Cloudflare Stream is a video hosting and delivery service used to host course lesson videos. In this system, videos are uploaded via tus-js-client and played through the Cloudflare player.

Each uploaded video is stored in the lesson as a media ID with this format:

cf:{mediaId}

You must enable Cloudflare Stream and obtain an API Token before uploading course videos.

Step 1 — Enable Cloudflare Stream

Sign in or register at Cloudflare Dashboard.

Then:

  1. Go to Dashboard.
  2. Select your Cloudflare account.
  3. In the left sidebar, find Stream.
  4. If Stream is not visible, click More products and search for Stream.
  5. Click Get started or Enable Stream.

Cloudflare Stream is billed by usage, usually based on storage and video delivery. Start by testing with short videos before uploading your full course library.

Step 2 — Get your Account ID

In Cloudflare Dashboard:

  1. Select your account.
  2. Open the Overview page.
  3. Find Account ID.
  4. Copy the Account ID.

The Account ID is usually a long hex string, for example:

a1b2c3d4e5f6...

You will enter it into:

cloudflare_account_id

inside DB Uni Admin Settings.

Step 3 — Create an API Token

Go to the API Tokens page in your Cloudflare profile and create a new token.

Suggested configuration:

Token name:
DB Uni Stream Upload

Permissions:
Account → Stream → Edit

Account Resources:
Include → your Cloudflare account

TTL:
No expiration or a suitable expiration period

After creating the token, copy it immediately. Cloudflare only shows the token once.

You will enter it into:

cloudflare_api_token

inside Admin Settings.

Security note: This API Token can upload and manage videos in your Cloudflare Stream account. Do not expose it in frontend code, do not share it publicly, and do not commit it to GitHub.

Step 4 — Enter credentials into Admin Settings

In your DB Uni app, go to:

Admin → Settings → Cloudflare Stream

Enter:

cloudflare_account_id = your Cloudflare Account ID
cloudflare_api_token = your Cloudflare API Token

Click Save configuration.

To test:

  1. Go to Admin → Courses.
  2. Create or edit a course.
  3. Open the Curriculum tab.
  4. Upload a video to a lesson.
  5. Check that the upload progress bar runs.
  6. After upload, the lesson video_url should look like this:
cf:{mediaId}

Backend functions using Cloudflare Stream

Function Purpose
uploadCloudflareVideo Receives a video file from the frontend through tus-js-client, uploads it to Cloudflare Stream, and returns the mediaId
getCloudflareSignedUrl Creates a signed URL for secure video playback so only enrolled students can watch the video

How tus-js-client works

tus-js-client is a resumable upload library.

This means that when an admin uploads a large video file, the upload can continue from where it stopped if the connection is interrupted. The admin does not need to restart the upload from the beginning.

The DB Uni video upload flow works like this:

  1. Admin selects a video in CourseEditor.
  2. Frontend uses tus-js-client to upload the video to Cloudflare Stream.
  3. Cloudflare processes the video and returns a mediaId.
  4. The app saves the lesson video URL as:
cf:{mediaId}
  1. When a student opens a lesson, ProtectedVideoPlayer checks the video_url.
  2. If the value starts with cf:, the player delegates playback to CloudflarePlayer.
  3. CloudflarePlayer plays the video through Cloudflare Stream.
  4. Student watermark is displayed during playback.

This keeps large video files outside your app server, improves delivery stability, and makes course video management easier.


Testing checklist before launching your online course website

Before you launch, test the full student and admin flow.

Course browsing

  • Home page loads correctly.
  • Courses page shows published courses.
  • Course detail page loads by slug.
  • Preview lessons are visible.
  • Paid lessons are protected.

Authentication

  • User can register.
  • User can log in.
  • User can reset password.
  • User is redirected to login before checkout if not authenticated.
  • Admin can access admin pages.
  • Normal users cannot access admin pages.

Stripe Checkout

  • Internal Order is created.
  • Stripe Checkout Session is created.
  • Student is redirected to Stripe Checkout.
  • Student completes payment in test mode.
  • Stripe sends checkout.session.completed.
  • Webhook verifies signature.
  • Order becomes completed.
  • Enrollment becomes active.
  • Failed payment does not unlock the course.

Cloudflare Stream

  • Admin can upload video.
  • Upload progress appears.
  • Video is saved as cf:{mediaId}.
  • Enrolled student can watch the video.
  • Non-enrolled user cannot watch paid lessons.
  • Preview lessons are accessible if marked as preview.
  • Watermark appears during playback.

Go-live checklist

Before accepting real payments, make sure you have:

  • Published your app.
  • Connected your production domain.
  • Added Terms of Service.
  • Added Privacy Policy.
  • Added Refund Policy.
  • Switched Stripe from test mode to live mode.
  • Verified your Stripe account.
  • Tested a real small payment.
  • Confirmed webhook delivery.
  • Confirmed automatic enrollment.
  • Confirmed video playback.
  • Confirmed admin order tracking.
  • Confirmed students can access purchased courses.

For Base44, make sure your live Stripe keys and live webhook secret are saved in AdminSettings, and that the webhook endpoint points to the production function URL.


Recommended implementation order

Use this order:

  1. Build authentication and entities.
  2. Build public course pages.
  3. Build admin course management.
  4. Build learning player.
  5. Set up Stripe keys and webhook.
  6. Add Stripe Checkout.
  7. Test payment in test mode.
  8. Add Cloudflare Stream.
  9. Test video upload and playback.
  10. Protect paid lessons with Enrollment.
  11. Add legal pages.
  12. Publish.
  13. Switch Stripe to live.
  14. Run a real payment test.
  15. Launch.

Frequently Asked Questions

Can I build an online course website with Base44?

Yes. You can build an online course website with Base44 by using structured prompts to create entities, pages, admin tools, learning features, Stripe Checkout, and Cloudflare Stream video delivery.

Is Base44 good for building an online course platform?

Base44 can be useful if you want to build an online course platform quickly with AI prompts. It is especially helpful when you define the app phase by phase instead of asking for everything in one prompt.

Why use Stripe Checkout for an online course website?

Stripe Checkout gives you a hosted payment page, so you do not need to build the payment UI from scratch. Your app creates a Checkout Session, redirects the student to Stripe, receives webhook confirmation, and then unlocks the course.

Should I unlock course access from the Stripe success page?

No. The safer approach is to unlock course access after webhook confirmation. The success page can show a friendly message, but the app should activate Enrollment only after the Stripe webhook confirms payment.

Why use Cloudflare Stream for course videos?

Cloudflare Stream helps you upload, process, and deliver course videos without storing large video files directly inside your app. It also works well with tus-js-client, which supports resumable uploads for large video files.

Can this setup support live Zoom courses?

Yes. You can use the format field to separate video courses from live_zoom courses. For live Zoom courses, after Stripe payment is completed, the app can send an email with the Zoom link instead of unlocking video lessons.

Do I need legal pages before launching?

Yes. Before accepting real payments, you should add Terms of Service, Privacy Policy, and Refund Policy. These pages help build trust and are often required by payment platforms, ad platforms, and affiliate partners.


Conclusion

Base44 can help you build a complete online course platform quickly if you use the right phase-by-phase prompt structure.

For the US/UK market, Stripe Checkout is a practical payment option because it is simple to implement, supports card payments, and works well for one-time course purchases.

The key is to implement Stripe correctly: create an internal order, create a Stripe Checkout Session on the backend, redirect the student to Stripe, verify checkout.session.completed via webhook, update the order status, and activate course enrollment.

For course videos, Cloudflare Stream helps you avoid storing large files directly inside your app while providing stable video delivery. Combined with tus-js-client, admins can upload large videos more reliably, and students can watch them through the course player.

By following the phases in this guide, you can build an online course website with public pages, admin management, student learning area, protected video player, Stripe Checkout, Cloudflare Stream video hosting, webhook confirmation, and automatic course access after payment.

If your goal is to build an online course business for the US/UK market, this Base44 workflow gives you a strong technical foundation to launch faster and improve later.