Cách tạo Website Affiliate bằng Base44 hoặc Lovable

How to Build an Online Course Website with Base44 for the US/UK Market: PayPal 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 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 platform in one request, you should separate the build into clear phases: foundation, public pages, admin, learning experience, video player, PayPal payment, optional Stripe support, and Cloudflare Stream configuration.

In this guide, I will show you how to create an online course website with Base44 for international users, especially the US/UK market, using PayPal 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, Ecommerce, and applied AI. You can replace this brand with your own academy name later.


1. 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, PayPal is a practical and familiar payment option. A proper PayPal flow should create an order, redirect the user to PayPal approval, capture the payment after the user returns, verify the payment amount, and activate the course.

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.


2. Why build the app by phase?

A course platform is too complex to build reliably with one single prompt. If you ask Base44 to build everything at once, it may miss key logic such as role-based access, payment idempotency, webhook backup, enrollment activation, amount verification, or secure video playback.

The better approach is to build in phases:

  • Phase 1: Foundation and authentication.
  • Phase 2: Public course pages.
  • Phase 3: Admin area.
  • Phase 4: Learning experience and video player.
  • Phase 5: PayPal payment.
  • Phase 6: Optional Stripe support.
  • After build: PayPal and Cloudflare Stream configuration.

This structure makes it easier to test the app after each step and fix issues before moving forward.


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. AppConfig
Fields:
- enable_stripe (default true)
- enable_paypal (default true)
- paypal_mode (sandbox/live)
- paypal_client_id
- paypal_client_secret
- paypal_webhook_id
- 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 PayPal client secret, Stripe secret key, and Cloudflare API token.


Phase 2 — Public course pages

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

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.


Phase 3 — Admin area

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

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. AdminSettings (/admin/settings):
- Toggle enable_stripe
- Toggle enable_paypal
- PayPal fields:
  - paypal_mode
  - paypal_client_id
  - paypal_client_secret
  - paypal_webhook_id
- 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 PayPal, Stripe, and Cloudflare fields are available.


Phase 4 — Learning experience and video player

The fourth phase creates the student learning area.

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.


PayPal Developer setup before Phase 5

Before asking Base44 to build the PayPal payment system, set up your PayPal Developer account.

Step 1 — Create or log in to your PayPal Developer account

Use PayPal Sandbox for testing before going live.

You will need:

PayPal Business sandbox account
PayPal Personal sandbox account
REST API app
Client ID
Client Secret
Webhook ID

The Business sandbox account represents your course business.

The Personal sandbox account represents the test student.

Step 2 — Create a REST API app

Inside the PayPal Developer Dashboard, create a REST API app.

Recommended app name:

DB Uni Course Platform

After creating the app, PayPal gives you:

Client ID
Client Secret

Save these values for Admin Settings:

paypal_mode = sandbox
paypal_client_id = your PayPal client ID
paypal_client_secret = your PayPal client secret

Only switch to live mode after sandbox testing works end to end.

Step 3 — Understand the PayPal payment flow

The PayPal payment flow should work like this:

  1. Student logs in.
  2. Student opens a course detail page.
  3. Student clicks “Buy now”.
  4. App creates an internal Order.
  5. App calls PayPal to create a PayPal order.
  6. PayPal returns an approval URL.
  7. Student is redirected to PayPal.
  8. Student approves payment.
  9. PayPal redirects the student back to your website.
  10. App calls capturePaypalOrder.
  11. App verifies that capture status is COMPLETED.
  12. App verifies that the paid amount matches the course price.
  13. App updates the internal Order to completed.
  14. App calls handlePaymentSuccess.
  15. Student Enrollment becomes active.
  16. Student can access the course.

Course access should only be activated after successful payment capture.

Step 4 — Create a PayPal webhook

In the PayPal Developer Dashboard, add a webhook URL pointing to your Base44 Function:

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

Subscribe to:

PAYMENT.CAPTURE.COMPLETED

After creating the webhook, copy the Webhook ID and store it in:

paypal_webhook_id

Step 5 — Configure PayPal in Admin Settings

In DB Uni, go to:

/admin/settings

Enter:

enable_paypal = true
enable_stripe = false
paypal_mode = sandbox
paypal_client_id = your client ID
paypal_client_secret = your client secret
paypal_webhook_id = your webhook ID

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

USD

PayPal Configuration

