implement credit card listing

This commit is contained in:
Nico Schottelius 2020-12-28 23:35:34 +01:00
commit e225bf1cc0
12 changed files with 183 additions and 63 deletions

View file

@ -88,5 +88,5 @@ admin.site.register(Bill, BillAdmin)
admin.site.register(ProductToRecurringPeriod)
admin.site.register(Product, ProductAdmin)
for m in [ Order, BillRecord, BillingAddress, RecurringPeriod, VATRate, StripeCustomer ]:
for m in [ Order, BillRecord, BillingAddress, RecurringPeriod, VATRate, StripeCustomer, StripeCreditCard ]:
admin.site.register(m)

View file

@ -1,4 +1,4 @@
# Generated by Django 3.1 on 2020-12-13 10:38
# Generated by Django 3.1 on 2020-12-28 22:19
from django.conf import settings
import django.core.validators
@ -83,6 +83,18 @@ class Migration(migrations.Migration):
('description', models.TextField(blank=True, default='')),
],
),
migrations.CreateModel(
name='StripeCreditCard',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('card_name', models.CharField(default='My credit card', max_length=128)),
('card_id', models.CharField(max_length=32)),
('last4', models.CharField(max_length=4)),
('brand', models.CharField(max_length=64)),
('expiry_date', models.DateField()),
('owner', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='ProductToRecurringPeriod',
fields=[
@ -140,6 +152,15 @@ class Migration(migrations.Migration):
('replaces', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='replaced_by', to='uncloud_pay.order')),
],
),
migrations.CreateModel(
name='Membership',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('starting_date', models.DateField(blank=True, null=True)),
('ending_date', models.DateField(blank=True, null=True)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='BillRecord',
fields=[

View file

@ -17,7 +17,6 @@ from django.utils import timezone
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.conf import settings
import uncloud_pay.stripe
from uncloud import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
from uncloud.models import UncloudAddress
@ -92,6 +91,21 @@ class StripeCustomer(models.Model):
def __str__(self):
return self.owner.username
class StripeCreditCard(models.Model):
owner = models.OneToOneField( get_user_model(),
on_delete=models.CASCADE)
card_name = models.CharField(null=False, max_length=128, default="My credit card")
card_id = models.CharField(null=False, max_length=32)
last4 = models.CharField(null=False, max_length=4)
brand = models.CharField(null=False, max_length=64)
expiry_date = models.DateField(null=False)
def __str__(self):
return f"{self.card_name}: {self.brand} {self.last4} ({self.expiry_date})"
###
# Payments and Payment Methods.
@ -148,14 +162,14 @@ class PaymentMethod(models.Model):
stripe_payment_method_id = models.CharField(max_length=32, blank=True, null=True)
stripe_setup_intent_id = models.CharField(max_length=32, blank=True, null=True)
@property
def stripe_card_last4(self):
if self.source == 'stripe' and self.active:
payment_method = uncloud_pay.stripe.get_payment_method(
self.stripe_payment_method_id)
return payment_method.card.last4
else:
return None
# @property
# def stripe_card_last4(self):
# if self.source == 'stripe' and self.active:
# payment_method = uncloud_pay.stripe.get_payment_method(
# self.stripe_payment_method_id)
# return payment_method.card.last4
# else:
# return None
@property
def active(self):
@ -1261,3 +1275,24 @@ class ProductToRecurringPeriod(models.Model):
def __str__(self):
return f"{self.product} - {self.recurring_period} (default: {self.is_default})"
class Membership(models.Model):
owner = models.ForeignKey(get_user_model(),
on_delete=models.CASCADE)
starting_date = models.DateField(blank=True, null=True)
ending_date = models.DateField(blank=True, null=True)
@classmethod
def user_has_membership(user, when):
"""
Return true if user has membership at a point of time,
return false if that is not the case
"""
pass
# cls.objects.filter(owner=user,
# starting_date)

View file

@ -6,13 +6,20 @@ from django.utils.translation import gettext_lazy as _
from .models import *
###
# Checked code
# 2020-12 Checked code
class StripeCreditCardSerializer(serializers.ModelSerializer):
class Meta:
model = StripeCreditCard
exclude = ['card_id', "owner" ]
read_only_fields = [ "last4", "brand", "expiry_date" ]
################################################################################
# Unchecked code
###
# Payments and Payment Methods.
@ -21,13 +28,6 @@ class PaymentSerializer(serializers.ModelSerializer):
model = Payment
fields = '__all__'
class PaymentMethodSerializer(serializers.ModelSerializer):
stripe_card_last4 = serializers.IntegerField()
class Meta:
model = PaymentMethod
fields = [ 'source', 'description', 'primary', 'stripe_card_last4', 'active']
class UpdatePaymentMethodSerializer(serializers.ModelSerializer):
class Meta:
model = PaymentMethod

View file

@ -1,11 +1,13 @@
import stripe
import stripe.error
import logging
import datetime
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings
from django.contrib.auth import get_user_model
import uncloud_pay.models
from .models import StripeCustomer, StripeCreditCard
CURRENCY = 'chf'
@ -56,12 +58,12 @@ def public_api_key():
def get_customer_id_for(user):
try:
# .get() raise if there is no matching entry.
return uncloud_pay.models.StripeCustomer.objects.get(owner=user).stripe_id
return StripeCustomer.objects.get(owner=user).stripe_id
except ObjectDoesNotExist:
# No entry yet - making a new one.
try:
customer = create_customer(user.username, user.email)
uncloud_stripe_mapping = uncloud_pay.models.StripeCustomer.objects.create(
uncloud_stripe_mapping = StripeCustomer.objects.create(
owner=user, stripe_id=customer.id)
return uncloud_stripe_mapping.stripe_id
except Exception as e:
@ -109,6 +111,7 @@ def get_customer_cards(customer_id):
customer=customer_id,
type="card",
)
print(stripe_cards["data"])
for stripe_card in stripe_cards["data"]:
card = {}
@ -116,8 +119,24 @@ def get_customer_cards(customer_id):
card['last4'] = stripe_card["card"]["last4"]
card['month'] = stripe_card["card"]["exp_month"]
card['year'] = stripe_card["card"]["exp_year"]
card['id'] = stripe_card["card"]["id"]
card['id'] = stripe_card["id"]
cards.append(card)
return cards
def sync_cards_for_user(user):
customer_id = get_customer_id_for(user)
cards = get_customer_cards(customer_id)
for card in cards:
StripeCreditCard.objects.get_or_create(card_id=card['id'],
owner = user,
defaults = {
'last4': card['last4'],
'brand': card['brand'],
'expiry_date': datetime.date(card['year'],
card['month'],
1)
}
)

View file

@ -63,7 +63,8 @@
} else {
// Return to API on success.
document.getElementById("ungleichmessage").innerHTML
= "Registered credit card with Stripe."
= "Registered credit card with
Stripe. <a href="/">Return to the main page.</a>"
}
});
});

