Skip to content

Commit

Permalink
Merge pull request #305 from jambonsw/duplicate_customer
Browse files Browse the repository at this point in the history
Added protection against creating duplicate customers.
  • Loading branch information
paltman authored Dec 21, 2016
2 parents 1f8d937 + 94b28dc commit acc400e
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 5 deletions.
20 changes: 15 additions & 5 deletions pinax/stripe/actions/customers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.db import IntegrityError, transaction
from django.utils import timezone
from django.utils.encoding import smart_str

Expand Down Expand Up @@ -28,7 +29,9 @@ def can_charge(customer):

def create(user, card=None, plan=settings.PINAX_STRIPE_DEFAULT_PLAN, charge_immediately=True):
"""
Creates a Stripe customer
Creates a Stripe customer.
If a customer already exists, the existing customer will be returned.
Args:
user: a user object
Expand All @@ -48,10 +51,17 @@ def create(user, card=None, plan=settings.PINAX_STRIPE_DEFAULT_PLAN, charge_imme
plan=plan,
trial_end=trial_end
)
cus = models.Customer.objects.create(
user=user,
stripe_id=stripe_customer["id"]
)
try:
with transaction.atomic():
cus = models.Customer.objects.create(
user=user,
stripe_id=stripe_customer["id"]
)
except IntegrityError:
# There is already a Customer object for this user
stripe.Customer.retrieve(stripe_customer["id"]).delete()
return models.Customer.objects.get(user=user)

sync_customer(cus, stripe_customer)

if plan and charge_immediately:
Expand Down
30 changes: 30 additions & 0 deletions pinax/stripe/tests/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,36 @@ def test_customer_create_user_only(self, CreateMock, SyncMock):
self.assertIsNone(kwargs["trial_end"])
self.assertTrue(SyncMock.called)

@patch("stripe.Customer.retrieve")
@patch("stripe.Customer.create")
def test_customer_create_user_duplicate(self, CreateMock, RetrieveMock):
# Create an existing database customer for this user
original = Customer.objects.create(user=self.user, stripe_id='cus_XXXXX')

new_customer = Mock()
RetrieveMock.return_value = new_customer

# customers.Create will return a new customer instance
CreateMock.return_value = dict(id="cus_YYYYY")
customer = customers.create(self.user)

# But only one customer will exist - the original one
self.assertEqual(Customer.objects.count(), 1)
self.assertEqual(customer.stripe_id, original.stripe_id)

# Check that the customer hasn't been modified
self.assertEqual(customer.user, self.user)
self.assertEqual(customer.stripe_id, "cus_XXXXX")
_, kwargs = CreateMock.call_args
self.assertEqual(kwargs["email"], self.user.email)
self.assertIsNone(kwargs["source"])
self.assertIsNone(kwargs["plan"])
self.assertIsNone(kwargs["trial_end"])

# But a customer *was* created, retrieved, and then disposed of.
RetrieveMock.assert_called_once_with("cus_YYYYY")
new_customer.delete.assert_called_once()

@patch("pinax.stripe.actions.invoices.create_and_pay")
@patch("pinax.stripe.actions.customers.sync_customer")
@patch("stripe.Customer.create")
Expand Down

0 comments on commit acc400e

Please sign in to comment.