-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add stripe subscription webhook handlers (#476)
- Loading branch information
Showing
12 changed files
with
260 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 4.2.6 on 2023-11-12 19:43 | ||
|
||
import uuid | ||
|
||
import django.db.models.deletion | ||
from django.conf import settings | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
("account", "0010_alter_account_is_admin"), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="Subscription", | ||
fields=[ | ||
("created_at", models.DateTimeField(auto_now_add=True)), | ||
("updated_at", models.DateTimeField(auto_now=True)), | ||
("id", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), | ||
( | ||
"is_active", | ||
models.BooleanField( | ||
default=False, | ||
help_text="Indica se a inscrição está ativa", | ||
verbose_name="Ativo", | ||
), | ||
), | ||
( | ||
"admin", | ||
models.OneToOneField( | ||
on_delete=django.db.models.deletion.DO_NOTHING, | ||
related_name="admin_subscription", | ||
to=settings.AUTH_USER_MODEL, | ||
), | ||
), | ||
("subscribers", models.ManyToManyField(to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
"verbose_name": "Subscription", | ||
"verbose_name_plural": "Subscriptions", | ||
}, | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +0,0 @@ | ||
# -*- coding: utf-8 -*- | ||
from django.contrib.auth.decorators import login_required | ||
from django.http import HttpRequest | ||
from django.utils.decorators import method_decorator | ||
from django.views import View | ||
from django.views.decorators.csrf import csrf_exempt | ||
|
||
|
||
@method_decorator(csrf_exempt, "dispatch") | ||
@method_decorator(login_required, "dispatch") | ||
class StripeCustomerSubscriptionView(View): | ||
def post(self, request: HttpRequest, account_id: str, subscription_id: str): | ||
"""Add customer to a stripe subscription""" | ||
... | ||
|
||
def delete(self, request: HttpRequest, account_id: str, subscription_id: str): | ||
"""Remove customer from a stripe subscription""" | ||
... | ||
|
||
|
||
# Reference | ||
# https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elementsf | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# -*- coding: utf-8 -*- | ||
from django.conf import settings | ||
from djstripe import webhooks | ||
from djstripe.models import Event | ||
from google.oauth2.service_account import Credentials | ||
from googleapiclient.discovery import Resource, build | ||
from googleapiclient.errors import HttpError | ||
from loguru import logger | ||
|
||
from bd_api.apps.account.models import Account, Subscription | ||
|
||
logger = logger.bind(codename="payment_webhook") | ||
|
||
|
||
def get_credentials(scopes: list[str] = None, impersonate: str = None): | ||
"""Get google credentials with scope or subject""" | ||
cred = Credentials.from_service_account_file( | ||
settings.GOOGLE_APPLICATION_CREDENTIALS, | ||
) | ||
if scopes: | ||
cred = cred.with_scopes(scopes) | ||
if impersonate: | ||
cred = cred.with_subject(impersonate) | ||
return cred | ||
|
||
|
||
def get_service() -> Resource: | ||
"""Get google directory service""" | ||
credentials = get_credentials( | ||
settings.GOOGLE_DIRECTORY_SCOPES, | ||
settings.GOOGLE_DIRECTORY_SUBJECT, | ||
) | ||
return build("admin", "directory_v1", credentials=credentials) | ||
|
||
|
||
def add_user( | ||
email: str, | ||
role: str = "MEMBER", | ||
group_key: str = settings.GOOGLE_DIRECTORY_GROUP_KEY, | ||
): | ||
"""Add user to google group""" | ||
try: | ||
service = get_service() | ||
service.members().insert( | ||
groupKey=group_key, | ||
body={"email": email, "role": role}, | ||
).execute() | ||
except HttpError as e: | ||
if e.resp.status == 490: | ||
logger.warning(f"{email} already exists") | ||
else: | ||
logger.error(e) | ||
raise e | ||
|
||
|
||
def remove_user( | ||
email: str, | ||
group_key: str = settings.GOOGLE_DIRECTORY_GROUP_KEY, | ||
) -> None: | ||
"""Remove user from google group""" | ||
try: | ||
service = get_service() | ||
service.members().delete( | ||
groupKey=group_key, | ||
memberKey=email, | ||
).execute() | ||
except Exception as e: | ||
logger.error(e) | ||
raise e | ||
|
||
|
||
@webhooks.handler("customer.subscription.created") | ||
def subscribe(event: Event, **kwargs): | ||
"""Add customer to allowed google groups""" | ||
add_user(event.customer.email) | ||
admin = Account.objects.get(email=event.customer.email).first() | ||
Subscription.objects.create(admin=admin, is_active=True) | ||
|
||
|
||
@webhooks.handler("customer.subscription.deleted") | ||
def unsubscribe(event: Event, **kwargs): | ||
"""Remove customer from allowed google groups""" | ||
remove_user(event.customer.email) | ||
admin = Account.objects.get(email=event.customer.email).first() | ||
admin.subscription.is_active = False | ||
admin.subscription.save() | ||
|
||
|
||
# Reference | ||
# https://developers.google.com/admin-sdk/directory/v1/guides/troubleshoot-error-codes | ||
# https://developers.google.com/admin-sdk/reseller/v1/support/directory_api_common_errors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters