Skip to content

Commit

Permalink
Feature/list create stripe cards (#39)
Browse files Browse the repository at this point in the history
* failing tests on create and list cards

* updated create card test

* fixing one test

* fix last test for creating stripe card

* fixing list test and endpoint
  • Loading branch information
hash committed Dec 18, 2017
1 parent 957efe7 commit 0b9b0d4
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 41 deletions.
28 changes: 23 additions & 5 deletions aa_stripe/api.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
import simplejson as json
import stripe
from rest_framework import status
from rest_framework.generics import CreateAPIView, RetrieveAPIView, get_object_or_404
from rest_framework.generics import CreateAPIView, ListCreateAPIView, RetrieveAPIView, get_object_or_404
from rest_framework.mixins import RetrieveModelMixin
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response

from aa_stripe.models import StripeCoupon, StripeCustomer, StripeWebhook
from aa_stripe.serializers import (StripeCouponSerializer, StripeCustomerRetriveSerializer, StripeCustomerSerializer,
StripeWebhookSerializer)
from aa_stripe.models import StripeCard, StripeCoupon, StripeCustomer, StripeWebhook
from aa_stripe.serializers import (StripeCardCreateSerializer, StripeCardListSerializer, StripeCouponSerializer,
StripeCustomerRetriveSerializer, StripeCustomerSerializer, StripeWebhookSerializer)
from aa_stripe.settings import stripe_settings


class StripeCardsAPI(ListCreateAPIView):
queryset = StripeCard.objects.all()
serializer_class = StripeCardListSerializer
permission_classes = (IsAuthenticated,)

def get_queryset(self):
if hasattr(self, "request"):
return self.queryset.filter(
customer=StripeCustomer.get_latest_active_customer_for_user(self.request.user),
is_created_at_stripe=True)
return self.queryset

def get_serializer_class(self):
if hasattr(self, "request") and self.request.method == "POST":
return StripeCardCreateSerializer
return self.serializer_class


class CouponDetailsAPI(RetrieveAPIView):
queryset = StripeCoupon.objects.all()
serializer_class = StripeCouponSerializer
Expand All @@ -20,7 +38,7 @@ class CouponDetailsAPI(RetrieveAPIView):


class CustomersAPI(CreateAPIView, RetrieveModelMixin):
queryset = StripeCustomer.objects.all()
queryset = StripeCustomer.objects.filter(is_created_at_stripe=True)
serializer_class = StripeCustomerSerializer
permission_classes = (IsAuthenticated,)

Expand Down
3 changes: 2 additions & 1 deletion aa_stripe/api_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
from django.conf.urls import url
from rest_framework.routers import DefaultRouter

from aa_stripe.api import CouponDetailsAPI, CustomersAPI, WebhookAPI
from aa_stripe.api import CouponDetailsAPI, CustomersAPI, StripeCardsAPI, WebhookAPI

urlpatterns = [
url(r"^aa-stripe/coupons/(?P<coupon_id>.*)$", CouponDetailsAPI.as_view(), name="stripe-coupon-details"),
url(r"^aa-stripe/customers$", CustomersAPI.as_view(), name="stripe-customers"),
url(r"^aa-stripe/customers/cards$", StripeCardsAPI.as_view(), name="stripe-customers-cards"),
url(r"^aa-stripe/webhooks$", WebhookAPI.as_view(), name="stripe-webhooks")
]

Expand Down
31 changes: 31 additions & 0 deletions aa_stripe/migrations/0019_auto_20171215_0928.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-12-15 14:28
from __future__ import unicode_literals

import jsonfield.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('aa_stripe', '0018_stripecard'),
]

