forked from uncloud/uncloud
Allow for charging customers
This commit is contained in:
parent
94a39ed81d
commit
e0cb6ac670
4 changed files with 32 additions and 20 deletions
|
@ -86,16 +86,19 @@ class PaymentMethod(models.Model):
|
||||||
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 stripe, see meooow-payv1/strip_utils.py
|
stripe_customer = StripeCustomer.objects.get(owner=self.owner).stripe_id
|
||||||
|
charge_request = uncloud_pay.stripe.charge_customer(amount, stripe_customer, self.stripe_card_id)
|
||||||
|
if charge_request['error'] == None:
|
||||||
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
|
||||||
|
|
||||||
return True
|
return payment
|
||||||
else:
|
else:
|
||||||
# We do not handle that source yet.
|
raise Exception('Stripe error: {}'.format(charge_request['error']))
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
return False
|
raise Exception('This payment method is unsupported/cannot be charged.')
|
||||||
|
else:
|
||||||
|
raise Exception('Cannot charge negative amount.')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [['owner', 'primary']]
|
unique_together = [['owner', 'primary']]
|
||||||
|
|
|
@ -38,7 +38,10 @@ class PaymentMethodSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PaymentMethod
|
model = PaymentMethod
|
||||||
fields = ['source', 'description', 'primary', 'stripe_card_last4']
|
fields = ['uuid', 'source', 'description', 'primary', 'stripe_card_last4']
|
||||||
|
|
||||||
|
class ChargePaymentMethodSerializer(serializers.Serializer):
|
||||||
|
amount = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||||
|
|
||||||
class CreditCardSerializer(serializers.Serializer):
|
class CreditCardSerializer(serializers.Serializer):
|
||||||
number = serializers.IntegerField()
|
number = serializers.IntegerField()
|
||||||
|
|
|
@ -21,7 +21,7 @@ def handle_stripe_error(f):
|
||||||
'error': None
|
'error': None
|
||||||
}
|
}
|
||||||
|
|
||||||
common_message = "Currently it's not possible to make payments."
|
common_message = "Currently it is not possible to make payments."
|
||||||
try:
|
try:
|
||||||
response_object = f(*args, **kwargs)
|
response_object = f(*args, **kwargs)
|
||||||
response = {
|
response = {
|
||||||
|
@ -114,11 +114,15 @@ def get_card(customer_id, card_id):
|
||||||
return stripe.Customer.retrieve_source(customer_id, card_id)
|
return stripe.Customer.retrieve_source(customer_id, card_id)
|
||||||
|
|
||||||
@handle_stripe_error
|
@handle_stripe_error
|
||||||
def charge_customer(amount, source):
|
def charge_customer(amount, customer_id, card_id):
|
||||||
|
# Amount is in CHF but stripes requires smallest possible unit.
|
||||||
|
# See https://stripe.com/docs/api/charges/create
|
||||||
|
adjusted_amount = int(amount * 100)
|
||||||
return stripe.Charge.create(
|
return stripe.Charge.create(
|
||||||
amount=amount,
|
amount=adjusted_amount,
|
||||||
currenty=CURRENCY,
|
currency=CURRENCY,
|
||||||
source=source)
|
customer=customer_id,
|
||||||
|
source=card_id)
|
||||||
|
|
||||||
@handle_stripe_error
|
@handle_stripe_error
|
||||||
def create_customer(name, email):
|
def create_customer(name, email):
|
||||||
|
|
|
@ -63,6 +63,8 @@ class PaymentMethodViewSet(viewsets.ModelViewSet):
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
if self.action == 'create':
|
if self.action == 'create':
|
||||||
return CreatePaymentMethodSerializer
|
return CreatePaymentMethodSerializer
|
||||||
|
elif self.action == 'charge':
|
||||||
|
return ChargePaymentMethodSerializer
|
||||||
else:
|
else:
|
||||||
return PaymentMethodSerializer
|
return PaymentMethodSerializer
|
||||||
|
|
||||||
|
@ -99,18 +101,18 @@ class PaymentMethodViewSet(viewsets.ModelViewSet):
|
||||||
output_serializer = PaymentMethodSerializer(payment_method)
|
output_serializer = PaymentMethodSerializer(payment_method)
|
||||||
return Response(output_serializer.data)
|
return Response(output_serializer.data)
|
||||||
|
|
||||||
# TODO: find a way to customize serializer for actions.
|
|
||||||
# drf-action-serializer module seems to do that.
|
|
||||||
@action(detail=True, methods=['post'])
|
@action(detail=True, methods=['post'])
|
||||||
def charge(self, request, pk=None):
|
def charge(self, request, pk=None):
|
||||||
payment_method = self.get_object()
|
payment_method = self.get_object()
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
amount = serializer.data['amount']
|
amount = serializer.validated_data['amount']
|
||||||
if payment_method.charge(amount):
|
try:
|
||||||
return Response({'charged', amount})
|
payment = payment_method.charge(amount)
|
||||||
else:
|
output_serializer = PaymentSerializer(payment)
|
||||||
return Response(status=status.HTTP_500_INTERNAL_ERROR)
|
return Response(output_serializer.data)
|
||||||
|
except Exception as e:
|
||||||
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
###
|
###
|
||||||
# Admin views.
|
# Admin views.
|
||||||
|
|
Loading…
Reference in a new issue