View file

@ -31,22 +31,7 @@ import uncloud_pay.stripe as uncloud_stripe
logger = logging.getLogger(__name__)
###
# Payments and Payment Methods.
class PaymentViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = PaymentSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return Payment.objects.filter(owner=self.request.user)
class OrderViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = OrderSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return Order.objects.filter(owner=self.request.user)
# 2020-12 checked code
class RegisterCard(LoginRequiredMixin, TemplateView):
login_url = '/login/'
@ -65,6 +50,44 @@ class RegisterCard(LoginRequiredMixin, TemplateView):
context['stripe_pk'] = uncloud_stripe.public_api_key
return context
class CreditCardViewSet(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet):
serializer_class = StripeCreditCardSerializer
permission_classes = [permissions.IsAuthenticated]
def list(self, request):
uncloud_stripe.sync_cards_for_user(self.request.user)
return super().list(request)
def get_queryset(self):
return StripeCreditCard.objects.filter(owner=self.request.user)
###
# Payments and Payment Methods.
class PaymentViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = PaymentSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return Payment.objects.filter(owner=self.request.user)
class OrderViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = OrderSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return Order.objects.filter(owner=self.request.user)
class ListCards(LoginRequiredMixin, TemplateView):
login_url = '/login/'
@ -80,22 +103,6 @@ class ListCards(LoginRequiredMixin, TemplateView):
return context
class DeleteCard(LoginRequiredMixin, TemplateView):
login_url = '/login/'
template_name = "uncloud_pay/delete_stripe_card.html"
def get_context_data(self, **kwargs):
customer_id = uncloud_stripe.get_customer_id_for(self.request.user)
cards = uncloud_stripe.get_customer_cards(customer_id)
context = super().get_context_data(**kwargs)
context['cards'] = cards
context['username'] = self.request.user
return context
class PaymentMethodViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated]