Skip to content

Commit

Permalink
Implements handling created and updated customer.source
Browse files Browse the repository at this point in the history
  • Loading branch information
Bart committed Jan 5, 2018
1 parent caf14f0 commit 3b78fee
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 10 deletions.
39 changes: 38 additions & 1 deletion aa_stripe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class Meta:

@python_2_unicode_compatible
class StripeCard(SafeDeleteModel, StripeBasicModel):
STRIPE_UPDATABLE_FIELDS = set(["exp_month", "exp_year"])
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()
Expand Down Expand Up @@ -125,6 +126,16 @@ def create_at_stripe(self):
self.save()
return self

def sync_with_stripe(self):
card = self._retrieve_from_stripe(True)
if card:
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
return not self.is_deleted

def delete(self, *args, **kwargs):
card = self._retrieve_from_stripe(set_deleted=True)
if card:
Expand Down Expand Up @@ -584,7 +595,33 @@ def _parse_coupon_notification(self, action):
StripeCoupon.objects.filter(coupon_id=coupon_id, created=created).delete()

def _parse_customer_source_notification(self, action):
pass
stripe_card_id = self.raw_data["data"]["object"]["id"]
stripe_customer_id = self.raw_data["data"]["object"]["customer"]
try:
customer = StripeCustomer.objects.get(stripe_customer_id=stripe_customer_id)
except StripeCustomer.DoesNotExist:
raise StripeWebhookParseError(
_("There is no customer with id that is assigned to this card in our database"))
if action == "created":
try:
StripeCard.objects.all_with_deleted().get(stripe_card_id=stripe_card_id, customer=customer)
except StripeCard.DoesNotExist:
card = StripeCard(stripe_card_id=stripe_card_id, customer=customer)
if card.sync_with_stripe():
card.save()
else:
raise StripeWebhookParseError(_("Card with this id does not exists at Stripe API"))
else:
raise StripeWebhookParseError(_("Card with this id and customer already exists"))
elif action == "updated":
try:
card = StripeCard.objects.get(stripe_card_id=stripe_card_id, customer=customer)
for updated_field in StripeCard.STRIPE_UPDATABLE_FIELDS & set(
self.raw_data["data"]["previous_attributes"]):
setattr(card, updated_field, self.raw_data["data"]["object"][updated_field])
card.save()
except StripeCard.DoesNotExist:
pass # do not update if does not exist

def parse(self, save=False):
if self.is_parsed:
Expand Down
106 changes: 97 additions & 9 deletions tests/test_webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from aa_stripe.exceptions import StripeWebhookAlreadyParsed
from aa_stripe.management.commands.check_pending_webhooks import StripePendingWebooksLimitExceeded
from aa_stripe.models import StripeCoupon, StripeWebhook, StripeCard
from aa_stripe.models import StripeCard, StripeCoupon, StripeWebhook
from aa_stripe.settings import stripe_settings


Expand Down Expand Up @@ -395,7 +395,8 @@ def test_coupon_delete(self, m):
with self.assertRaises(StripeWebhookAlreadyParsed):
webhook.parse()

def test_customer_source_create(self):
@requests_mock.Mocker()
def test_customer_source_create(self, m):
self.assertEqual(StripeWebhook.objects.count(), 0)
payload = json.loads('''{
"api_version": "2017-08-15",
Expand Down Expand Up @@ -437,17 +438,105 @@ def test_customer_source_create(self):
},
"type": "customer.source.created"
}''')
payload["id"] = "evt_1"
stripe_customer_response = json.loads('''{
"id": "cus_BxAF4YEKGafO4G",
"object": "customer",
"account_balance": 0,
"created": 1515145927,
"currency": "usd",
"default_source": null,
"delinquent": false,
"description": null,
"discount": null,
"email": null,
"livemode": false,
"metadata": {
},
"shipping": null,
"sources": {
"object": "list",
"data": [
{
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": null,
"address_zip_check": null,
"brand": "Visa",
"country": "US",
"customer": "cus_BxAF4YEKGafO4G",
"cvc_check": "pass",
"dynamic_last4": null,
"exp_month": 4,
"exp_year": 2024,
"fingerprint": "DsGmBIQwiNOvChPk",
"funding": "credit",
"id": "card_1BZFvCLoWm2f6pRwtPCp9r8W",
"last4": "4242",
"metadata": {},
"name": null,
"object": "card",
"tokenization_method": null
}
],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_BxAF4YEKGafO4G/sources"
},
"subscriptions": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_BxAF4YEKGafO4G/subscriptions"
}
}''')
stripe_response = json.loads('''{
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": null,
"address_zip_check": null,
"brand": "Visa",
"country": "US",
"customer": "cus_BxAF4YEKGafO4G",
"cvc_check": "pass",
"dynamic_last4": null,
"exp_month": 4,
"exp_year": 2024,
"fingerprint": "DsGmBIQwiNOvChPk",
"funding": "credit",
"id": "card_1BZFvCLoWm2f6pRwtPCp9r8W",
"last4": "4242",
"metadata": {},
"name": null,
"object": "card",
"tokenization_method": null
}''')
url = reverse("stripe-webhooks")

self.client.credentials(**self._get_signature_headers(payload))
m.register_uri(
"GET",
"https://api.stripe.com/v1/customers/cus_BxAF4YEKGafO4G",
text=json.dumps(stripe_customer_response))
m.register_uri(
"GET",
"https://api.stripe.com/v1/customers/cus_BxAF4YEKGafO4G/sources/card_1BZFvCLoWm2f6pRwtPCp9r8W",
text=json.dumps(stripe_response))
response = self.client.post(url, data=payload, format="json")
self.assertEqual(StripeWebhook.objects.count(), 1)
self.assertEqual(response.status_code, 201)
# there is no customer with id cus_BxAF4YEKGafO4G yet, so no card should be created
self.assertFalse(StripeCard.objects.filter(stripe_card_id=payload["data"]["object"]["id"]).exists())

payload["id"] = "evt_2"
payload["id"] = "evt_1"
self._create_user()
self._create_customer("cus_BxAF4YEKGafO4G")
self.client.credentials(**self._get_signature_headers(payload))
Expand Down Expand Up @@ -506,7 +595,6 @@ def test_customer_source_update(self):
},
"type": "customer.source.updated"
}''')
payload["id"] = "evt_1"
url = reverse("stripe-webhooks")

self.client.credentials(**self._get_signature_headers(payload))
Expand All @@ -517,7 +605,7 @@ def test_customer_source_update(self):
self.assertEqual(card.exp_year, 2024)
self.assertEqual(card.exp_month, 4)

payload["id"] = "evt_2"
payload["id"] = "evt_1"
self.card.stripe_card_id = "card_1BXobrLoWm2f6pRwXOAv0OOW"
self.card.save()
self.client.credentials(**self._get_signature_headers(payload))
Expand All @@ -528,7 +616,7 @@ def test_customer_source_update(self):
self.assertEqual(card.exp_year, 2024)
self.assertEqual(card.exp_month, 4)

payload["id"] = "evt_3"
payload["id"] = "evt_2"
self.customer.stripe_customer_id = "cus_BvfxAxtzApkqRa"
self.customer.save()
self.client.credentials(**self._get_signature_headers(payload))
Expand All @@ -539,7 +627,7 @@ def test_customer_source_update(self):
self.assertEqual(card.exp_year, 2020)
self.assertEqual(card.exp_month, 4)

payload["id"] = "evt_4"
payload["id"] = "evt_3"
payload["data"]["object"]["exp_month"] = 6
payload["data"]["object"]["exp_year"] = 2023
payload["data"]["previous_attributes"]["exp_month"] = 4
Expand All @@ -554,7 +642,7 @@ def test_customer_source_update(self):
self.assertEqual(card.exp_year, 2023)
self.assertEqual(card.exp_month, 6)

payload["id"] = "evt_5"
payload["id"] = "evt_4"
payload["data"]["object"]["exp_month"] = 8
payload["data"]["previous_attributes"]["exp_month"] = 6
del payload["data"]["previous_attributes"]["exp_year"]
Expand Down

0 comments on commit 3b78fee

Please sign in to comment.