operations = [
migrations.AddField(
model_name='stripecard',
name='is_created_at_stripe',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='stripecard',
name='stripe_js_response',
field=jsonfield.fields.JSONField(default=dict),
),
migrations.AlterField(
model_name='stripecharge',
name='stripe_refund_id',
field=models.CharField(blank=True, db_index=True, max_length=255),
),
]
19 changes: 19 additions & 0 deletions aa_stripe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,13 @@ class Meta:
class StripeCard(SafeDeleteModel, StripeBasicModel):
customer = models.ForeignKey("StripeCustomer", on_delete=models.CASCADE)
stripe_card_id = models.CharField(max_length=255, db_index=True, help_text=_("Unique card id in Stripe"))
stripe_js_response = JSONField()
last4 = models.CharField(max_length=4, help_text=_("Last 4 digits of the card"))
exp_month = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(12)],
help_text=_("Two digit number representing the card’s expiration month"))
exp_year = models.PositiveIntegerField(validators=[MinValueValidator(1900)],
help_text=_("Four digit number representing the card’s expiration year"))
is_created_at_stripe = models.BooleanField(default=False)

objects = SafeDeleteManager()

Expand All @@ -106,6 +108,23 @@ def _retrieve_from_stripe(self, set_deleted=False):
if set_deleted:
self.is_deleted = True

def create_at_stripe(self):
if self.is_created_at_stripe:
raise StripeMethodNotAllowed()

stripe.api_key = stripe_settings.API_KEY
customer = stripe.Customer.retrieve(self.customer.stripe_customer_id)
card = customer.sources.create(source=self.stripe_js_response["source"])

self.stripe_card_id = card["id"]
self.last4 = card["last4"]
self.exp_month = card["exp_month"]
self.exp_year = card["exp_year"]
self.stripe_response = card
self.is_created_at_stripe = True
self.save()
return self

def delete(self, *args, **kwargs):
card = self._retrieve_from_stripe(set_deleted=True)
if card:
Expand Down
49 changes: 42 additions & 7 deletions aa_stripe/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,39 @@
logging.getLogger("aa-stripe")


class StripeCardListSerializer(ModelSerializer):

class Meta:
model = StripeCard
fields = ["stripe_card_id", "last4", "exp_month", "exp_year"]


class StripeCardCreateSerializer(ModelSerializer):
stripe_js_response = JSONField()

def create(self, validated_data):
instance = None
if validated_data.get("stripe_js_response"):
try:
user = self.context['request'].user
stripe_js_response = validated_data.pop("stripe_js_response")
customer = StripeCustomer.get_latest_active_customer_for_user(user)
instance = StripeCard.objects.create(
exp_month=1, exp_year=1900, customer=customer, stripe_js_response=stripe_js_response)
instance.create_at_stripe()
except stripe.StripeError as e:
logging.error(
"[AA-Stripe] creating card failed for user {user.id}: {error}".format(user=user, error=e)
)
raise ValidationError({"stripe_error": e._message})

return instance

class Meta:
model = StripeCard
fields = ["stripe_js_response"]


class StripeCouponSerializer(ModelSerializer):
class Meta:
model = StripeCoupon
Expand All @@ -36,18 +69,20 @@ def create(self, validated_data):
try:
user = self.context['request'].user
stripe_js_response = validated_data.pop("stripe_js_response")
instance = StripeCustomer.objects.create(
user=user, stripe_js_response=stripe_js_response)
instance = StripeCustomer.objects.create(user=user, stripe_js_response=stripe_js_response)
card_data = stripe_js_response["card"]
card = StripeCard.objects.create(
stripe_card_id=card_data["id"], customer=instance, last4=card_data["last4"],
exp_month=card_data["exp_month"], exp_year=card_data["exp_year"], stripe_response=card_data)
stripe_card_id=card_data["id"],
customer=instance,
last4=card_data["last4"],
exp_month=card_data["exp_month"],
exp_year=card_data["exp_year"],
stripe_response=card_data)
instance.default_card = card # .create_at_stripe() will call .save()
instance.create_at_stripe()
except stripe.StripeError as e:
logging.error(
"[AA-Stripe] creating customer failed for user {user.id}: {error}".format(user=user, error=e)
)
logging.error("[AA-Stripe] creating customer failed for user {user.id}: {error}".format(
user=user, error=e))
raise ValidationError({"stripe_error": e._message})

return instance
Expand Down

0 comments on commit 0b9b0d4

Please sign in to comment.