webhook separate file
This commit is contained in:
@@ -227,7 +227,9 @@ class PaymentProcessingService:
|
||||
return None
|
||||
|
||||
def _get_subscription(self):
|
||||
logger.debug("subscription_id: ", self.charge_data["metadata"]["subscription_id"])
|
||||
logger.debug(
|
||||
"subscription_id: ", self.charge_data["metadata"]["subscription_id"]
|
||||
)
|
||||
subscription_id = self.charge_data["metadata"]["subscription_id"]
|
||||
if subscription_id:
|
||||
try:
|
||||
@@ -257,7 +259,9 @@ class PaymentProcessingService:
|
||||
self.principal_subscription = principal_subscription
|
||||
return principal_subscription
|
||||
except Subscription.DoesNotExist:
|
||||
logger.error("SOmething Went Wrong inside _create_principal_subscription().")
|
||||
logger.error(
|
||||
"SOmething Went Wrong inside _create_principal_subscription()."
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
261
goodtimes/webhook.py
Normal file
261
goodtimes/webhook.py
Normal file
@@ -0,0 +1,261 @@
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
import requests
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
from onesignal_sdk.client import Client as OneSignalClient
|
||||
from accounts.models import IAmPrincipal, IAmPrincipalOtp, IAmPrincipalType
|
||||
from manage_referrals.models import (
|
||||
GoodTimeCoins,
|
||||
ReferralRecord,
|
||||
ReferralRecordReward,
|
||||
ReferralTracking,
|
||||
)
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from manage_subscriptions.models import PrincipalSubscription, Subscription
|
||||
from manage_wallets.models import (
|
||||
TransactionStatus,
|
||||
TransactionType,
|
||||
Wallet,
|
||||
Transaction,
|
||||
)
|
||||
import logging
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebhookService:
|
||||
def __init__(self, webhook_data):
|
||||
self.webhook_data = webhook_data
|
||||
self.event_type = webhook_data["type"]
|
||||
self.charge_data = webhook_data["data"]["object"]
|
||||
|
||||
def get_event_type(self):
|
||||
return self.event_type
|
||||
|
||||
def get_principal(self):
|
||||
principal_id = self.charge_data["metadata"]["principal"]
|
||||
try:
|
||||
return IAmPrincipal.objects.select_related("type").get(id=int(principal_id))
|
||||
except (ObjectDoesNotExist, ValueError):
|
||||
logger.error(f"Invalid principal ID: {principal_id}")
|
||||
raise ValueError(f"Invalid principal ID: {principal_id}")
|
||||
|
||||
def get_transaction(self):
|
||||
transaction_id = self.charge_data["metadata"]["transaction_id"]
|
||||
try:
|
||||
return Transaction.objects.select_related("principal_subscription").get(
|
||||
id=int(transaction_id)
|
||||
)
|
||||
except (ObjectDoesNotExist, ValueError):
|
||||
logger.error(f"Invalid transaction ID: {transaction_id}")
|
||||
raise ValueError(f"Invalid transaction ID: {transaction_id}")
|
||||
|
||||
def get_subscription(self):
|
||||
subscription_id = self.charge_data["metadata"]["subscription_id"]
|
||||
try:
|
||||
return Subscription.objects.get(id=int(subscription_id))
|
||||
except (ObjectDoesNotExist, ValueError):
|
||||
logger.error(f"Invalid subscription ID: {subscription_id}")
|
||||
raise ValueError(f"Invalid subscription ID: {subscription_id}")
|
||||
|
||||
def get_order_id(self):
|
||||
return self.charge_data["metadata"]["order_id"]
|
||||
|
||||
|
||||
class ReferralRewardService:
|
||||
def __init__(self, principal, principal_subscription, subscription):
|
||||
self.notification_service = NotificationService()
|
||||
self.principal = principal
|
||||
self.principal_subscription = principal_subscription
|
||||
self.subscription = subscription
|
||||
|
||||
def _fetch_referral_record(self):
|
||||
return ReferralRecord.objects.filter(
|
||||
referred_principal=self.principal,
|
||||
is_completed=True,
|
||||
active=True, # Assuming 'active' is a field determining if the record is currently relevant
|
||||
deleted=False, # Assuming logical deletion is handled by a 'deleted' field
|
||||
).first()
|
||||
|
||||
def _check_active_subscription(self, referrer_principal):
|
||||
today = timezone.now().date()
|
||||
return (
|
||||
PrincipalSubscription.objects.filter(
|
||||
principal=referrer_principal,
|
||||
is_paid=True,
|
||||
end_date__gte=today,
|
||||
cancelled=False,
|
||||
deleted=False,
|
||||
)
|
||||
.order_by("-end_date")
|
||||
.first()
|
||||
)
|
||||
|
||||
def _credit_reward(self, referral_record, subscription):
|
||||
amount = subscription.referral_percentage * subscription.amount / 100
|
||||
ReferralRecordReward.objects.create(
|
||||
referral_record=referral_record,
|
||||
subscription=subscription,
|
||||
coins=1, # This value could be dynamically calculated or configured elsewhere
|
||||
value=amount,
|
||||
)
|
||||
self._credit_transaction(referral_record.referrer_principal, amount)
|
||||
self.notification_service.referral_received_notification(
|
||||
referral_record.referrer_principal, amount, self.principal.email
|
||||
)
|
||||
|
||||
def _credit_transaction(self, referrer_principal, amount):
|
||||
Transaction.objects.create(
|
||||
principal=referrer_principal,
|
||||
transaction_type=TransactionType.CREDIT,
|
||||
payment_method="",
|
||||
transaction_status=TransactionStatus.SUCCESS,
|
||||
amount=amount,
|
||||
coins=1,
|
||||
comment="Referral reward",
|
||||
# Populate other fields as necessary, such as `order_id`, `product_id`, or `reference_id` if applicable
|
||||
)
|
||||
|
||||
def _update_reward_status(self, referral_record, active_subscription):
|
||||
# Check if the referrer has an active subscription and get its ID if it exists
|
||||
referrer_subscription_id = (
|
||||
active_subscription.id if active_subscription else None
|
||||
)
|
||||
|
||||
# Create a new subscription for the referred principal
|
||||
referred_subscription_id = self.principal_subscription.id
|
||||
|
||||
ReferralTracking.objects.create(
|
||||
referral_record=referral_record,
|
||||
referrer_subscription_id=referrer_subscription_id,
|
||||
referred_subscription_id=referred_subscription_id,
|
||||
is_referrer_subscribed=active_subscription,
|
||||
)
|
||||
|
||||
def credit_referral_reward_if_applicable(self):
|
||||
referral_record = self._fetch_referral_record()
|
||||
if referral_record:
|
||||
active_subscription = self._check_active_subscription(
|
||||
referral_record.referrer_principal
|
||||
)
|
||||
if active_subscription:
|
||||
if self.subscription:
|
||||
self._credit_reward(referral_record, self.subscription)
|
||||
|
||||
self._update_reward_status(referral_record, active_subscription)
|
||||
else:
|
||||
self._update_reward_status(referral_record, None)
|
||||
|
||||
|
||||
class SubscriptionService:
|
||||
def __init__(self):
|
||||
self.principal_subscription = None
|
||||
|
||||
def create_principal_subscription(self, principal, subscription, order_id):
|
||||
subscription_days = subscription.plan.days
|
||||
today = timezone.now().date()
|
||||
last_date = today + timedelta(days=subscription_days)
|
||||
principal_subscription = PrincipalSubscription.objects.create(
|
||||
principal=principal,
|
||||
subscription=subscription,
|
||||
is_paid=True,
|
||||
order_id=order_id,
|
||||
start_date=today,
|
||||
end_date=last_date,
|
||||
grace_period_end_date=last_date + timedelta(days=15),
|
||||
)
|
||||
self.principal_subscription = principal_subscription
|
||||
return principal_subscription
|
||||
|
||||
def update_transaction_success(self, principal_transaction, principal_subscription):
|
||||
principal_transaction.transaction_status = TransactionStatus.SUCCESS
|
||||
principal_transaction.principal_subscription = principal_subscription
|
||||
principal_transaction.save()
|
||||
|
||||
def update_transaction_failure(self, principal_transaction):
|
||||
principal_transaction.transaction_status = TransactionStatus.FAIL
|
||||
principal_transaction.save()
|
||||
|
||||
|
||||
class PaymentProcessingService:
|
||||
def __init__(self, webhook_data):
|
||||
self.webhook_service = WebhookService(webhook_data)
|
||||
self.subscription_service = SubscriptionService()
|
||||
self.notification_service = NotificationService()
|
||||
|
||||
# Retrieve objects
|
||||
self.principal = self.webhook_service.get_principal_id()
|
||||
self.transaction = self.webhook_service.get_transaction_id()
|
||||
self.subscription = self.webhook_service.get_subscription_id()
|
||||
self.order_id = self.webhook_service.get_order_id()
|
||||
self.principal_subscription = None
|
||||
|
||||
def process_event(self):
|
||||
if self.webhook_service.get_event_type() == "checkout.session.completed":
|
||||
self.handle_success()
|
||||
else:
|
||||
self.handle_failure()
|
||||
|
||||
def handle_success(self):
|
||||
with transaction.atomic():
|
||||
# Create or update the principal subscription
|
||||
self.principal_subscription = (
|
||||
self.subscription_service.create_principal_subscription(
|
||||
self.principal, self.subscription, self.order_id
|
||||
)
|
||||
)
|
||||
|
||||
# Update transaction status to success
|
||||
self.subscription_service.update_transaction_success(
|
||||
self.transaction, self.principal_subscription
|
||||
)
|
||||
|
||||
# Now handle referral rewards, if applicable
|
||||
if self.principal_subscription:
|
||||
referral_service = ReferralRewardService(
|
||||
self.principal, self.principal_subscription, self.subscription
|
||||
)
|
||||
referral_service.credit_referral_reward_if_applicable()
|
||||
|
||||
self.notification_service.payment_success_notification(
|
||||
self.principal, self.subscription, self.transaction.amount
|
||||
)
|
||||
|
||||
def handle_failure(self):
|
||||
self.subscription_service.update_transaction_failure(self.transaction)
|
||||
self.notification_service.payment_failed_notification(
|
||||
self.principal, self.subscription, self.transaction.amount
|
||||
)
|
||||
|
||||
|
||||
class NotificationService:
|
||||
def __init__(self):
|
||||
self.client = OneSignalClient(
|
||||
app_id=settings.ONE_SIGNAL_APP_ID, rest_api_key=settings.ONE_SIGNAL_API_KEY
|
||||
)
|
||||
|
||||
def send_notification(self, title, message, player_id):
|
||||
notification_payload = {
|
||||
"headings": {"en": title},
|
||||
"contents": {"en": message},
|
||||
"include_player_ids": player_id,
|
||||
}
|
||||
response = self.client.send_notification(notification_payload)
|
||||
return response
|
||||
|
||||
def payment_success_notification(self, principal, subscription, amount):
|
||||
title = "Payment Successful"
|
||||
message = f"Your payment for {subscription} of ${amount} was successfully processed. Your subscription is valid till {subscription.end_date}"
|
||||
self.send_notification(title, message, principal.player_id)
|
||||
|
||||
def referral_received_notification(self, principal, amount, email):
|
||||
title = "Congratulations! You got a referral G-Token."
|
||||
message = f"Your referral {email} has subscribed to GoodTimesApp. You have received {amount} (£)"
|
||||
self.send_notification(title, message, principal.player_id)
|
||||
|
||||
def payment_failed_notification(self, principal, subscription, amount):
|
||||
title = "Payment Failed!"
|
||||
message = f"Your payment for {subscription} of ${amount} was failed."
|
||||
self.send_notification(title, message, principal.player_id)
|
||||
@@ -3,7 +3,8 @@ import requests
|
||||
from django.db.models import Q
|
||||
from onesignal_sdk.client import Client as OneSignalClient
|
||||
from django.conf import settings
|
||||
from .models import IAmPrincipalNotificationSettings, IAmPrincipal, PrincipalType
|
||||
from accounts.models import IAmPrincipal
|
||||
from .models import IAmPrincipalNotificationSettings, PrincipalType
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -35,6 +35,7 @@ from .serializers import PrincipalSubscriptionSerializer
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.utils.decorators import method_decorator
|
||||
from rest_framework.response import Response
|
||||
from goodtimes.webhook import PaymentProcessingService
|
||||
|
||||
|
||||
class CreatePrincipalSubscriptionApi(APIView):
|
||||
@@ -200,7 +201,8 @@ class StripeWebhookTest(APIView):
|
||||
message="Active principal subscription already exists",
|
||||
)
|
||||
|
||||
payment_service = services.PaymentProcessingService(webhook_data=event)
|
||||
# payment_service = services.PaymentProcessingService(webhook_data=event)
|
||||
payment_service = PaymentProcessingService(webhook_data=event)
|
||||
payment_service.process_event()
|
||||
webhook_event = WebhookEvent.objects.get(event_id=event_id)
|
||||
webhook_event.status = "processed"
|
||||
|
||||
Reference in New Issue
Block a user