updated webhook module

This commit is contained in:
rizwanisready
2024-08-08 13:58:45 +05:30
parent 6e8eb96f4f
commit 33a11af520
5 changed files with 141 additions and 82 deletions

View File

@@ -136,8 +136,10 @@ class PasswordResetSerializer(BasePasswordSerializer, serializers.ModelSerialize
model = IAmPrincipal
fields = ["password", "confirm_password"]
from phonenumbers import parse, phonenumberutil, NumberParseException
class ProfileSerializer(serializers.ModelSerializer):
profile_photo = serializers.ImageField(required=False)
email = serializers.CharField(read_only=True)
@@ -227,6 +229,7 @@ class ProfileSerializer(serializers.ModelSerializer):
"has_active_subscription": False,
"in_grace_period": False,
"grace_period_end_date": None,
"subscription_id": None,
}
today = timezone.now().date()
@@ -245,6 +248,9 @@ class ProfileSerializer(serializers.ModelSerializer):
) # Order by descending grace_period_end_date and take the first
if latest_subscription:
subscription_status["subscription_id"] = (
latest_subscription.stripe_subscription_id
)
# Check if we're within the grace period
if today <= latest_subscription.grace_period_end_date:
subscription_status["has_active_subscription"] = (
@@ -369,4 +375,4 @@ class AppVersionSerializer(serializers.ModelSerializer):
class IAmPrincipalExtendedDataSerializer(serializers.ModelSerializer):
class Meta:
model = IAmPrincipalExtendedData
fields = "__all__"
fields = "__all__"

View File

@@ -27,35 +27,40 @@ class PaymentProcessingService:
self.stripe_subscription = stripe_subscription
self.current_period_start = current_period_start
self.current_period_end = current_period_end
self.principal_subscription = None
@property
def charge_data(self):
"""Return charge data from the webhook service."""
return self.webhook_service.charge_data
@property
def principal(self):
"""Return the principal from the webhook service."""
return self.webhook_service.get_principal()
@property
def subscription(self):
"""Return the subscription from the webhook service."""
return self.webhook_service.get_subscription()
@property
def order_id(self):
"""Return the order ID from the webhook service."""
return self.webhook_service.get_order_id()
@property
def coupon(self):
"""Return the coupon from the webhook service."""
return self.webhook_service.get_coupon()
@property
def amount(self):
"""Return the final amount from the webhook service."""
return self.webhook_service.get_final_amount()
def create_transaction(self):
"""Create a transaction based on webhook data."""
transaction = Transaction.objects.create(
return Transaction.objects.create(
principal=self.principal,
principal_subscription=None,
transaction_type=TransactionType.PAYMENT,
@@ -65,69 +70,92 @@ class PaymentProcessingService:
order_id=self.order_id,
comment="Principal Subscription Initiated",
)
return transaction
def process_event(self):
event_type = self.webhook_service.event_type
if event_type == "checkout.session.completed":
self.handle_success()
elif event_type == "invoice.payment_succeeded":
if self.charge_data.get("billing_reason") != "subscription_create":
self.handle_success()
else:
self.handle_failure()
def handle_success(self):
"""Process the webhook event."""
with transaction.atomic():
# Create or update the principal subscription
transactio = self.create_transaction()
event_type = self.webhook_service.event_type
txn = self.create_transaction()
try:
self.principal_subscription = (
self.subscription_service.create_principal_subscription(
principal=self.principal,
subscription=self.subscription,
stripe_subscription=self.stripe_subscription,
order_id=self.order_id,
current_period_start=self.current_period_start,
current_period_end=self.current_period_end,
coupon=self.coupon,
)
)
print("First Part Done....!!!!!")
# Update transaction status to success
self.subscription_service.update_transaction_success(
transactio, self.principal_subscription
)
print("Second Part Done....!!!!!")
# Handle referral rewards
referral_service = ReferralRewardService(
self.principal, self.principal_subscription, self.subscription
)
print("Third Part Done...!!!!!!!!!!!")
referral_service.credit_referral_reward_if_applicable()
print("Fourth Part Done....!!!!!")
# Send payment success notification
self.notification_service.payment_success_notification(
self.principal,
self.subscription,
self.principal_subscription,
transactio.amount,
)
if event_type == "checkout.session.completed":
self.handle_success(txn)
elif event_type == "invoice.payment_succeeded":
if self.charge_data.get("billing_reason") != "subscription_create":
self.handle_success(txn)
else:
self.handle_failure(txn)
except Exception as e:
transactio.transaction_status = TransactionStatus.FAIL
transactio.error_message = str(e)
logger.error(f"Transaction Error: {str(e)}")
transactio.save()
raise e
else:
transactio.transaction_status = TransactionStatus.SUCCESS
transactio.save()
def handle_failure(self, transactio):
self.subscription_service.update_transaction_failure(transactio)
# self.notification_service.payment_failed_notification(
# self.principal, self.subscription, self.transaction.amount
# )
def handle_success(self, transaction):
"""Handle a successful payment."""
try:
self.create_principal_subscription()
self.process_referral_rewards()
self.send_success_notification(transaction)
self.update_transaction_status(
transaction,
TransactionStatus.SUCCESS,
self.subscription_service.principal_subscription,
)
except Exception as e:
self.handle_failure(transaction, error_message=str(e))
logger.error(f"Transaction Error: {str(e)}")
raise e
def create_principal_subscription(self):
"""Create or update the principal subscription."""
self.subscription_service.principal_subscription = (
self.subscription_service.create_principal_subscription(
principal=self.principal,
subscription=self.subscription,
stripe_subscription=self.stripe_subscription,
order_id=self.order_id,
current_period_start=self.current_period_start,
current_period_end=self.current_period_end,
coupon=self.coupon,
)
)
print("Principal Subscription Created")
def process_referral_rewards(self):
"""Handle referral rewards."""
referral_service = ReferralRewardService(
self.principal,
self.subscription_service.principal_subscription,
self.subscription,
)
referral_service.credit_referral_reward_if_applicable()
print("Referral Rewards Processed")
def send_success_notification(self, transaction):
"""Send a payment success notification."""
self.notification_service.payment_success_notification(
self.principal,
self.subscription,
self.subscription_service.principal_subscription,
transaction.amount,
)
print("Payment Success Notification Sent")
def handle_failure(self, transaction, error_message=None):
"""Handle a failed payment."""
self.update_transaction_status(
transaction, TransactionStatus.FAIL, error_message=error_message
)
self.notification_service.payment_failed_notification(
self.principal, self.subscription, transaction
)
print("Payment Failure Notification Sent")
def update_transaction_status(
self, transaction, status, principal_subscription=None, error_message=None
):
"""Update the transaction status and associate with a subscription if provided."""
transaction.transaction_status = status
if principal_subscription:
transaction.principal_subscription = principal_subscription
if error_message:
transaction.error_message = error_message
transaction.save()

View File

@@ -2,7 +2,6 @@ from datetime import timedelta
from django.utils import timezone
import datetime
from manage_subscriptions.models import PrincipalSubscription
from manage_wallets.models import TransactionStatus
class SubscriptionService:
@@ -11,10 +10,12 @@ class SubscriptionService:
@property
def principal_subscription(self):
"""Get the current principal subscription."""
return self._principal_subscription
@principal_subscription.setter
def principal_subscription(self, value):
"""Set the current principal subscription."""
self._principal_subscription = value
def create_principal_subscription(
@@ -51,16 +52,6 @@ class SubscriptionService:
self.principal_subscription = principal_subscription
return principal_subscription
def update_transaction_success(self, principal_transaction, principal_subscription):
"""Update transaction status to success and associate with a subscription."""
self._update_transaction_status(
principal_transaction, TransactionStatus.SUCCESS, principal_subscription
)
def update_transaction_failure(self, principal_transaction):
"""Update transaction status to failure."""
self._update_transaction_status(principal_transaction, TransactionStatus.FAIL)
def _calculate_dates(
self, current_period_start, current_period_end, subscription_days
):
@@ -83,12 +74,3 @@ class SubscriptionService:
coupon.no_of_redeems += 1
coupon.save()
print("Coupon Saved Successfully!!!")
def _update_transaction_status(
self, transaction, status, principal_subscription=None
):
"""Update the transaction status and associate with a subscription if provided."""
transaction.transaction_status = status
if principal_subscription:
transaction.principal_subscription = principal_subscription
transaction.save()

View File

@@ -293,3 +293,47 @@ class LastActiveSubscriptionView(APIView):
message="No Active Subscription Found",
errors="No Active Subscription Found",
)
class CancelSubscription(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request):
data = json.loads(request.body)
subscription_id = data.get("subscription_id")
try:
subscription = PrincipalSubscription.objects.get(
id=subscription_id, principal=request.user
)
except PrincipalSubscription.DoesNotExist:
return ApiResponse(
message=constants.FAILURE,
errors="Subscription not found.",
status=status.HTTP_404_NOT_FOUND,
)
with transaction.atomic():
if subscription.is_stripe_subscription:
# Cancel Stripe subscription
try:
stripe.Subscription.modify(subscription.stripe_subscription_id, cancel_at_period_end=True)
except stripe.error.InvalidRequestError as e:
return ApiResponse(
message=constants.FAILURE,
errors=f"Stripe error: {str(e)}",
status=status.HTTP_400_BAD_REQUEST,
)
# Updating subscription status in the local database
subscription.status = SubscriptionStatus.INACTIVE
subscription.cancelled = True
subscription.cancelled_date_time = timezone.now()
subscription.save()
return ApiResponse(
message=constants.SUCCESS,
data="Subscription cancelled successfully.",
status=status.HTTP_200_OK,
)

View File

@@ -727,7 +727,6 @@ def create_checkout_session(request):
order_id = f"order_{timezone.localtime().timestamp()}_{request.user.email}"
# Default transaction amount based on subscription amount
final_amount = subscription.amount
print("Before Session Data")
session_data = {
"payment_method_types": ["card"],