After the app has been built, complete PayPal setup:

  1. Create a PayPal Developer account.
  2. Create a REST API app.
  3. Copy Client ID and Client Secret.
  4. Create a webhook and subscribe to PAYMENT.CAPTURE.COMPLETED.
  5. Copy the Webhook ID.
  6. Go to Admin → Settings.
  7. Enter PayPal mode, client ID, client secret, and webhook ID.
  8. Get the webhook URL from Base44 Function paypalWebhook.
  9. Paste the webhook URL into PayPal Dashboard.
  10. Test checkout with PayPal Sandbox.

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.


Phase 5 — PayPal payment system for the US/UK market

After PayPal Developer setup is complete, enter the following prompt into Base44:

Create a PayPal 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 (paypal/stripe)
- payment_status (pending/completed/failed/refunded)
- paypal_order_id
- stripe_session_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
- paymentId
- paymentCode
- payload
- headersSnapshot
- status
- errorMessage
- amountReceived
- amountExpected

RLS:
- Admin only.

Backend functions:

1. createPaypalOrder

Requirements:
- Auth: me()
- Load AppConfig using asServiceRole:
  paypal_client_id
  paypal_client_secret
  paypal_mode
- Fetch the internal Order from the database by order_id.
- Do not trust amount, currency, or course_title directly from frontend params.
- Use the real amount, currency, and course_title stored in the internal Order.
- Check that order.payment_status is not already completed.
- The function must be idempotent.

Currency handling:
- PayPal does not support VND.
- If order.currency === "VND", convert the amount to USD using a fixed exchange rate of 25,000 VND = 1 USD.
- payAmount = order.amount / 25000
- payCurrency = "USD"
- Round payAmount to 2 decimal places.
- If order.currency is already "USD", use USD directly.

PayPal base URL:
- If paypal_mode === "live", use:
  https://api-m.paypal.com
- Otherwise use:
  https://api-m.sandbox.paypal.com

Step 1 — Get access token:
- POST {baseUrl}/v1/oauth2/token
- Use Authorization Basic base64(client_id:client_secret)
- Body: grant_type=client_credentials

Step 2 — Create PayPal order:
- POST {baseUrl}/v2/checkout/orders
- Body:
  {
    intent: "CAPTURE",
    purchase_units: [
      {
        reference_id: order_id,
        description: course_title.slice(0, 127),
        amount: {
          currency_code: payCurrency,
          value: payAmount.toFixed(2)
        }
      }
    ],
    application_context: {
      return_url,
      cancel_url,
      brand_name: "DB Uni",
      user_action: "PAY_NOW"
    }
  }

Error handling:
- If PayPal order creation fails, parse orderData.details if available.
- Map each detail to:
  `${field}: ${issue} ${description}`
- Join all details with "; ".
- Include PayPal debug_id if available.
- Return a clear error message to the frontend.
- Do not return only a generic error.

Success handling:
- Find the approval link where rel === "approve".
- Save paypal_order_id to the internal Order.
- Return:
  { approvalUrl, paypalOrderId }

2. capturePaypalOrder

Requirements:
- Auth: me()
- Fetch the internal Order by order_id.
- If payment_status === "completed", return success immediately.
- Load PayPal config from AppConfig using asServiceRole.
- Get PayPal access token using the same logic as createPaypalOrder.
- POST {baseUrl}/v2/checkout/orders/{paypal_order_id}/capture

Verification:
- Verify that:
  captureData.purchase_units[0].payments.captures[0].status === "COMPLETED"
- Verify the captured amount:
  - If order.currency === "VND" and capturedCurrency === "USD":
    expectedAmount = order.amount / 25000
    Allow difference <= 0.01
  - If order.currency === "USD":
    expectedAmount = order.amount
    Allow difference <= 0.01
- If amount does not match, do not activate enrollment.
- Return a clear error and mark the order for review if needed.

Success handling:
- Update Order:
  payment_status = "completed"
  paid_at = now
- Call handlePaymentSuccess using base44.functions.invoke.
- Return success.

3. paypalWebhook

Purpose:
- Backup confirmation for PayPal payment events.

Requirements:
- Use allowUnauthenticated: true + asServiceRole.
- Read the raw webhook body.
- Deduplicate webhook events by PayPal event ID and store the event in WebhookLog.
- Only process:
  PAYMENT.CAPTURE.COMPLETED
- Verify PayPal webhook signature by calling:
  POST {baseUrl}/v1/notifications/verify-webhook-signature
