From 6c660c873f7a8618f5556a5aef17e8abc2219275 Mon Sep 17 00:00:00 2001 From: "CHIEFSOFT\\ameye" Date: Sat, 30 Aug 2025 05:11:59 -0400 Subject: [PATCH] SubscriptionsService added --- app/api/routes/routes.py | 4 +- app/api/services/__init__.py | 2 +- app/api/services/subscriptions.py | 287 ++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 app/api/services/subscriptions.py diff --git a/app/api/routes/routes.py b/app/api/routes/routes.py index e4aecd8..ca0eb20 100644 --- a/app/api/routes/routes.py +++ b/app/api/routes/routes.py @@ -10,7 +10,7 @@ from app.api.services import ( OfficeAuthService, OfficeDashboardService, WebContentsService, - SubscriptionService, + SubscriptionsService, CommonDataService, OfficeCustomerService ) @@ -241,7 +241,7 @@ def stripe_payments_webhook_post(): logger.info(f"Route Stripe Webhook POST ENTRY==>>>>") data = request.get_json() logger.info(f"Route Stripe Webhook POST ==>>>> {data}") - SubscriptionService.subscription_webhook_start(data) + SubscriptionsService.subscription_webhook_start(data) return [] except Exception as e: # A general exception handler for any other type of exception diff --git a/app/api/services/__init__.py b/app/api/services/__init__.py index 7351637..0af21d6 100644 --- a/app/api/services/__init__.py +++ b/app/api/services/__init__.py @@ -7,7 +7,7 @@ from app.api.services.myproduct import MyProductsService from app.api.services.contacts import ContactService from app.api.services.office_auth import OfficeAuthService from app.api.services.web_contents import WebContentsService -from app.api.services.subscription import SubscriptionService +from app.api.services.subscriptions import SubscriptionsService from app.api.services.common_data import CommonDataService diff --git a/app/api/services/subscriptions.py b/app/api/services/subscriptions.py new file mode 100644 index 0000000..0d43146 --- /dev/null +++ b/app/api/services/subscriptions.py @@ -0,0 +1,287 @@ +from flask import jsonify +from app.utils.logger import logger +from app.api.services.base_service import BaseService +from sqlalchemy import func, desc +from app.api.helpers.response_helper import ResponseHelper +from app.api.schemas.user import UserSchema +from app.api.schemas.subscription_session import SubscriptionSession +from marshmallow import ValidationError +from app.models import Members, MembersProducts, SubscriptionOptions, SubscriptionOptionsItems, PaymentsSession +from app.extensions import db +from app.api.integrations import StripeIntegration +import json + + + +class SubscriptionsService(BaseService): + # def get_all_subscriptions(cls, product_id=None, member_id=None, page=1, limit=20): + @staticmethod + def get_subscription_data(filters=None): + try: + if filters is None: + filters = {} + + # Extract filters + product_id = filters.get('product_id') + member_id = filters.get('member_id') + + # Extract pagination parameters + page = int(filters.get('page', 1)) + limit = int(filters.get('limit', 20)) + + # Ensure page and limit are valid + if page < 1: + page = 1 + if limit < 1 or limit > 100: + limit = 20 + + membersSubList, total_count = MembersProducts.get_all_subscriptions(product_id, member_id, page, limit) + # Convert loans to dictionary format + member_sub_data = [] + for subs in membersSubList: + member_sub_data.append({ + 'id': subs.id, + 'member_id': subs.member_id, + 'product_id': subs.product_id, + 'internal_url': subs.internal_url, + 'external_url': subs.external_url, + 'dns_group': subs.dns_group, + 'status': subs.status, + 'updated': subs.updated, + "added": subs.added, + }) + + # Calculate total pages + total_pages = (total_count + limit - 1) // limit + + response_data = { + 'subscriptions': member_sub_data, + 'count': len(member_sub_data), + 'pagination': { + 'total_count': total_count, + 'total_pages': total_pages, + 'current_page': page, + 'limit': limit, + 'has_next': page < total_pages, + 'has_prev': page > 1 + } + } + + return response_data + + except Exception as e: + logger.error(f"An error occurred while getting dashboard data: {str(e)}", exc_info=True) + return jsonify({"message": "Internal Server Error"}), 500 + + @staticmethod + def subscription_available_products(data): + try: + with db.session.begin(): + validated_data = SubscriptionService.validate_data(data, UserSchema()) + token = validated_data.get('token') + uid = validated_data.get('uid') + member_data = Members.get_member_by_uid(uid) + if not member_data: + invalid_data = { + "error_message": "Member Data", + "message_key": "invalid_member", + } + return ResponseHelper.success(data=invalid_data) + # option_name + + member_id = member_data.id + option_name = member_data.option_name + trial_name = "TRIAL-90" + + res_options = [] + sub_options = SubscriptionOptions.get_subscription_options(1) + for t in sub_options: + if t.stripe_product_id is None or t.stripe_product_id.strip() == "" or t.stripe_price_id is None or t.stripe_price_id.strip() == "": + price_create_result = StripeIntegration.create_product(t.display_name, t.monthly) + logger.info(f"Inside Stripe_Product ===== : {price_create_result}") + if price_create_result: + SubscriptionOptions.set_stripe_product_id(t.uid,price_create_result['product_id']) + SubscriptionOptions.set_stripe_price_id(t.uid,price_create_result['price_id']) + + res_options.append({ + 'option_id': t.id, + 'package_uid': t.uid, + 'display_name': t.display_name, + 'option_name': t.option_name, + 'monthly': round(t.monthly * 0.01, 2), + 'stripe_product_id': t.stripe_product_id, + 'stripe_price_id': t.stripe_price_id, + "items": SubscriptionService.subscription_items_data(t.option_name), + 'added': t.added.isoformat() if t.added else None, + 'updated': t.updated.isoformat() if t.updated else None + }) + + subscription_products_data = { + "current_product": { + "display_name": trial_name if option_name is None else option_name, + "subs": [ + 'Post Jobs', + 'advanced instructors search', + 'invite candidates', + 'post events', + 'Cancel Anytime' + ], + "next_payment": '2025-10-15 11:00:07.47214' + }, + 'stripe_customer_id': member_data.stripe_customer_id, + "options": res_options + } + + return ResponseHelper.success(data=subscription_products_data) + + except Exception as e: + logger.error(f"An error occurred while getting dashboard data: {str(e)}", exc_info=True) + return jsonify({"message": "Internal Server Error"}), 500 + + @staticmethod + def subscription_items_data(option_name): + items_data = SubscriptionOptionsItems.get_subscription_item(option_name) + res_options_items = [] + for t in items_data: + res_options_items.append({ + 'description': t.description + }) + + # return ['Post Jobs 222', 'advanced instructors search', 'invite candidates', 'post events', + # 'Cancel anytime'] + return res_options_items + + @staticmethod + def subscription_session_start(data): + try: + with db.session.begin(): + # logger.info(f"Incoming MyProduct data ==>>>> {data}") + validated_data = SubscriptionService.validate_data(data, SubscriptionSession()) + token = validated_data.get('token') + uid = validated_data.get('uid') + customerId = validated_data.get('customerId') + priceId = validated_data.get('priceId') + member_data = Members.get_member_by_uid(uid) + member_id = member_data.id + option_name = validated_data.get('option_name') + sub_option = SubscriptionOptions.get_subscription_options_by_option_name(option_name) + + if sub_option is None: + logger.error(f"Invalid Option Name") + return [] + + stripe_session = StripeIntegration.create_checkout_session_subscription(priceId, customerId) + logger.info(f"Inside Stripe_Session ===== : {stripe_session}") + logger.info(f"Inside Stripe_Session ID ===== : {stripe_session.id}") + PaymentsSession.add_payment_session(member_id, option_name, 'STRIPE', stripe_session.id) + # + # product_id = validated_data.get('product_id') + # product_subscription_uid='' + # product_subscription_external_url = '' + # product_subscription_internal_url = '' + # product_data = Products.get_product_by_product_id(product_id) + # product_description = ProductsDetails.get_product_details_with_product_id('A000002') + # productDataStatus = product_data.status + # + # memberSubscription = MembersProducts.get_member_product_by_product_member_id(member_id, product_id) + # if memberSubscription is not None: + # logger.info(f"Incoming MyProduct data ==>>>> {memberSubscription}") + # productDataStatus = memberSubscription.status + # product_subscription_uid = memberSubscription.uid + # product_subscription_external_url = memberSubscription.external_url + # product_subscription_internal_url = memberSubscription.internal_url + + + # "banner": "banner.jpg", + # myproduct_data = { + # "myproudct": { + # "banner": product_data.banner, + # "description": product_description.details, + # "sale_text" : product_description.sale_text, + # "internal_url": product_subscription_internal_url, + # "external_url": product_subscription_external_url, + # "price_text": "90 days free and 3.95/Month", + # "product_id": product_data.product_id, + # "product_name": product_data.name, + # "product_subscription_uid": product_subscription_uid, + # "product_uid": product_data.uid, + # "promotion_text": "Start Free Today !", + # "subscription_uid": product_subscription_uid, + # "status": productDataStatus, + # "subscription_text": "Start with your goals in mind and then work possible.ith yand Goals. If the plan doesn\u2019t support the vision then change it!", + # "title": "Your personal professional web presence" + # } + # } + + # Simulate processing + response_data = { + "stripe_session":stripe_session.url, + "member_id": member_id, + "uid": uid, + } + + return ResponseHelper.success(data=response_data) + + except ValidationError as err: + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + db.session.rollback() + return ResponseHelper.unprocessable_entity(result_description="Validation exception") + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + db.session.rollback() + return ResponseHelper.error(result_description=str(err)) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + db.session.rollback() + return ResponseHelper.internal_server_error() + + + + @staticmethod + def subscription_webhook_start(data): + try: + with db.session.begin(): + hook_data = data["data"]["object"] + # logger.info(f"***** HOOK Data ALL ==>>>> {hook_data}") + # json_hook_output = json.dumps(hook_data["data"]) + + for key, value in hook_data.items(): + logger.info(f"**************** Key: {key}, *********** Value: {value}") + + data_id = hook_data["id"] + amount_subtotal =hook_data["amount_subtotal"] + logger.info(f"HOOK Result DATA ==>>>> {data_id} {amount_subtotal}") + + + + + + # Simulate processing + response_data = { + "data_id": data_id, + "member_id": "", + "uid": "", + } + + return ResponseHelper.success(data=response_data) + + except ValidationError as err: + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + db.session.rollback() + return ResponseHelper.unprocessable_entity(result_description="Validation exception") + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + db.session.rollback() + return ResponseHelper.error(result_description=str(err)) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + db.session.rollback() + return ResponseHelper.internal_server_error() + +