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:
- Student logs in.
- Student opens a course detail page.
- Student clicks “Buy now”.
- App creates an internal Order.
- App calls PayPal to create a PayPal order.
- PayPal returns an approval URL.
- Student is redirected to PayPal.
- Student approves payment.
- PayPal redirects the student back to your website.
- App calls
capturePaypalOrder. - App verifies that capture status is
COMPLETED. - App verifies that the paid amount matches the course price.
- App updates the internal Order to
completed. - App calls
handlePaymentSuccess. - Student Enrollment becomes
active. - 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:
- Create a PayPal Developer account.
- Create a REST API app.
- Copy Client ID and Client Secret.
- Create a webhook and subscribe to
PAYMENT.CAPTURE.COMPLETED. - Copy the Webhook ID.
- Go to Admin → Settings.
- Enter PayPal mode, client ID, client secret, and webhook ID.
- Get the webhook URL from Base44 Function
paypalWebhook. - Paste the webhook URL into PayPal Dashboard.
- 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:
- Go to Dashboard.
- Select your Cloudflare account.
- In the left sidebar, find Stream.
- If Stream is not visible, click More products and search for Stream.
- 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:
- Select your account.
- Open the Overview page.
- Find Account ID.
- 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:
- Go to Admin → Courses.
- Create or edit a course.
- Open the Curriculum tab.
- Upload a video to a lesson.
- Check that the upload progress bar runs.
- After upload, the lesson
video_urlshould 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:
- Admin selects a video in CourseEditor.
- Frontend uses
tus-js-clientto upload the video to Cloudflare Stream. - Cloudflare processes the video and returns a
mediaId. - The app saves the lesson video URL as:
cf:{mediaId}
- When a student opens a lesson,
ProtectedVideoPlayerchecks thevideo_url. - If the value starts with
cf:, the player delegates playback toCloudflarePlayer. CloudflarePlayerplays the video through Cloudflare Stream.- 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. capturePaypalOrderverifies 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.
