Remove poetry #93019
@ -34,7 +34,7 @@ python3.10 -m venv .venv ; source .venv/bin/activate
|
||||
After `virtualenv` has been created and activated, continue with the following:
|
||||
|
||||
```
|
||||
pip install -r requirements_dev.txt # Install dependencies.
|
||||
pip install -e .[dev] # Install package and its development dependencies.
|
||||
cp looper_example_project/settings.example.py looper_example_project/settings.py # Adjust settings.py as needed.
|
||||
# Make sure you have your database setup according to your settings.py
|
||||
./manage.py migrate # Create database tables and run migrations.
|
||||
|
@ -1,159 +0,0 @@
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.views.generic import UpdateView, ListView, DeleteView, DetailView, View
|
||||
import stripe
|
||||
|
||||
from .. import forms, models, pdf, stripe_utils
|
||||
from looper import admin_log
|
||||
import looper.views.checkout
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@login_required
|
||||
def settings_receipts(request):
|
||||
order_set = request.user.customer.order_set
|
||||
orders = order_set.paid().select_related('subscription__plan')
|
||||
context = {
|
||||
'orders': orders,
|
||||
'can_woosh': request.user.is_staff and request.user.has_perm('looper.change_order')
|
||||
}
|
||||
|
||||
return render(request, 'looper/settings/receipts.html', context=context)
|
||||
|
||||
|
||||
class ReceiptView(LoginRequiredMixin, DetailView):
|
||||
template_name = 'looper/settings/receipt.html'
|
||||
pk_url_kwarg = 'order_id'
|
||||
|
||||
def get_queryset(self):
|
||||
"""Limit the allowed order IDs to the ones owned by the user.
|
||||
|
||||
Staff users with the permission to view orders can access
|
||||
all orders of all users.
|
||||
"""
|
||||
user = self.request.user
|
||||
if user.is_staff and user.has_perm('looper.view_order'):
|
||||
return looper.models.Order.objects.paid()
|
||||
return user.customer.order_set.paid()
|
||||
|
||||
|
||||
class ReceiptPDFView(ReceiptView):
|
||||
template_name = 'looper/settings/receipt_pdf.html'
|
||||
response_class = pdf.PDFResponse
|
||||
|
||||
|
||||
class BillingAddressView(LoginRequiredMixin, UpdateView):
|
||||
template_name = 'looper/settings/billing_address.html'
|
||||
form_class = forms.AddressForm
|
||||
model = models.Address
|
||||
success_url = reverse_lazy('looper:settings_billing_info')
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return self.request.user.customer.billing_address
|
||||
|
||||
|
||||
class PaymentMethodsView(LoginRequiredMixin, ListView):
|
||||
template_name = 'looper/settings/payment_methods.html'
|
||||
model = models.PaymentMethod
|
||||
|
||||
def get_queryset(self):
|
||||
superset = super().get_queryset()
|
||||
return superset.filter(customer=self.request.user.customer, is_deleted=False)
|
||||
|
||||
|
||||
class PaymentMethodDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
||||
template_name = 'looper/settings/payment_method_delete.html'
|
||||
model = models.PaymentMethod
|
||||
success_url = reverse_lazy('looper:payment_methods')
|
||||
|
||||
log = log.getChild('PaymentMethodDeleteView')
|
||||
|
||||
def has_permission(self) -> bool:
|
||||
payment_method = self.get_object()
|
||||
if self.request.user.customer.pk == payment_method.customer.pk:
|
||||
return True
|
||||
self.log.error(
|
||||
"User %d tried to delete someone else's payment method %d",
|
||||
self.request.user.pk,
|
||||
payment_method.pk,
|
||||
)
|
||||
return False
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
payment_method = self.get_object()
|
||||
message = (
|
||||
'User pk=%s is deleting payment method pk=%s',
|
||||
self.request.user.id,
|
||||
payment_method.pk,
|
||||
)
|
||||
admin_log.attach_log_entry(payment_method, message[0] % message[1:])
|
||||
|
||||
self.log.info(message)
|
||||
return super().delete(request, *args, **kwargs)
|
||||
|
||||
|
||||
class PaymentMethodChangeView(LoginRequiredMixin, View):
|
||||
"""Redirect to Stripe setup to switch a subscription's payment method."""
|
||||
|
||||
log = log.getChild('PaymentMethodChangeView')
|
||||
subscription: models.Subscription
|
||||
success_url = '/'
|
||||
cancel_url = 'settings_home'
|
||||
|
||||
def get_cancel_url(self):
|
||||
return reverse(self.cancel_url)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.subscription = get_object_or_404(
|
||||
request.user.customer.subscription_set, pk=kwargs['subscription_id']
|
||||
)
|
||||
success_url = self.request.build_absolute_uri(
|
||||
reverse(
|
||||
self.success_url,
|
||||
kwargs={
|
||||
'subscription_id': self.subscription.id,
|
||||
'stripe_session_id': 'CHECKOUT_SESSION_ID',
|
||||
}
|
||||
)
|
||||
)
|
||||
# we have to do it to avoid uri-encoding of curly braces,
|
||||
# otherwise stripe doesn't do the template substitution
|
||||
success_url = success_url.replace('CHECKOUT_SESSION_ID', '{CHECKOUT_SESSION_ID}', 1)
|
||||
cancel_url = self.request.build_absolute_uri(self.get_cancel_url())
|
||||
session = stripe_utils.setup_stripe_payment_method(
|
||||
self.subscription.currency.lower(),
|
||||
request.user.customer,
|
||||
success_url,
|
||||
cancel_url,
|
||||
setup_intent_metadata={
|
||||
'subscription_id': self.subscription.id,
|
||||
},
|
||||
)
|
||||
return redirect(session.url)
|
||||
|
||||
|
||||
class PaymentMethodChangeDoneView(LoginRequiredMixin, View):
|
||||
log = log.getChild('PaymentMethodChangeDoneView')
|
||||
subscription: models.Subscription
|
||||
success_url = '/'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.subscription = get_object_or_404(
|
||||
request.user.customer.subscription_set, pk=kwargs['subscription_id']
|
||||
)
|
||||
stripe_session_id = self.kwargs['stripe_session_id']
|
||||
stripe_session = stripe.checkout.Session.retrieve(
|
||||
stripe_session_id,
|
||||
expand=['setup_intent', 'setup_intent.payment_method']
|
||||
)
|
||||
setup_intent = stripe_session.setup_intent
|
||||
assert stripe_session.status == 'complete'
|
||||
assert setup_intent.status == 'succeeded'
|
||||
stripe_utils.process_setup_intent_for_subscription(setup_intent, self.subscription)
|
||||
|
||||
return redirect(self.success_url)
|
118
pyproject.toml
118
pyproject.toml
@ -1,8 +1,7 @@
|
||||
[project]
|
||||
name = "looper"
|
||||
version = "3.4.0rc1"
|
||||
version = "3.4.0rc2"
|
||||
description = "Billing system used in Blender Development Fund and Blender Studio website."
|
||||
readme = {file = "README.md", content-type = "text/markdown"}
|
||||
authors = [
|
||||
{name = "Francesco Siddi", email = "francesco@blender.org"},
|
||||
{name = "Pablo Vazquez", email = "pablo@blender.org"},
|
||||
@ -13,17 +12,116 @@ authors = [
|
||||
]
|
||||
license = {text = "GPL-3.0"}
|
||||
requires-python = "== 3.10.*"
|
||||
dependencies = [
|
||||
"appdirs==1.4.4",
|
||||
"arabic-reshaper==3.0.0",
|
||||
"asgiref==3.6.0",
|
||||
"asn1crypto==1.5.1",
|
||||
"attrs==19.3.0",
|
||||
"babel==2.12.1",
|
||||
"bleach==3.3.1",
|
||||
"braintree==4.17.1",
|
||||
"cached-property==1.5.2",
|
||||
"certifi==2022.12.7",
|
||||
"cffi==1.15.1",
|
||||
"charset-normalizer==3.0.1",
|
||||
"click==8.1.3",
|
||||
"colorhash==1.2.1",
|
||||
"cryptography==39.0.0",
|
||||
"cssselect2==0.7.0",
|
||||
"defusedxml==0.7.1",
|
||||
"django-countries==7.5.1",
|
||||
"django-nested-admin==4.0.2",
|
||||
"django-pipeline==3.1.0",
|
||||
"django==4.2.13",
|
||||
"geoip2==3.0.0",
|
||||
"html5lib==1.1",
|
||||
"idna==3.4",
|
||||
"isodate==0.6.1",
|
||||
"lxml==4.9.2",
|
||||
"maxminddb==2.2.0",
|
||||
"oscrypto==1.3.0",
|
||||
"packaging==23.0",
|
||||
"pillow==9.4.0",
|
||||
"pycparser==2.21",
|
||||
"pyhanko-certvalidator==0.20.1",
|
||||
"pyhanko==0.17.0",
|
||||
"pypdf==3.5.0",
|
||||
"python-bidi==0.4.2",
|
||||
"python-dateutil==2.8.2",
|
||||
"python-monkey-business==1.0.0",
|
||||
"python-stdnum==1.18",
|
||||
"pytz-deprecation-shim==0.1.0.post0",
|
||||
"pytz==2022.7.1",
|
||||
"pyyaml==6.0",
|
||||
"qrcode==7.4.2",
|
||||
"reportlab==3.6.9",
|
||||
"requests-file==1.5.1",
|
||||
"requests-toolbelt==0.10.1",
|
||||
"requests==2.31.0",
|
||||
"six==1.16.0",
|
||||
"sqlparse==0.4.3",
|
||||
"stripe==7.1.0",
|
||||
"svglib==1.5.1",
|
||||
"tinycss2==1.2.1",
|
||||
"tqdm==4.64.1",
|
||||
"typing-extensions==4.8.0",
|
||||
"tzdata==2022.7",
|
||||
"tzlocal==4.2",
|
||||
"uritools==4.0.1",
|
||||
"urllib3==1.26.14",
|
||||
"webencodings==0.5.1",
|
||||
"xhtml2pdf==0.2.9",
|
||||
"zeep==4.0.0",
|
||||
]
|
||||
readme = {file = "README.md", content-type = "text/markdown"}
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"black==22.12.0",
|
||||
"django-stubs-ext==0.7.0",
|
||||
"django-stubs==1.13.1",
|
||||
"factory-boy==3.3.0",
|
||||
"faker==20.1.0",
|
||||
"flake8-implicit-str-concat==0.1.0",
|
||||
"flake8==3.9.2",
|
||||
"ghp-import==2.1.0",
|
||||
"importlib-metadata==6.0.0",
|
||||
"jinja2==3.1.2",
|
||||
"markdown==3.3.7",
|
||||
"markupsafe==2.1.2",
|
||||
"mccabe==0.6.1",
|
||||
"mergedeep==1.3.4",
|
||||
"mkdocs-material==4.6.3",
|
||||
"mkdocs==1.4.2",
|
||||
"mypy-extensions==0.4.4",
|
||||
"mypy==0.991",
|
||||
"pathspec==0.10.3",
|
||||
"platformdirs==2.6.2",
|
||||
"psycopg2==2.9.5",
|
||||
"pycodestyle==2.7.0",
|
||||
"pyflakes==2.3.1",
|
||||
"pygments==2.14.0",
|
||||
"pymdown-extensions==9.9.2",
|
||||
"pyyaml-env-tag==0.1",
|
||||
"responses==0.25.3",
|
||||
"tblib==3.0.0",
|
||||
"tomli==2.0.1",
|
||||
"types-pytz==2022.7.1.0",
|
||||
"types-pyyaml==6.0.12.2",
|
||||
"watchdog==2.3.1",
|
||||
"zipp==3.15.0",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=61"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["."] # list of folders that contain the packages (["."] by default)
|
||||
include = ["looper"] # package names should match these glob patterns (["*"] by default)
|
||||
exclude = [] # exclude packages matching these glob patterns (empty by default)
|
||||
namespaces = false # to disable scanning PEP 420 namespaces (true by default)
|
||||
where = ["."]
|
||||
include = ["looper"] # alternatively: `exclude = ["additional*"]`
|
||||
namespaces = false # Disable implicit namespaces
|
||||
|
||||
[tool.black]
|
||||
line-length = 100
|
||||
skip_string_normalization = true
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=61", "pyodbc"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
@ -1,61 +0,0 @@
|
||||
appdirs==1.4.4
|
||||
arabic-reshaper==3.0.0
|
||||
asgiref==3.6.0
|
||||
asn1crypto==1.5.1
|
||||
attrs==19.3.0
|
||||
babel==2.12.1
|
||||
bleach==3.3.1
|
||||
braintree==4.17.1
|
||||
cached-property==1.5.2
|
||||
certifi==2022.12.7
|
||||
cffi==1.15.1
|
||||
charset-normalizer==3.0.1
|
||||
click==8.1.3
|
||||
colorama==0.4.6 ; platform_system == "Windows"
|
||||
colorhash==1.2.1
|
||||
cryptography==39.0.0
|
||||
cssselect2==0.7.0
|
||||
defusedxml==0.7.1
|
||||
django-countries==7.5.1
|
||||
django-nested-admin==4.0.2
|
||||
django-pipeline==3.1.0
|
||||
django==4.2.13
|
||||
geoip2==3.0.0
|
||||
html5lib==1.1
|
||||
idna==3.4
|
||||
isodate==0.6.1
|
||||
lxml==4.9.2
|
||||
maxminddb==2.2.0
|
||||
oscrypto==1.3.0
|
||||
packaging==23.0
|
||||
pillow==9.4.0
|
||||
pycparser==2.21
|
||||
pyhanko-certvalidator==0.20.1
|
||||
pyhanko==0.17.0
|
||||
pypdf==3.5.0
|
||||
python-bidi==0.4.2
|
||||
python-dateutil==2.8.2
|
||||
python-monkey-business==1.0.0
|
||||
python-stdnum==1.18
|
||||
pytz-deprecation-shim==0.1.0.post0
|
||||
pytz==2022.7.1
|
||||
pyyaml==6.0
|
||||
qrcode==7.4.2
|
||||
reportlab==3.6.9
|
||||
requests-file==1.5.1
|
||||
requests-toolbelt==0.10.1
|
||||
requests==2.31.0
|
||||
six==1.16.0
|
||||
sqlparse==0.4.3
|
||||
stripe==7.1.0
|
||||
svglib==1.5.1
|
||||
tinycss2==1.2.1
|
||||
tqdm==4.64.1
|
||||
typing-extensions==4.8.0
|
||||
tzdata==2022.7
|
||||
tzlocal==4.2
|
||||
uritools==4.0.1
|
||||
urllib3==1.26.14
|
||||
webencodings==0.5.1
|
||||
xhtml2pdf==0.2.9
|
||||
zeep==4.0.0
|
@ -1,35 +0,0 @@
|
||||
-r requirements.txt
|
||||
black==22.12.0
|
||||
django-stubs-ext==0.7.0
|
||||
django-stubs==1.13.1
|
||||
factory-boy==3.3.0
|
||||
faker==20.1.0
|
||||
flake8-implicit-str-concat==0.1.0
|
||||
flake8==3.9.2
|
||||
ghp-import==2.1.0
|
||||
importlib-metadata==6.0.0
|
||||
jinja2==3.1.2
|
||||
markdown==3.3.7
|
||||
markupsafe==2.1.2
|
||||
mccabe==0.6.1
|
||||
mergedeep==1.3.4
|
||||
mkdocs-material==4.6.3
|
||||
mkdocs==1.4.2
|
||||
mypy-extensions==0.4.4
|
||||
mypy==0.991
|
||||
pathspec==0.10.3
|
||||
platformdirs==2.6.2
|
||||
psycopg2==2.9.5
|
||||
pycodestyle==2.7.0
|
||||
pyflakes==2.3.1
|
||||
pygments==2.14.0
|
||||
pymdown-extensions==9.9.2
|
||||
pyyaml-env-tag==0.1
|
||||
responses==0.25.3
|
||||
tblib==3.0.0
|
||||
tomli==2.0.1
|
||||
types-pytz==2022.7.1.0
|
||||
types-pyyaml==6.0.12.2
|
||||
tzdata==2022.7 ; sys_platform == "win32"
|
||||
watchdog==2.3.1
|
||||
zipp==3.15.0
|
Loading…
Reference in New Issue
Block a user