Initial stripe playground

This commit is contained in:
fnux 2020-03-02 22:26:40 +01:00
parent c651c4ddaa
commit 4ad737ed90
7 changed files with 124 additions and 3 deletions

View file

@ -14,4 +14,7 @@ LDAP_ADMIN_DN=""
LDAP_ADMIN_PASSWORD="" LDAP_ADMIN_PASSWORD=""
LDAP_SERVER_URI = "" LDAP_SERVER_URI = ""
# Stripe (Credit Card payments)
STRIPE_API_key=""
SECRET_KEY="dx$iqt=lc&yrp^!z5$ay^%g5lhx1y3bcu=jg(jx0yj0ogkfqvf" SECRET_KEY="dx$iqt=lc&yrp^!z5$ay^%g5lhx1y3bcu=jg(jx0yj0ogkfqvf"

View file

@ -176,3 +176,8 @@ USE_TZ = True
STATIC_URL = '/static/' STATIC_URL = '/static/'
stripe.api_key = uncloud.secrets.STRIPE_KEY stripe.api_key = uncloud.secrets.STRIPE_KEY
############
# Stripe
STRIPE_API_KEY = uncloud.secrets.STRIPE_API_KEY

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-03-02 20:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0012_orderrecord'),
]
operations = [
migrations.AddField(
model_name='paymentmethod',
name='stripe_card_id',
field=models.CharField(blank=True, max_length=32, null=True),
),
]

View file

@ -143,10 +143,13 @@ class PaymentMethod(models.Model):
description = models.TextField() description = models.TextField()
primary = models.BooleanField(default=True) primary = models.BooleanField(default=True)
# Only used for "Stripe" source
stripe_card_id = models.CharField(max_length=32, blank=True, null=True)
def charge(self, amount): def charge(self, amount):
if amount > 0: # Make sure we don't charge negative amount by errors... if amount > 0: # Make sure we don't charge negative amount by errors...
if self.source == 'stripe': if self.source == 'stripe':
# TODO: wire to strip, see meooow-payv1/strip_utils.py # TODO: wire to stripe, see meooow-payv1/strip_utils.py
payment = Payment(owner=self.owner, source=self.source, amount=amount) payment = Payment(owner=self.owner, source=self.source, amount=amount)
payment.save() # TODO: Check return status payment.save() # TODO: Check return status

View file

@ -7,6 +7,8 @@ from functools import reduce
from uncloud_vm.serializers import VMProductSerializer from uncloud_vm.serializers import VMProductSerializer
from uncloud_vm.models import VMProduct from uncloud_vm.models import VMProduct
import uncloud_pay.stripe as stripe
# TODO: remove magic numbers for decimal fields # TODO: remove magic numbers for decimal fields
class BillRecordSerializer(serializers.Serializer): class BillRecordSerializer(serializers.Serializer):
order = serializers.CharField() order = serializers.CharField()
@ -27,10 +29,35 @@ class PaymentSerializer(serializers.ModelSerializer):
model = Payment model = Payment
fields = ['owner', 'amount', 'source', 'timestamp'] fields = ['owner', 'amount', 'source', 'timestamp']
class CreditCardSerializer(serializers.Serializer):
number = serializers.IntegerField()
exp_month = serializers.IntegerField()
exp_year = serializers.IntegerField()
cvc = serializers.IntegerField()
class PaymentMethodSerializer(serializers.ModelSerializer): class PaymentMethodSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = PaymentMethod model = PaymentMethod
fields = '__all__' fields = ['source', 'description', 'primary']
class CreatePaymentMethodSerializer(serializers.ModelSerializer):
credit_card = CreditCardSerializer()
class Meta:
model = PaymentMethod
fields = ['source', 'description', 'primary', 'credit_card']
def create(self, validated_data):
credit_card = stripe.CreditCard(**validated_data.pop('credit_card'))
user = self.context['request'].user
customer = stripe.create_customer(user.username, user.email)
# TODO check customer error
customer_id = customer['response_object']['id']
stripe_card = stripe.create_card(customer_id, credit_card)
# TODO: check credit card error
validated_data['stripe_card_id'] = stripe_card['response_object']['id']
payment_method = PaymentMethod.objects.create(**validated_data)
return payment_method
class ProductSerializer(serializers.Serializer): class ProductSerializer(serializers.Serializer):
vms = VMProductSerializer(many=True, read_only=True) vms = VMProductSerializer(many=True, read_only=True)

View file

@ -1,5 +1,16 @@
import stripe import stripe
import stripe.error
import logging
from django.conf import settings
# Static stripe configuration used below.
CURRENCY = 'chf'
# Register stripe (secret) API key from config.
stripe.api_key = settings.STRIPE_API_KEY
# Helper (decorator) used to catch errors raised by stripe logic.
def handle_stripe_error(f): def handle_stripe_error(f):
def handle_problems(*args, **kwargs): def handle_problems(*args, **kwargs):
response = { response = {
@ -53,3 +64,51 @@ def handle_stripe_error(f):
return response return response
return handle_problems return handle_problems
# Convenience CC container, also used for serialization.
class CreditCard():
number = None
exp_year = None
exp_month = None
cvc = None
def __init__(self, number, exp_month, exp_year, cvc):
self.number=number
self.exp_year = exp_year
self.exp_month = exp_month
self.cvc = cvc
# Actual Stripe logic.
@handle_stripe_error
def create_card(customer_id, credit_card):
# Test settings
credit_card.number = "5555555555554444"
return stripe.Customer.create_source(
customer_id,
card={
'number': credit_card.number,
'exp_month': credit_card.exp_month,
'exp_year': credit_card.exp_year,
'cvc': credit_card.cvc
})
@handle_stripe_error
def get_card(customer_id, card_id):
return stripe.Card.retrieve_source(customer_id, card_id)
@handle_stripe_error
def charge_customer(amount, source):
return stripe.Charge.create(
amount=amount,
currenty=CURRENCY,
source=source)
@handle_stripe_error
def create_customer(name, email):
return stripe.Customer.create(name=name, email=email)
@handle_stripe_error
def get_customer(customer_id):
return stripe.Customer.retrieve(customer_id)

View file

@ -57,9 +57,15 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
return get_user_model().objects.all() return get_user_model().objects.all()
class PaymentMethodViewSet(viewsets.ModelViewSet): class PaymentMethodViewSet(viewsets.ModelViewSet):
serializer_class = PaymentMethodSerializer
permission_classes = [permissions.IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
def get_serializer_class(self):
if self.action == 'create':
return CreatePaymentMethodSerializer
else:
return PaymentMethodSerializer
def get_queryset(self): def get_queryset(self):
return PaymentMethod.objects.filter(owner=self.request.user) return PaymentMethod.objects.filter(owner=self.request.user)