- Use:
  paypal-transmission-id
  paypal-transmission-time
  paypal-cert-url
  paypal-auth-algo
  paypal-transmission-sig
  paypal_webhook_id from AppConfig
  raw webhook body

If verification succeeds:
- Find the internal Order using the PayPal reference_id.
- If the Order is not already completed:
  - Update payment_status to "completed"
  - Set paid_at
  - Call handlePaymentSuccess
- Update WebhookLog status to processed.

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

4. handlePaymentSuccess

Requirements:
- Use allowUnauthenticated: true + asServiceRole.
- Create or update Enrollment:
  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.

5. getPublicPaymentConfig

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

Frontend:

1. Checkout page (/checkout/:courseId)

Requirements:
- Call getPublicPaymentConfig.
- Show PayPal as the default payment method.
- Show Stripe only if enabled.
- Create an internal Order with:
  payment_method = "paypal"
  currency = course.currency
  amount = course.price
- Call createPaypalOrder with:
  {
    order_id,
    return_url: `${origin}/payment/paypal-success?order_id=${order.id}&course_id=${courseId}`,
    cancel_url: `${origin}/checkout/${courseId}`
  }
- If res.data.approvalUrl exists:
  window.location.href = approvalUrl
- If there is an error:
  show toast.error with the exact backend error message.
- Do not show only a generic error.

2. PaypalSuccess page (/payment/paypal-success)

Requirements:
- Full-screen status page.
- Read order_id and course_id from URL params.
- Call capturePaypalOrder with:
  { order_id }
- Show loading while capturing payment.
- If successful:
  show success state
  redirect to /learn/:courseId
- If error:
  show clear error message
  show buttons:
  - Try again
  - Back to course

3. AdminOrders page (/admin/orders)

Requirements:
- Show a table of Orders.
- Filter by payment_status.
- Show PayPal order ID.
- Show course title, amount, currency, user email, and paid_at.
- Allow admin to inspect failed or pending orders.

After this phase, test the full PayPal sandbox flow:

  • Internal Order is created.
  • PayPal approval URL is returned.
  • Student approves payment.
  • Student returns to /payment/paypal-success.
  • capturePaypalOrder verifies the payment.
  • Order becomes completed.
  • Enrollment becomes active.
  • Student can access the course.

Phase 6 — Optional Stripe support

If you want to support card payments, enter this prompt:

Create Stripe payment for DB Uni. This is optional and should be enabled or disabled via AdminSettings.

Backend:

1. createStripeSession
Requirements:
- Auth: me()
- Create an internal Order with:
  payment_method = "stripe"
  payment_status = "pending"
- Get stripe_secret_key from AppConfig
- Call Stripe Checkout API
- Return the Stripe Checkout session URL
- Use window.location.origin for success_url and cancel_url

2. stripeWebhook
Requirements:
- Use allowUnauthenticated: true + asServiceRole
- Verify Stripe webhook signature
- Use SubtleCrypto async constructEventAsync
- Update the internal Order:
  payment_status = "completed"
  paid_at = now
- Call handlePaymentSuccess

Frontend:
- Show Stripe button in Checkout only if enable_stripe is true
- When the user selects Stripe:
  - create internal Order
  - call createStripeSession
  - redirect to Stripe Checkout URL

Recommended implementation checklist

  • Run Phase 1: entities and authentication.
  • Test login, register, reset password, and admin role.
  • Run Phase 2: public course pages.
  • Add sample course data.
  • Run Phase 3: admin area.
  • Test course creation, sections, lessons, and thumbnail upload.
  • Run Phase 4: learning experience.
  • Set up PayPal Developer app and webhook.
  • Configure PayPal in Admin Settings.
  • Configure Cloudflare Stream in Admin Settings.
  • Test video upload.
  • Run Phase 5: PayPal payment.
  • Test PayPal Sandbox approval and capture.
  • Confirm that enrollment activates automatically.
  • Run Phase 6 if you want Stripe.
  • Switch PayPal from sandbox to live only after everything works.

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, PayPal is a practical payment option because many users already trust it. The key is to implement PayPal correctly: create the order, redirect the student to PayPal, capture the order after approval, verify the amount, 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 platform with public pages, admin management, student learning area, protected video player, PayPal checkout, Cloudflare Stream video hosting, webhook backup, and automatic course access after payment.