Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store Stripe billing info + auto recharge without subscription #421

Merged
merged 10 commits into from
Aug 23, 2024
5 changes: 0 additions & 5 deletions app_users/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,3 @@ class AppUsersConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "app_users"
verbose_name = "App Users"

def ready(self):
from . import signals

assert signals
19 changes: 0 additions & 19 deletions app_users/signals.py

This file was deleted.

54 changes: 54 additions & 0 deletions app_users/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import stripe
from loguru import logger

from app_users.models import PaymentProvider, TransactionReason
from celeryapp.celeryconfig import app
from payments.models import Subscription
from payments.plans import PricingPlan
from payments.webhooks import set_user_subscription


@app.task
def save_stripe_default_payment_method(
*,
payment_intent_id: str,
uid: str,
amount: int,
charged_amount: int,
reason: TransactionReason,
):
pi = stripe.PaymentIntent.retrieve(payment_intent_id, expand=["payment_method"])
pm = pi.payment_method
if not (pm and pm.customer):
logger.error(
f"Failed to retrieve payment method for payment intent {payment_intent_id}"
)
return

# update customer's defualt payment method
# note: if a customer has an active subscription, the payment method attached there will be preferred
# see `stripe_get_default_payment_method` in payments/models.py module
logger.info(
f"Updating default payment method for customer {pm.customer} to {pm.id}"
)
stripe.Customer.modify(
pm.customer,
invoice_settings=dict(default_payment_method=pm),
)

# if user doesn't already have a active billing/autorecharge info, so we don't need to do anything
# set user's subscription to the free plan
if (
reason == TransactionReason.ADDON
and not Subscription.objects.filter(
user__uid=uid, payment_provider__isnull=False
nikochiko marked this conversation as resolved.
Show resolved Hide resolved
).exists()
):
set_user_subscription(
uid=uid,
plan=PricingPlan.STARTER,
provider=PaymentProvider.STRIPE,
external_id=None,
amount=amount,
charged_amount=charged_amount,
)
69 changes: 24 additions & 45 deletions daras_ai_v2/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from daras_ai_v2.exceptions import InsufficientCredits
from daras_ai_v2.fastapi_tricks import get_route_path
from daras_ai_v2.grid_layout_widget import grid_layout
from daras_ai_v2.gui_confirm import confirm_modal
from daras_ai_v2.html_spinner_widget import html_spinner
from daras_ai_v2.manage_api_keys_widget import manage_api_keys
from daras_ai_v2.meta_preview_url import meta_preview_url
Expand Down Expand Up @@ -697,10 +698,29 @@ def _render_options_modal(
save_as_new_button = gui.button(
f"{save_as_new_icon} Save as New", className="w-100"
)
delete_button = not published_run.is_root() and gui.button(
f'<i class="fa-regular fa-trash"></i> Delete',
className="w-100 text-danger",
)

if not published_run.is_root():
confirm_delete_modal, confirmed = confirm_modal(
title="Are you sure?",
key="--delete-run-modal",
text=f"""
Are you sure you want to delete this published run?

**{published_run.title}**

This will also delete all the associated versions.
""",
button_label="Delete",
button_class="border-danger bg-danger text-white",
)
if gui.button(
f'<i class="fa-regular fa-trash"></i> Delete',
className="w-100 text-danger",
):
confirm_delete_modal.open()
if confirmed:
published_run.delete()
raise gui.RedirectException(self.app_url())

if duplicate_button:
duplicate_pr = self.duplicate_published_run(
Expand Down Expand Up @@ -730,47 +750,6 @@ def _render_options_modal(
gui.write("#### Version History", className="mb-4")
self._render_version_history()

confirm_delete_modal = gui.Modal("Confirm Delete", key="confirm-delete-modal")
if delete_button:
confirm_delete_modal.open()
if confirm_delete_modal.is_open():
modal.empty()
with confirm_delete_modal.container():
self._render_confirm_delete_modal(
published_run=published_run,
modal=confirm_delete_modal,
)

def _render_confirm_delete_modal(
self,
*,
published_run: PublishedRun,
modal: gui.Modal,
):
gui.write(
"Are you sure you want to delete this published run? "
f"_({published_run.title})_"
)
gui.caption("This will also delete all the associated versions.")
with gui.div(className="d-flex"):
confirm_button = gui.button(
'<span class="text-danger">Confirm</span>',
type="secondary",
className="w-100",
)
cancel_button = gui.button(
"Cancel",
type="secondary",
className="w-100",
)

if confirm_button:
published_run.delete()
raise gui.RedirectException(self.app_url())

if cancel_button:
modal.close()

def _render_admin_options(self, current_run: SavedRun, published_run: PublishedRun):
if (
not self.is_current_user_admin()
Expand Down
Loading
Loading