feat: subscription ended webhook
refactor: subscription table
This commit is contained in:
parent
f545a5b75b
commit
7f2f730426
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""updated_subscription_table
|
||||||
|
|
||||||
|
Revision ID: 8d19e726b997
|
||||||
|
Revises: 5ed8ac3d258c
|
||||||
|
Create Date: 2025-06-03 19:07:17.107577
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '8d19e726b997'
|
||||||
|
down_revision: Union[str, None] = '5ed8ac3d258c'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('subscriptions', sa.Column('total', sa.String(), nullable=True))
|
||||||
|
op.add_column('subscriptions', sa.Column('setup_fee', sa.String(), nullable=True))
|
||||||
|
op.add_column('subscriptions', sa.Column('subscription_fee', sa.String(), nullable=True))
|
||||||
|
op.add_column('subscriptions', sa.Column('per_call_charge', sa.String(), nullable=True))
|
||||||
|
op.drop_index('ix_subscriptions_session_id', table_name='subscriptions')
|
||||||
|
op.drop_column('subscriptions', 'session_id')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('subscriptions', sa.Column('session_id', sa.VARCHAR(length=255), autoincrement=False, nullable=True))
|
||||||
|
op.create_index('ix_subscriptions_session_id', 'subscriptions', ['session_id'], unique=False)
|
||||||
|
op.drop_column('subscriptions', 'per_call_charge')
|
||||||
|
op.drop_column('subscriptions', 'subscription_fee')
|
||||||
|
op.drop_column('subscriptions', 'setup_fee')
|
||||||
|
op.drop_column('subscriptions', 'total')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
@ -5,9 +5,13 @@ from .CustomBase import CustomBase
|
||||||
class Subscriptions(Base, CustomBase):
|
class Subscriptions(Base, CustomBase):
|
||||||
__tablename__ = "subscriptions"
|
__tablename__ = "subscriptions"
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
session_id = Column(String(255), index=True)
|
# session_id = Column(String(255), index=True)
|
||||||
customer_id = Column(String,index=True)
|
customer_id = Column(String,index=True)
|
||||||
account_id = Column(String,index=True)
|
account_id = Column(String,index=True)
|
||||||
|
total = Column(String)
|
||||||
|
setup_fee = Column(String)
|
||||||
|
subscription_fee = Column(String)
|
||||||
|
per_call_charge = Column(String)
|
||||||
subscription_id = Column(String,index=True)
|
subscription_id = Column(String,index=True)
|
||||||
clinic_id = Column(Integer, index=True)
|
clinic_id = Column(Integer, index=True)
|
||||||
status = Column(String)
|
status = Column(String)
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class StripeServices:
|
||||||
|
|
||||||
async def create_customer(self, user_id: int, email: str, name: str):
|
async def create_customer(self, user_id: int, email: str, name: str):
|
||||||
try:
|
try:
|
||||||
customer = stripe.Customer.create(
|
customer = await stripe.Customer.create_async(
|
||||||
email=email, name=name, metadata={"user_id": user_id}
|
email=email, name=name, metadata={"user_id": user_id}
|
||||||
)
|
)
|
||||||
return customer
|
return customer
|
||||||
|
|
@ -45,14 +45,14 @@ class StripeServices:
|
||||||
|
|
||||||
async def delete_customer(self, customer_id: str):
|
async def delete_customer(self, customer_id: str):
|
||||||
try:
|
try:
|
||||||
stripe.Customer.delete(customer_id)
|
await stripe.Customer.delete_async(customer_id)
|
||||||
except stripe.error.StripeError as e:
|
except stripe.error.StripeError as e:
|
||||||
self.logger.error(f"Error deleting customer: {e}")
|
self.logger.error(f"Error deleting customer: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def create_account(self, user_id: int, email: str, name: str, phone: str):
|
async def create_account(self, user_id: int, email: str, name: str, phone: str):
|
||||||
try:
|
try:
|
||||||
account = stripe.Account.create(
|
account = await stripe.Account.create_async(
|
||||||
type="express",
|
type="express",
|
||||||
country="AU",
|
country="AU",
|
||||||
capabilities={
|
capabilities={
|
||||||
|
|
@ -70,7 +70,7 @@ class StripeServices:
|
||||||
|
|
||||||
async def delete_account(self, account_id: str):
|
async def delete_account(self, account_id: str):
|
||||||
try:
|
try:
|
||||||
stripe.Account.delete(account_id)
|
await stripe.Account.delete_async(account_id)
|
||||||
except stripe.error.StripeError as e:
|
except stripe.error.StripeError as e:
|
||||||
self.logger.error(f"Error deleting account: {e}")
|
self.logger.error(f"Error deleting account: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
@ -96,9 +96,9 @@ class StripeServices:
|
||||||
if not subscription:
|
if not subscription:
|
||||||
raise ResourceNotFoundException("Subscription not found!")
|
raise ResourceNotFoundException("Subscription not found!")
|
||||||
|
|
||||||
stripe_subscription = stripe.Subscription.retrieve(subscription.subscription_id)
|
stripe_subscription = await stripe.Subscription.retrieve_async(subscription.subscription_id)
|
||||||
|
|
||||||
invoice = stripe.Invoice.retrieve(
|
invoice = await stripe.Invoice.retrieve_async(
|
||||||
stripe_subscription["latest_invoice"]
|
stripe_subscription["latest_invoice"]
|
||||||
)
|
)
|
||||||
return invoice.hosted_invoice_url
|
return invoice.hosted_invoice_url
|
||||||
|
|
@ -154,7 +154,7 @@ class StripeServices:
|
||||||
|
|
||||||
async def create_checkout_session(self, user_id: int):
|
async def create_checkout_session(self, user_id: int):
|
||||||
try:
|
try:
|
||||||
checkout_session = stripe.checkout.Session.create(
|
checkout_session = await stripe.checkout.Session.create_async(
|
||||||
payment_method_types=["card"],
|
payment_method_types=["card"],
|
||||||
line_items=[
|
line_items=[
|
||||||
{
|
{
|
||||||
|
|
@ -182,7 +182,7 @@ class StripeServices:
|
||||||
|
|
||||||
async def create_setup_fees(self, customer_id: str, amount: int):
|
async def create_setup_fees(self, customer_id: str, amount: int):
|
||||||
try:
|
try:
|
||||||
setup_intent = stripe.InvoiceItem.create(
|
setup_intent = await stripe.InvoiceItem.create_async(
|
||||||
customer=customer_id,
|
customer=customer_id,
|
||||||
amount=amount,
|
amount=amount,
|
||||||
currency="aud",
|
currency="aud",
|
||||||
|
|
@ -256,7 +256,7 @@ class StripeServices:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session = stripe.checkout.Session.create(**session_data)
|
session = await stripe.checkout.Session.create_async(**session_data)
|
||||||
|
|
||||||
payment_log = PaymentLogs(
|
payment_log = PaymentLogs(
|
||||||
customer_id=customer_id,
|
customer_id=customer_id,
|
||||||
|
|
@ -294,8 +294,11 @@ class StripeServices:
|
||||||
)
|
)
|
||||||
self.logger.info(f"Stripe webhook event type: {event['type']}")
|
self.logger.info(f"Stripe webhook event type: {event['type']}")
|
||||||
|
|
||||||
if event["type"] == "checkout.session.expired":
|
if event["type"] == "customer.subscription.deleted":
|
||||||
pass
|
self.logger.info("customer subscription ended")
|
||||||
|
subscription_id = event["data"]["object"]["items"]["data"][0]["subscription"]
|
||||||
|
|
||||||
|
await self._subscription_expired(subscription_id)
|
||||||
|
|
||||||
if event["type"] == "checkout.session.completed":
|
if event["type"] == "checkout.session.completed":
|
||||||
unique_clinic_id = event["data"]["object"]["metadata"]["unique_clinic_id"]
|
unique_clinic_id = event["data"]["object"]["metadata"]["unique_clinic_id"]
|
||||||
|
|
@ -307,9 +310,9 @@ class StripeServices:
|
||||||
session_id = event["data"]["object"]["id"]
|
session_id = event["data"]["object"]["id"]
|
||||||
subscription_id = event["data"]["object"]["subscription"]
|
subscription_id = event["data"]["object"]["subscription"]
|
||||||
|
|
||||||
self._update_payment_log(unique_clinic_id, clinic_id, customer_id, account_id, total, metadata, session_id)
|
await self._update_payment_log(unique_clinic_id, clinic_id, customer_id, account_id, total, metadata)
|
||||||
|
|
||||||
self._create_subscription_entry({
|
await self._create_subscription_entry({
|
||||||
"clinic_id": clinic_id,
|
"clinic_id": clinic_id,
|
||||||
"customer_id": customer_id,
|
"customer_id": customer_id,
|
||||||
"account_id": account_id,
|
"account_id": account_id,
|
||||||
|
|
@ -326,7 +329,7 @@ class StripeServices:
|
||||||
finally:
|
finally:
|
||||||
self.db.close()
|
self.db.close()
|
||||||
|
|
||||||
def _update_payment_log(self, unique_clinic_id:str, clinic_id:int, customer_id:str, account_id:str, total:float, metadata:any, session_id:str):
|
async def _update_payment_log(self, unique_clinic_id:str, clinic_id:int, customer_id:str, account_id:str, total:float, metadata:any):
|
||||||
try:
|
try:
|
||||||
self.db.query(PaymentSessions).filter(PaymentSessions.clinic_id == clinic_id).delete()
|
self.db.query(PaymentSessions).filter(PaymentSessions.clinic_id == clinic_id).delete()
|
||||||
|
|
||||||
|
|
@ -353,16 +356,22 @@ class StripeServices:
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
self.db.close()
|
self.db.close()
|
||||||
|
|
||||||
def _create_subscription_entry(self,data:dict):
|
async def _create_subscription_entry(self,data:dict):
|
||||||
try:
|
try:
|
||||||
|
|
||||||
subscription = stripe.Subscription.retrieve(data["subscription_id"])
|
subscription = stripe.Subscription.retrieve(data["subscription_id"])
|
||||||
|
|
||||||
|
metadata_dict = json.loads(subscription.metadata)
|
||||||
|
fees_to_be = json.loads(metadata_dict["fees_to_be"])
|
||||||
|
|
||||||
new_subscription = Subscriptions(
|
new_subscription = Subscriptions(
|
||||||
clinic_id=data["clinic_id"],
|
clinic_id=data["clinic_id"],
|
||||||
customer_id=data["customer_id"],
|
customer_id=data["customer_id"],
|
||||||
account_id=data["account_id"],
|
account_id=data["account_id"],
|
||||||
session_id=data["session_id"],
|
total=fees_to_be["total"],
|
||||||
|
setup_fee=fees_to_be["setup_fees"],
|
||||||
|
subscription_fee=fees_to_be["subscription_fees"],
|
||||||
|
per_call_charge=fees_to_be["per_call_charges"],
|
||||||
subscription_id=data["subscription_id"],
|
subscription_id=data["subscription_id"],
|
||||||
status=subscription.status,
|
status=subscription.status,
|
||||||
current_period_start=subscription["items"]["data"][0]["current_period_start"],
|
current_period_start=subscription["items"]["data"][0]["current_period_start"],
|
||||||
|
|
@ -385,3 +394,25 @@ class StripeServices:
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
self.db.close()
|
self.db.close()
|
||||||
|
|
||||||
|
async def _subscription_expired(self,subscription_id):
|
||||||
|
try:
|
||||||
|
subscription = stripe.Subscription.retrieve(subscription_id)
|
||||||
|
|
||||||
|
db_subscription = self.db.query(Subscriptions).filter(Subscriptions.subscription_id == subscription_id).first()
|
||||||
|
|
||||||
|
if not db_subscription:
|
||||||
|
self.logger.error("Subscription not found!")
|
||||||
|
raise Exception("Subscription not found!")
|
||||||
|
|
||||||
|
db_subscription.status = subscription.status
|
||||||
|
self.db.add(db_subscription)
|
||||||
|
|
||||||
|
# TODO: update clinic status
|
||||||
|
# TODO: send email to user
|
||||||
|
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error ending subscription: {e}")
|
||||||
|
finally:
|
||||||
|
self.db.commit()
|
||||||
|
self.db.close()
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import asyncio
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
|
@ -61,14 +62,13 @@ class UserServices:
|
||||||
self.db.add(new_user)
|
self.db.add(new_user)
|
||||||
self.db.flush() # Flush to get the user ID without committing
|
self.db.flush() # Flush to get the user ID without committing
|
||||||
|
|
||||||
# Create stripe customer
|
stripe_customer, stripe_account = await asyncio.gather(
|
||||||
stripe_customer = await self.stripe_service.create_customer(
|
self.stripe_service.create_customer(
|
||||||
new_user.id, user.email, user.username
|
new_user.id, user.email, user.username
|
||||||
)
|
),
|
||||||
|
self.stripe_service.create_account(
|
||||||
# Create stripe account
|
new_user.id, user.email, user.username, user.mobile
|
||||||
stripe_account = await self.stripe_service.create_account(
|
),
|
||||||
new_user.id, user.email, user.username, user.mobile
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create stripe user
|
# Create stripe user
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue