Overview
EducaStream uses Stripe to process course payments securely. The integration handles:
- Shopping cart checkout
- Payment session creation
- Redirect to Stripe-hosted checkout
- Payment confirmation and fulfillment
Stripe handles all sensitive payment data, so you never need to process credit card information directly.
Stripe Dependencies
EducaStream uses the following Stripe packages from package.json:14-15,27:
"dependencies": {
"@stripe/react-stripe-js": "^2.3.1",
"@stripe/stripe-js": "^2.1.11",
"stripe": "^14.3.0"
}
@stripe/stripe-js
Official Stripe.js library for client-side
@stripe/react-stripe-js
React components for Stripe Elements
stripe
Server-side Stripe SDK (backend)
Setup Stripe Account
Get API Keys
- Navigate to Developers → API Keys
- Copy your Publishable key (starts with
pk_)
- Copy your Secret key (starts with
sk_)
Keep your Secret key secure! Never commit it to version control or expose it in client-side code.
Configure Test Mode
For development, use Stripe’s test mode:
- Toggle Test mode in the top right of the dashboard
- Use test API keys (pk_test_… and sk_test_…)
- Use test card numbers for testing
Environment Variables
Add your Stripe keys to environment variables:
# Stripe Publishable Key (safe for client-side)
VITE_STRIPE_PUBLIC_KEY=pk_test_51XXXXXXXXXXXXXXXXXXXXX
# Stripe Secret Key (server-side only!)
STRIPE_SECRET_KEY=sk_test_51XXXXXXXXXXXXXXXXXXXXX
The secret key should ONLY be used on your backend, never in frontend code!
Payment Flow
The payment implementation from src/Components/PayButton/Paybutton.jsx:1-35:
import axios from "axios";
import style from "./Paybutton.module.css";
const PayButton = ({ text, disabled }) => {
const user = JSON.parse(localStorage.getItem("userOnSession"));
const cartItems = JSON.parse(localStorage.getItem("cart"));
const handleCheckout = () => {
axios
.post(`/payment/create-checkout-session`, {
cartItems,
userId: user.id,
})
.then((response) => {
if (response.data.url) {
localStorage.setItem("payment", JSON.stringify(response.data));
window.location.href = response.data.url;
}
})
.catch((err) => console.log(err.message));
};
return (
<>
<button
className={style.finalizePurchase}
onClick={() => handleCheckout()}
disabled={disabled}
>
{text}
</button>
</>
);
};
export default PayButton;
Payment Process
User Initiates Checkout
When user clicks the PayButton:
- Cart items retrieved from localStorage
- User ID retrieved from session
- Request sent to backend
Backend Creates Checkout Session
Backend endpoint /payment/create-checkout-session:
- Validates cart items and user
- Creates Stripe Checkout Session
- Returns session URL
Redirect to Stripe
User is redirected to Stripe-hosted checkout:
- Secure payment form
- Support for cards, Apple Pay, Google Pay
- PCI-compliant payment processing
Payment Completion
After successful payment:
- User redirected to
/payment/checkout/sucess
- Payment data stored in localStorage
- Order fulfillment triggered
Backend Implementation
Your backend needs to implement the checkout session creation:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
app.post('/payment/create-checkout-session', async (req, res) => {
const { cartItems, userId } = req.body;
try {
// Create line items from cart
const lineItems = cartItems.map(item => ({
price_data: {
currency: 'usd',
product_data: {
name: item.name,
description: item.description,
images: [item.image],
},
unit_amount: item.price * 100, // Convert to cents
},
quantity: 1,
}));
// Create Stripe Checkout Session
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: lineItems,
mode: 'payment',
success_url: `${process.env.CLIENT_URL}/payment/checkout/sucess?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.CLIENT_URL}/cart/${userId}`,
metadata: {
userId: userId,
},
});
res.json({ url: session.url });
} catch (error) {
console.error('Stripe error:', error);
res.status(500).json({ error: error.message });
}
});
Success Page Implementation
The success route is defined in App.jsx:120-122:
<Route
path="/payment/checkout/sucess"
element={<CheckOut updateContextUser={updateContextUser} />}
/>
Implement the CheckOut component to handle successful payments:
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
export const CheckOut = ({ updateContextUser }) => {
const navigate = useNavigate();
useEffect(() => {
const processPayment = async () => {
// Get session ID from URL
const params = new URLSearchParams(window.location.search);
const sessionId = params.get('session_id');
if (sessionId) {
// Verify payment with backend
// Update user's purchased courses
// Clear cart
localStorage.removeItem('cart');
// Redirect to student dashboard
const user = JSON.parse(localStorage.getItem('userOnSession'));
navigate(`/student/${user.id}`);
}
};
processPayment();
}, []);
return (
<div>
<h1>Payment Successful!</h1>
<p>Thank you for your purchase. Redirecting...</p>
</div>
);
};
Webhooks for Payment Verification
For production, implement Stripe webhooks to verify payments:
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'checkout.session.completed':
const session = event.data.object;
// Fulfill the purchase
fulfillOrder(session);
break;
case 'payment_intent.payment_failed':
const paymentIntent = event.data.object;
// Handle failed payment
console.log('Payment failed:', paymentIntent.id);
break;
default:
console.log(`Unhandled event type ${event.type}`);
}
res.json({received: true});
});
Testing Payments
Test Card Numbers
Use these test cards in Stripe test mode:
Successful Payment
4242 4242 4242 4242Any future expiry, any CVC
Payment Declined
4000 0000 0000 0002Card declined
Insufficient Funds
4000 0000 0000 9995Insufficient funds
3D Secure
4000 0025 0000 3155Requires authentication
Testing Workflow
Add Items to Cart
Browse courses and add them to cart using the CartContext
Proceed to Checkout
Click the PayButton component to initiate checkout
Complete Payment
Use a test card number on Stripe’s checkout page
Verify Success
Confirm redirect to success page and cart is cleared
Cart Integration
The cart is managed by CartContext.jsx:1-117 with localStorage persistence:
const initialState = {
cart: JSON.parse(localStorage.getItem("cart")) || [],
};
const cartReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_CART:
// Prevent duplicate courses
const isCourseInCart = state.cart.find(
(product) => product.id === action.payload.id
);
if (isCourseInCart) {
// Show warning toast
return state;
}
return {
...state,
cart: [...state.cart, action.payload],
};
case REMOVE_FROM_CART:
const updatedCart = state.cart.filter(
(product) => product.id !== action.payload
);
return { ...state, cart: updatedCart };
case CLEAR:
return { ...state, cart: action.payload };
default:
return state;
}
};
Security Best Practices
Never expose Stripe Secret Key in frontend code
Always validate cart items on the backend
Verify payment amounts server-side
Use webhooks for payment confirmation
Implement idempotency keys for duplicate requests
Enable Stripe Radar for fraud detection
Common Issues
CORS errors when calling Stripe
Ensure your backend has proper CORS configuration:app.use(cors({
origin: process.env.CLIENT_URL,
credentials: true
}));
- Verify you’re using the correct key (test vs live)
- Check the key is properly set in environment variables
- Ensure no extra spaces in the key
Payment succeeds but order not fulfilled
- Implement webhook handlers
- Check webhook signing secret is correct
- Verify webhook endpoint is publicly accessible
Redirect not working after payment
- Check success_url and cancel_url are correct
- Verify URLs are properly encoded
- Ensure frontend routes exist
Production Checklist
Switch to Live Mode
- Use live API keys (pk_live_… and sk_live_…)
- Update environment variables
- Complete Stripe account activation
Configure Webhooks
- Set up webhook endpoint
- Add webhook secret to environment
- Test webhook delivery
Enable Security Features
- Turn on Stripe Radar
- Configure 3D Secure
- Set up fraud detection rules
Test End-to-End
- Complete test purchase in production
- Verify order fulfillment
- Check email notifications
Monitoring Payments
In Stripe Dashboard:
- Payments - View all transactions
- Customers - Manage customer data
- Products - Configure course products
- Reports - Analyze revenue and trends
Next Steps
Architecture
Understand the full application architecture
Firebase Config
Configure Firebase Storage
Additional Resources
Stripe Checkout Docs
Official Stripe Checkout documentation
Testing Stripe
Stripe testing guide with test card numbers