Initial stripe playground
This commit is contained in:
parent
c651c4ddaa
commit
4ad737ed90
7 changed files with 124 additions and 3 deletions
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue