[credit card] implement payment
This commit is contained in:
		
					parent
					
						
							
								e225bf1cc0
							
						
					
				
			
			
				commit
				
					
						1b06d8ee03
					
				
			
		
					 16 changed files with 290 additions and 64 deletions
				
			
		| 
						 | 
				
			
			@ -47,12 +47,12 @@ router.register(r'v1/service/generic', serviceviews.GenericServiceProductViewSet
 | 
			
		|||
router.register(r'v1/my/address', payviews.BillingAddressViewSet, basename='billingaddress')
 | 
			
		||||
router.register(r'v1/my/bill', payviews.BillViewSet, basename='bill')
 | 
			
		||||
router.register(r'v1/my/order', payviews.OrderViewSet, basename='order')
 | 
			
		||||
router.register(r'v1/my/payment', payviews.PaymentViewSet, basename='payment')
 | 
			
		||||
#router.register(r'v1/my/payment', payviews.PaymentViewSet, basename='payment')
 | 
			
		||||
router.register(r'v1/my/payment-method', payviews.PaymentMethodViewSet, basename='payment-method')
 | 
			
		||||
 | 
			
		||||
# admin/staff urls
 | 
			
		||||
router.register(r'v1/admin/bill', payviews.AdminBillViewSet, basename='admin/bill')
 | 
			
		||||
router.register(r'v1/admin/payment', payviews.AdminPaymentViewSet, basename='admin/payment')
 | 
			
		||||
#router.register(r'v1/admin/payment', payviews.AdminPaymentViewSet, basename='admin/payment')
 | 
			
		||||
router.register(r'v1/admin/order', payviews.AdminOrderViewSet, basename='admin/order')
 | 
			
		||||
router.register(r'v1/admin/vmhost', vmviews.VMHostViewSet)
 | 
			
		||||
router.register(r'v1/admin/vmcluster', vmviews.VMClusterViewSet)
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +74,7 @@ router.register(r'v2/net/wireguardvpnsizes', netviews.WireGuardVPNSizes, basenam
 | 
			
		|||
 | 
			
		||||
# Payment related
 | 
			
		||||
router.register(r'v2/payment/credit-card', payviews.CreditCardViewSet, basename='credit-card')
 | 
			
		||||
router.register(r'v2/payment/payment', payviews.PaymentViewSet, basename='payment')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
| 
						 | 
				
			
			@ -83,16 +84,13 @@ urlpatterns = [
 | 
			
		|||
    path('openapi', get_schema_view(
 | 
			
		||||
        title="uncloud",
 | 
			
		||||
        description="uncloud API",
 | 
			
		||||
        version="1.0.0"
 | 
			
		||||
        version="2.0.0"
 | 
			
		||||
    ), name='openapi-schema'),
 | 
			
		||||
 | 
			
		||||
    # web/ = stuff to view in the browser
 | 
			
		||||
#    path('web/vpn/create/', netviews.WireGuardVPNCreateView.as_view(), name="vpncreate"),
 | 
			
		||||
    path('admin/', admin.site.urls),
 | 
			
		||||
 | 
			
		||||
    path('login/', authviews.LoginView.as_view(), name="login"),
 | 
			
		||||
    path('logout/', authviews.LogoutView.as_view(), name="logout"),
 | 
			
		||||
    path('admin/', admin.site.urls),
 | 
			
		||||
 | 
			
		||||
    path('cc/reg/', payviews.RegisterCard.as_view(), name="cc_register"),
 | 
			
		||||
 | 
			
		||||
    path('', uncloudviews.UncloudIndex.as_view(), name="uncloudindex"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,10 +10,8 @@ from django.core.files.temp import NamedTemporaryFile
 | 
			
		|||
from django.http import FileResponse
 | 
			
		||||
from django.template.loader import render_to_string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from uncloud_pay.models import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BillRecordInline(admin.TabularInline):
 | 
			
		||||
    model = BillRecord
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,8 +83,17 @@ class BillAdmin(admin.ModelAdmin):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
admin.site.register(Bill, BillAdmin)
 | 
			
		||||
admin.site.register(ProductToRecurringPeriod)
 | 
			
		||||
admin.site.register(Product, ProductAdmin)
 | 
			
		||||
 | 
			
		||||
for m in [ Order, BillRecord, BillingAddress, RecurringPeriod, VATRate, StripeCustomer, StripeCreditCard ]:
 | 
			
		||||
for m in [
 | 
			
		||||
        BillRecord,
 | 
			
		||||
        BillingAddress,
 | 
			
		||||
        Order,
 | 
			
		||||
        Payment,
 | 
			
		||||
        ProductToRecurringPeriod,
 | 
			
		||||
        RecurringPeriod,
 | 
			
		||||
        StripeCreditCard,
 | 
			
		||||
        StripeCustomer,
 | 
			
		||||
        VATRate,
 | 
			
		||||
]:
 | 
			
		||||
    admin.site.register(m)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										24
									
								
								uncloud_pay/migrations/0002_auto_20201228_2244.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								uncloud_pay/migrations/0002_auto_20201228_2244.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-28 22:44
 | 
			
		||||
 | 
			
		||||
import django.core.validators
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('uncloud_pay', '0001_initial'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='payment',
 | 
			
		||||
            name='currency',
 | 
			
		||||
            field=models.CharField(choices=[('CHF', 'Swiss Franc'), ('EUR', 'Euro'), ('USD', 'US Dollar')], default='CHF', max_length=32),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='payment',
 | 
			
		||||
            name='amount',
 | 
			
		||||
            field=models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										28
									
								
								uncloud_pay/migrations/0003_auto_20201228_2256.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								uncloud_pay/migrations/0003_auto_20201228_2256.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-28 22:56
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('uncloud_pay', '0002_auto_20201228_2244'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='order',
 | 
			
		||||
            name='currency',
 | 
			
		||||
            field=models.CharField(choices=[('CHF', 'Swiss Franc')], default='CHF', max_length=32),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='payment',
 | 
			
		||||
            name='currency',
 | 
			
		||||
            field=models.CharField(choices=[('CHF', 'Swiss Franc')], default='CHF', max_length=32),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='product',
 | 
			
		||||
            name='currency',
 | 
			
		||||
            field=models.CharField(choices=[('CHF', 'Swiss Franc')], default='CHF', max_length=32),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										18
									
								
								uncloud_pay/migrations/0004_stripecreditcard_active.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								uncloud_pay/migrations/0004_stripecreditcard_active.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-28 23:34
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('uncloud_pay', '0003_auto_20201228_2256'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='stripecreditcard',
 | 
			
		||||
            name='active',
 | 
			
		||||
            field=models.BooleanField(default=True),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										18
									
								
								uncloud_pay/migrations/0005_auto_20201228_2335.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								uncloud_pay/migrations/0005_auto_20201228_2335.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-28 23:35
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('uncloud_pay', '0004_stripecreditcard_active'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='stripecreditcard',
 | 
			
		||||
            name='active',
 | 
			
		||||
            field=models.BooleanField(default=False),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										21
									
								
								uncloud_pay/migrations/0006_auto_20201228_2337.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								uncloud_pay/migrations/0006_auto_20201228_2337.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-28 23:37
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
			
		||||
        ('uncloud_pay', '0005_auto_20201228_2335'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='stripecreditcard',
 | 
			
		||||
            name='owner',
 | 
			
		||||
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										17
									
								
								uncloud_pay/migrations/0007_auto_20201228_2338.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								uncloud_pay/migrations/0007_auto_20201228_2338.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-28 23:38
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('uncloud_pay', '0006_auto_20201228_2337'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddConstraint(
 | 
			
		||||
            model_name='stripecreditcard',
 | 
			
		||||
            constraint=models.UniqueConstraint(condition=models.Q(active=True), fields=('owner',), name='one_active_card_per_user'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										18
									
								
								uncloud_pay/migrations/0008_payment_external_reference.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								uncloud_pay/migrations/0008_payment_external_reference.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-29 00:04
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('uncloud_pay', '0007_auto_20201228_2338'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='payment',
 | 
			
		||||
            name='external_reference',
 | 
			
		||||
            field=models.CharField(default='', max_length=256),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										18
									
								
								uncloud_pay/migrations/0009_auto_20201229_0037.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								uncloud_pay/migrations/0009_auto_20201229_0037.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-29 00:37
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('uncloud_pay', '0008_payment_external_reference'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='payment',
 | 
			
		||||
            name='external_reference',
 | 
			
		||||
            field=models.CharField(blank=True, default='', max_length=256, null=True),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										19
									
								
								uncloud_pay/migrations/0010_auto_20201229_0042.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								uncloud_pay/migrations/0010_auto_20201229_0042.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
# Generated by Django 3.1 on 2020-12-29 00:42
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.utils.timezone
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('uncloud_pay', '0009_auto_20201229_0037'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='payment',
 | 
			
		||||
            name='timestamp',
 | 
			
		||||
            field=models.DateTimeField(default=django.utils.timezone.now),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +64,8 @@ class Currency(models.TextChoices):
 | 
			
		|||
    Possible currencies to be billed
 | 
			
		||||
    """
 | 
			
		||||
    CHF   = 'CHF', _('Swiss Franc')
 | 
			
		||||
    EUR   = 'EUR', _('Euro')
 | 
			
		||||
    USD   = 'USD', _('US Dollar')
 | 
			
		||||
#    EUR   = 'EUR', _('Euro')
 | 
			
		||||
#    USD   = 'USD', _('US Dollar')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_balance_for_user(user):
 | 
			
		||||
| 
						 | 
				
			
			@ -93,28 +93,30 @@ class StripeCustomer(models.Model):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class StripeCreditCard(models.Model):
 | 
			
		||||
    owner = models.OneToOneField( get_user_model(),
 | 
			
		||||
            on_delete=models.CASCADE)
 | 
			
		||||
    owner = models.ForeignKey(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)
 | 
			
		||||
    active = models.BooleanField(default=False)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        constraints = [
 | 
			
		||||
            models.UniqueConstraint(fields=['owner'],
 | 
			
		||||
                                    condition=Q(active=True),
 | 
			
		||||
                                    name='one_active_card_per_user')
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"{self.card_name}: {self.brand} {self.last4} ({self.expiry_date})"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
###
 | 
			
		||||
# Payments and Payment Methods.
 | 
			
		||||
 | 
			
		||||
class Payment(models.Model):
 | 
			
		||||
    owner = models.ForeignKey(get_user_model(),
 | 
			
		||||
            on_delete=models.CASCADE)
 | 
			
		||||
    owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
 | 
			
		||||
 | 
			
		||||
    amount = models.DecimalField(
 | 
			
		||||
            default=0.0,
 | 
			
		||||
            max_digits=AMOUNT_MAX_DIGITS,
 | 
			
		||||
            decimal_places=AMOUNT_DECIMALS,
 | 
			
		||||
            validators=[MinValueValidator(0)])
 | 
			
		||||
| 
						 | 
				
			
			@ -128,21 +130,18 @@ class Payment(models.Model):
 | 
			
		|||
                                  ('unknown', 'Unknown')
 | 
			
		||||
                              ),
 | 
			
		||||
                              default='unknown')
 | 
			
		||||
    timestamp = models.DateTimeField(editable=False, auto_now_add=True)
 | 
			
		||||
 | 
			
		||||
    # We override save() in order to active products awaiting payment.
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        # _state.adding is switched to false after super(...) call.
 | 
			
		||||
        being_created = self._state.adding
 | 
			
		||||
    timestamp = models.DateTimeField(default=timezone.now)
 | 
			
		||||
 | 
			
		||||
        unpaid_bills_before_payment = Bill.get_unpaid_for(self.owner)
 | 
			
		||||
        super(Payment, self).save(*args, **kwargs) # Save payment in DB.
 | 
			
		||||
        unpaid_bills_after_payment = Bill.get_unpaid_for(self.owner)
 | 
			
		||||
    currency = models.CharField(max_length=32, choices=Currency.choices, default=Currency.CHF)
 | 
			
		||||
 | 
			
		||||
        newly_paid_bills = list(
 | 
			
		||||
                set(unpaid_bills_before_payment) - set(unpaid_bills_after_payment))
 | 
			
		||||
        for bill in newly_paid_bills:
 | 
			
		||||
            bill.activate_products()
 | 
			
		||||
    external_reference = models.CharField(max_length=256, default="", null=True, blank=True)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"{self.amount}{self.currency} from {self.owner} via {self.source} on {self.timestamp}"
 | 
			
		||||
 | 
			
		||||
###
 | 
			
		||||
# Payments and Payment Methods.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PaymentMethod(models.Model):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,17 +4,33 @@ from uncloud_auth.serializers import UserSerializer
 | 
			
		|||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from .models import *
 | 
			
		||||
import uncloud_pay.stripe as uncloud_stripe
 | 
			
		||||
 | 
			
		||||
###
 | 
			
		||||
# 2020-12 Checked code
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StripeCreditCardSerializer(serializers.ModelSerializer):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = StripeCreditCard
 | 
			
		||||
        exclude = ['card_id', "owner" ]
 | 
			
		||||
        exclude = [ "card_id", "owner" ]
 | 
			
		||||
        read_only_fields = [ "last4", "brand", "expiry_date" ]
 | 
			
		||||
 | 
			
		||||
class PaymentSerializer(serializers.ModelSerializer):
 | 
			
		||||
    owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Payment
 | 
			
		||||
        fields = '__all__'
 | 
			
		||||
        read_only_fields = [ "external_reference", "source", "timestamp" ]
 | 
			
		||||
 | 
			
		||||
    def validate(self, data):
 | 
			
		||||
        payment_intent = uncloud_stripe.charge_customer(data['owner'],
 | 
			
		||||
                                                        data['amount'])
 | 
			
		||||
 | 
			
		||||
        data["external_reference"] = payment_intent["id"]
 | 
			
		||||
        data["source"] = "stripe"
 | 
			
		||||
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
| 
						 | 
				
			
			@ -23,10 +39,6 @@ class StripeCreditCardSerializer(serializers.ModelSerializer):
 | 
			
		|||
###
 | 
			
		||||
# Payments and Payment Methods.
 | 
			
		||||
 | 
			
		||||
class PaymentSerializer(serializers.ModelSerializer):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Payment
 | 
			
		||||
        fields = '__all__'
 | 
			
		||||
 | 
			
		||||
class UpdatePaymentMethodSerializer(serializers.ModelSerializer):
 | 
			
		||||
    class Meta:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ import stripe.error
 | 
			
		|||
import logging
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.contrib.auth import get_user_model
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -80,20 +80,6 @@ def get_setup_intent(setup_intent_id):
 | 
			
		|||
def get_payment_method(payment_method_id):
 | 
			
		||||
    return stripe.PaymentMethod.retrieve(payment_method_id)
 | 
			
		||||
 | 
			
		||||
@handle_stripe_error
 | 
			
		||||
def charge_customer(amount, customer_id, card_id):
 | 
			
		||||
    # Amount is in CHF but stripes requires smallest possible unit.
 | 
			
		||||
    # https://stripe.com/docs/api/payment_intents/create#create_payment_intent-amount
 | 
			
		||||
    adjusted_amount = int(amount * 100)
 | 
			
		||||
    return stripe.PaymentIntent.create(
 | 
			
		||||
        amount=adjusted_amount,
 | 
			
		||||
        currency=CURRENCY,
 | 
			
		||||
        customer=customer_id,
 | 
			
		||||
        payment_method=card_id,
 | 
			
		||||
        off_session=True,
 | 
			
		||||
        confirm=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
@handle_stripe_error
 | 
			
		||||
def create_customer(name, email):
 | 
			
		||||
    return stripe.Customer.create(name=name, email=email)
 | 
			
		||||
| 
						 | 
				
			
			@ -111,7 +97,6 @@ def get_customer_cards(customer_id):
 | 
			
		|||
        customer=customer_id,
 | 
			
		||||
        type="card",
 | 
			
		||||
    )
 | 
			
		||||
    print(stripe_cards["data"])
 | 
			
		||||
 | 
			
		||||
    for stripe_card in stripe_cards["data"]:
 | 
			
		||||
        card = {}
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +114,21 @@ def sync_cards_for_user(user):
 | 
			
		|||
    customer_id = get_customer_id_for(user)
 | 
			
		||||
    cards = get_customer_cards(customer_id)
 | 
			
		||||
 | 
			
		||||
    active_cards = StripeCreditCard.objects.filter(owner=user,
 | 
			
		||||
                                                   active=True)
 | 
			
		||||
 | 
			
		||||
    if len(active_cards) > 0:
 | 
			
		||||
        has_active_card = True
 | 
			
		||||
    else:
 | 
			
		||||
        has_active_card = False
 | 
			
		||||
 | 
			
		||||
    for card in cards:
 | 
			
		||||
        active = False
 | 
			
		||||
 | 
			
		||||
        if not has_active_card:
 | 
			
		||||
            active = True
 | 
			
		||||
            has_active_card = True
 | 
			
		||||
 | 
			
		||||
        StripeCreditCard.objects.get_or_create(card_id=card['id'],
 | 
			
		||||
                                               owner = user,
 | 
			
		||||
                                               defaults = {
 | 
			
		||||
| 
						 | 
				
			
			@ -137,6 +136,36 @@ def sync_cards_for_user(user):
 | 
			
		|||
                                                   'brand': card['brand'],
 | 
			
		||||
                                                   'expiry_date': datetime.date(card['year'],
 | 
			
		||||
                                                                                card['month'],
 | 
			
		||||
                                                                                1)
 | 
			
		||||
                                                                                1),
 | 
			
		||||
                                                   'active': active
 | 
			
		||||
                                                   }
 | 
			
		||||
                                               )
 | 
			
		||||
 | 
			
		||||
@handle_stripe_error
 | 
			
		||||
def charge_customer(user, amount, currency='CHF'):
 | 
			
		||||
    # Amount is in CHF but stripes requires smallest possible unit.
 | 
			
		||||
    # https://stripe.com/docs/api/payment_intents/create#create_payment_intent-amount
 | 
			
		||||
    # FIXME: might need to be adjusted for other currencies
 | 
			
		||||
 | 
			
		||||
    if currency == 'CHF':
 | 
			
		||||
        adjusted_amount = int(amount * 100)
 | 
			
		||||
    else:
 | 
			
		||||
        return Exception("Programming error: unsupported currency")
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        card = StripeCreditCard.objects.get(owner=user,
 | 
			
		||||
                                     active=True)
 | 
			
		||||
 | 
			
		||||
    except StripeCreditCard.DoesNotExist:
 | 
			
		||||
        raise ValidationError("No active credit card - cannot create payment")
 | 
			
		||||
 | 
			
		||||
    customer_id = get_customer_id_for(user)
 | 
			
		||||
 | 
			
		||||
    return stripe.PaymentIntent.create(
 | 
			
		||||
        amount=adjusted_amount,
 | 
			
		||||
        currency=currency,
 | 
			
		||||
        customer=customer_id,
 | 
			
		||||
        payment_method=card.card_id,
 | 
			
		||||
        off_session=True,
 | 
			
		||||
        confirm=True,
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,8 +63,7 @@
 | 
			
		|||
		      } else {
 | 
			
		||||
			      // Return to API on success.
 | 
			
		||||
			      document.getElementById("ungleichmessage").innerHTML
 | 
			
		||||
		              = "Registered credit card with
 | 
			
		||||
		      Stripe. <a href="/">Return to the main page.</a>"
 | 
			
		||||
		              = "Registered credit card with Stripe."
 | 
			
		||||
		      }
 | 
			
		||||
	      });
 | 
			
		||||
      });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,17 +68,18 @@ class CreditCardViewSet(mixins.RetrieveModelMixin,
 | 
			
		|||
        return StripeCreditCard.objects.filter(owner=self.request.user)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
###
 | 
			
		||||
# Payments and Payment Methods.
 | 
			
		||||
 | 
			
		||||
class PaymentViewSet(viewsets.ReadOnlyModelViewSet):
 | 
			
		||||
class PaymentViewSet(viewsets.ModelViewSet):
 | 
			
		||||
    serializer_class = PaymentSerializer
 | 
			
		||||
    permission_classes = [permissions.IsAuthenticated]
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        return Payment.objects.filter(owner=self.request.user)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
###
 | 
			
		||||
# Payments and Payment Methods.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OrderViewSet(viewsets.ReadOnlyModelViewSet):
 | 
			
		||||
    serializer_class = OrderSerializer
 | 
			
		||||
    permission_classes = [permissions.IsAuthenticated]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue