Skip to content

Commit

Permalink
CR
Browse files Browse the repository at this point in the history
  • Loading branch information
poxip committed Sep 7, 2017
1 parent ef53809 commit 6bbf698
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 25 deletions.
10 changes: 1 addition & 9 deletions aa_stripe/api.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import simplejson as json
import stripe
from django.utils.translation import ugettext_lazy as _
from rest_framework import status
from rest_framework.generics import CreateAPIView, RetrieveAPIView
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response

from aa_stripe.exceptions import StripeCouponAlreadyExists
from aa_stripe.models import StripeCoupon, StripeCustomer, StripeWebhook
from aa_stripe.serializers import StripeCouponSerializer, StripeCustomerSerializer, StripeWebhookSerializer
from aa_stripe.settings import stripe_settings
Expand Down Expand Up @@ -54,13 +52,7 @@ def post(self, request, *args, **kwargs):
return Response(status=400, data={"message": "already received"})
except StripeWebhook.DoesNotExist:
# correct, first time. Create webhook
try:
webhook = StripeWebhook.objects.create(id=event["id"], raw_data=data["raw_data"])
except stripe.error.InvalidRequestError:
# API must return 2xx to let Stripe know that the webhook has been parsed
return Response({"coupon_id": _("Coupon with this coupon_id does not exists at Stripe API")})
except StripeCouponAlreadyExists:
return Response({"coupon_id": _("Coupon with this coupon_id already exists")})
webhook = StripeWebhook.objects.create(id=event["id"], raw_data=data["raw_data"])

serializer = self.serializer_class(webhook)

Expand Down
4 changes: 4 additions & 0 deletions aa_stripe/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,9 @@ class StripeWebhookAlreadyParsed(Exception):
details = _("This webhook has already been parsed")


class StripeWebhookParseError(Exception):
details = _("Unable to parse webhook")


class StripeCouponAlreadyExists(Exception):
details = _("Coupon with this coupon_id and creation date already exists")
20 changes: 20 additions & 0 deletions aa_stripe/migrations/0011_auto_20170907_1201.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-09-07 16:01
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('aa_stripe', '0010_auto_20170822_1004'),
]

operations = [
migrations.AddField(
model_name='stripewebhook',
name='parse_error',
field=models.TextField(blank=True),
),
]
34 changes: 25 additions & 9 deletions aa_stripe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from django.utils.translation import ugettext_lazy as _
from jsonfield import JSONField

from aa_stripe.exceptions import StripeCouponAlreadyExists, StripeMethodNotAllowed, StripeWebhookAlreadyParsed
from aa_stripe.exceptions import (StripeCouponAlreadyExists, StripeMethodNotAllowed, StripeWebhookAlreadyParsed,
StripeWebhookParseError)
from aa_stripe.settings import stripe_settings
from aa_stripe.utils import timestamp_to_timezone_aware_date

Expand Down Expand Up @@ -466,20 +467,32 @@ class StripeWebhook(models.Model):
updated = models.DateTimeField(auto_now=True)
is_parsed = models.BooleanField(default=False)
raw_data = JSONField()
parse_error = models.TextField(blank=True)

def _parse_coupon_notification(self, action):
coupon_id = self.raw_data["data"]["object"]["id"]
created = timestamp_to_timezone_aware_date(self.raw_data["data"]["object"]["created"])
if action == "created":
if StripeCoupon.objects.filter(coupon_id=coupon_id, created=created).exists():
raise StripeCouponAlreadyExists

StripeCoupon(coupon_id=coupon_id).save(force_retrieve=True)
try:
StripeCoupon.objects.get(coupon_id=coupon_id, created=created)
except StripeCoupon.DoesNotExist:
try:
StripeCoupon(coupon_id=coupon_id).save(force_retrieve=True)
except stripe.error.InvalidRequestError:
raise StripeWebhookParseError(_("Coupon with this coupon_id does not exists at Stripe API"))
except StripeCouponAlreadyExists as e:
raise StripeWebhookParseError(e.details)
else:
raise StripeWebhookParseError(StripeCouponAlreadyExists.details)
elif action == "updated":
StripeCoupon.objects.filter(coupon_id=coupon_id, created=created, is_deleted=False).update(
metadata=self.raw_data["data"]["object"]["metadata"])
try:
coupon = StripeCoupon.objects.get(coupon_id=coupon_id, created=created, is_deleted=False)
coupon.metadata = self.raw_data["data"]["object"]["metadata"]
super(StripeCoupon, coupon).save() # use the super method not to call Stripe API
except StripeCoupon.DoesNotExist:
pass # do not update if does not exist
elif action == "deleted":
StripeCoupon.objects.filter(coupon_id=coupon_id, created=created).update(is_deleted=True)
StripeCoupon.objects.filter(coupon_id=coupon_id, created=created, is_deleted=False).delete()

def parse(self, save=False):
if self.is_parsed:
Expand All @@ -506,7 +519,10 @@ def parse(self, save=False):

def save(self, *args, **kwargs):
if not self.is_parsed:
self.parse()
try:
self.parse()
except StripeWebhookParseError as e:
self.parse_error = str(e)

return super(StripeWebhook, self).save(*args, **kwargs)

Expand Down
31 changes: 24 additions & 7 deletions tests/test_webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,7 @@ def test_coupon_create(self):
self.client.credentials(**self._get_signature_headers(payload))
self.assertEqual(StripeCoupon.objects.filter(coupon_id="doesnotexist").count(), 0)
response = self.client.post(url, data=payload, format="json")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["coupon_id"], "Coupon with this coupon_id does not exists at Stripe API")
self.assertEqual(response.status_code, 201)
self.assertEqual(StripeCoupon.objects.filter(coupon_id="doesnotexist").count(), 0)

# test receiving coupon.created to a coupon that already exists in our database
Expand All @@ -231,8 +230,9 @@ def test_coupon_create(self):
self.assertEqual(coupon_qs.count(), 1)
self.client.credentials(**self._get_signature_headers(payload))
response = self.client.post(url, data=payload, format="json")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["coupon_id"], "Coupon with this coupon_id already exists")
self.assertEqual(response.status_code, 201)
self.assertEqual(StripeWebhook.objects.get(id=response.data["id"]).parse_error,
"Coupon with this coupon_id and creation date already exists")
self.assertEqual(coupon_qs.count(), 1)

# test receiving coupon.created to a coupon that already exists in our
Expand Down Expand Up @@ -263,8 +263,9 @@ def test_coupon_create(self):
self.assertEqual(coupon_qs.count(), 2)
self.client.credentials(**self._get_signature_headers(payload))
response = self.client.post(url, data=payload, format="json")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["coupon_id"], "Coupon with this coupon_id already exists")
self.assertEqual(response.status_code, 201)
self.assertEqual(StripeWebhook.objects.get(id=response.data["id"]).parse_error,
"Coupon with this coupon_id and creation date already exists")
self.assertEqual(coupon_qs.count(), 2)

def test_coupon_update(self):
Expand Down Expand Up @@ -323,7 +324,18 @@ def test_coupon_update(self):
"lol2": "yeah"
})

def test_coupon_delete(self):
# test updating non existing coupon - nothing else than saving the webhook should happen
payload["id"] = "evt_1"
payload["request"]["id"] = "req_1"
payload["data"]["object"]["id"] = "doesnotexist"
self.assertFalse(StripeCoupon.objects.filter(coupon_id="doesnotexist").exists())
self.client.credentials(**self._get_signature_headers(payload))
response = self.client.post(url, data=payload, format="json")
self.assertEqual(response.status_code, 201)
self.assertFalse(StripeCoupon.objects.filter(coupon_id="doesnotexist").exists())

@requests_mock.Mocker()
def test_coupon_delete(self, m):
coupon = self._create_coupon("nicecoupon", amount_off=100, duration=StripeCoupon.DURATION_ONCE)
self.assertFalse(coupon.is_deleted)
payload = json.loads("""{
Expand Down Expand Up @@ -359,6 +371,11 @@ def test_coupon_delete(self):
"type": "coupon.deleted"
}""")
payload["data"]["object"]["created"] = coupon.stripe_response["created"]
m.register_uri("GET", "https://api.stripe.com/v1/coupons/nicecoupon", text=json.dumps({
"error": {
"type": "invalid_request_error"
}
}))

url = reverse("stripe-webhooks")
self.client.credentials(**self._get_signature_headers(payload))
Expand Down

0 comments on commit 6bbf698

Please sign in to comment.