diff --git a/frontend/public/images/pfp/monkey.png b/frontend/public/images/pfp/monkey.png new file mode 100644 index 0000000..2be60e8 Binary files /dev/null and b/frontend/public/images/pfp/monkey.png differ diff --git a/frontend/src/components/Checkout/CheckoutForm.tsx b/frontend/src/components/Checkout/CheckoutForm.tsx index c304bbd..1b79a52 100644 --- a/frontend/src/components/Checkout/CheckoutForm.tsx +++ b/frontend/src/components/Checkout/CheckoutForm.tsx @@ -21,9 +21,11 @@ export const Return: React.FC = () => { const sessionId = urlParams.get("session_id"); if (sessionId) { + console.log("1") fetch(`/api/session-status?session_id=${sessionId}`) .then((res) => res.json()) .then((data) => { + console.log("Response Data:", data); setStatus(data.status); setCustomerEmail(data.customer_email); }); @@ -54,9 +56,9 @@ interface CheckoutFormProps { onClose: () => void; } -const CheckoutForm: React.FC = ({ onClose }) => { +const CheckoutForm: React.FC = ({ onClose, streamerID }) => { const fetchClientSecret = () => { - return fetch(`/api/create-checkout-session`, { + return fetch(`/api/create-checkout-session?streamer_id=${streamerID}`, { method: "POST", }) .then((res) => res.json()) diff --git a/frontend/src/components/Video/ChatPanel.tsx b/frontend/src/components/Video/ChatPanel.tsx index 23a77ab..8874a40 100644 --- a/frontend/src/components/Video/ChatPanel.tsx +++ b/frontend/src/components/Video/ChatPanel.tsx @@ -117,32 +117,48 @@ const ChatPanel: React.FC = ({ className="max-w-[30vw] h-full flex flex-col rounded-lg p-4" style={{ gridArea: "1 / 2 / 3 / 3" }} > -

Stream Chat

+

Stream Chat

{messages.map((msg, index) => (
- - {" "} - {msg.chatter_username}:{" "} - - {msg.message} - + {/* User avatar with image */} +
+ User Avatar +
+ +
+
+ {/* Username */} + + {msg.chatter_username} + +
+ {/* Message content */} +
{msg.message}
+
+ + {/* Time sent */} +
{new Date(msg.time_sent).toLocaleTimeString()} - +
))}
diff --git a/frontend/src/pages/VideoPage.tsx b/frontend/src/pages/VideoPage.tsx index 8388d04..f82ded1 100644 --- a/frontend/src/pages/VideoPage.tsx +++ b/frontend/src/pages/VideoPage.tsx @@ -213,7 +213,7 @@ const VideoPage: React.FC = ({ streamerId }) => { - {showCheckout && setShowCheckout(false)} />} + {showCheckout && setShowCheckout(false)} streamerID={streamerId}/>} {showReturn && } {showAuthModal && setShowAuthModal(false)} />} diff --git a/web_server/blueprints/stripe.py b/web_server/blueprints/stripe.py index 4ea3ef7..2dd1ec8 100644 --- a/web_server/blueprints/stripe.py +++ b/web_server/blueprints/stripe.py @@ -1,32 +1,41 @@ -from flask import Blueprint, request, jsonify +from flask import Blueprint, request, jsonify, session as s +from blueprints.middleware import login_required +from utils.user_utils import subscribe import os, stripe stripe_bp = Blueprint("stripe", __name__) stripe.api_key = os.getenv("STRIPE_SECRET_KEY") +endpoint_secret = "" +subscription = os.getenv("GANDER_SUBSCRIPTION") + +@login_required @stripe_bp.route('/create-checkout-session', methods=['POST']) def create_checkout_session(): """ Creates the stripe checkout session """ - print("Creating checkout session") + print("Creating checkout session", flush=True) try: + user_id = s.get("user_id") + streamer_id = request.args.get("streamer_id") session = stripe.checkout.Session.create( ui_mode = 'embedded', payment_method_types=['card'], line_items=[ { - 'price': 'price_1QikNCGk6yuk3uA86mZf3dmM', #Subscription ID + 'price': 'price_1QikNCGk6yuk3uA86mZf3dmM', 'quantity': 1, }, ], mode='subscription', - redirect_on_completion = 'never' + redirect_on_completion = 'never', + client_reference_id = f"{user_id}-{streamer_id}" ) except Exception as e: - print(e) - return str(e) + print(e, flush=True) + return str(e), 500 return jsonify(clientSecret=session.client_secret) @@ -38,3 +47,32 @@ def session_status(): session = stripe.checkout.Session.retrieve(request.args.get('session_id')) return jsonify(status=session.status, customer_email=session.customer_details.email) + +@stripe_bp.route('/stripe/webhook', methods=['POST']) +def stripe_webhook(): + """ + Webhook for handling stripe payments + """ + event = None + payload = request.data + sig_header = request.headers['STRIPE_SIGNATURE'] + + try: + event = stripe.Webhook.construct_event( + payload, sig_header, endpoint_secret + ) + except ValueError as e: + raise e + except stripe.error.SignatureVerificationError as e: + raise e + + if event['type'] == "checkout.session.completed": + session = event['data']['object'] + product_id = stripe.checkout.Session.list_line_items(session['id'])['data'][0]['price']['product'] + if product_id == subscription: + client_reference_id = session.get("client_reference_id") + user_id, streamer_id = client_reference_id.split("-") + print(f"user_id: {user_id} is subscribing to streamer_id: {streamer_id}", flush=True) + subscribe(user_id, streamer_id) + + return "Success", 200 \ No newline at end of file diff --git a/web_server/blueprints/user.py b/web_server/blueprints/user.py index 672e4f2..2f6c4ca 100644 --- a/web_server/blueprints/user.py +++ b/web_server/blueprints/user.py @@ -22,17 +22,6 @@ def user_data(username: str): return jsonify(data) ## Subscription Routes -@login_required -@user_bp.route('/user/subscribe/') -def user_subscribe(streamer_id): - """ - Given a streamer subscribes as user - """ - #TODO: Keep this route secure so only webhooks from Stripe payment can trigger it - user_id = session.get("user_id") - subscribe(user_id, streamer_id) - return jsonify({"status": True}) - @login_required @user_bp.route('/user/subscription/') def user_subscribed(subscribed_id: int): diff --git a/web_server/utils/user_utils.py b/web_server/utils/user_utils.py index 91de868..420e36c 100644 --- a/web_server/utils/user_utils.py +++ b/web_server/utils/user_utils.py @@ -52,21 +52,18 @@ def is_user_partner(user_id: int) -> bool: return bool(data) def is_subscribed(user_id: int, subscribed_to_id: int) -> bool: - """ - Returns True if user is subscribed to a streamer, else False - """ + """Returns True if user is subscribed to a streamer, else False""" with Database() as db: - result = db.fetchone(""" - SELECT * + return bool(db.fetchone( + """ + SELECT 1 FROM subscribes WHERE user_id = ? - AND subscribed_id = ? + AND subscribed_id = ? AND expires > ?; - """, (user_id, subscribed_to_id, datetime.now())) - print(result) - if result: - return True - return False + """, + (user_id, subscribed_to_id, datetime.now()) + )) def is_following(user_id: int, followed_id: int) -> bool: """