import datetime
from app.config import appTimezone
from app.models import Users, Tiers
from app.emails import (
send_email_about_subscription_confirmation,
send_email_about_subscription_cancellation,
send_email_about_subscription_deletion,
)
[docs]
def is_subscription_event(event):
return event.type[:21] == "customer.subscription"
[docs]
def is_checkout_completed_event(event):
return event.type == "checkout.session.completed"
[docs]
def user_from_stripe_customer_id(customer_id):
user_matched = Users.query.filter_by(stripe_customer_id=customer_id).first()
if user_matched is None:
raise Exception(f"App user not found for customer_id {customer_id}.")
return user_matched
[docs]
def tier_from_stripe_price_id(price_id):
tier_matched = Tiers.query.filter_by(stripe_price_id=price_id).first()
if tier_matched is None:
raise Exception(f"Tier not found for price_id {price_id}.")
return tier_matched
[docs]
def handle_stripe_event(event):
# If it is a credit purchase event, try to parse user and update credits
if is_checkout_completed_event(event):
# Get the Stripe customer and find the matching user
customer_id = event.data.object["customer"]
user_matched = user_from_stripe_customer_id(customer_id)
# Update the user's credits
quantity = event.data.object.metadata.quantity
user_matched.add_credits(int(quantity))
# If it is a subscription event, try to parse user and tier
if is_subscription_event(event):
subscription = event.data.object
# Get the Stripe customer and find the matching user
customer_id = subscription["customer"]
user_matched = user_from_stripe_customer_id(customer_id)
# Get the price_id and find the matching tier
price_id = subscription["items"]["data"][0]["price"]["id"]
tier_matched = tier_from_stripe_price_id(price_id)
# Are we canceling the subscription now?
if event.type == "customer.subscription.deleted":
# Send the email about subscription ending while we can still read the tier
send_email_about_subscription_deletion(user_matched, tier_matched)
# Change the tier to the first level
user_matched.tier_id = 1
user_matched.cancel_at = None
elif event.type == "customer.subscription.updated":
# Is the update about an upcoming cancellation?
if subscription["cancel_at_period_end"]:
cancellation_date = datetime.datetime.fromtimestamp(
subscription["current_period_end"]
).astimezone(appTimezone)
user_matched.cancel_at = cancellation_date
# Send email about subscription cancellation
send_email_about_subscription_cancellation(
user_matched, tier_matched, cancellation_date
)
# Is the update about a tier change? Including from free to paid.
if subscription["cancel_at"] is None:
# Update the user's tier
# TODO: Check if they had a subscription to a better tier before cancellation, if yes, keep that until that ends, then switch to new tier
user_matched.tier_id = tier_matched.id
# Send email about subscription confirmation
send_email_about_subscription_confirmation(user_matched, tier_matched)
# Clear any prior cancellation date if it exists
user_matched.cancel_at = None
else:
print(f"Unhandled subscription event: {event.type}")
# In any case, Save the changes made above
user_matched.save()